3

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

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

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

到目前为止,我得出的结论是,没有简单的方法可以做到这一点,但我发现的一些解决方法是:

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

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

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

编辑:

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

http://sourceware.org/libffi/

“一个可移植的外部函数接口库”

(尽管我承认我本来可以在原始问题中更清楚!)

4

5 回答 5

6

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

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

由于对函数一无所知,因此无法调用它,我假设您的解释器/“运行时系统”至少有一些用户输入或类似的输入,它可以用来推断它正在尝试调用一个看起来像采取那些参数并返回一些并不完全出乎意料的东西。即使使用反射和体面的运行时类型系统,这种查找本身也很难实现。混合调用约定、链接样式和平台,事情很快就会变得很糟糕。

坚持你的计划,为你动态加载的函数强制执行一个定义明确的合约,并希望能做到这一点。

于 2010-07-07T12:15:38.987 回答
2

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

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

于 2010-07-07T12:14:15.197 回答
1

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

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

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

——布拉德·费兰 http://xtargets.heroku.com

于 2010-07-07T13:27:25.680 回答
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。

该接口的早期版本随着时间的推移变得无法维护,从而给客户、产品开发人员和第 3 方插件开发人员带来了问题。由于插件很少被调用(与到处使用的 SQL 相比,开销仍然微不足道),因此可以随意使用 std::string。参数stdin根据插件类型填充输入。stderr如果在输出参数中任何字符串以“E:”开头(“W:”用于警告,rest 被静默忽略,因此可用于插件开发/调试),则认为插件调用失败。

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

于 2010-07-07T13:40:49.060 回答
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
}

在用户定义的文件中,您必须添加定义来进行转换:

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

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

于 2014-01-27T09:26:51.797 回答