Dynamically creating a C++ function argument list at runtime
我试图在运行时生成函数调用的参数列表,但我想不出在C++中实现这一点的方法。
这是给我正在写的助手库的。我通过网络从客户机获取输入数据,并使用该数据调用用户先前设置的函数指针。该函数接受一个字符串(标记,类似于printf)和不同数量的参数。我需要的是根据从客户机接收到的数据添加更多参数的方法。
我将函数存储在函数指针的映射中
1 2 | typedef void (*varying_args_fp)(string,...); map<string,varying_args_fp> func_map; |
一个示例用法是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | void printall(string tokens, ...) { va_list a_list; va_start(a_list, tokens); for each(auto x in tokens) { if (x == 'i') { cout <<"Int:" << va_arg(a_list, int) << ' '; } else if(x == 'c') { cout <<"Char:" << va_arg(a_list, char) << ' '; } } va_end(a_list); } func_map["printall"] = printall; func_map["printall"]("iic",5,10,'x'); // prints"Int: 5 Int: 10 Char: x" |
号
当硬编码函数调用和它的参数时,这很好地工作,但是如果我已经收到数据"createx 10 20",程序需要能够使参数调用本身。如
1 2 | // func_name ="CreateX", tokens = 'ii', first_arg = 10, second_arg = 20 func_map[func_name](tokens,first_arg,second_arg); |
我无法预测用户将如何布置函数并事先对此进行编码。
如果有人对以其他方式完成这项任务有建议,请随时提出建议。我需要用户能够将一个函数"绑定"到库中,并且库在从联网客户机接收到数据后稍后调用它,这本质上是一个回调。
这里是一个C++ 11解决方案。它不支持像
另一方面,此方法支持所有(?)其他功能统一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <vector> #include <iostream> #include <functional> #include <stdexcept> #include <string> #include <boost/any.hpp> template <typename Ret, typename... Args> Ret callfunc (std::function<Ret(Args...)> func, std::vector<boost::any> anyargs); template <typename Ret> Ret callfunc (std::function<Ret()> func, std::vector<boost::any> anyargs) { if (anyargs.size() > 0) throw std::runtime_error("oops, argument list too long"); return func(); } template <typename Ret, typename Arg0, typename... Args> Ret callfunc (std::function<Ret(Arg0, Args...)> func, std::vector<boost::any> anyargs) { if (anyargs.size() == 0) throw std::runtime_error("oops, argument list too short"); Arg0 arg0 = boost::any_cast<Arg0>(anyargs[0]); anyargs.erase(anyargs.begin()); std::function<Ret(Args... args)> lambda = ([=](Args... args) -> Ret { return func(arg0, args...); }); return callfunc (lambda, anyargs); } template <typename Ret, typename... Args> std::function<boost::any(std::vector<boost::any>)> adaptfunc (Ret (*func)(Args...)) { std::function<Ret(Args...)> stdfunc = func; std::function<boost::any(std::vector<boost::any>)> result = ([=](std::vector<boost::any> anyargs) -> boost::any { return boost::any(callfunc(stdfunc, anyargs)); }); return result; } |
基本上,您称为
实际调用时检查参数类型及其编号。
不支持返回
这是一个测试驱动程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | int func1 (int a) { std::cout <<"func1(" << a <<") ="; return 33; } int func2 (double a, std::string b) { std::cout <<"func2(" << a <<","" << b <<"") ="; return 7; } int func3 (std::string a, double b) { std::cout <<"func3(" << a <<","" << b <<"") ="; return 7; } int func4 (int a, int b) { std::cout <<"func4(" << a <<"," << b <<") ="; return a+b; } int main () { std::vector<std::function<boost::any(std::vector<boost::any>)>> fcs = { adaptfunc(func1), adaptfunc(func2), adaptfunc(func3), adaptfunc(func4) }; std::vector<std::vector<boost::any>> args = {{777}, {66.6, std::string("yeah right")}, {std::string("whatever"), 0.123}, {3, 2}}; // correct calls will succeed for (int i = 0; i < fcs.size(); ++i) std::cout << boost::any_cast<int>(fcs[i](args[i])) << std::endl; // incorrect calls will throw for (int i = 0; i < fcs.size(); ++i) try { std::cout << boost::any_cast<int>(fcs[i](args[fcs.size()-1-i])) << std::endl; } catch (std::exception& e) { std::cout <<"Could not call, got exception:" << e.what() << std::endl; } } |
。
正如@tonythelion已经提到的,您可以使用
1 2 | typedef std::function<void(const std::string&, const std::vector<boost::variant<char, int>>&)> varying_args_fn; std::map<std::string, varying_args_fn> func_map; |
您可以使用静态访问者来区分类型。下面是一个完整的示例,请注意,实际上不再需要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <map> #include <vector> #include <string> #include <functional> #include <iostream> #include <boost/variant.hpp> #include <boost/any.hpp> typedef std::function<void(const std::string&, const std::vector<boost::variant<char, int>>&)> varying_args_fn; void printall(const std::string& tokens, const std::vector<boost::variant<char, int>>& args) { for (const auto& x : args) { struct : boost::static_visitor<> { void operator()(int i) { std::cout <<"Int:" << i << ' '; } void operator()(char c) { std::cout <<"Char:" << c << ' '; } } visitor; boost::apply_visitor(visitor, x); } } int main() { std::map<std::string, varying_args_fn> func_map; func_map["printall"] = printall; func_map["printall"]("iic", {5, 10, 'x'}); } |
号