我想动态调用 C 函数(例如,形成 stdlib、数学......)。这意味着我的 C 程序只知道指向随机函数的指针(例如
printf
)及其签名(编码为字符数组:char *
,...)。
我的目标是一个 ReflectCall 函数,它获取指向函数的指针 (
&printf
)、签名(以某种方式编码为 char[]
)以及作为 long[]
的参数(long
不是实际的数据类型) ,一个long值也可以代表一个double值,指针,...)。
我的反射函数的签名如下所示:
long reflectCall(void *funcPointer, char[] types, long[] args)
该函数应该执行函数的实际调用
*funcPointer
并最终返回其结果。
因此,我可以不创建一个指针指针;例如比如这个:
int (*functionPtr)(int,int);
任何人都可以给我提示如何解决这个问题或建议任何参考实现吗?
可以用纯 C 语言来完成,但不是那么简单,也不是那么快:
为所有要调用的函数创建包装函数,例如:
int WrapPrintf(const char* types,long* args,long* results)
{
// Function specific code, in this case you can call printf for each parameter
while(*types)
{
switch(*types){
case 'i':
printf("%d",(int)*args);
break;
case 'c':
printf("%c",(char)*args);
break;
// .. and so on
}
++types;
++args;
}
// Return number of filled results
return 0;
}
int WrapFoo(const char* types,long* args,long* results)
{
// ..function specific code..
return 0;
}
指向包装函数的指针:
typedef int (*TWrapper)(const char*,long*,long*);
为包装函数创建表结构:
struct STableItem{
const char *strName;
TWrapper pFunc;
};
创建表:
STableItem table[] = {
{"printf", &WrapPrintf},
{"foo", &WrapFoo},
{NULL, NULL}
};
创建接口来调用表中的任何函数(按名称搜索函数并调用它):
int DynamicCall(const char *func_name,const char* types,long* args,long* results)
{
int k;
for(k=0;table[k].strName != NULL;++k){
if(strcmp(func_name,table[k].strName) == 0){
return table[k].pFunc(types,args,results);
}
}
return -1;
}
最后拨打电话:
long args[] = {123,'b'};
long results[8]; // not nice but just for an example
int res_count = DynamicCall("printf","ic",(long*)args,(long*)results);
注意:使用哈希函数可以更快地搜索名称
C 不提供执行此操作的设施。您必须使用特定于平台的 ASM 编写函数主体。
我建议你看看libffi,它是否符合你的需求......
http://sourceware.org/libffi/
http://en.wikipedia.org/wiki/Libffi
正如其他地方所解释的,没有办法真正动态地做到这一点。但是,如果您希望使用指针构建函数表,并使用某种字符串或索引来描述您想要执行的操作,那么以可移植的方式这当然是可能的。作为各种解析和其他“基于命令运行代码等”的解决方案,这并不罕见。
但它确实要求您使用某种函数指针[或在某个时刻将您的
void *
转换为一个函数指针]。没有其他(甚至几乎)可移植的方式在 C 中动态调用函数。
抱歉发帖,但我想我可以为这里的讨论做出贡献。
您可以使用 POSIX 'dlfcn' API 通过名称动态获取函数。我不知道 Windows 的等效项是什么。
对于 C++ 函数,有名称修饰,简而言之,它是 C++ 对函数名称和参数类型进行编码的一种机制,以便与 C 链接器兼容。这将允许您调用 C++ 函数并指定要调用的重载(也称为参数类型)。
对于 C,不保留任何参数类型数据,因此 .dll 或 .so 将仅保留函数的名称。如果你想调用C函数,你必须提前知道签名。
此外,您需要知道函数的调用约定是什么。在 C++ 中,我相信此数据是在损坏的名称中编码的,但我不确定。在 C 中,它是平台相关的,因此您必须为每个平台编写代码来调用函数。