从0到1构建自己的插件系统–插件管理
之前的文章已经可以完整的写完一个插件,在本章节中我们解决插件的管理问题
插件管理是可以让各个插件调用的桥梁,达到连通各个插件的目的,插件编写者无需考虑插件之间的依赖关系,所有的这一切都由插件管理类来解决。插件管理主要有两个部分组成,插件管理类和模块信息类。插件管理本身也可以认为是一个插件,这个插件是需要依赖lib的插件,在这个特殊插件中我们需要解决不同类型插件(如C++,JAVA,C#写的二次开发插件)
插件加载
插件接口类
为了解决插件可以使用不同的语言实现的问题,我们通过接口的方式在不同的语言中继承(这个方案在我的SWIG技术专栏中有解决办法)
//IDynamicLibrary.h接口文件,实现插件的加载;
#define CLASS_LIST std::vector<ClassEntry *>
//这个模块的也可以使用插件系统;
#ifdef SWIG
%feature("director") IDynamicLibrary;
#endif
class IDynamicLibrary : public IUnkown
{
REGISTER_INTERFACE(IDynamicLibrary)
public:
enum LangModuleType //之前在注册类提到了一个标记问题,在这里使用枚举标记不同的实现接口类;
{
LangInvalid,
LangCpp,
LangCSharp,
LangJava
};
//关键的两个接口,通过文件名加载插件,可以将文件放在某个固定的文件夹下;
virtual void loadPlugin(const std::string &name) = 0;
//获取所有的类的注册信息;
virtual CLASS_LIST classList() = 0;
//通过注册类信息创建类对象,对于C++而言,只要调用ClassEntry的函数指针CreateClassInstance即可;
virtual IUnkown* createClass(ClassEntry* class_entry)=0;
};
插件加载实现
插件的加载主要是解决动态库的加载,dll内部函数查找,以及类的注册信息处理问题
//cppdynamiclibray类,继承接口IDynamicLibrary
//插件类是一个插件一个实例类对象;
class CppDynamicLibrary : public IDynamicLibrary
{
public:
//这个宏在之前的文章中没有提及,主要是在宏中加了一个获取标记的函数
ClASS_DECLARE2(CppDynamicLibrary, IDynamicLibrary, LangCpp)
virtual bool loadPlugin(const std::string &name) //加载插件
{
int64 hmod = loadLibrary(name);
if (hmod == 0)
{
return false;
}
return _initClasses(hmod);
}
virtual CLASS_LIST classList()
{
return _class_list;
}
private:
//加载类信息;
bool _initClasses(int64 module)
{
if (!module)
{
return false;
}
//这个函数指针的返回值也可以使用void*处理,当然也需要修改插件实现的那块定义;
typedef std::vector<ClassEntry *> (*initClass)();
//查找dll的函数名;
initClass plug_initclasses = (initClass)procAddress(module, "initClasses");
if (!plug_initclasses)
{
std::cout << "非插件dll"; return false;
}
_class_list = plug_initclasses();//执行函数;
return true;
}
private:
CLASS_LIST _class_list;
};
支持其他语言编写插件的优化(不需要其他语言编写插件,可忽略)
根据我们前面所说,注册类信息由ClassEntry类来存储,其中实例化类通过ClassEntry的CreateClass函数指针来创建,但是对于其他语言而言,可能没有函数指针或者委托,那么我们就不能用这个方式创建了,因此我们如果要支持其他语言的插件类注册,需要解决创建实例化类函数的多态问题,解决办法是建立一个创建对象的基类,每种语言编写一个语言插件一种实现,并注册到插件管理器中.同时在ClassEntry注册类中增加一个方法标记这个注册类是那种语言实现的即可
#ifdef SWIG
%feature("director") ILanguageAdapter;
#endif
//这个接口类无需注册,通过插件管理器静态注册的方法来解决;
class ILanguageAdapter : public IUnkown
{
virtual IUnkown* createClass(ClassEntry* class_entry)=0;
};
//对于C++而言,非常简单
class CppLanguageAdapter:public ILanguageAdapter
{
virtual IUnkown* createClass(ClassEntry* class_entry)
{
return class_entry->createClass()();//获取函数指针并调用函数指针;
}
};
//其他语言在各种的语言中继承ILanguageAdapter
插件类注册
//随便一个cpp文件中加上自己的注册代码即可;
MODULE_BEGIN(PluginCore) \\函数开头
DEFINE_CLASSENTRY(CppDynamicLibrary) \\注册插件;
MODEL_END() \\模块结束标记
notes:IDynamicLibrary是一个插件一个对象(负责插件信息的存储,调用),ILanguageAdapter是一种开发语言的插件方式支持一个实例化对象,他们一般情况下是在同一个动态库中实现的.
插件管理
插件管理主要完成的几件事情
- 对加载的插件进行管理
- 插件的延迟加载与卸载(卸载可能并不是特别安全,如果插件的类对象没有及时释放的话)
- 类信息的收集以及创建类对象的核心函数
- 通过类接口获取所有其是实现类的标记列表
- 查找并创建类处对象(下篇文章<<类对象创建机制>>)
插件管理实现
class DLLOUT PluginManager //这个类需要导,该类是一个单例类;
{
enum LoadType
{
LoadTypeNeed,
LoadTypeForce
};
private:
PluginManager()
{
//当前模块中有这个initClasses,可以直接加载,在这个模块的其他地方需要做函数的声明;
_addClassInfo(initClasses());
//注册创建类实例的adpater;
registerAdapter(new CppLanguageAdapter());
}
public:
static PluginManager* instance()
{
static PluginManager* _plugin_manager=null;
if(_plugin_manager==null)
{
_plugin_manager=new PluginManager();
}
return _plugin_manager;
}
bool loadPlugin(const std::string &file_name, LangModuleType module_type, LoadType load_type)
{
//这个定义的实现在后续文章<<类对象创建机制>>中会有说明,Object<T>是一个智能指针类;
Object<IDynamicLibrary> dynamic_lib(module_type);
if (!dynamic_lib->loadPlugin(file_name))
{
return false;
}
auto class_list = dynamic_lib->classList();
_addClassInfo(class_list);
_class_module_list.push_back(dynamic_lib);
}
private:
//获取类信息之后再存储成接口id和对于的实现类列表的方式,这样在类创建时查找类注册信息就会非常快;
void _addClassInfo(CLASS_LIST class_list)
{
//内部函数;
auto load_clisid_list=[&](std::vector<long> clisd_list)
{
for(int i = 0; i < clsid_list.size(); ++i)
{
long id = clsid_list.at(i);
auto cls_entry_iter = _clisd_class_map.find(id);
if (cls_entry_iter == _clisd_class_map.end())
{
CLASS_LIST list_value;
list_value.push_back(*it);
_clisd_class_map[id] = list_value;
}
else
{
cls_entry_iter->second.push_back(*it);
}
}
};
for (auto it = class_list.begin(); it != class_list.end(); ++it)
{
ClassEntry *cls_entry = (*it);
//类的接口ID列表
auto clsid_list = cls_entry->clsidList();
load_clisid_list(clsid_list);
}
}
//通过接口获取当前实现该接口的所有标记列表(通过标记可以找到不同的实现类)
std::vector<int> classListByID(long iid)
{
auto cls_list = _clisd_class_map[iid];
std::vector<int> flag_list;
for (auto it = cls_list.begin(); it != cls_list.end(); ++it)
{
flag_list.append((*it)->flag());
}
return flag_list;
}
IUnkown* createObject(long id,int class_flag)
{
//该实现后后面介绍;
}
void registerAdapter(LangModuleType language_type,ILanguageAdapter* adpater)
{
std::map[language_type]=adapter;
}
private:
//多语言插件化对象用,如果没这个需要,可以不做这个处理;
//其他语言ILanguageAdapter这个对应的实现类的获取基本上可以通过反射获取;
std::map<LangModuleType,ILanguageAdapter*> _adapter_map;
//接口,类注册信息列表;
std::hashMap<long, CLASS_LIST> _clisd_class_map;
//当前的插件列表;
std::vector<Object<IDynamicLibrary>> _class_module_list;
};
至此,插件管理器的主要实现就已经完成了,后一章主要解决类对象的问题。
ps:文中所有的代码都属于伪代码