2019独角兽企业重金招聘Python工程师标准>>>

linux下C++ 插件(plugin)实现技术

  • 作者:掌门狗

  • 时间:2008-12-01 20:21

  • 分类:默认分类

标签: c++ 插件

应用程序中使用插件技术,有利于日后的版本更新、维护(比如打补丁)和功能扩展,是一种很实用的技术。其最大的特点是更新插件时无需重新编译主程序,对于一个设计良好的应用系统而言,甚至可以做到业务功能的在线升级。本文介绍了linux下用C++实现插件的一个简单实例,希望能对大家有所启发。

为了能做到更新插件时无需重新编译主程序,要求主程序中定义的接口是定死的,而接口的实现被放到了具体的插件中,这样主程序在运行时刻将插件加载进来,就可以使用这些接口所提供的功能了。在面向对象的系统中,各个功能模块被封装到类中,因此在C++中实现插件技术,就需要在主程序中提供基类,并为这些基类定义明确的接口,然后在插件(动态库或共享库)中定义派生类,并实现基类中所有的接口。

我们以计算多边形面积为例,首先定义一个基类CPolygon:

/*+********************************************************/
/*+********************************************************/
/*+********************************************************/

/* polygon.h */

#ifndef __POLYGON_H__
#define __POLYGON_H__

#include < iostream >

class CPolygon
{
public:

CPolygon(){}

virtual ~CPolygon(){}

virtual double area(void) const = 0;
};

#endif /* __POLYGON_H__ */

/*-********************************************************/
/*-********************************************************/
/*-********************************************************/

注意基类不一定是虚类(有纯虚函数的类),但是接口一定要定义成虚函数,因为最终主程序是通过基类指针
来调派生类的接口函数,另外如果基类中有资源分配(new)的话,析构函数一定要定义成虚的,否则不会被
调用,造成内存泄漏。

接下来要定义派生类,并放到共享库(triangle.so)中:

/*+********************************************************/
/*+********************************************************/
/*+********************************************************/

/* triangle.h */

#ifndef __TRIANGLE_H__
#define __TRIANGLE_H__

#include " polygon.h "
#include < iostream >

class CTriangle : public CPolygon
{
public:

virtual double area(void) const;

};

#endif /* __TRIANGLE_H__ */

/* triangle.cpp */

#include "triangle.h"

extern "C"
{
void * create()
{
return new CTriangle;
}

}

double CTriangle::area(void) const
{
std::cout << "area of triangle" << std::endl;
return 0;
}

/*-********************************************************/
/*-********************************************************/
/*-********************************************************/

其中定义了函数“create”用来创建CTriangle类对象,该函数可让主程序获得CTriangle对象指针,从而
可以访问CTriangle类对象。主程序通过调用dlsym获取指向该函数的指针,需要指出的是,由于dlsym被
设计成c-style方式,因此调用c++定义的函数时,需要加上extern "C"

那么主程序是如何调用共享库的呢,代码片段如下:

/*+********************************************************/
/*+********************************************************/
/*+********************************************************/

typedef CPolygon* create_t();

void * handle = dlopen("triangle.so", RTLD_LAZY);

if( !handle )
{
std::cerr << dlerror() << std::endl;
exit(1);
}

create_t * create_triangle = (create_t *)dlsym(handle, "create");

CPolygon * pObj = create_triangle();

if( 0 != pObj )
{
pObj->area();
}

delete pObj;

dlclose(handle);

/*-********************************************************/
/*-********************************************************/
/*-********************************************************/

主程序通过dlopen打开triangle.so,然后通过dlsym得到库中的函数create指针,调用create后返回了
指向CTriangle类对象的指针,类型是CPolygon的,由于虚函数的多态性, pObj->area() 实际是调用
了CTriangle::area.

好了,插件技术就是这么简单,回顾一下实现过程:写一个基类,定义接口函数,然后在共享库中写
派生类,最后在主程序运行时刻打开共享库(dlopen),并通过create函数得到指向新创建的派生类
对象的指针,然后利用虚函数的多态性,调用派生类的各种方法。

不过进一步使用后你可能会发现,这样实现会有些问题:

1. 每写一个派生类就需要重写一个create函数

注意到CTriangle类实现时定义的create函数必须返回 new CTriangle:

extern "C"
{
void * create()
{
return new CTriangle;
}

}

那么如果再建一个类比如CRectangle, create函数必须重写,返回 new CRectangle

这样做一方面麻烦,另外CTriangle、CRectangle两个类不能放到同一个共享库中,否则会编译时刻
提示重复定义错误。

2. 主程序无法判断create函数返回的是哪个类所创建的对象

当只有一个基类(CPolygon)时主程序当然知道返回的是CPolygon派生类的对象指针:
create_t * create_triangle = (create_t *)dlsym(handle, "create");
CPolygon * pObj = create_triangle();

假如有多个基类,根据这些基类派生出不同类型的类时,无法在主程序中判断返回的是那个类的对象。

3. 操作繁琐

没有一个统一的操作界面,实现共享库的加载、卸载、派生类对象的创建,特别是当需要加载一个目录
下所有的共库时,感觉一个一个地加载太麻烦了,能不能批量加载呢。

通过动态类加载和建立Helper类可以很好地解决上述问题,其中dynclass.h/dynclass.cpp中实现了动态
加载类对象,pluginhelper.h/pluginhelper.cpp实现了Plugin Helper,具体细节见附件。

下面简单介绍一下使用步骤:

1. 首先定义基类(CPolygon),方法同上

2. 在共享库中实现派生类

比如CTriangle:

/*+********************************************************/
/*+********************************************************/
/*+********************************************************/

/* triangle.h */

#ifndef __TRIANGLE_H__
#define __TRIANGLE_H__

#include " polygon.h "
#include < iostream >

class CTriangle : public CPolygon
{
public:

virtual double area(void) const;

};

#endif /* __TRIANGLE_H__ */

/* triangle.cpp */

#include " triangle.h "
#include " dynclass.h "

DYN_DECLARE(CTriangle);

double CTriangle::area(void) const
{
std::cout << "area of triangle" << std::endl;
return 0;
}

/*-********************************************************/
/*-********************************************************/
/*-********************************************************/

注意到此时派生类的实现(triangle.cpp)中已没有了那个讨厌的create了,被我偷偷放到
dynclass.cpp中了:

extern "C"
{
void * createByClassName(const char * strClassName)
{
return DYN_CREATE(strClassName);
}
}

由于对任何派生类而言,该函数的实现都一样,因此只需要实现一次,对使用者是不可见的,达到
了从派生类中拿走的目的。

另外增加了一个宏:DYN_DECLARE(CTriangle); 参数是类名(这里用到了RTTI),每个派生类对应
一个这样的宏,该类就可以支持类对象的动态加载了,需要包含头文件dynclass.h

2. 在主程序中如何使用

使用起来也非常简单,在主程序(main.cpp)中:

/*+********************************************************/
/*+********************************************************/
/*+********************************************************/
...

#include " pluginhelper.h "
#include " polygon.h "

...

CPluginHelper pluginHelper;

pluginHelper.Load( "./plugin", "*.so" );

CPolygon * pbase = (CPolygon *)pluginHelper.Create("CTriangle");

if( 0 != pbase )
{
pbase->area();
}

delete pbase;

pluginHelper.Unload( "./plugin", "*.so" );

/*-********************************************************/
/*-********************************************************/
/*-********************************************************/

首先定义CPluginHelper对象,调用Load方法加载共享库,其中第一个参数是共享库的路径,第二
个参数是共享库的名称,共享库名支持模式匹配,这里表示要加载./plugin目录所有so共享库,
当然也可以是某个具体的共享库名。

随后可以通过CPluginHelper::Create方法,根据类名称创建该类的对象,实现了参数化创建对象
的目的,然后就是对该对象的调用,当不用该对象时,需要调用delete来删除。

最后,调用CPluginHelper::Unload将指定共享库卸载。

转载于:https://my.oschina.net/u/1450061/blog/204564

linux下C++ 插件(plugin)实现技术相关推荐

  1. linux下的几种隐藏技术

    0x00 前言 攻击者在获取服务器权限后,会通过一些技巧来隐藏自己的踪迹和后门文件,本文介绍Linux下的几种隐藏技术. 0x01 隐藏文件 Linux 下创建一个隐藏文件:touch .test.t ...

  2. linux eclipse插件安装,Linux 下 EclipseME 插件的安装步骤

    Linux 下 EclipseME 插件的安装步骤 最近在linux下作开发,参考网络资料,成功将 eclipseME 插件安装至 eclipse.这里记录下来,只为了方便更多的人! 背景: linu ...

  3. Linux 下的屏幕取词技术

    Linux 下的屏幕取词技术 作者: 于明俭 屏幕取词即当鼠标在应用软件所显示中/英文的地方滑过, 则有一小窗口 出现在离单词附近, 上面出现鼠标下面单词的解释. 屏幕取词以前一直是 MS Windo ...

  4. Linux下C开发实用小技术、好代码总结 —— 银行项目

    我现在从事的工作是做银行外包的软件项目(HCE.ApplePay)的后台业务处理,作为公司外派人员,在银行上班.主要开发环境就是在Red Hat的linux服务器上用C语言进行二次或者三次的开发来实现 ...

  5. linux下浏览器插件 打开本地程序,使用Url Schemes打开本地程序

    测试: 将下列内容保存为test.html,用浏览器打开,单击"Test Url Schemes"超链接,即可看到效果(会启动相应的程序) 实现: windows下: 只要向注册表 ...

  6. linux 无线传输,嵌入式Linux下图像存储与无线传输技术研究

    摘要: 随着嵌入式系统和无线通信技术的迅速发展,以嵌入式操作系统为平台,构建文件系统实时存储数据并依赖于无线网络传输数据的技术得到越来越广泛的应用,尤其是应用于远程无线监控系统中的视频图像业务的实时存 ...

  7. Linux下gdb(插件pwndbg、pead、gef)安装及调试常用指令

    gdb 一.安装指令 如果没安装gdb,先使用以下指令安装gdb sudo apt-get install gdb 先装,因为这个带有 parseheap.以及 heapinfo 等指令,有的场景下更 ...

  8. linux下如何搜索某个文件,技术|如何在 Linux 中查找一个文件

    对于新手而言,在 Linux 中使用命令行可能会非常不方便.没有图形界面,很难在不同文件夹间浏览,找到需要的文件.本篇教程中,我会展示如何在 Linux 中查找特定的文件. 第一步要做的是find 命 ...

  9. linux下下载fnl数据,「技术讲堂第二期」|不用到处找,FNL数据直接用!

    FNL是National Centers for Environmental Prediction (NCEP)提供的一套覆盖全球大气的多变量的(如:风场.温度.位势高度等).空间均匀分布的(水平分辨 ...

最新文章

  1. 一: 建立Vue sampleproject
  2. chmod a+r *:用户自己使用此命令,柯给所有用户添加可读的权限
  3. php 论坛_推荐一个基于话题的高性能轻型开源PHP论坛程序
  4. 按实际价格重估在版本 0, 财政年度 2016 中不可能
  5. 5.创建表,使用alter进行表信息的增删改,Oracle回收站,集合运算
  6. 十六进制转double
  7. ESP3 + ESP-IDF | 串口1 - 简单的串口回环测试
  8. django-分页自带的分页-自定义分页
  9. mysql 创建定时任务_mysql创建定时任务
  10. DELPHI XE5-8 弹出列表框供选择
  11. java执行Sql脚本
  12. php 小程序生成海报,轻松生成小程序分享海报
  13. 全国计算机绘图师第三期工业产品,工业产品类CAD技能等级考试试题集
  14. 下载C语言标准库源码
  15. 解决go get时,遇到unrecognized import path的问题
  16. 少爷秋游云台山:沿途风景为其绽放
  17. python代码复制运行不了_pycharm 复制代码出现空格的解决方式
  18. SQL 查询的分布式执行与调度
  19. 小记!华为 8.0系统切换APP内语言(中英文)无效(其他版本手机均有效)。
  20. 顾往前行,我的前端之路系列(二)

热门文章

  1. Centos7安装Nginx+PHP
  2. 学习资料,欧姆社学习漫画-电力电气类-漫画电池
  3. 南京邮电大学网络攻防训练平台(NCTF)-异性相吸-Writeup
  4. POJ_1195 Mobile phones 【二维树状数组】
  5. 从 NavMesh 网格寻路回归到 Grid 网格寻路。
  6. 傲游5里保存的网址,在傲游4不能同步?外加几句吐槽
  7. Android自定义控件之轮播图控件
  8. B/S系统常见缺陷整理和解决方案
  9. linux中如何清空一个文件的内容
  10. 类型初始值设定项引发异常