linux下C++ 外掛程式(plugin)實現技術

來源:互聯網
上載者:User
應用程式中使用外掛程式技術,有利於日後的版本更新、維護(比如打補丁)和功能擴充,是一種很實用的技術。
其最大的特點是更新外掛程式時無需重新編譯主程式,對於一個設計良好的應用系統而言,甚至可以做到業務功能
的線上升級。本文介紹了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
<iostream>

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
<iostream>

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,具體實現就不細說了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.