Map function to a particular value in C++

Imagine a simple task. You have a bunch of values, for example, strings (but it can be any other object, int, float, whatever..) and you a have a bunch of functions, (more generally callables, so it can be a functor, lambda.. etc.) and you want to call particular function based on a given value.

How do you do that? We will see in a moment.

It is pretty straightforward with the use of std::map, std::function and std::bind.

When declaring a map you have to specify key, value pair as types std::map<key_type,value_type>. Value type cannot be a function as is, so first we need to wrap the plain function into a std::function class.

void jarda(){std::cout << "hi i am  jarda\n";}
void pepa(){std::cout << "hi i am pepa\n";}
void milan(){std::cout << "hi i am milan\n";}
std::function<void()> f_pepa = pepa;
std::function<void()> f_milan = milan;
std::function<void()> f_jarda = jarda;

The important part is, that inside of std::function the return type (void in our example) and the parameters for the particular function, (non in this case) has to match. std::function<void()> f_pepa = pepa creates an object called “f_pepa” from function pepa that has void as return type and takes no parameters.

Now, map those to a string of your choice.

std::map<std::string, std::function<void()>> map_of_function;
map_of_function.insert({ {"pepa",f_pepa}, {"milan",f_milan}, {"jarda",f_jarda} });

Here I create a map with key as a std::string and a value as the std::function<void()>>, and fill that using insert overload taking initialization list. That is it. Now I have a map with string=>function mapping.

Let's say, based upon some logic I want to call a function that is mapped to the key “milan”. What do I do?

Simple.

map_of_function.at("milan")();
//alternatively
map_of_function["milan"]();

Milan function will be called. Don't omit the parens (), if you do, nothing will happen.

Now, let's say I need to map functions taking some parameters.

int add(int a, int b) { return a + b; }
int subtracts(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
double divide(double a, double b) { return a / b; }
std::function<int(int, int)> f_add = add;
std::function<int(int, int)> f_sub = subtracts;
std::function<double(double, double)> f_div = divide;
std::function<int(int, int)> f_mult = multiply;

Calling the same way as before, except I put the parameters into to parens ()

std::cout << map_of_func["add"](100, 8) << "\n";
std::cout << map_of_func["div"](100, 8) << "\n";
for (auto i : map_of_func) std::cout << i.second(100, 8) << "\n";
for(int i{}; i < 10; ++i) std::cout << map_of_func["add"](i, 10) << "\n";

But what if you have functions that take a variable number of arguents and variable types. Something like this

void one_arg(int a) { std::cout << a << "\n"; }
void two_args(int a, double b) { std::cout << a << " " << b << "\n"; }
void three_args(int a, double b, const char* c) { std::cout << a << " " << b << " " << c << "\n"; }

To map these functions, you need to use std::bind to get the same type of std::function object, that will be passed as a value to the map. This way

std::function<void()> f_onearg = std::bind(one_arg, 10);
std::function<void()> f_twoargs = std::bind(two_args, 10, 3.14);
std::function<void()> f_threeargs = std::bind(three_args, 10, 3.14, "helo");

Now all the three functions are wrapped in the same object type, to create a map is as simple as before.

std::map<std::string, std::function<void()>> map_of_func{ {"one", f_onearg}, {"two", f_twoargs}, {"three", f_threeargs} };
//call
map_of_func["one"]();
map_of_func["two"]();  
map_of_func["three"]();