论坛首页 编程语言技术论坛

C语言插件机制(下)

浏览 9592 次
精华帖 (7) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-08-25  
C

前言

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

 

  • 大小: 14.5 KB
   发表时间:2010-09-02  
放眼望去皆动态啊!
1 请登录后投票
   发表时间:2010-09-02  
思路挺好的。
1 请登录后投票
   发表时间:2010-09-02  
smzd 写道
放眼望去皆动态啊!

这个主要看如何权衡了,动态的好处是松耦合,大家面向接口写程序,效率较静态的肯定会低一些。这个例子当然完全可以用静态的实现,但是如果应用较复杂,而对效率要求不是很高的话,可以考虑向动态转。不过话又说回来了,现在代码的执行效率,要求貌似已经不那么严格了,看看现在的Java应用有多少,呵呵。
0 请登录后投票
   发表时间:2010-09-03  
和vm比较的话,感觉
差别:静态编译
共同点:主动的动态链接.
0 请登录后投票
   发表时间:2010-09-03  
但是很多场合下函数原型不可能固定的啊...
0 请登录后投票
   发表时间:2010-09-04  
这个列子里动态库的参数只能有两个,是否有必要把参数也配置话呢
0 请登录后投票
   发表时间:2010-09-05  
mallon 写道
但是很多场合下函数原型不可能固定的啊...

主要看接口的定义了,一般而言,接口这种东西,设计好之后就很少变化了。如果你所说的函数原型不固定,那么有很多方式来解决,如va_args,结构体数据等。
0 请登录后投票
   发表时间:2010-09-05  
sunday1207 写道
这个列子里动态库的参数只能有两个,是否有必要把参数也配置话呢

va_args,或者结构体如下形式:

typedef struct{
void *data;
unsigned int length;
}data_t;

这样,在dl*函数内部可以通过判断length来判断真正的数据长度。这个只是程序实现上的技巧。
0 请登录后投票
   发表时间:2010-09-10  
这个有点意思~~
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics