c++插件的实现方式
2014-05-28 18:16:14 wm198966 阅读数 103

最近工作需要,要实现一个java接口调用c++实现的功能。。

记录一下探索过程。

在现有的eclipse 环境安装c++,所以只能采取插件方式

期间查考了下安装文献:http://blog.csdn.net/wangxueang224/article/details/9366731

http://wenku.baidu.com/link?url=_ZTo-CKWCX0SMXzKPaHOirvekIeZhFP9s0ncE2oAMTAM4DGLxQLTKVcQdgDh8N9YnneSDliOxZ7OMIonhu4jTnfsjGyympRkiu3fg6ioF2e

http://aofengblog.blog.163.com/blog/static/6317021201011972451154/

但是期间还是有不少问题。记录下最终成功的过程

1.安装cdt:cdt 介意手动安装因为在线安装貌似问题多多。手动下载 

解压复制到eatures plugins 下面下载地址:http://www.eclipse.org/cdt/downloads.php

注意版本。
解压复制到 features  plugins  下面,再打开elcipse就里面就出现 C\C++ 
2.安装MinGW
安装这个的时候..开始下的时官网的最新版本.但无论怎么配置环境变量。始终无法在运行里执行gcc命令。
无奈之下选了之前的版本。瞬间就成功了。郁闷。
重要的是环境变量的配置。这个配置不好。eclipse的工程目录就有问题
我安装的位置在C:\MinGW目录。
    
右击我的电脑,点属性->高级->环境变量。然后:
a、在PATH里加入C:\MinGW\bin
b、新建LIBRARY_PATH变量,如果有的话,在值中加入C:\MinGW\lib,这是标准库的位置。
c、新建C_INCLUDEDE_PATH变量,值设为C:\MinGW\include
d、新建CPLUS_INCLUDE_PATH变量,值为C:\MinGW\include\c++\3.4.5;C:\MinGW\include\c++\3.4.5;C:\MinGW\include\c++\3.4.5\backward;C:\MinGW\include
现在,环境变量已经配置完毕,我们打开一个CMD窗口,进行下验证,看我们的环境变量有没有配置成功。

cmd下输入gcc –v 



成功

然后eclipse 里面必须配置的几个地方:
1.
1.

2.



另外




2013-08-14 01:01:57 xiaohaizi77881314 阅读数 1141

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

  
  
  为了能做到更新插件时无需重新编译主程序,要求主程序中定义的接口是定死的,而接口的实现被放到了具体
  的插件中,这样主程序在运行时刻将插件加载进来,就可以使用这些接口所提供的功能了。在面向对象的系统
  中,各个功能模块被封装到类中,因此在C++中实现插件技术,就需要在主程序中提供基类,并为这些基类定义
  明确的接口,然后在插件(动态库或共享库)中定义派生类,并实现基类中所有的接口。
  
  我们以计算多边形面积为例,首先定义一个基类CPolygon:
  
  /*+********************************************************/
  /*+********************************************************/
  /*+********************************************************/
  
  /* polygon.h */
  
  #ifndef __POLYGON_H__
  #define __POLYGON_H__
  
  
  class CPolygon
  {
  public:
  
   CPolygon(){}
  
   virtual ~CPolygon(){}
  
   virtual double area(void) const = 0;
  };
  
  #endif /* __POLYGON_H__ */
  
  /*-********************************************************/
  /*-********************************************************/
  /*-********************************************************/
  
  注意基类不一定是虚类(有纯虚函数的类),但是接口一定要定义成虚函数,因为最终主程序是通过基类指针
  来调派生类的接口函数,另外如果基类中有资源分配(new)的话,析构函数一定要定义成虚的,否则不会被
  调用,造成内存泄漏。
  
  接下来要定义派生类CTriangle,并放到共享库(.so)中:
  
  /*+********************************************************/
  /*+********************************************************/
  /*+********************************************************/
  
  /* .h */
  
  #ifndef __TRIANGLE_H__
  #define __TRIANGLE_H__
  
  #include "polygon.h"
  #include 
  
  class CTriangle : public CPolygon
  {
  public:
  
   virtual double area(void) const;
  
  };
  
  #endif /* __TRIANGLE_H__ */
  
  
  /* .cpp */
  
  #include ".h"
  
  extern "C"
  {
   void * create()
   {
   return new CTriangle;
   }
  
  }
  
  double CTriangle::area(void) const
  {
   std::cout << "area of " << std::endl;
   return 0;
  }
  
  /*-********************************************************/
  /*-********************************************************/
  /*-********************************************************/
  
  其中定义了函数"create"用来创建CTriangle类对象,该函数可让主程序获得CTriangle对象指针,从而
  可以访问CTriangle类对象。主程序通过调用dlsym获取指向该函数的指针,需要指出的是,由于dlsym被
  设计成c-style方式,因此调用c++定义的函数时,需要加上extern "C"
  
  那么主程序是如何调用共享库的呢,代码片段如下:
  
  /*+********************************************************/
  /*+********************************************************/
  /*+********************************************************/
  
  typedef CPolygon* create_t();
  
  void * handle = dlopen(".so", RTLD_LAZY);
  
  if( !handle )
  {
   std::cerr << dlerror() << std::endl;
   exit(1);
  }
  
  create_t * create_ = (create_t *)dlsym(handle, "create");
  
  CPolygon * pObj = create_();
  
  if( 0 != pObj )
  {
   pObj->area();
  }
  
  delete pObj;
  
  dlclose(handle);
  
  /*-********************************************************/
  /*-********************************************************/
  /*-********************************************************/
  
  主程序通过dlopen打开.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_ = (create_t *)dlsym(handle, "create");
  CPolygon * pObj = create_();
  
  假如有多个基类,根据这些基类派生出不同类型的类时,无法在主程序中判断使用哪个基类指针。
  
  
  3. 操作繁琐
  
  没有一个统一的操作界面,实现共享库的加载、卸载、派生类对象的创建,特别是当需要加载一个目录
  下所有的共享库时,感觉一个一个地加载太麻烦了,能不能批量加载呢。
  
  
  通过动态类加载和建立Helper类可以很好地解决上述问题,其中dynclass.h/dynclass.cpp中实现了动态
  加载类对象,pluginhelper.h/pluginhelper.cpp实现了Plugin Helper,具体细节见附件。
  
  
  下面简单介绍一下使用步骤:
  
  
  1. 首先定义基类(CPolygon),方法同上。
  
  2. 在共享库中实现派生类
  
  比如CTriangle:
  
  /*+********************************************************/
  /*+********************************************************/
  /*+********************************************************/
  
  /* .h */
  
  #ifndef __TRIANGLE_H__
  #define __TRIANGLE_H__
  
  #include "polygon.h"
  #include 
  
  class CTriangle : public CPolygon
  {
  public:
  
   virtual double area(void) const;
  
  };
  
  #endif /* __TRIANGLE_H__ */
  
  
  /* .cpp */
  
  #include ".h"
  #include "dynclass.h"
  
  DYN_DECLARE(CTriangle);
  
  double CTriangle::area(void) const
  {
   std::cout << "area of " << std::endl;
   return 0;
  }
  
  /*-********************************************************/
  /*-********************************************************/
  /*-********************************************************/
  
  
  注意到此时派生类的实现(.cpp)中已没有了那个讨厌的create了,被我偷偷放到
  dynclass.cpp中了:
  
  extern "C"
  {
   void * createByClassName(const char * strClassName)
   {
   return DYN_CREATE(strClassName);
   }
  }
  
  由于对任何派生类而言,该函数的实现都一样,因此只需要实现一次,对使用者是不可见的,这样
  了从派生类中拿走的目的。
  
  另外增加了一个宏:DYN_DECLARE(CTriangle); 参数是类名(这里用到了RTTI),每个派生类对应
  一个这样的宏,该类就可以支持类对象的动态加载了,需要包含头文件dynclass.h
  
  
  3. 在主程序中如何使用
  
  使用起来也非常简单,在主程序(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将指定共享库卸载。
  
  
  本文提供了linux下的实现插件技术的方法,其实下在window下也一样,可以用Loadlibrary代替
  dlopen,用GetProcAddress代替dlsym,具体实现就不细说了。
  
2018-06-19 11:06:09 m0_37944991 阅读数 853

 

 

文中的Qt类(Q开头的类)都可以通过STL库代替

简述

首先由开发人员编写系统框架,并预先定义好系统的扩展借口。插件由其他开发人员根据系统预定的接口编写的扩展功能,实际上就是系统的扩展功能模块。插件都是以一个独立文件的形式出现。
对于系统来说并不知道插件的具体功能,仅仅是为插件留下预定的接口,系统启动的时候根据插件的配置寻找插件,根据预定的接口把插件挂接到系统中。

插件系统所用到的技术

一般而言,构建一个插件系统所用到的步骤为
- 面相接口编程
- 插件管理器
- 动态加载链接库

应用程序提供接口

为了实现功能的扩展,必须向外部插件提供接口,在module_interface.h中定义一个抽象类ModuleInterface作为接口:

#include "plugin_register.h"
#include "module_interface.h"
class ModuleInterface
{
public:
	ModuleInterface();
	virtual ~ModuleInterface() ;
         
        //对外部插件提供的接口
	virtual int parseParamByJson(const QString &json) = 0;
	virtual QString getParamJsonStr() = 0;
	virtual int prepareParam() = 0;
	virtual int process() = 0;
};

实现插件

插件应该包含并实现应用程序提供的接口。在test.h中定义Test,让Test继承并实现ModuleInterface中提供的所有接口:

//Test.h文件
class Test : public ModuleInterface
{
public:
	Test();
	~Test();

	int prepareParam();
	int process();
	QString getParamJsonStr();
	int parseParamByJson(const QString &json);
private:
	int test_int;
};
REGISTER_CLASS(Test, ModuleInterface)//通过宏实现getObject,getName,后面会讲


//Test.cpp文件
#include "test.h"
#include <QDebug>

Test::Test()
	: test_int(0)
{
	qDebug() << "Create Test";
}

Test::~Test()
{
	if (results_->result_data)
	{
		delete[] results_->result_data;
		results_->result_data = nullptr;
	}
}

int Test::prepareParam()
{
	return 0;
}

int Test::process()
{
	return 0;
}

QString Test::getParamJsonStr()
{
	return "None";
}

int Test::parseParamByJson(const QString &json)
{
	return 0;
}

为了让应用程序动态加载插件,需要将插件编译为dll文件。在"plugin_register.h中,插件声明三个导出函数:
- getObject:用于新建一个Test对象并返回该对象的指针;
- getName:用于打印Test相关信息。
- isPlugin:用于判断是否是插件,防止其他dll文件错误导入

#pragma once
#include <QtCore/qglobal.h>

#ifndef BUILD_STATIC
#if defined(TEST_LIB) //这里需要换成对应的库的名字
#define QTCLASSLIBRARY1_EXPORT Q_DECL_EXPORT
#else
#define QTCLASSLIBRARY1_EXPORT Q_DECL_IMPORT
#endif
#else
#define QTCLASSLIBRARY1_EXPORT
#endif

//将getObject和getName封装成宏
#define CLASS_NAME(Name) #Name
#ifdef __cplusplus
#define REGISTER_CLASS(child,base)				\
extern "C" {							\
	QTCLASSLIBRARY1_EXPORT base *getObject(void)	        \
	{							\
		return new child;				\
	};							\
	QTCLASSLIBRARY1_EXPORT const char* getName(void)        \
	{							\
		return CLASS_NAME(child);			\
	};							\
	QTCLASSLIBRARY1_EXPORT bool isPlugin(void)		\
	{							\
		return true;					\
	};							\
}
#else
#define REGISTER_CLASS(child,base)				\
	QTCLASSLIBRARY1_EXPORT base *getObject(void)	        \
	{							\
		return new child;				\
	};							\
	QTCLASSLIBRARY1_EXPORT const char* getName(void)        \
	{							\
		return CLASS_NAME(child);			\
	};							\
	QTCLASSLIBRARY1_EXPORT bool* isPlugin(void)		\
	{							\
		return true;					\
	};
#endif

至此一个插件就实现了。可以按照此方式实现多个dll插件。

动态加载插件

现在来写一个应用程序,功能是加载plugins目录中的所有dll插件,打印出dll相关信息,并调用在插件中实现的函数。

//plugin.h文件
#pragma once
#include <QString>
#include <QFileInfoList>
#include <QVector>
#include <QLibrary>
#include <QDir>
#include <gt_singleton.h>
class ModuleInterface;

#define M_OBJECT ModuleInterface
#define FUN_PTR(fun_ptr_name,result_type_ptr) typedef result_type_ptr* (*fun_ptr_name)(void);
FUN_PTR(GetFunPtr, void);
FUN_PTR(GetObjFunPtr, ModuleInterface);
FUN_PTR(GetObjNameFunPtr,const char);

class Plugin
{
public:
	~Plugin();
	static Plugin& getInstance(void);//实现单例
	bool getFunObject(GetObjFunPtr& funPtr, int index);//获取实例化对象的方法
	bool getFunObjName(GetObjNameFunPtr& funPtr, int index);//获取对象名字的方法
	int getPluginNumber(void);//获取插件的数量
private:
	Plugin();
	Plugin operator=(const Plugin);
	Plugin(const Plugin&);

	QDir m_dir;
	QFileInfoList m_fileInfoList;
	QVector<QLibrary*> m_libs;

	bool getPaths(QFileInfoList& fileInfoList, QString path);//获取所有dll库的路径
	bool loadAll(QVector<QLibrary*>& libs,const QFileInfoList fileInfoList);//动态加载dll库
	bool getFun(GetFunPtr& funPtr, int index, const char* str);//获取事先定义好的dll库导出的函数getObject(),getName();
	bool isPlugin(QLibrary& lib);//判断是不是我们定义的插件库
};
//plugin.cpp文件
#include "plugins.h"
#include <QDir>
const char LIBS_PATH[] = "plugins";
const char GET_OBJECT[] = "getObject";
const char GET_OBJ_NAME[] = "getName";
const char GET_IS_PLUGIN[] = "isPlugin";

Plugin::Plugin()
{
	getPaths(m_fileInfoList, LIBS_PATH);
	loadAll(m_libs, m_fileInfoList);
}

Plugin::~Plugin()
{
	for (int i = 0; i < m_libs.size(); i++)
	{
		m_libs.at(i)->unload();
	}
	qDeleteAll(m_libs);
	m_libs.clear();
}

Plugin& Plugin::getInstance(void)
{
	static Plugin instance;
	return instance;
}

bool Plugin::getFun(GetFunPtr& funPtr, int index, const char* str)
{
	if (index >= m_libs.size())
	{
		return false;
	}
	funPtr = (GetFunPtr)m_libs.at(index)->resolve(str);//通过函数名str和索引,在m_libs中获取函数指针
	if (funPtr)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Plugin::isPlugin(QLibrary& lib)
{
	typedef bool(*Fun_p)(void);
	Fun_p funPtr = (Fun_p)(lib.resolve(GET_IS_PLUGIN));
	if (funPtr)
	{
		return funPtr();//只有lib库中存在isPlugin,并返回值为true。才确定这是插件
	}
	return false;
}

bool Plugin::getFunObject(GetObjFunPtr& funPtr, int index)
{
	return getFun((GetFunPtr&)funPtr, index, GET_OBJECT);
}

bool Plugin::getFunObjName(GetObjNameFunPtr& funPtr, int index)
{
	return getFun((GetFunPtr&)funPtr, index, GET_OBJ_NAME);
}

int Plugin::getPluginNumber(void)
{
	return m_libs.size();
}

bool Plugin::getPaths(QFileInfoList& fileInfoList, QString path)
{
	m_dir.setPath(path);//设置文件夹路径
	QStringList filters;
	filters << "*.dll";
	m_dir.setNameFilters(filters);//设置过滤
	fileInfoList = m_dir.entryInfoList();//扫描并返回文件夹内所有dll库的路径
	if (fileInfoList.size() > 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Plugin::loadAll(QVector<QLibrary*>& libs, const QFileInfoList fileInfoList)
{
	for (int i = 0; i < fileInfoList.size(); i++)
	{//遍历所有dll文件,将正确的插件添加到libs中。
		QLibrary *lib = new QLibrary(fileInfoList.at(i).absoluteFilePath());//放入dll文件路径
		if (lib->load())//加载动态库
		{
			if (isPlugin(*lib))//如果是插件,则添加到libs中
			{
				libs.push_back(lib);
			}
			else//不是插件
			{
				lib->unload();//释放动态库
				delete lib;
			}
		}
		else//加载失败
		{
			delete lib;
		}
	}
	if (libs.size()>0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

整个插件系统已经基本完成,在另一节中将使用反射将插件实例化。使用Qt、C++实现简单的反射

C++继承的实现和方式

阅读数 509

了解继承的概念之后,我们就来学习一下如何实现继承。私有和保护在第14章中我们说到,成员函数或成员数据可以是公有或者私有的。如果是公有的,那么它们可以被直接访问;如果是私有的,那么它们无法被直接访问。同时,我们还提到一个protected保留字,在没有使用继承的时候,它与private的效果是一样的,即无法被直接访问。如果使用了继承,我们就能体会到protected和private的差别。

博文 来自: u011923747

C++回调实现方式

阅读数 6

1。对象指针与函数指针:回调函数参数传入对象this和函数名称,否则只有全局函数或静态函数才能作为函数指针传递2。std::function和std::bindhttps://www.jianshu.com/p/f191e88dcc80C++参考手册中对function的介绍:std::function的实例能存储、复制及调用任何可调用的目标,包括:函数、lambda表达式、bin...

博文 来自: smartgps2008

c++之回调实现方式

阅读数 123

很多时候,我们需要提供一个类,这个类用来发出事件,通知程序中其他的组件进行处理,这可以通过回调来实现,下面是比较优雅的实现:#include#includeusingnamespaceboost;classserialwraper{public:typedefboost::functionCallBack;voidRegisterCallBack

博文 来自: qq_29573053

C++类的实现方式

阅读数 586

假设有一个简单类如下:classTest{private: intm;public: Test(inti){ m=i; } intgetM(){ returnm; } staticvoidprint(){ cout

博文 来自: xufeng0991

字符串分割的c++实现方式

阅读数 883

char*p=strtok(str,seprator);while(p){CCLOG("%s\n",p);p=strtok(NULL,seprator);}

博文 来自: sonikk
没有更多推荐了,返回首页