如何从动态加载库执行未知函数?
当您在设计时了解该函数时,从动态库加载函数就很容易。 只需执行以下操作:
int (*fn)(int);
l0 = dlopen("./libfoo.so", RTLD_LAZY);
if (!l0)
{
fprintf(stderr, "l0 %s\n", dlerror());
return 1;
}
fn = (int (*)(int))dlsym(l0, "foo");
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "fn:%s\n", error);
return 1;
}
x=(*fn)(y);
...
当设计时未知时如何执行库函数?在运行时,您有一个函数名称和参数指针数组以及参数大小数组:
char* fn_name="foo"; int foo_argc; 无效* foo_argv[]; int foo_argv_size[];
在脚本语言中这是小菜一碟的任务,但是如何在 C++ 中很好地实现呢?
It's easy to load functions from dynamic libraries when you know this function in design time.
just do something like this:
int (*fn)(int);
l0 = dlopen("./libfoo.so", RTLD_LAZY);
if (!l0)
{
fprintf(stderr, "l0 %s\n", dlerror());
return 1;
}
fn = (int (*)(int))dlsym(l0, "foo");
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "fn:%s\n", error);
return 1;
}
x=(*fn)(y);
...
How to execute library function when it's unknown in design time? In runtime you have a function name and array of arguments pointers and array of arguments sizes:
char* fn_name="foo";
int foo_argc;
void* foo_argv[];
int foo_argv_size[];
In scripting language it's a piece a cake task, but how to implement this nicely in c++?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您知道函数的调用约定及其接收的参数,实际上有一种方法可以在运行时调用函数。然而,这超出了标准 C/C++ 语言范围。
对于 x86 汇编器:
假设如下:
然后您可以使用以下函数:
您可能需要调整此函数,具体取决于所使用的调用约定
There's actually a method to call a function at run-time if you know its calling convention and which parameters it receives. This however lies out of the standard C/C++ language scope.
For x86 assembler:
Assuming the following:
You may use then the following function:
You may need to adjust this function, depending on the calling convention used
作为一种静态语言,C++ 实际上依赖于在编译时了解类型/接口。我怀疑是否有一种好的低级方法来完成您想要解决的任务,但与往常一样,如果您添加另一层抽象,您可能可以实现目标。具体来说,在这种情况下,我过去在不确切知道需要将哪些“参数”传递给动态加载的插件时使用的方法是传递
std::map
std::map
的单个参数。 std::string,std::string > >
(即 key=value 字典)只需将需要传入的任何信息打包起来,然后让插件弄清楚如何处理它。这有点不方便,因为您必须对大部分数据进行编码/解码,但它至少可以用来优雅地处理我遇到的大多数情况。当然,这是假设你有能力通过标准化Plugin接口来控制动态加载DLL的API。如果你做不到……你可能就不走运了。
Being a static language, C++ really depends on knowing the type/interface at compile time. I doubt there's a good low-level way to accomplish the task you're looking to solve but, as always, if you add another layer of abstraction you can probably accomplish the goal. Specifically in this case the approach I've used in the past when not knowing exactly what "arguments" need to be passed to a dynamically loaded plugin is to pass a single argument of
std::map< std::string, std::string > >
(i.e a key=value dictionary) Just pack it with whatever information needs to be passed in and let the plugin figure out what to do with it. It's somewhat inconvenient in that you must encode/decode most of your data but it can be used to gracefully handle most cases I've run in to at least.Of course, this assumes that you have the ability to control the API of the dynamically loaded DLL by standardizing the Plugin interface. If you can't do that... you're probably out of luck.
正如问题所述,您无法在标准 C++ 中执行此操作。除非编译器在编译时知道被调用者的参数,否则无法有效地调用函数。即使在 varargs 函数的情况下,调用代码也必须在知道传递的参数的情况下进行编译 - 无法构建
va_list
。如果您控制 dll 中的函数,那么最好的办法可能是传递
boost::any
的集合(如果两者都使用相同的编译器和库进行编译),或void *
(如果你没有那么多的兼容性),或者参数的某种序列化形式(谷歌的协议缓冲区,也许)。如果 dll 已经存在并且您无法更改其接口,那么您需要对堆栈使用非标准技巧,根据您的调用约定将参数放在正确的位置(这就是动态语言在具有C 调用机制),否则您需要一个巨大的 switch 语句来覆盖所有可能的函数签名。显然,只有当您已经清楚地了解正在处理的 dll 时,后者才有可能。
另外,一般来说,您需要的不仅仅是参数及其大小的指针列表。调用约定很可能取决于参数的类型,而不仅仅是大小。例如,浮点参数可以在 FPU 寄存器中传递,而不是在堆栈上传递。
您还需要担心返回类型以及调用代码期望用它做什么。
As the problem is stated, you can't do this in standard C++. There is no way to validly call a function unless the compiler knows the callee's parameters at compilation time. Even in the case of a varargs function, the calling code must be compiled knowing what arguments are passed - there's no way to build a
va_list
.If you control the functions in the dll, then probably the best thing is to pass a collection of
boost::any
(if both will be compiled with the same compiler and libraries), orvoid*
(if you don't have that much compatibility), or some serialised form of the parameters (Google's protocol buffer, maybe).If the dll already exists and you can't change its interface, then you need either to play non-standard tricks with the stack to put the parameters in the right place according to your calling convention (which is what dynamic languages do when they have a C calling mechanism), or else you need a huge switch statement covering all the function signatures that it might be. Obviously the latter is only possible if you already have a good idea what dll(s) you're dealing with.
Also, in general you need more than a list of pointers to arguments and their sizes. The calling convention quite possibly depends on the types of the parameters, not just the sizes. For example, floating-point arguments could be passed in FPU registers instead of on the stack.
You also need to worry about the return type, and what the calling code is expecting to do with it.
你不能。 C++中没有反射。您必须知道函数原型才能调用它。 (无论如何,除非你实现自己的反射。)
You can't. There's no reflection in C++. You'll have to know the functions prototype in order to be able to call it. (Unless you implement your own reflection, anyway.)