经过几个小时的研究后,我什么都没有,所以我向你们求助于希望得到解决方案的好人。我将用c ++编写一个bot,并且在某些时候想为它创建一个插件系统。现在我知道我可以为它编写脚本语言,但是,我知道可以只编写一个api并在运行时动态地将程序链接到该API。我的问题是,我如何获得动态链接(就像hexchat的插件一样)?是否有任何优雅的解决方案,或者至少是典型设计的理论?
答案 0 :(得分:3)
在Linux和Posix系统上,您希望使用dlopen(3)& dlsym
(或包含这些函数的一些库,例如来自GTK的Glib,Qt,POCO等等。)。更准确地说,
构建一个position independent code共享库作为您的插件:
gcc -fPIC -Wall -c plugin1.c -o plugin1.pic.o
gcc -fPIC -Wall -c plugin2.c -o plugin2.pic.o
请注意,如果插件是用C ++编码的,那么您将使用g++
对其进行编译,并且您应该将插件函数声明为extern "C"
以避免name mangling。
然后将您的插件链接为
gcc -shared -Wall plugin1.pic.o plugin2.pic.o -o plugin.so
如果您的插件需要GNU readline,您可以在上面的命令末尾添加动态库(例如-lreadline
。)
最后,在主程序中使用完整路径调用dlopen
,例如
void* dlh = dlopen("./plugin.so", RTLD_NOW);
if (!dlh) { fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(EXIT_FAILURE); };
(通常dlh
是全球数据)
然后使用dlsym
获取函数指针。因此,在程序和插件代码(如
typedef int readerfun_t (FILE*);
声明一些(通常)全局函数指针
readerfun_t* readplugfun;
并在插件句柄dlsym
上使用dlh
:
readplugfun = (readerfun_t*) dlsym(dlh, "plugin_reader");
if (!readplugfun) { fprintf (stderr, "dlsym failed: %s\n", dlerror());
exit(EXIT_FAILURE); };
当然,在您的插件源代码中(例如在plugin1.cc
中),您将定义
extern "C" int plugin_reader (FILE*inf) { // etc...
您可以在插件中定义一些构造函数(或析构函数)函数(请参阅GCC function attributes);将在dlopen
(或dlclose
)时间调用。在C ++中,您应该只使用静态对象。 (他们的构造函数在dlopen
时调用,它们的析构函数在dlclose
时调用;因此函数属性的名称)。
在程序结束时
dlclose(dlh), dlh = NULL;
在实践中,您可以做很多(可能是一百万)dlopen
次电话。
您通常希望将主程序与-rdynamic
相关联,以便通过插件显示其符号。
gcc -rdynamic prog1.o prog2.o -o yourprog -ldl
阅读Program Library HowTo& C++ dlopen mini HowTo& Drepper's paper: How to Write a Shared Library
最重要的部分是定义和文档插件约定(即“协议”),即函数的集合(和API)(为dlsym
- ed)您的插件中需要它们以及如何使用它们,它们的调用顺序,内存所有权策略是什么等等。如果您允许几个类似的插件,您可能在主程序中有一些记录良好的钩子,它们调用所有{{ 1}} - 相关dlsym
- ed插件的ed函数。示例:GCC plugins conventions,GNU make modules,Gedit plugins,...