`
abruzzi
  • 浏览: 454365 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

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
分享到:
评论
10 楼 sinoyster 2010-09-12  
我们就是这样在做,思路和楼主差不多
9 楼 phoenixup 2010-09-10  
这个有点意思~~
8 楼 abruzzi 2010-09-05  
sunday1207 写道
这个列子里动态库的参数只能有两个,是否有必要把参数也配置话呢

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

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

这样,在dl*函数内部可以通过判断length来判断真正的数据长度。这个只是程序实现上的技巧。
7 楼 abruzzi 2010-09-05  
mallon 写道
但是很多场合下函数原型不可能固定的啊...

主要看接口的定义了,一般而言,接口这种东西,设计好之后就很少变化了。如果你所说的函数原型不固定,那么有很多方式来解决,如va_args,结构体数据等。
6 楼 sunday1207 2010-09-04  
这个列子里动态库的参数只能有两个,是否有必要把参数也配置话呢
5 楼 mallon 2010-09-03  
但是很多场合下函数原型不可能固定的啊...
4 楼 mxswl 2010-09-03  
和vm比较的话,感觉
差别:静态编译
共同点:主动的动态链接.
3 楼 abruzzi 2010-09-02  
smzd 写道
放眼望去皆动态啊!

这个主要看如何权衡了,动态的好处是松耦合,大家面向接口写程序,效率较静态的肯定会低一些。这个例子当然完全可以用静态的实现,但是如果应用较复杂,而对效率要求不是很高的话,可以考虑向动态转。不过话又说回来了,现在代码的执行效率,要求貌似已经不那么严格了,看看现在的Java应用有多少,呵呵。
2 楼 raojl 2010-09-02  
思路挺好的。
1 楼 smzd 2010-09-02  
放眼望去皆动态啊!

相关推荐

    C语言插件机制(下) 转

    在本篇中,我们将深入探讨"C语言插件机制(下)"这一主题,主要关注如何在C语言中实现插件系统,以及与之相关的工具和技术。首先,插件机制是一种允许程序在运行时动态加载和卸载附加功能的方式,它极大地提高了软件的...

    C语言插件.zip

    C语言插件通常是指在开发环境中为了增强对C语言支持而设计的工具或组件。这些插件可能提供语法高亮、自动补全、调试功能、代码分析等特性,以提升C程序员的开发效率和代码质量。 在C语言的开发过程中,一些关键知识...

    cdt插件 eclipse的c语言编译环境

    本文将深入探讨如何利用CDT插件在Eclipse中构建C语言的编译环境,以及相关辅助工具和配置技巧。 首先,CDT插件提供了代码编辑、调试、构建系统等一站式服务。安装CDT后,用户可以在Eclipse内直接编写、编译和运行C/...

    sublime text3插件的安装方法和示例

    Sublime Text 3 是一款功能强大且广受欢迎的代码编辑器,它的插件机制使得用户可以根据需要安装各种插件以满足不同的需求。本文将详细介绍 Sublime Text 3 插件的安装方法,并以 Emmet 插件为例展示插件的安装和...

    华为C语言编程规范

    4.3 异常处理:使用`try-catch`机制或自定义错误处理流程,确保程序在异常情况下能正常退出。 五、模块化与可扩展性 5.1 封装原则:将功能相近的代码组织在一起,提高代码复用性。每个源文件只包含一个主要功能。 ...

    Cedar硬件编码 的 GStreamer 插件_c语言

    在IT领域,尤其是在多媒体处理和流媒体应用中,GStreamer是一个强大的开源框架,它允许开发者构建复杂的...通过深入研究源代码和GStreamer插件机制,开发者可以进一步定制和优化这个插件,以满足特定应用场景的需求。

    Snort源码 C语言实现

    7. **动态库和插件机制**:Snort允许用户通过动态加载库(插件)来扩展其功能,比如添加新的检测规则、日志格式或协议解析器。 8. **性能优化**:为了处理海量网络流量,Snort使用了多种优化技术,如多线程处理、...

    book_cprogramming:C语言中的框架和插件设计

    C语言本身并不直接支持面向对象编程,但通过巧妙的设计,可以模拟出类似的功能,构建出强大的框架系统和插件机制。本资源"book_cprogramming"可能是一个教程或代码示例集,旨在帮助开发者理解和掌握这一技术。 一、...

    C语言easyx飞机大战源码+素材

    “软件/插件”可能暗示着这不只是一个简单的程序,它可能包含了一些特定的库或者插件,如EasyX,以增强其功能。“范文/模板/素材”则表明这个项目可以作为一个学习的样本,或者是开发者创建类似游戏时可以参考的模板...

    C语言贪吃蛇大作战源代码,C语言期末作业

    5. **内存管理**:C语言没有自动垃圾回收机制,所以开发者需要手动分配和释放内存。在创建和删除蛇的节点时,要特别注意内存泄漏问题。 6. **屏幕绘图**:在C语言中,可以使用ANSI转义码或者`ncurses`库来在终端上...

    c语言编译dll给易语言调用

    2. **DLL项目设置**:在C语言环境下,如Visual Studio,你需要创建一个Win32 DLL项目。在项目属性中,配置生成动态链接库的目标类型,并确定所需的API和头文件。 3. **导出函数**:在C语言中,使用`__declspec(dll...

    使用C语言开发的小游戏超级玛丽源码,适合刚入门学习C语言编程的爱好者

    7. **错误处理**:在源码中,错误处理机制是必不可少的,它可以帮助程序在遇到问题时优雅地退出,而不是崩溃。通常会使用assert宏或自定义错误处理函数。 8. **优化与调试**:游戏源码中还可能包含一些性能优化的...

    c语言实现slip协议源码

    2. **错误检测**:SLIP不提供内置的错误检测机制,如校验和或循环冗余校验(CRC)。这通常需要在应用层或更高层次协议中处理。 3. **数据透明性**:SLIP协议能够传输任何8位字节的数据,因为它能编码特殊字符,确保...

    c语言火车票订票管理源码

    5. 并发控制:尽管这是一个单用户系统,但源码可能包含了一些并发控制的概念,如锁或其他同步机制,以模拟多用户环境下可能遇到的问题。 6. 日志记录:可能包含简单的日志记录功能,用于追踪系统的运行状态和用户...

    【计算机毕业设计】C语言项目源码 c语言贪吃蛇游戏的双人对战版

    创建两个线程分别控制两条蛇,通过同步机制(如互斥锁或条件变量)保证游戏逻辑的正确性,防止并发冲突。 【游戏逻辑与算法】 贪吃蛇游戏的逻辑包括蛇的移动、食物生成、碰撞检测和游戏结束条件判断。其中,移动...

    c语言ppp拨号源码ppp2.4.4

    现在,尽管宽带连接更为常见,但PPP在某些场景下仍然有用,例如在路由器配置、远程访问服务器和物联网设备中。 首先,让我们深入了解PPP协议。PPP协议主要由三部分组成:链路控制协议(LCP)、网络控制协议(NCP)...

    PLL 锁相环 仿真 C代码实现,经过验证的锁相环仿真与C语言实现

    学习PLL的C语言实现和仿真,有助于初学者理解PLL的工作机制,提高分析和设计能力。通过编程实现,可以更直观地看到PLL如何响应不同输入信号,以及在不同参数设置下的行为,这对于理解和优化PLL系统至关重要。 5. ...

    C语言日历系统.pdf

    10. 可扩展性:可扩展性是日历系统的重要特征之一,需要使用C语言实现可扩展机制,包括插件机制、接口机制等。 11. 可维护性:可维护性是日历系统的重要特征之一,需要使用C语言实现可维护机制,包括日志机制、错误...

    【计算机毕业设计】C语言项目源码 c语言学生信息系统

    8. 用户界面:虽然C语言本身不支持图形用户界面(GUI),但在命令行环境下,可以设计简单的文本交互界面,通过获取用户输入的命令来执行相应的功能。 9. 测试与调试:完成编码后,需要对系统进行详尽的测试,确保...

    纯c语言迷宫源码rar,-c语言小游戏

    标签为"软件/插件 c语言 游戏",这进一步明确了我们要探讨的内容。C语言在游戏开发中通常用于编写底层逻辑和高性能部分,而“软件/插件”则可能指的是游戏的可扩展性和模块化设计。 在压缩包内,我们找到了名为“纯...

Global site tag (gtag.js) - Google Analytics