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

So, imagine that we had 2 functions (void : ( void ) ) and (std::string : (int, std::string)) , and we could have 10 more. All (or some of them) accept different types of arguments and can return different types. We want to save them in std::map , so we get the API as follows:

 //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 in the same container. In particular, how to do this in C ++ 03 using Boost.

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

+3
source share
4 answers
 #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); } }; 

Using:

 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); } 

http://ideone.com/SXAPu Not very pretty. It’s better to advise you not to do anything even remotely, like the whosttever you are trying to do.

Please note: this requires that all functions with the same parameters return the same type.

+7
source

You can use boost :: any ...

 #include <boost/any.hpp> #include <iostream> #include <map> #include <string> void voidFunc() { std::cout << "void called" << std::endl; } void stringFunc(std::string str) { std::cout << str << std::endl; } int main() { std::map<std::string, boost::any> funcs; funcs.insert(std::pair<std::string, boost::any>("voidFunc", &voidFunc)); funcs.insert(std::pair<std::string, boost::any>("stringFunc", &stringFunc)); boost::any_cast<void(*)(void)>(funcs["voidFunc"])(); boost::any_cast<void(*)(std::string)>(funcs["stringFunc"])("hello"); return 0; } 

Please note that you will get a runtime exception if you did not specify the correct function signature in any_cast.

+4
source

The fact is that when you call your functions, you already know what type they will be.

If we do something like

 int x = map["key"](1, "2") 

we can already deduce that any function stored in the "key" is of type (int (*)(int, char*)) , so we could do something like

 int x = map_of_int_and_string_to_int["key"](1, "2"); 

and avoid all the problems of combining all the keys together ... Although it is true that C ++ has some overload functions specifically for this kind of thing, I cannot understand why you should worry in this particular case.

And in the end, why do you want to transfer all these functions to the same card? They do not have similar interfaces, so you cannot even access them, you cannot iterate over them, and you cannot easily transfer them to someone else. Without anything in common, you cannot do anything with the functions on this hypothetical map.

+2
source

You can do this by pointing function pointers to void pointers and vice versa. It is expected that you recognize the signature at runtime, so it will not be a problem to tightly bind casting operators. However, the logic of this eludes me. This makes no sense, at least in C ++. Using template classes / functions or function pointer structures makes much more sense.

for example with templates:

 template <typename X> foo(X param1) { /* do something with param1*/}; template <typename X, typename Y> foo(X param1, Y param2) {/* do something with 2 params*/}; template <int X> foo(X param1) { /* only one parameter, which is int */}; 

Now:

 foo(5); // calls the third one foo("5"); // calls the first one foo("5", 5); // calls the second one. 

Who needs a card?

0
source

Source: https://habr.com/ru/post/1387271/


All Articles