C++实现插件化开发

 

产品开发中,为了降低功能增加或变更的成本,在系统设计上就要有可扩展以及可替换的考虑,功能插件化是为软件系统增加扩展点的一种实用方法。

最近在翻vsomeip代码的时候关注到了里面的插件功能实现,这篇文章简要说明一下。

插件实现的简易框架就像下面这张图:

下面是代码示例,大部分内容都写到注释了,这张图就不再一一解释。

//定义插件的公共接口
class Plugin {
public:
  virtual std::string GetName() = 0;
  virtual int GetVersion() = 0;
  virtual int GetType() = 0;
};
​
// 被所有的功能插件继承
template <class T> class PluginImpl : public Plugin {
public:
  virtual ~PluginImpl() {
  }
  PluginImpl(std::string name, int version, int type)
      : name_(name), version_(version), type_(type) {
  }
  // 静态函数便于于获取函数地址
  // 模板参数T既是功能插件的实现类
  static std::shared_ptr<Plugin> GetPlugin() {
    std::shared_ptr<Plugin> p = std::make_shared<T>();
    return p;
  }
    
  // 实现插件公共接口
  std::string GetName() {
    return name_;
  }
    
  // 实现插件公共接口
  int GetVersion() {
    return version_;
  }
    
  // 实现插件公共接口
  int GetType() {
    return type_;
  }
​
private:
  std::string name_;
  int version_;
  int type_;
};
​
typedef std::shared_ptr<Plugin> (*GetPluginFunction)();
typedef GetPluginFunction (*InitPluginFunction)();
​
// 每个插件都需要包含InitPlugin这个symbol作为插件的初始化接口
// 告知编译器是C接口,不进行C++的name managling
// 初始化接口返回获取插件实例的接口
// pluginName是功能插件实现类的名称
#define PLUGIN_INIT_FUNCTION_NAME "InitPlugin"
#define DEFINE_PLUGIN(pluginName)                                              \
  extern "C" {                                                                 \
  GetPluginFunction InitPlugin() {                                             \
    return pluginName::GetPlugin;                                              \
  }                                                                            \
  }

在一个软件系统中,功能接口类可以预先定义多种类型,这里拿Demo举例。

// Demo.h
// 功能接口类定义
class Demo {
public:
  virtual void Test() = 0;
};
​
// DemoPlugin.h
// 定义插件名称
#define DEMO_PLUGIN_NAME "demoplugin"
​
// 功能插件实现类
// 继承PluginImpl的公共接口,将自己作为模板参数传递
// 实现Demo类中定义的接口
class DemoPlugin : public PluginImpl<DemoPlugin>, public Demo {
public:
  DemoPlugin();
  ~DemoPlugin() {
  }
  void Test();
};
​
// DemoPlugin.cpp
// 包含公共的InitPlugin接口
DEFINE_PLUGIN(DemoPlugin)
​
DemoPlugin::DemoPlugin():PluginImpl(DEMO_PLUGIN_NAME,1,1)
{
​
}
// 实现Demo中定义的虚函数
void DemoPlugin::Test()
{
    printf("DemoPlugin::Test()\n\r");
}

通常插件管理还需要实现加载路径下所有插件、加载某类插件、管理所有插件的功能,这里仅是框架示例,并未实现。

// PluginMgr.h
​
typedef void *PluginHandler;
​
// 定义插件管理类接口
// 实现单例的GetInstance接口
class PluginMgr {
public:
  static PluginMgr *GetInstance();
  virtual PluginHandler LoadLibrary(std::string plugin) = 0;
  virtual void *GetSymbolAddress(PluginHandler pluginHandler,
                                 std::string symbolName) = 0;
  virtual void UnloadLibrary(PluginHandler pluginHandler) = 0;
};
​
// PluginMgr.cpp
// PluginMgr虚函数未实现,不能创建实例,实际获取的是PluginMgrImpl实例
PluginMgr *PluginMgr::GetInstance() {
    static PluginMgrImpl pmi;
    return &pmi;
}
​
// PluginMgrImpl.h
​
// 实现插件管理接口
class PluginMgrImpl : public PluginMgr {
public:
  PluginHandler LoadLibrary(std::string plugin);
  void *GetSymbolAddress(PluginHandler pluginHandler, std::string symbolName);
  void UnloadLibrary(PluginHandler pluginHandler);
};
​
​
// PluginMgrImpl.cpp
// 实现加载、卸载以及获取符号地址的接口
​
PluginHandler PluginMgrImpl::LoadLibrary(std::string plugin) {
  return dlopen(plugin.c_str(), RTLD_LAZY);
}
void *PluginMgrImpl::GetSymbolAddress(PluginHandler pluginHandler,
                                      std::string symbolName) {
  return dlsym(pluginHandler, symbolName.c_str());
}
void PluginMgrImpl::UnloadLibrary(PluginHandler pluginHandler) {
  dlclose(pluginHandler);
}

下面是测试代码:

  auto pm = PluginMgr::GetInstance();
  
  // 插件管理器加载libdemoplugin.so
  PluginHandler ph = pm->LoadLibrary("../lib/libdemoplugin.so");
  if (ph) {
    printf("lib%s.so is loaded\n\r", DEMO_PLUGIN_NAME);
​
    InitPluginFunction init =
        (InitPluginFunction)pm->GetSymbolAddress(ph, PLUGIN_INIT_FUNCTION_NAME);
    if (init) {
      printf("get %s() from libdemoplugin.so\n\r", PLUGIN_INIT_FUNCTION_NAME);
​
      GetPluginFunction get = init();
      if (get) {
        printf("call init() from libdemoplugin.so ok\n\r");
​
        std::shared_ptr<Plugin> plug = get();
        // 访问Plugin中定义的公共接口
        printf("plugname is %s version is %d type is %d\n\r",
               plug->GetName().c_str(), plug->GetVersion(), plug->GetType());
        // 访问Demo类中定义的功能接口
        std::dynamic_pointer_cast<Demo>(plug)->Test();
      } else {
        printf("call init() from libdemoplugin.so NOT ok\n\r");
      }
    } else {
      printf("NOT get %s() form libdemoplugin.so\n\r",
             PLUGIN_INIT_FUNCTION_NAME);
    }
​
    pm->UnloadLibrary(ph);
    printf("lib%s.so is unloaded\n\r", DEMO_PLUGIN_NAME);
  } else {
    printf("libdemoplugin.so is NOT loaded\n\r");
  }

运行的输出如下。

libdemoplugin.so is loaded
get InitPlugin() from libdemoplugin.so
call init() from libdemoplugin.so ok
plugname is demoplugin version is 1 type is 1
DemoPlugin::Test()
libdemoplugin.so is unloaded

 

  • 9
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值