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