Creating an unordered_map of std::functions with any arguments?

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?

Answer

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

Live demo

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

Leave a Reply

Your email address will not be published. Required fields are marked *