C++:调用从文本文件解析参数列表的函数的方法

发布于 2024-11-25 10:53:58 字数 342 浏览 0 评论 0原文

我希望允许从有点像脚本的文本文件调用程序中的函数。

我希望能够使用这个“脚本管理器”注册任何函数,而不强制它符合某些特定的签名。因此能够从脚本调用:MyFunc(bool, string) 或 MyFunc2(int, float, char)。在解析方面,我可以将这些参数放入参数列表中,但问题是如何将这些参数传递给函数?

我不能像 MyFunc(paramlist[0], paramlist[1]) 那样调用它,因为这会强制使用特定的签名。我也不希望被调用的函数需要了解“脚本管理器”,因此它们不需要能够处理参数列表。

如何解耦这两个组件(被调用的函数和“脚本管理器”)而不在前者(被调用的函数)周围编写一些包装器?

I'm looking to allow functions within my programme to be called from text file that is somewhat like a script.

I'd like to be able to register any function with this "script manager" without forcing it to conform to some specific signature. Hence being able to call from the script: MyFunc(bool, string) or MyFunc2(int, float, char). On the parsing side I can put these parameters into a parameter list but the problem is how can I pass these parameters to the function?

I cannot call it like MyFunc(paramlist[0], paramlist[1]) since that forces a specific signature. I also do not want the functions being called to be required to know about "script manager" and thus they should not need to be able to handle the parameter lists.

How can I decouple these two components (the functions being called and the "script manager") without writing some wrapper around the former (the functions being called)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

撧情箌佬 2024-12-02 10:53:58

当然你可以自己做类似的事情,但我认为最好使用 Lua (http://lua.org)。 Lua 与 c/c++ 程序集成度很高,并且有大量全面的文档和教程可用;例如,看看这个: http://csl.sublevel3.org/lua/ (注意“从Lua调用C函数”一节,这就是你想要的)。

Of course you can do stuff like that yourself, but I think that it is better just to use Lua (http://lua.org ). Lua integrates greatly with c/c++ programs and there is a lot of comprehensive documentation and tutorials available; for example, have a look at this one: http://csl.sublevel3.org/lua/ (pay attention to the section "calling C functions from Lua", this is what you want).

旧夏天 2024-12-02 10:53:58

和其他人一样,我建议您如果可以避免的话,就不要重新发明轮子。也就是说,如果可以的话,使用现有的框架。但我将稍微扩展一下可能的解决方案。

C++ 没有反射,这意味着您无法在运行时接受函数指针、检查它并解释参数是什么。但您可以在编译时执行此操作。解决方案并不简单,但可以通过类型擦除来完成。为了简化问题,我假设所有函数都返回 void ,这样就足够复杂了。

在一个潜在的解决方案中,调度程序结构可能如下所示(错误被忽略):

while (true) {
   std::string func = read_function_name();
   std::vector<parameter_t> params = read_parameters();
   functions[ func ]( params );
}

现在填写空白: read_function_name 是一个简单的函数,它返回要调用的函数的名称。不是功能,只是名称。 read_parameters 是一个处理输入文件并构建要传递给该函数的参数序列的函数。 functions 是一个关联容器,它将函数名称映射到我们的函数之一。这些参数是实际参数类型的某种类型擦除,以便我们可以在单个容器中通用地管理它们。我们的函数是一个类的实例,该类实现了一个operator(),该运算符采用一系列参数并对要调用的确切函数执行类型擦除。很简单!至少如果你忽略细节的话。

参数的实现并不太复杂,您可以只使用 boost::any 并希望得到最好的结果,或者您可以使用更多信息来实现您自己的类型擦除,以便您可以执行更好的错误检测,或者隐式转换或者...你能想到的。

function_t 类型的实现有点复杂。我们需要对可调用的实际元素执行类型擦除,而 std::function<> 适合这里的问题。但我们不能使用它,因为这会给我们留下尽可能小的函数接口:operator()你知道参数,而我们拥有的是operator () 谁知道参数

这是大部分工作要做的地方,并且您需要手动类型擦除。这可以通过一个基抽象类 function_base 来实现,该类提供了一个虚拟运算符()( std::vector[const] & ) [const] (play具有常量性或暂时忘记它以简化问题)。 function_t 将仅保存指向该基类型的指针,并执行调用。到这里还是很简单。

函数类型擦除的实现...现在下一个问题是如何实现来自 function_base 的每个具体派生类型,并且事情变得有点棘手(我们同意其余的很简单,没有不是吗?)您需要在 function_t 中提供一个构造函数模板,它将接受任何函数,实例化从 function_base 派生的模板化类型并存储里面的指针function_t

再次为了避免复杂性,假设您可以使用单个参数来完成。剩下的只是更多代码... function_impl<> 的实现只需要存储原始函数指针,并记住参数的类型和数量(在本例中为 1)。 operator() 的实现迭代参数向量,并且对于每个参数,它都会取消删除类型(转换回原始类型)并调用该函数。该代码在模板中必须是手动编写的,但对于相同数量的所有功能来说,它是可重用的。

为了让事情变得更简单,您可以使用函数特征库,它将能够从您收到的函数中提取参数的类型和数量。

那么用户代码还剩下什么呢?这很简单,在这种情况下我就是这么说的。您的接口应该提供function_t,它可以从任何函数指针构造(只要它符合您在那里实现的要求),以及一个要注册的函数通过提供名称来在您的系统中添加任何此类 function_t

// user code: registration of a new function
void print_single_int( int );
lib::function_t f( &print_single_int );
lib::register( "print_single_int", f );

必须为要添加到系统中的每个函数键入这两行。当然,您可能会认为解决方案的复杂性并不能解决问题,因此会选择手动实现,因为用户代码只是创建手动编码的函数库派生,该派生是手动实现的处理参数向量并调用函数...如果您想在脚本语言中实现很少的操作,则可能不值得付出额外的努力使其通用。

As others, I recommend that you do not reinvent the wheel if you can avoid it. That is, use an existing framework if you can. But I am going to extend a little bit into a possible solution.

C++ does not have reflection, which means that you cannot, at runtime, accept a function pointer, inspect it and interpret what the arguments are. But you can do that at compile time. The solution will not be simple, but it can be done with type erasure. To simplify the problem, I will assume that all functions return void it is complex enough like that.

In one potential solution the dispatcher structure may look like (errors being ignored):

while (true) {
   std::string func = read_function_name();
   std::vector<parameter_t> params = read_parameters();
   functions[ func ]( params );
}

Now to fill in the blanks: read_function_name is a simple function that returns the name of the function to call. Not the function, just the name. read_parameters is a function that processes the input file and builds a sequence of parameters to be passed to the function. functions is an associative container that maps a function name to one of our functions. The parameters are some sort of type erasure of the actual parameter types, so that we can manage them generically in a single container. Our functions are instances of a class that that implements an operator() that takes one sequence of parameters and performs type erasure on the exact function to call. That was simple! At least if you ignore the details.

Implementation of parameters is not too complex, you could just use boost::any and hope for the best, or you could implement your own type erasure with a bit more information so that you can perform better error detection, or implicit conversions or... you name it.

Implementation of the function_t type is a bit more complex. We need to perform type erasure on the actual element that is callable, and std::function<> fits the problem up to here. But we cannot use it because that will leave us with the smallest possible interface for a function: operator() you know the parameters, and what we have is operator() who knows the parameters?

This is where most of the work is to be done, and were you will need manual type erasure. That can be implemented by a base abstract class function_base that offers a virtual operator()( std::vector<parameter_t> [const] & ) [const] (play with const-ness or forget about it for the time being to simplify a bit the problem). The function_t will just hold a pointer to that base type, and perform the call. Up to here still simple.

Implementation of type erasure for the functions... Now the next problem is how to implement each concrete derived type from function_base, and there is where things get a bit trickier (we agreed the rest was simple, didn't we?) You need to provide a constructor template in function_t that will take any function, instantiate a templated type derived from function_base and store the pointer inside the function_t.

Again to avoid complexity, assume that you can do with a single argument. The rest is just more code... The implementation of function_impl<> just needs to store the original function pointer, and remember the type and number (in this case 1) of arguments. The implementation of operator() iterates the vector of parameters and for each parameter it unerases the type (converts back to the original type) and calls the function. This code will have to be pretty much manual in the template, but it will be reusable for all of the functions of the same arity.

To make things a bit simpler here you can make use of function traits libraries, that will be able to extract the type and number of arguments from the function that you receive.

So what is left for user code? That is simple, and in this case I mean it. Your interface should offer function_t, which can be constructed from any function pointer (as long as it fits the requirements of what you managed to implement there), and a function to register any such function_t in your system by providing a name:

// user code: registration of a new function
void print_single_int( int );
lib::function_t f( &print_single_int );
lib::register( "print_single_int", f );

Those two lines will have to be typed for each function that you want to add to your system. Of course, you might decide that the complexity of the solution does not compensate the problem and go for a manual implementation, were user code just creates hand coded derivations of function_base that manually process the vector of parameters and calls the function... if there are few operations that you want to implement in your scripting language, it might not be worth the extra effort of making it generic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文