与其他人一样,如果可以避免,我建议您不要重新发明轮子。也就是说,如果可以,请使用现有框架。但我将稍微扩展一下可能的解决方案。
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
提供一个virtual operator()( std::vector<parameter_t> [const] & ) [const]
(玩 const-ness 或暂时忘记它以简化问题)的基本抽象类来实现。将function_t
只保存一个指向该基类型的指针,并执行调用。到这里还是很简单的。
实现函数的类型擦除......现在下一个问题是如何实现每个具体派生类型 from 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 );
必须为要添加到系统的每个功能键入这两行。当然,您可能会决定解决方案的复杂性并不能弥补问题并进行手动实现,如果用户代码只是创建手动编码的推导function_base
,手动处理参数向量并调用函数......如果有您想在脚本语言中实现的操作很少,因此使其通用化可能不值得付出额外的努力。