Reputation: 1181
I am trying to create a unordered map of std::functions
. Where a key is a string where you will look up the function you want to call and the function is the value.
I have written a small program:
#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>
void func1()
{
std::cout << "Function1 has been called." << std::endl;
}
int doMaths(int a)
{
return a + 10;
}
int main()
{
std::unordered_map<std::string,std::function<void()>> myMap;
myMap["func1"] = func1;
}
This compiles just fine, and I can call the function (however I am not sure if this is the correct way of doing it) by placing:
auto mapIter = myMap.find("func1");
auto mapVal = mapIter->second;
mapVal();
That then calls the function, however I think this is at the cost of creating a new copy of the function? Please correct me if I am wrong about that.
However, if I try to do: myMap["doMaths"] = doMaths;
I get a compiler error since the value in myMap
is std::function<void()>>
and not std::function<int(int)>>
. I did get this to compile when I did: myMap["doMaths"] = std::bind(doMaths,int());
However I do not know what that actually does. And when I try to call it in the same manner as func1
, I get a compiler error.
So I guess I have two questions:
How do I create an unordered_map that will take any type of function for it's value? And how do I call the function within the map without having to make a copy of the function?
Upvotes: 6
Views: 6105
Reputation: 25388
As eerorika says, you can do this with a map of std::any
. Here is some example code, showing also how you call the function pointers stored in the map:
#include <iostream>
#include <unordered_map>
#include <string>
#include <any>
void func1()
{
std::cout << "Function1 has been called.\n";
}
int doMaths(int a)
{
std::cout << "doMaths has been called, a = " << a << "\n";
return a + 10;
}
int main()
{
std::unordered_map<std::string,std::any> myMap;
myMap["func1"] = func1;
auto mapIter = myMap.find("func1");
std::any_cast <void (*) ()> (mapIter->second) ();
myMap["doMaths"] = doMaths;
mapIter = myMap.find("doMaths");
int result = std::any_cast <int (*) (int)> (mapIter->second) (5);
std::cout << result;
}
std::any_cast
will throw a std::bad_any_cast
exception at runtime if the types (or, in this case, function signatures) don't match.
Please note: std::any
requires C++17, see: https://en.cppreference.com/w/cpp/utility/any
Upvotes: 7
Reputation: 119867
If you know that the type of your function is std::function<void(void)>
, put it in a std::unordered_map<std::string,std::function<void()>>
, and look it up there.
If you know that the type of your function is std::function<int(double, char)>
, put it in a std::unordered_map<std::string,std::function<int(double, char)>>
, and look it up there.
If you don't know the type of your function, you cannot use it, so it is also useless to store it in a map.
If you have more than one type of functions, also have more than one map. A variable template (or a function template with a static map variable) can help with having any number of such maps without duplicating any code. Of course this way you only can have a global map. A class that can manage such a collection of maps could be a bit more involved, but only a bit.
Upvotes: 1
Reputation: 1231
Like Paul said, but with std::function
syntax. You can use this to put functions with any signature in your map, even lambdas :D
#include <vector>
#include <iostream>
#include <functional>
#include <map>
#include <any>
int doMaths(int a)
{
std::cout << "doMaths has been called, a = " << a << "\n";
return a + 10;
}
int main()
{
std::map<std::string, std::any> map;
map["foo"] = std::function<int(int)>([](int a) { return a * 2; });
map["bar"] = std::function<int(int, int)>([](int a, int b) { return a + b; });
map["maths"] = std::function<int(int)>(doMaths);
int a = std::any_cast<std::function<int(int)>>(map["foo"])(4);
int b = std::any_cast<std::function<int(int, int)>>(map["bar"])(4, 5);
int c = std::any_cast<std::function<int(int)>>(map["maths"])(5);
std::cout << a << " " << b << " " << c <<std::endl;
}
Just be careful when you cast, you'll need to know the correct signature and return type.
Upvotes: 1