UNIX下C++实现动态载入对象

VC里面实现动态对象载入已经不是什么新鲜事情了,很多的plug-in技术就是例子。Unix下,通过动态载入so获得一个对象也不是什么难事,不过对这个对象的管理就是一件比较麻烦的事情了。一般的需求如下:
  有class TMyObj,准确说TMyObj应该是一个接口,根据不同具体情况会有不同的实现,例如 TMyObj1、TMyObj2等等……而这些TMyObj1和TMyObj2分别保存在不同的so当中,需要根据不同的时候load不同的so,建立相应的对象。由于这些对象都拥有TMyObj的接口,所以对于外部来说对这些类的使用就像对TMyObj的使用一样。
  看起来好像比较简单,只要在so里面引出一个函数: 

TMyObj * onCreateObject(void);

  而函数在so中的具体实现就是建立不同的子类,例如在obj1.so中:

  TMyObj * onCreateObject(void)
   { return new TMyObj1; }

  使用的时候只需要动态load入obj1.so,并且找到onCreateObject函数的入口,就可以建立一个具有TMyObj接口的TMyObj1了。
  至于释放对象,一般有两种方法:
方法一:
  so中包含另外一个函数:

  void onDestroyObj(void * p)
  {
    TMyObj1 * tp = (TMyObj1 *)p;
    delete tp;
  }

  从so中导出该函数,并在删除对象的时候调用。
方法二:
  TMyObj的析构函数声明为虚函数,那么从so导出的onCreateObject()建立的对象,直接执行delete删除就行了,由于析构函数是虚函数,编译器会正确的调用TMyObj1的析构函数。
  当然,方法二是比较简单而优雅的方法,既然对于C++来说接口就相当于纯虚函数,多增加一个析构的虚函数又何妨呢。但是无论使用哪种方法,都要注意一个问题,就是载入的obj1.so的生命周期要比最后一个TMyObj1的生存周期长。即只要内存中还存在TMyObj1对象,obj1.so就要一直在内存中,不能卸载。要保证这个同步,是比较麻烦的事情。下面就说说我的解决方法:
  
  首先,要选择一个通用的载入so的lib,这个可以参考一下common c++的DSO(在file.h)里面。(不想使用common c++?我也只是说“参考”而已)。这个支持DLL和so,通过成员函数void *operator[](const char *);获得指定的symbol的入口。
  其次,就要选择一个通用的SmartPtr。这个当然Loki是首选,Loki的SmartPtr的灵活性比boost的smart_ptr强多了,而且Loki也小巧的多。
  然后就要实现一个简单的so的manager,其实应该说是一个动态object的factory:

class TObjFactory : protected DSO
  {
  public:
    TObjFactory(void);
    
    void load(const std::string & strPath);
    void * createObj(void) const throw (TSOException);
  protected:
    typedef void * (*funcCreate)(void ** p);
    funcCreate  m_pCreator;
  };

  可以想象这个类干些什么:load就是载入相应的so,然后获得so中onCreateObject函数的入口,并赋给成员m_pCreator。而createObj就是调用m_pCreator建立对象。不过有所不同的是 m_pCreator所指向的函数形式是void * funcCreate(void ** p),而多出来void **p用处就是可以让so中的构造函数中产生的exception能够传递出来。这个不能说不是so的麻烦之处,so中函数的exception不能被外部捕获,所以只好这样子做了。
  现在,关键的地方来了,就是要保证这个TObjFactory的生存周期了。选择Loki的SmartPtr就能派上用场了。
  Loki的SmartPtr可以自己选定适用的StoragePolicy,这正是我们需要的,参考DefaultSPStorage,可以做我们的TMySOStoragePolicy:

template<class T>
   class TMySOStoragePolicy
   {
    ..
   protected:
    void Destroy()
        { 
         delete pointee_;
         m_pFactory = SmartPtr<TObjFactory>();
        }   
   private:
    SmartPtr<TObjFactory> m_pFactory;
    StoredType       pointee_;
   };

  显而易见,这样做的目的就是要保证释放指针的时候就减少TObjFactory的引用计数。
  好了,现在就是主角了:
  

template<class T>
  class TDObj : public SmartPtr<T,RefCounted,DisallowConversion,AssertCheck,TMySOStoragePolicy>
  {
  public:
    TDObj(void);
    TDObj(const TDObj & obj);
    ..
    
  protected:
    friend class TDObjManager;
    TDObj(T * p, SmartPtr<TObjFactory> pManager);
  };
  
  class TDObjManager
  {
  public:
    
    template<class T>
     static TDObj<T>  createObj(const std::string & strKeyName)
     {
       SmartPtr<TObjFactory> pFactory = getFactoryByName(strKeyName);
       //这里面可以做很多事情了,例如访问内存,查找相应的Factory;或者读取配置文件、读入新的so并建立新的Factory。
       //或者根据一些淘汰算法,先淘汰内存的Factory,然后重新载入新的Factory等等。
       std::auto_ptr<T> _au( static_cast<T *>(pFactory->createObj()) );
       return TDObj<T>( _au.release(), pFactory);
     }
  };

  
  以后用起来就简单多了:
  

class TMyObj
  {
  public:
   virtual ~TMyObj(void);
   virtual int func(void) = 0;
  };
  
  TDObj<TMyObj> obj1 = TDObjManager::createObj<TMyObj>( "obj1.so") );
  TDObj<TMyObj> obj2 = TDObjManager::createObj<TMyObj>( "obj2.so") );
  
  cout << obj1->func() << endl;
  cout << obj2->func() << endl;

  说了这么久,都是主程序的调用,而so中应该如何呢?其实也很简单:
  

class TMyObj1 : public TMyObj
  {
  public:
    TMyObj1(void);
    ~TMyObj1(void);

    static void onStaticInit(void);
    static void onStaticDestroy(void);
    static const char * getVersion(void);
    static const char * getObjectName(void);
    
    virtual int  func(void);
  };
  
  DECLARE_SO_INTERFACE(TMyObj1);

  
  DECLARE_SO_INTERFACE其实是一个为了方便编写程序而定义的宏:

#define DECLARE_SO_INTERFACE(x) extern "C" { \
    void onInstallDLL(void);   \
    void onUninstallDLL(void);   \
    const char * onGetVersion(void); \
    const char * onObjectName(void); \
    void * onCreateObject(void ** ppException);  \
   }; \
   void onInstallDLL(void) { x::onStaticInit(); }    \
   void onUninstallDLL(void) { x::onStaticDestroy(); }  \
   const char * onGetVersion(void) { return x::getVersion(); }  \
   const char * onObjectName(void) { return x::getObjectName(); } \
   void * onCreateObject(void ** pException) { \
    try { \
     *pException = NULL; x * p = new x(); return (void *)p; \
    }catch(std::exception & e) { \
     *pException = new std::exception(e); \
     return NULL;  \
    } \
   }

   
  可以看到除了导出onCreateObject函数以外,还导出了:
  TMyObj1::onStaticInit用于载入so的时候执行初始化操作;
  TMyObj1::onStaticDestroy用于卸载so的时候执行清理操作;
  TMyObj1::getVersion 获得对象的版本信息
  TMyObj1::onObjectName 获得对象名信息等
  可以扩展前面的TObjFactory,实现这些功能。

  同理,我们可以做obj2.so:

class TMyObj2 : public TMyObj
  {
  public:
   TMyObj2(void);
   ~TMyObj2(void);
  
   static void onStaticInit(void);
   static void onStaticDestroy(void);
   static const char * getVersion(void);
   static const char * getObjectName(void);
  
   virtual int  func(void);
  };
  
  DECLARE_SO_INTERFACE(TMyObj2);

  
  
  另外,一个值得讨论的问题是:C++由于没有反射机制,所以无法实现设值注入和构造注入,只能实现接口注入。不过一般来说也已经足够使用了。

时间: 2024-10-21 16:46:50

UNIX下C++实现动态载入对象的相关文章

Linux/Unix 下调试动态库(.so文件)

问题描述 Linux/Unix 下调试动态库(.so文件) 需要调试一个C语言编写的动态库,这个动态库也是我自己写的编译的时候加了-g参数. 但是这个动态库是给oracle数据库调用的,也就是在存储过程里面调用这个动态库.由于这个动态库是新写的,经常有问题需要用gdb跟踪代码调试.我要怎么做才能调试这个动态库呢??? 目前想到的一个办法就是再写一个C程序调用这个动态库然后gdb调试.但是这个动态库提供给数据库的接口很多全部写出来比较费时间.希望找个方便点的方法,类似于gdb直接调试运行中的程序.

如何使用动态共享对象的模式来安装PHP_php基础

PHP 通常被安装在 Linux/Unix 操作系统上,并且搭配 Apache 服务器一起使用.在将 PHP 与 Apache 服务器一起安装的时候,你有三种不同的安装方式可以选择:静态模块,动态共享对象(Dynamic Shared Object, DSO)以及 CGI 程序执行文件.  在这里我建议大家使用动态共享对象的模式来安装 PHP,这是因为这种安装方式为日后的维护与升级提供了极大的便利.假设你一开始安装 PHP 的时候,只加入了PHP 的数据库相关模块.几天以后你决定再加装 PHP 

Asp.Net中页面运行时动态载入的UserControl内元素的事

在Asp.Net页面的开发过程中,我们肯定经常会用到自定义的UserControl来复用部分页面元素,我们有两种使用UserControl的方式 1.在设计时往页面里添加需要的UserControl(最常用的就是从SolutionExplorer拖ascx到设计页面) 此种情况下,如果将UserControl放置在runat=server的html标签中,将可能导致UserControl内的元素事件处理不能正确执行. 例如:我们有时用一个div作为边框包含了需要的UserControl,而出于在

JQuery快速实现Tab的AJAX动态载入实例讲解

 这篇文章主要介绍了使用JQuery快速实现Tab的AJAX动态载入(实例讲解)需要的朋友可以过来参考下,希望对大家有所帮助 下面我就简单讲一下实现过程:   1.找到链接源,我这里是一串的Li下的链接    2.处理样式    3.当鼠标移过时载入链接源的网站到指定容器,并切换样式让点击事件返回false,这里不会应该点击到链接源网页    5.Over了.    脚本:   代码如下:     {     //homeNews           var tid = "#homeNews&q

javascript-求前端大神解释js动态访问对象属性的问题

问题描述 求前端大神解释js动态访问对象属性的问题 在js我有一个这样的需求:对象temp里面有name1name2,name3 属性这样访问属性:temp.(""name""+1)temp.(""name""+2)temp.(""name""+3)为什么不行?但是在groovy里是可以的 解决方案 这个市解释器不一样吧你要是使用 temp['name'+1]肯定是好使的.所以编写的js

.net下实现Word动态填加数据打印_实用技巧

今天研究了一下.net下实现Word动态填加数据打印的做法,觉得颇有收获~       以前做过Excel相关的东西,所以对OFFICE的COM有一些了解,很顺利的找到了需要引用的COM和其帮助文档~具体做法是在引用里添加 COM --------Microsoft word 11.0 object library, 然后引入命名空间: 复制代码 代码如下: using WordApplication = Microsoft.Office.Interop.Word.Application;  u

动态载入js提高网页打开速度的方法_javascript技巧

一般来说如果一次性的载入所有需要的JavaScript代码,就会造成初始网页打开速度变慢,但是很多载入的代码又并不需要使用,这种无谓的性能浪费应该予以避免.如果要动态载入JavaScript代码,可以利用DOM模型在HTML文档中添加<script>结点,并将此结点的src属性(即 外联 Javascript文件)设置为需要动态载入的JavaScript代码. 下面就是完成这样功能的一个示例: (1).新建JsLoaderTest.html文件 <html xmlns="htt

jQuery实现列表内容的动态载入特效_jquery

采用Jquery实现的列表数据动态更新效果,更新的数据可以是ajax请求的数据. CSS: .main { width: 100%; margin-top: 100px; text-align: center; font-size: 12.5px; } th, td { border: 1px solid #ccc; line-height: 40px; padding-left: 5px; } .item:hover { background-color: #efefef; } .item:n

Python中动态获取对象的属性和方法的教程_python

首先通过一个例子来看一下本文中可能用到的对象和相关概念. #coding: UTF-8 import sys # 模块,sys指向这个模块对象 import inspect def foo(): pass # 函数,foo指向这个函数对象 class Cat(object): # 类,Cat指向这个类对象 def __init__(self, name='kitty'): self.name = name def sayHi(self): # 实例方法,sayHi指向这个方法对象,使用类或实例.