2014-10-31 95 views
2

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

+1

插件系统没有_one_协议。您将需要编写一组类工厂/加载的模块,并在运行时从动态库加载你的代码,根据您的应用程序的需要(我不知道hexchat是什么,以及它如何与插件工作)。实际上,你的问题太宽泛无法回答(但是,请看一下WinAPI LoadLibrary和Linux API dlopen,并考虑围绕这些建立一些东西)。投票结束。 – utnapistim 2014-10-31 08:53:59

+1

由于你标记C:看一看GLibs [GModule](https://developer.gnome.org/glib/stable/glib-Dynamic-Loading-of-Modules.html),然后[GTypeModule](HTTPS:/ /developer.gnome.org/gobject/stable/GTypeModule.html)以获得具有自动界面加载的更复杂版本。 – jku 2014-10-31 09:05:02

+0

我不确定是否理解你的问题。我给了一些答案。 – 2014-10-31 11:14:21

回答

3

在Linux和POSIX系统,你要使用dlopen(3) & dlsym(或一些库包装这些功能,例如Glib从GTK,QtPOCO,等...)。更确切地说,

构建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)需要你的插件,以及如何使用它们,其中调用顺序排列,什么是内存的所有权政策,等等。如果你让几个类似的插件,你可能有一些有据可查的钩子钩住你的主要程序,调用所有的dlsym函数相关的dlopen -ed插件。示例:GCC plugins conventions,GNU make modules,Gedit plugins,...