C/C++动态加载未知原型函数

4
我正在编写一种运行时系统/解释器,其中需要能够调用位于外部库中的c/c++函数。
在linux上,我使用dlfcn.h函数来打开一个库,并调用其中的函数。问题是,当使用dlsysm()时,返回的函数指针需要转换为适当的类型,然后才能被调用,以便知道函数参数和返回类型,但是如果我调用库中的某个任意函数,显然我在编译时不会知道这个原型。
所以我的问题是,是否有一种方法可以调用动态加载的函数并传递参数,并检索它的返回值,而不需要知道它的原型?
到目前为止,我得出的结论是没有简单的方法来做到这一点,但是我发现了一些解决方法:
  • 确保我想要加载的所有函数具有相同的原型,并为这些函数提供一些机制以检索参数和返回值。这是我目前正在做的。

  • 使用内联汇编将参数推送到堆栈上,并读取返回值。如果可能的话,我真的想避免这样做!

如果有人有任何想法,那将不胜感激。

编辑:

我现在已经找到了我要找的东西:

http://sourceware.org/libffi/

一个可移植的外部函数接口库
(虽然我承认在原问题中可能没有表述清楚!)

我不理解你所声称的在编译时无法知道函数原型的说法。我的意思是,你调用dlsym()并对结果做了什么?你从哪里获取“参数”?参数的存在意味着 - 对我来说 - 你对函数原型有一些了解。 - Chris Becke
如果您不知道函数的参数或返回类型,该如何调用它? - Stephen
这些函数在源代码中以不同的语言指定,就像Java JNI一样。因此,我可以轻松地获取要调用的dlysm函数的名称,但是参数和类型表示为各种AST节点,我无法简单地将它们传递给加载的函数。 - R Campbell
5个回答

6
你所询问的是C/C++是否支持函数反射(即在运行时获取有关其类型的信息)。遗憾的是答案是否定的。
你将不得不使这些函数符合标准契约(正如你所说),或者开始实现机制,以尝试在不知道它们的参数的情况下调用函数。
由于对函数没有任何了解会使其无法被调用,我假设你的解释器/“运行时系统”至少具有一些用户输入或类似的东西,可以用来推断它正在尝试调用一个看起来像使用这些参数并返回一些不完全意外的东西的函数。即使使用反射和一个体面的运行时类型系统来工作,这种查找本身也很难实现。加入调用约定、链接样式和平台,事情很快就会变得非常复杂。
坚持你的计划,强制执行动态加载的函数的明确定义契约,并希望能够通过这种方式完成。

2
你能否在外部库中添加一个调度函数,例如接受函数名称和 N(可选)某种变量类型的参数,并返回一个变量?这样就可以知道调度函数的原型。然后,调度函数会在函数名称上进行查找(或切换),并调用相应的函数。
显然,如果有很多函数,这将成为一个维护问题。

1

我相信 Ruby FFI 库可以实现你要求的功能。它可以调用外部动态链接库中的函数而无需特别进行链接。

http://wiki.github.com/ffi/ffi/

你可能不能直接在你的脚本语言中使用它,但也许这些想法是可移植的。

-- Brad Phelan http://xtargets.heroku.com


0
我正在编写一种运行时系统/解释器,其中一个需要能够做到的事情是调用位于外部库中的c/c++函数。
你可以查看TclPython如何实现。如果您熟悉Perl,也可以查看Perl XS
通常的方法是需要在解释器和目标C库之间添加额外的网关库。从我使用Perl XS的经验来看,主要原因是内存管理/垃圾回收和C数据类型很难/无法直接映射到解释器的语言上。
所以我的问题是,有没有一种方法可以调用动态加载的函数并传递参数,并检索它的返回值而不知道它的原型?
据我所知,没有这样的方法。
确保我想要加载的所有函数具有相同的原型,并为这些函数提供一些机制来检索参数和返回值。这是我目前正在做的事情。
这也是我的项目中其他团队正在做的事情。他们为外部插件制定了标准化的API之类的东西:
typedef std::list< std::string > string_list_t;
string_list_t func1(string_list_t stdin, string_list_t &stderr);

插件的常见任务是执行转换、映射或扩展输入,通常使用RDBMS。

接口的先前版本随着时间的推移变得难以维护,给客户、产品开发人员和第三方插件开发人员带来了问题。由于插件的调用相对较少(而且仍然与SQL相比开销微不足道),因此允许轻率地使用std::string。参数stdin根据插件类型填充输入。如果输出参数stderr中的任何字符串以'E:'开头,则认为插件调用失败('W:'用于警告,其余内容被静默忽略,因此可用于插件开发/调试)。

dlsym仅在具有预定义名称的函数上使用一次,以从共享库数组中提取函数表(函数公共名称、类型、指针等)。


0
我的解决方案是,您可以定义一个“通用代理函数”,它将动态函数转换为统一的原型,类似于这样:
#include <string>
#include <functional>

using result = std::function<std::string(std::string)>;

template <class F>
result proxy(F func) {
  // some type-traits technologies based on func type
}

在用户定义的文件中,您必须添加定义来执行转换:
double foo(double a) { /*...*/ }
auto local_foo = proxy(foo);

在您的运行时系统/解释器中,您可以使用 dlsym 定义一个 foo-function。用户定义的函数 foo 的责任是进行计算。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接