编写插件系统?

时间:2014-10-31 07:14:38

标签: c++ c linux plugins posix

经过几个小时的研究后,我什么都没有,所以我向你们求助于希望得到解决方案的好人。我将用c ++编写一个bot,并且在某些时候想为它创建一个插件系统。现在我知道我可以为它编写脚本语言,但是,我知道可以只编写一个api并在运行时动态地将程序链接到该API。我的问题是,我如何获得动态链接(就像hexchat的插件一样)?是否有任何优雅的解决方案,或者至少是典型设计的理论?

1 个答案:

答案 0 :(得分:3)

在Linux和Posix系统上,您希望使用dlopen(3)& dlsym(或包含这些函数的一些库,例如来自GTK的GlibQtPOCO等等。)。更准确地说,

构建一个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 conventionsGNU make modulesGedit plugins,...