Search code examples
c++function-pointersreinterpret-cast

Is there a way to hold many type of pointer to function without reinterpret_cast?


I have some code that deal with many pointer to function of different signature. Here's a snippet:

#include <unordered_map>

// Simple type id system
template<typename> void type_id(){}
using type_id_t = void(*)();

// value type of our map. We reinterpret functions pointer to this type
using held_type = void(*)();

// actual function type.
template<typename T>
using funct_type = T(*)();

int main() {
    std::unordered_map<type_id_t, held_type> function_map;

    function_map.emplace(type_id<int>, reinterpret_cast<held_type>(
        static_cast<func_type<int>>([]() -> int { return 42; });
    ));

    function_map.emplace(type_id<double>, reinterpret_cast<held_type>(
        static_cast<func_type<double>>([]() -> double { return 9.4; });
    ));

    // later on

    // prints 42
    std::cout << reinterpret_cast<func_type<int>>(function_map[type_id<int>])();

    // prints 9.4
    std::cout << reinterpret_cast<func_type<double>>(function_map[type_id<double>])();
}        

Is there a way to achieve similar result without significant overhead and without reinterpret casts?


Solution

  • If you can rework a bit your functions, you could approach it by reversing the flow and removing the return type.
    Here is an example of what I mean:

    #include <unordered_map>
    #include <iostream>
    
    template<typename T>
    void call(T t, void *ptr) {
        *static_cast<T *>(ptr) = t;
    }
    
    // Simple type id system
    template<typename> void type_id() {}
    using type_id_t = void(*)();
    
    // value type of our map. We reinterpret functions pointer to this type
    using held_type = void(*)(void *);
    
    // actual function type.
    template<typename T>
    using funct_type = T(*)();
    
    int main() {
        std::unordered_map<type_id_t, held_type> function_map;
    
        function_map.emplace(type_id<int>, +[](void *ptr){ return call(42, ptr); });
        function_map.emplace(type_id<double>, +[](void *ptr) { call(9.4, ptr); });
    
        // prints 42
        int i;
        function_map[type_id<int>](&i);
        std::cout << i << std::endl;
    
        // prints 9.4
        double d;
        function_map[type_id<double>](&d);
        std::cout << d << std::endl;
    }
    

    See it up and running on wandbox.

    Probably you can use the type of the variable to pick the right specialization of type_id up when you query function_map. Just hide everything behind a function template.