从0到1构建自己的插件系统--插件管理

从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:文中所有的代码都属于伪代码
喜欢我就关注我吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

揽月凡尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值