编写插件系统?

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

在Linux和Posix系统上,你想使用dlopen(3)和dlsym (或者包含这些函数的一些库,例如来自GTK的Glib , Qt , POCO等……)。 更确切地说,

构建一个与位置无关的代码共享库作为您的插件:

  gcc -fPIC -Wall -c plugin1.c -o plugin1.pic.o gcc -fPIC -Wall -c plugin2.c -o plugin2.pic.o 

请注意,如果插件是用C ++编码的,那么你将用g++编译它,你应该将插件函数声明为extern "C"以避免名称错位 。

然后将您的插件链接为

  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; 

并在插件句柄dlh上使用dlsym

  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函数属性 ); 将在dlopen (或dlclose )时间调用。 在C ++中,您应该只使用静态对象。 (他们的构造函数在dlopen时调用,它们的析构函数在dlclose时调用;因此函数属性的名称)。

在程序调用结束时

  dlclose(dlh), dlh = NULL; 

在实践中,你可以做很多(也许是一百万) dlopen调用。

您通常希望将主程序与-rdynamic以使其符号可以从插件中看到。

 gcc -rdynamic prog1.o prog2.o -o yourprog -ldl 

阅读程序库HowTo & C ++ dlopen mini HowTo & Drepper的论文: 如何编写共享库

最重要的部分是定义和记录插件约定(即“协议”),即插件中所需的函数集(和API)以及如何使用它们,它们按顺序排列如果您允许几个类似的插件,您可能在主程序中有一些记录良好的钩子,它们调用相关dlopen -ed插件的所有dlsym -ed函数。 示例: GCC插件约定 , GNU make模块 , Gedit插件 ,…