【问题标题】:C++: Method of calling functions whose parameter list was parsed from a text fileC++:调用从文本文件解析参数列表的函数的方法
【发布时间】:2011-07-22 04:52:36
【问题描述】:

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

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

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

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

【问题讨论】:

  • 解决方案将是关于如何发明一个动态类型系统。我建议研究一种可以与 C++ 挂钩的动态语言,这样所有的脏活都已经完成了。我个人推荐 Lua,有或没有 Luabind,但还有其他可能性。
  • 除非您真的需要一种自定义脚本语言,否则最好使用现有的。例如,查看 Python 和 Python C-API 或 Boost.Python。了解各种脚本语言(如 Python 和 Lua)的优缺点,并确定哪一种最适合您的需求。

标签: c++ callback parameter-passing boost-bind


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    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() 的类的实例,该operator() 接受一个参数序列并对要调用的确切函数执行类型擦除。那很简单!至少如果你忽略细节的话。

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

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

    这是完成大部分工作的地方,如果您需要手动擦除类型。这可以通过提供virtual operator()( std::vector&lt;parameter_t&gt; [const] &amp; ) [const] 的基本抽象类function_base 来实现(玩const-ness 或暂时忘记它以简化问题)。 function_t 将只保存指向该基类型的指针,并执行调用。到这里还是很简单的。

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

    再次为避免复杂性,假设您可以使用单个参数。剩下的只是更多的代码......function_impl&lt;&gt; 的实现只需要存储原始函数指针,并记住参数的类型和数量(在本例中为 1)。 operator() 的实现会迭代参数向量,并且对于每个参数,它都会取消擦除类型(转换回原始类型)并调用函数。这段代码在模板中几乎必须是手动的,但它可以被相同数量的所有函数重用。

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

    那么用户代码还剩下什么?这很简单,在这种情况下,我是认真的。你的接口应该提供function_t,它可以由any函数指针构造(只要它符合你在那里实现的要求),以及一个注册任何这样的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 );
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-06
      • 1970-01-01
      相关资源
      最近更新 更多