当您在设计时知道此功能时,可以轻松地从动态库加载函数。 做这样的事情:
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; void * foo_argv []; int foo_argv_size [];
在脚本语言中,这是一个蛋糕任务,但如何在c ++中很好地实现它?
答案 0 :(得分:1)
正如问题所述,您无法在标准C ++中执行此操作。除非编译器在编译时知道被调用者的参数,否则无法有效地调用函数。即使在varargs函数的情况下,也必须编译调用代码,知道传递了什么参数 - 没有办法构建va_list
。
如果您控制dll中的函数,那么最好的方法是传递boost::any
的集合(如果两者都将使用相同的编译器和库进行编译),或者void*
(如果你没有那么多的兼容性),或参数的一些序列化形式(可能是谷歌的协议缓冲区)。
如果dll已经存在并且您无法更改其界面,那么您需要使用堆栈播放非标准技巧,以根据您的调用约定将参数放在正确的位置(这是动态语言所执行的操作)当他们有一个C调用机制时),否则你需要一个覆盖它可能的所有函数签名的巨大switch语句。显然后者只有在你已经知道你正在处理什么dll的情况下才有可能。
此外,通常您需要的不仅仅是参数及其大小的指针列表。调用约定很可能取决于参数的类型,而不仅仅是大小。例如,浮点参数可以在FPU寄存器中传递,而不是在堆栈中传递。
你还需要担心返回类型,以及调用代码期望用它做什么。
答案 1 :(得分:1)
作为一种静态语言,C ++实际上依赖于在编译时知道类型/接口。我怀疑是否有一个很好的低级方法来完成你想要解决的任务,但是,如果你添加另一层抽象,你可以一如既往地完成目标。特别是在这种情况下我过去使用的方法,当不知道究竟需要将什么“参数”传递给动态加载的插件时,是传递std::map< std::string, std::string > >
的单个参数(即键=值字典)只需将其包含在需要传递的任何信息中,然后让插件弄清楚如何处理它。你必须对大部分数据进行编码/解码,但它可以用来优雅地处理我至少运行过的大多数情况,这有点不方便。
当然,这假设您可以通过标准化插件接口来控制动态加载的DLL的API。如果你不能这样做......你可能运气不好。
答案 2 :(得分:1)
实际上有一种方法可以在运行时调用函数,如果你知道它的调用约定以及它接收的参数。然而,这不属于标准的C / C ++语言范围。
对于x86汇编程序:
假设如下:
您可以使用以下功能:
int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
{
// Reserve the space on the stack
// This is equivalent (in some sense) to 'push' all the parameters into the stack.
// NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
// care of ensuring all the consumed memory pages are accessible
_alloca(nSizeParams);
// Obtain the stack top pointer
char* pStack;
_asm {
mov pStack, esp
};
// Copy all the parameters into the stack
// NOTE: Don't use the memcpy function. Because the call to it
// will overwrite the stack (which we're currently building)
for (size_t i = 0; i < nSizeParams; i++)
pStack[i] = ((char*) pParams)[i];
// Call your function
int retVal;
_asm {
call pfn
// Most of the calling conventions return the value of the function (if anything is returned)
// in EAX register
mov retVal, eax
};
return retVal;
}
您可能需要调整此功能,具体取决于使用的调用约定
答案 3 :(得分:0)
如果在设计时未知库函数,该如何执行?
你做不到。 C ++中没有任何反映。您必须知道函数原型才能调用它。 (无论如何,除非你实施自己的反思。)