UNIX下C++实现动态载入对象
发布日期:2021-10-03 22:59:37 浏览次数:9 分类:技术文章

本文共 4584 字,大约阅读时间需要 15 分钟。

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(voidconst 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( voidreturn x::getVersion(); }  \
    const  char * onObjectName( voidreturn 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++由于没有反射机制,所以无法实现设值注入和构造注入,只能实现接口注入。不过一般来说也已经足够使用了。

转载地址:https://blog.csdn.net/lclflash/article/details/8856800 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:linux下C++ 插件(plugin)实现技术
下一篇:轻量级C++插件框架 x3py 可以用了

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2023年03月15日 20时01分15秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

最新文章

将计算机与局域网互连 需要_,计算机与局域网连接需要的硬件是什么 2019-12-19 12:50:18
第七代电子计算机,Intel第七代酷睿为PC带来的10大革新:Optane存储服了 2019-12-19 12:50:18
线切割软件测试工资,线切割自动编程软件可以得到免费测试版 2019-12-19 12:50:18
计算机对用户的操作做出反应,【2013年自考计算机应用软件专业多媒体技术复习资料(26)】- 环球网校... 2019-12-19 12:50:16
计算机里有什么游戏,电脑上都有哪些游戏 2019-12-19 12:50:16
html 课件DW,DW《网页设计与制作》教学课件.ppt 2019-12-19 12:50:16
计算机应用基础第一章第二章知识点,全国2013年自考《计算机应用基础》教材大纲第二章... 2019-12-19 12:50:17
中职计算机组装与维修教学总结,中职教师工作总结 2019-12-19 12:50:17
计算机三级上机考试题库,2016年3月计算机三级上机题库及答案 2019-12-19 12:50:17
zend studio html乱码,zend studio出现乱码如何解决 2019-12-19 12:50:17
html 点击滚动条滚动位置,jQuery scrollTop() 方法关于点击滚动指定位置的应用 2019-12-19 12:50:14
html5lib 安装命令,git安装、常用命令、错误解决 2019-12-19 12:50:15
三级目录怎么做html,三级目录.html 2019-12-19 12:50:15
html5随鼠标移动动画,使用JS实现气泡跟随鼠标移动的动画效果 2019-12-19 12:50:15
html+table+row+col,html – css位置:粘不使用display:table-row-group 2019-12-19 12:50:15
html文本域实例,Struts2 <s:textarea>文本域实例 2019-12-19 12:50:15
html5制作目录,创建或查看目录 - getDirectory《 HTML5:文件系统 》 2019-12-19 12:50:16
android activity 通信,Android应用开发教程:两个运行的Activity之间的通信 2019-12-19 12:50:13
android 8187驱动 win7,8187无线网卡驱动,教您Realtek瑞昱8187无线网卡驱动 2019-12-19 12:50:13
html背景图渐变自动切换,html – 在背景图像上使用CSS渐变 2019-12-19 12:50:14