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

时间:2010-07-07 11:58:59

标签: c++ c linux interpreter dlsym

我正在编写一种运行时系统/解释器,我需要做的一件事就是调用位于外部库中的c / c ++函数。

在linux上,我正在使用dlfcn.h函数打开一个库,并调用位于其中的函数。问题是,当使用dlsysm()时,返回的函数指针需要在被调用之前被转换为适当的类型,以便知道函数参数和返回类型,但是如果我在库中调用一些任意函数那么显然我不会在编译时知道这个原型。

所以我要问的是,有没有办法调用一个动态加载的函数并传递它的参数,并检索它的返回值,而知道它的原型?

到目前为止,我已经得出结论并不容易做到这一点,但我找到的一些解决方法是:

  • 确保我要加载的所有函数都具有相同的原型,并为这些函数提供一些排序机制来检索参数和返回值。这就是我目前正在做的事情。

  • 使用内联asm将参数压入堆栈,并读取返回值。如果可能的话,我真的想避免这样做!

如果有人有任何想法,那将非常感激。

修改

我现在找到了我正在寻找的东西:

http://sourceware.org/libffi/

“便携式外部函数接口库”

(虽然我承认我原来的问题可以更清楚了!)

5 个答案:

答案 0 :(得分:6)

您要求的是C / C ++是否支持函数反射(即在运行时获取有关其类型的信息)。可悲的是答案是否定的。

您必须使函数符合标准契约(正如您所说的那样),或者开始实现在运行时尝试调用函数而不知道其参数的函数。

由于不知道某个函数使得无法调用它,我认为你的解释器/“运行时系统”至少有一些用户输入或者它可以用来推断它试图调用一个看起来像的函数采取这些论点并返回不完全出乎意料的东西。这种查找本身很难实现,即使使用反射和一个不错的运行时类型系统也可以使用。混合使用调用约定,链接样式和平台,事情很快就会变得很糟糕。

坚持您的计划,为您动态加载的功能强制执行明确定义的合同,并希望能够做到这一点。

答案 1 :(得分:2)

您可以向外部库添加调度功能,例如一个采用某种变体类型的函数名和N(可选)参数并返回变量?这样,调度函数原型是已知的。然后,调度函数对函数名执行查找(或切换)并调用相应的函数。

如果有很多功能,显然会成为维护问题。

答案 2 :(得分:1)

我相信ruby FFI库可以满足您的要求。它可以调用函数 在外部动态链接库中,没有专门链接它们。

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

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

- 布拉德费伦 http://xtargets.heroku.com

答案 3 :(得分: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。

以前版本的界面随着时间的推移而不断增长,从而导致客户,产品开发人员和第三方插件开发人员出现问题。插件被称为相对较少的事实允许轻微使用std :: string(并且与整个地方使用的SQL相比,开销仍然是花生)。参数stdin填充了输入,具体取决于插件类型。如果在输出参数stderr内部任何字符串以'E:'开头,则插件调用被视为失败('W:'用于警告,静默忽略休息,因此可用于插件开发/调试)。 / p>

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

答案 4 :(得分:0)

我的解决方案是你可以定义一个generic proxy function,它将动态函数转换为统一原型,如下所示:

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

在用户定义的文件中,您必须添加define才能执行转换:

double foo(double a) { /*...*/ }
auto local_foo = proxy(foo);

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