在运行时动态创建C++函数参数列表

Tre*_*ent 5 c++ function-pointers function

我试图在运行时为函数调用生成一个参数列表,但我想不出在c ++中实现这一点的方法.

这是我写的助手库.我通过网络从客户端获取输入数据,并使用该数据调用用户先前设置的函数指针.该函数采用一个字符串(类似于printf的标记)和不同数量的参数.我需要的是根据从客户端收到的数据添加更多参数的方法.

我将函数存储在函数指针的映射中

typedef void (*varying_args_fp)(string,...);
map<string,varying_args_fp> func_map;
Run Code Online (Sandbox Code Playgroud)

一个示例用法是

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"
Run Code Online (Sandbox Code Playgroud)

当硬编码函数调用及其参数时,这很好用,但如果我收到了数据"CreateX 10 20",程序需要能够自己调用参数.例如

// func_name = "CreateX", tokens = 'ii', first_arg = 10, second_arg = 20
func_map[func_name](tokens,first_arg,second_arg);
Run Code Online (Sandbox Code Playgroud)

我无法预测用户将如何预先布置功能和编码.

如果有人建议以另一种方式完成这项任务,请随时提出建议.我需要用户能够将函数"绑定"到库中,并且库在以后从网络客户端接收数据后调用它,本质上是回调.

n. *_* m. 6

这是一个C++ 11解决方案.它支持可变参数的功能,如printallprintf,这是不可能用这种技术和IMO不可能在所有的,或至少是非常棘手.无论如何,这样的功能很难在像你这样的环境中安全使用,因为来自任何客户端的任何不良请求都可能导致服务器崩溃,完全没有任何追索权.您可能应该转向基于容器的界面以获得更好的安全性和稳定性.

另一方面,该方法统一支持所有(?)其他函数.

#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;
}
Run Code Online (Sandbox Code Playgroud)

基本上你打电话adaptfunc(your_function),哪里your_function是任何类型的功能(除了varargs).作为回报,你得到一个std::function接受向量的对象boost::any并返回一个boost::any.你把这个物体放在你的func_map身上,或者做任何你想要的东西.

在实际调用时检查参数的类型及其编号.

函数返回void不支持开箱即用,因为boost::any<void>不支持.这可以通过将返回类型包装在一个简单的模板中并专门用于处理void.为了清楚起见,我把它留了下来.

这是一个测试驱动程序:

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;
        }
}
Run Code Online (Sandbox Code Playgroud)