Search code examples
c++boost

How to store functional objects with different signatures in a container?


So imagine we had 2 functions (void : ( void ) ) and (std::string : (int, std::string)) and we could have 10 more. All (or some of them) take in different argument types and can return different types. We want to store them in a std::map, so we get an API like this:

//Having a functions like:
int hello_world(std::string name, const int & number )
{
    name += "!";
    std::cout << "Hello, " << name << std::endl;
    return number;
}
//and
void i_do_shadowed_stuff()
{
    return;
}

//We want to be capable to create a map (or some type with similar API) that would hold our functional objects. like so:
myMap.insert(std::pair<std::string, fun_object>("my_method_hello", hello_world) )
myMap.insert(std::pair<std::string, fun_object>("my_void_method", i_do_shadowed_stuff) )
//And we could call tham with params if needed:
int a = myMap["my_method_hello"]("Tim", 25);
myMap["my_void_method"];

I wonder how to put many different functions into the same container. Specifically, how to do this in C++03 using Boost.

The API should be independent from the actual function types (having int a = myMap["my_method_hello"]("Tim", 25); not int a = myMap<int, (std::string, int)>["my_method_hello"]("Tim", 25);).


Solution

  • #include <functional>
    #include <iostream>
    #include <string>
    #include <map>
    
    class api {
        // maps containing the different function pointers
        typedef void(*voidfuncptr)();
        typedef int(*stringcrintptr)(std::string, const int&);
    
        std::map<std::string, voidfuncptr> voida;
        std::map<std::string, stringcrintptr> stringcrint;
    public:
        // api temp class
        // given an api and a name, it converts to a function pointer
        // depending on parameters used
        class apitemp {
            const std::string n;
            const api* p;
        public:
            apitemp(const std::string& name, const api* parent)
                : n(name), p(parent) {}
            operator voidfuncptr()
            { return p->voida.find(n)->second; }
            operator stringcrintptr()
            { return p->stringcrint.find(n)->second; }
        };
    
        // insertion of new functions into appropriate maps
        void insert(const std::string& name, voidfuncptr ptr)
        { voida[name]=ptr; }
        void insert(const std::string& name, stringcrintptr ptr)
        { stringcrint[name]=ptr; }
        // operator[] for the name gets halfway to the right function
        apitemp operator[](std::string n) const
        { return apitemp(n, this); }
    };
    

    Usage:

    api myMap; 
    
    int hello_world(std::string name, const int & number )
    {
        name += "!";
        std::cout << "Hello, " << name << std::endl;
        return number;
    }
    
    int main()
    {
        myMap.insert("my_method_hello", &hello_world );
        int a = myMap["my_method_hello"]("Tim", 25);
    }
    

    Not very pretty. Better advice is to not do anything even remotely like whatever it is you're trying to do.

    Note that this requires all functions with the same parameters to return the same type.