在运行时动态创建一个C ++函数参数列表 [英] Dynamically creating a C++ function argument list at runtime

查看:157
本文介绍了在运行时动态创建一个C ++函数参数列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

p>

  typedef void(* varying_args_fp)(string,...); 
map< string,varying_args_fp> func_map;

一个示例用法是

<$ p $ void printall(string tokens,...)
{
va_list a_list;
va_start(a_list,tokens); (x =='i')
{
cout <<< b


(代币中的自动x)
{
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');
//打印Int:5 Int:10 Char:x

当对函数调用进行硬编码并且它是参数时,但是如果我收到了CreateX 10 20的数据,程序需要能够自动调用参数。例如

  // func_name =CreateX,tokens ='ii',first_arg = 10,second_arg = 20 
func_map [FUNC_NAME](令牌,first_arg,second_arg);

我无法预测用户如何布置函数并预先编写代码。 p>

如果任何人有另一种方式完成此任务的建议,请随时提出建议。我需要用户能够将一个函数绑定到库中,并让库在它从联网客户端接收到数据后进行调用,本质上是回调。

解决方案

这是一个C ++ 11解决方案。它不支持可变参数函数,如 printall printf ,这是不可能的国际海事组织根本不可能,或者至少非常棘手。这样的功能很难在你的环境中安全地使用,因为来自任何客户端的任何不良请求都可能导致服务器崩溃,绝对没有任何追索权。另一方面,这种方法统一支持所有(?)其他函数。

另一方面,这种方法可以统一支持所有(?)其他函数。

  #include< vector> 
#include< iostream>
#include< functional>
#include< stdexcept>
#include< string>
#include< boost / any.hpp>


模板< typename Ret,typename ... Args>
Ret callfunc(std :: function< Ret(Args ...)> func,std :: vector< boost :: any> anyargs);

模板< 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);
返回func();
}

模板< 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 ...);
});
返回callfunc(lambda,anyargs);
}

模板< 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));
});
返回结果;

基本上你可以调用 adaptfunc(your_function),其中 your_function 是任何类型的函数(可变参数除外)。作为回报,你会得到一个 std :: function 对象,它接受一个 boost :: any 向量并返回一个的boost ::任何。你把这个对象放在 func_map 中,或者用它们做任何你想做的事。



参数的类型和他们的号码在实际调用时被检查。

返回 void 的函数不支持开箱即用,因为不支持 boost :: any< void> 。这可以通过将返回类型包装在一个简单的模板中并专门用于 void 来轻松处理。我清楚地表明了这一点。



以下是一个测试驱动程序:

  int func1(int a)
{
std :: cout<< func1(<< a<<)=;
返回33;
}

int func2(double a,std :: string b)
{
std :: cout<< func2(<< a<<,\<<<<<"")=;
返回7;
}

int func3(std :: string a,double b)
{
std :: cout<< func3(<< a<<,\<<<<<"")=;
返回7;
}

int func4(int a,int b)
{
std :: cout<< func4(<< a<<,<< b<<)=;
返回a + b;
}


int main()
{
std :: vector< std :: function< boost :: any(std :: vector<升压::任何>)>> 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}};

//正确调用将成功
for(int i = 0; i< fcs.size(); ++ i)
std :: cout<< boost :: any_cast< int>(fcs [i](args [i]))<<的std :: ENDL;

//错误的调用会为(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<< 无法呼叫,得到异常:<< e.what()<<的std :: ENDL;
}
}


I'm trying to generate an argument list for a function call during runtime, but I can't think of a way to accomplish this in c++.

This is for a helper library I'm writing. I'm taking input data from the client over a network and using that data to make a call to a function pointer that the user has set previously. The function takes a string(of tokens, akin to printf), and a varying amount of arguments. What I need is a way to add more arguments depending on what data has been received from the client.

I'm storing the functions in a map of function pointers

typedef void (*varying_args_fp)(string,...);
map<string,varying_args_fp> func_map;

An example usage would be

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"

This works nicely when hardcoding the function call and it's arguments, but if I've received the data "CreateX 10 20", the program needs to be able to make the argument call itself. eg

// func_name = "CreateX", tokens = 'ii', first_arg = 10, second_arg = 20
func_map[func_name](tokens,first_arg,second_arg);

I can't predict how users are going to lay out the functions and code this beforehand.

If anyone has suggestions on accomplishing this task another way, feel free to suggest. I need the user to be able to "bind" a function to the library, and for the library to call it later after it has received data from a networked client, a callback in essence.

解决方案

Here is a C++11 solution. It does not support varargs functions like printall or printf, this is impossible with this technique and IMO impossible at all, or at the very least extremely tricky. Such function are difficult to use safely in an environment like yours anyway, since any bad request from any client could crash the server, with absolutely no recourse whatsoever. You probably should move to container-based interface for better safety and stability.

On the other hand, this method supports all (?) other functions uniformly.

#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;
}

Basically you call adaptfunc(your_function), where your_function is a function of any type (except varargs). In return you get an std::function object that accepts a vector of boost::any and returns a boost::any. You put this object in your func_map, or do whatever else you want with them.

Types of the arguments and their number are checked at the time of actual call.

Functions returning void are not supported out of the box, because boost::any<void> is not supported. This can be dealt with easily by wrapping the return type in a simple template and specializing for void. I've left it out for clarity.

Here's a test driver:

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;
        }
}

这篇关于在运行时动态创建一个C ++函数参数列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆