上一篇文章简单介绍了*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的使用等与插件机制关系不大,感兴趣的朋友可以在附件中查看。