锁定老帖子 主题:C语言插件机制(下)
精华帖 (7) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-08-25
前言上一篇文章简单介绍了*NIX下的动态库的使用,我们在这篇文章中实现一个计算器,计算器程序calc本身不做运算,只是将操作数传递给具体的插件(adder, suber, muler, diver)来完成实际运算。首先,计算器根据插件配置文件plugin.xml来确定插件的位置,名称,入口符号的定义,然后依次调用各个插件完成计算。
插件列表文中涉及到的插件定义在plugin.xml中,文档结构如下:
<plugins> <plugin name="adder"> <library path="/home/juntao/.libs/adder.so"> </library> <entry name="add"> </entry> </plugin> <plugin name="suber"> <library path="/home/juntao/.libs/suber.so"> </library> <entry name="sub"> </entry> </plugin> <plugin name="muler"> <library path="/home/juntao/.libs/muler.so"> </library> <entry name="mul"> </entry> </plugin> <plugin name="diver"> <library path="/home/juntao/.libs/diver.so"> </library> <entry name="div"> </entry> </plugin> </plugins>
每个插件为一个plugin标签,plugin标签中包含library, entry两个字标签,分别定义动态库文件的路径及名称和插件函数的入口。为了简便,我们不重复设计list及xml解析,这里使用libxml2作为xml的分析器,GLIB中的GSList(单链表)来作为插件列表的链表对象。
每一个插件在C语言中的定义如下,非常简单(plugin.h)
#ifndef _PLUGIN_H_ #define _PLUGIN_H_ typedef struct{ char name[64]; char path[256]; char entry[128]; int version; }Plugin; #endif
这里为了行文方便,Plugin结构中的字符串为静态尺寸。
计算器计算器调用parseconf模块中的load_plugins将plugin.xml中定义的Plugin加载进一个GSList,以备后用:
#include "plugin.h" extern int load_plugins(char *config, GSList **list);
插件中的函数原型应该符合接口定义:
//pointer to function, which return a double, and get 2 double as input double (*pfunc)(double a, double b);
计算器的主要代码如下:
int calc_test(double a, double b){ GSList *list = NULL, *it = NULL; Plugin *pl = NULL; //insert a null node into list at first list = g_slist_append(list, NULL); int code = 0; double result; //load plugin defined in plugin.xml into list load_plugins("plugin.xml", &list); for(it = list; it != NULL; it = it->next){ pl = (Plugin *)it->data; if(pl == NULL){ continue; }else{ //open the library flib = dlopen(pl->path, RTLD_LAZY); dlError = dlerror(); if(dlError){ fprintf(stderr, "open %s failed\n", pl->name); g_slist_free(list); return -1; } //get the entry *(void **)(&pfunc) = dlsym(flib, pl->entry); dlError = dlerror(); if(dlError){ fprintf(stderr, "find symbol %s failed\n", pl->entry); g_slist_free(list); return -1; } //call the function result = (*pfunc)(a, b); printf("%s(%f, %f) = %f\n", pl->entry, a, b, result); //then close it code = dlclose(flib); dlError = dlerror(); if(code){ fprintf(stderr, "close lib error\n"); g_slist_free(list); return -1; } } } g_slist_free(list); return 0; }
首先,定义一个GSList,然后将其传递给load_plugins,load_plugins解析plugin.xml,然后填充list返回,calc_test遍历插件列表,并调用每一个插件定义的entry.
除法器我们来看一个具体的插件:做除法的模块
#include <stdio.h> double div(double a, double b){ if(b == 0){ fprintf(stderr, "div zero error\n"); return -1; }else{ return a / b; } } diver.c在编译之后,生成diver.so,将其置于plugin.xml定义的位置处即可。
运行结果如下图所示: 其他代码如xml的解析,GSList的使用等与插件机制关系不大,感兴趣的朋友可以在附件中查看。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-09-02
放眼望去皆动态啊!
|
|
返回顶楼 | |
发表时间:2010-09-02
思路挺好的。
|
|
返回顶楼 | |
发表时间:2010-09-02
smzd 写道 放眼望去皆动态啊!
这个主要看如何权衡了,动态的好处是松耦合,大家面向接口写程序,效率较静态的肯定会低一些。这个例子当然完全可以用静态的实现,但是如果应用较复杂,而对效率要求不是很高的话,可以考虑向动态转。不过话又说回来了,现在代码的执行效率,要求貌似已经不那么严格了,看看现在的Java应用有多少,呵呵。 |
|
返回顶楼 | |
发表时间:2010-09-03
和vm比较的话,感觉
差别:静态编译 共同点:主动的动态链接. |
|
返回顶楼 | |
发表时间:2010-09-03
但是很多场合下函数原型不可能固定的啊...
|
|
返回顶楼 | |
发表时间:2010-09-04
这个列子里动态库的参数只能有两个,是否有必要把参数也配置话呢
|
|
返回顶楼 | |
发表时间:2010-09-05
mallon 写道 但是很多场合下函数原型不可能固定的啊...
主要看接口的定义了,一般而言,接口这种东西,设计好之后就很少变化了。如果你所说的函数原型不固定,那么有很多方式来解决,如va_args,结构体数据等。 |
|
返回顶楼 | |
发表时间:2010-09-05
sunday1207 写道 这个列子里动态库的参数只能有两个,是否有必要把参数也配置话呢
va_args,或者结构体如下形式: typedef struct{ void *data; unsigned int length; }data_t; 这样,在dl*函数内部可以通过判断length来判断真正的数据长度。这个只是程序实现上的技巧。 |
|
返回顶楼 | |
发表时间:2010-09-10
这个有点意思~~
|
|
返回顶楼 | |