c++ - 编写插件系统?

标签 c++ c linux plugins posix

经过数小时的研究,我一无所获,所以我向各位好心人寻求解决方案。我打算用 C++ 编写一个机器人,并且在某个时候想为它制作一个插件系统。现在我知道我可以为它编写脚本语言,但是,我知道可以只编写一个 api 并让程序在运行时动态链接到它。我的问题是,我如何获得动态链接(就像 hexchat 的插件一样)?是否有任何优雅的解决方案,或者至少有关于典型设计的理论?

最佳答案

在 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 获取函数指针。因此,在程序和插件代码都包含的一些 header 中声明他们的签名,例如

 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 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

最重要的部分是定义和记录一个插件约定(即“协议(protocol)”),这是一组(和API)函数(成为dlsym -ed) 在你的插件中需要以及如何使用它们,它们被调用的顺序,内存所有权策略是什么等等。如果你允许几个类似的插件,你可能在你的主程序中有一些记录良好的钩子(Hook)调用所有相关 dlopen 插件的 dlsym 功能。示例:GCC plugins conventions , GNU make modules , Gedit plugins , ...

关于c++ - 编写插件系统?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26669302/

相关文章:

c++ - 为什么 stringstream 会产生奇怪的值?

c++ - 错误: request for member 'clear' in 'graph' , which is of non-class type

c++ - 析构函数 C++ 中的异常

php - 如何在 PHP 中引用文档根目录以上的文件夹?

linux -/usr/bin/ld : cannot find -lglfw

c++ - HeapAlloc 返回 0xC0000017 : Not Enough Quota

c++ - 通过传递引用或返回它来初始化结构是更好的风格吗?

c - 链表逻辑错误

c++ - 此指针类型转换符号的含义

c# - Mono Process.Start 返回 ExitCode 255?