You need to replace the ???
with placeholders, to forward the arguments of the closure to the function.
std::bind(&BeliefCondFunc::Greater, _1, _2);
You should note however, that your map holds function objects that accept no arguments. So you'll likely get a call mismatch error. Since C++14, probably even on the attempted assignment into the newly constructed value.
I also feel you over complicate the solution. For static member functions, a simple function pointer type can suffice. You'd then need to preform some casting, however that can be encapsulated in another templated static member function. Now, casting function pointer types isn't very safe (think about what happens when you get the number arguments wrong). But you seem intent on sidestepping the type system already, so here it is:
#include <unordered_map>
#include <string>
#include <utility>
struct BeliefCondFunc
{
using cb_type = bool(*)();
static std::unordered_map<std::string, cb_type> const FuncMap;
static bool Greater(int A, int B)
{
return A > B;
}
static bool Between(float A, float B, float C)
{
return A > B && A < C;
}
template<typename ...Args>
static bool call(std::string const& key, Args&&... args){
using prototype = bool(*)(Args...);
return reinterpret_cast<prototype>(FuncMap.at(key))(std::forward<Args>(args)...);
};
};
std::unordered_map<std::string, BeliefCondFunc::cb_type> const BeliefCondFunc::FuncMap {
{"Greater", reinterpret_cast<cb_type>(&BeliefCondFunc::Greater) },
{"Between", reinterpret_cast<cb_type>(&BeliefCondFunc::Between) }
};
int main() {
BeliefCondFunc::call("Greater", 1, 2);
return 0;
}
See it live
If the casting worries you, you can store the function pointers prototype's std::type_info
pointer alongside with it. Then it's a simple matter of comparing the two and throwing an exception upon a mismatch at run time. Like so:
#include <unordered_map>
#include <string>
#include <utility>
#include <typeinfo>
#include <stdexcept>
#include <iostream>
struct BeliefCondFunc
{
using cb_type = bool(*)();
using map_val = std::pair<cb_type, std::type_info const*>;
static std::unordered_map<std::string, map_val> const FuncMap;
static bool Greater(int A, int B)
{
return A > B;
}
static bool Between(float A, float B, float C)
{
return A > B && A < C;
}
template<typename ...Args>
static map_val make_map_val(bool (*func)(Args...)) {
return std::make_pair(reinterpret_cast<cb_type>(func),
&typeid(decltype(func)));
}
template<typename ...Args>
static bool call(std::string const& key, Args&&... args){
using prototype = bool(*)(Args...);
auto& func_n_typeid = FuncMap.at(key);
if (typeid(prototype) != *func_n_typeid.second )
throw std::domain_error("Prototype mismatch");
return reinterpret_cast<prototype>(func_n_typeid.first)(std::forward<Args>(args)...);
};
};
std::unordered_map<std::string, BeliefCondFunc::map_val> const BeliefCondFunc::FuncMap {
{"Greater", make_map_val(&BeliefCondFunc::Greater) },
{"Between", make_map_val(&BeliefCondFunc::Between) }
};
int main() {
BeliefCondFunc::call("Greater", 1, 2);
try {
BeliefCondFunc::call("Lesser", 1, 2);
} catch (std::out_of_range&) {
std::cout << "No such function\n";
}
try {
BeliefCondFunc::call("Between", 1, 2);
} catch (std::domain_error&) {
std::cout << "Wrong number of arguments\n";
}
return 0;
}
Once again, see it live.
And as promised, an explanation:
The following two lines create two type aliases (c++11 syntax, equivalent to a typedef, but clearly separates the new identifier from the type). I name the dummy callback type I'll store all pointers as, and the pair of values I'll store in the map.
using cb_type = bool(*)();
using map_val = std::pair<cb_type, std::type_info const*>;
make_map_val
is a template function that can be passed any function pointer which returns a bool
. Whenever I call it with a function pointer, it uses template argument deduction to figure out the argument types of the function. I don't the argument types directly, but instead rely on decltype
to get the type of the function. In retrospect, I could have just passed the function pointer directly to typeid
, since it would have deduced the type the same way. The importance of using a tempalted function here, is to capture as much static type data about the function pointer automatically, without asking the call site of make_map_val
for it.
call
again uses template argument type deduction to figure out the types of parameters for the function we want to call. A type alias is also used to conveniently reference the function pointer type we will ultimately cast to, as well as the parameter for typeid
. The alias and function call itself are written using parameter pack expansion. The parameters are passed to the function with a forwarding reference, and onward to the function pointer via a call to std::forward
, all to preserve the value category of the parameter. If all your functions operate only on small, cheap to copy values, you could probably get away with passing everything by value.