You can get pretty far with variadic templates and some templates / virtual methods. With the following codes you can do something like:
std::string select_string (bool cond, std::string a, std::string b) { return cond ? a : b; } int main () { Registry reg; reg.set ("select_it", select_string); reg.invoke ("select_it", "1 John Wayne")); reg.invoke ("select_it", "0 John Wayne")); }
exit:
John Wayne
Full implementation:
These codes are exemplary. You must optimize it to ensure perfect forwarding of less redundancy while expanding the list of options.
Headers and test function
#include <functional> #include <string> #include <sstream> #include <istream> #include <iostream> #include <tuple> std::string select_string (bool cond, std::string a, std::string b) { return cond ? a : b; }
This helps us parse the string and put the results in a tuple:
//---------------------------------------------------------------------------------- template <typename Tuple, int Curr, int Max> struct init_args_helper; template <typename Tuple, int Max> struct init_args_helper<Tuple, Max, Max> { void operator() (Tuple &, std::istream &) {} }; template <typename Tuple, int Curr, int Max> struct init_args_helper { void operator() (Tuple &tup, std::istream &is) { is >> std::get<Curr>(tup); return init_args_helper<Tuple, Curr+1, Max>() (tup, is); } }; template <int Max, typename Tuple> void init_args (Tuple &tup, std::istream &ss) { init_args_helper<Tuple, 0, Max>() (tup, ss); }
This expands the function pointer and tuple in the function call (by function pointer):
//---------------------------------------------------------------------------------- template <int ParamIndex, int Max, typename Ret, typename ...Args> struct unfold_helper; template <int Max, typename Ret, typename ...Args> struct unfold_helper<Max, Max, Ret, Args...> { template <typename Tuple, typename ...Params> Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params) { return fun (params...); } }; template <int ParamIndex, int Max, typename Ret, typename ...Args> struct unfold_helper { template <typename Tuple, typename ...Params> Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params) { return unfold_helper<ParamIndex+1, Max, Ret, Args...> (). unfold(fun, tup, params..., std::get<ParamIndex>(tup)); } }; template <typename Ret, typename ...Args> Ret unfold (Ret (*fun) (Args...), std::tuple<Args...> tup) { return unfold_helper<0, sizeof...(Args), Ret, Args...> ().unfold(fun, tup); }
This function combines:
Here is our test:
int main () { std::cout << foo (select_string, "0 John Wayne") << '\n'; std::cout << foo (select_string, "1 John Wayne") << '\n'; }
Warning: the code needs additional verification during parsing and should use std::function<> instead of a bare function pointer
Based on the above code, just write a registry function:
class FunMeta { public: virtual ~FunMeta () {} virtual boost::any call (std::string args) const = 0; }; template <typename Ret, typename ...Args> class ConcreteFunMeta : public FunMeta { public: ConcreteFunMeta (Ret (*fun) (Args...)) : fun(fun) {} boost::any call (std::string args) const {
You might even think about supporting function overloading with this, using multimap and dispatching solutions based on what content the arguments passed in are.
Here's how to use it:
int main () { Registry reg; reg.set ("select_it", select_string); std::cout << boost::any_cast<std::string> (reg.invoke ("select_it", "0 John Wayne")) << '\n' << boost::any_cast<std::string> (reg.invoke ("select_it", "1 John Wayne")) << '\n'; }