从0到1构建自己的插件系统--类注册

从0到1构建自己的插件系统–类注册

ps:纠正前面一个错误,之前的函数获取类的接口列表返回了数组,这是没有这个用法。可以使用std::vector代替,或者返回数组指针。

插件的核心

在之前的一篇文章中,我么了解到了建立插件的优势以及如何去建立一个插件类,但最终的目的还是为了能够在在插件dll中找到这个类并且创建它,这就是我们写插件的目的,这也是我们写插件的核心。类注册就是为了实现类的对象的创建工作,类注册的方式最核心的其实就是写回调函数(可以是全局函数或者是类的静态函数),这种方式可以理解为抽象工厂模式。
dll有两种加载模式,一种是静态加载模式(依赖lib文件)以及动态加载模式(直接通过windows api加载dll库)。对于插件使用的是动态库的加载模式。因此我们需要在加载的时候识别某个函数并执行它,完成初始化的工作。那么类注册的信息就应该写在这个函数中,在调用的时候就可以获取类的注册信息,那么类的信息是如何存储的呢?

类信息存储

类的信息在插件中主要有类名,对应的接口的ID列表,创建类对象的回调函数。当然有的时候为了识别一个接口的多个实现(我们可以给不同的实现加一个int的标记,也可以是通过真实类名来获取,但是真实的类名如果改了可能不容易识别,因此需要在接口文件中增加类名的宏定义)。

//类信息存储类,classentry类;
//类的创建回调函数申明,通过类的接口ID来查找实现类(不要要注意的是,如果一个接口的ID有多个实现类,那么会直接找第一个,如果要查找特定的类,在后面的程序中会有介绍;
typedef IUnkown *(*CreateClass)(long iid); 
class ClassEntry //这个类需要导出;
{
private:
    char* _class_name;//类名是有模块名字的;
    std::vector<long> _clsid_list;//接口的ID列表,主要是为了多继承,单继承只有一个接口;
    CreateClass _create_class;//创建类的函数指针;
    int _flag;//通过一个接口的多个实现类标记,如果没有这个,就通过类名来判断;
public:
    ClassEntry(const char* class_name,std::vector<long> clsid_list,CreateClass create_class,int flag=0)
    {
        _class_name=class_name;
        _clsid_list=clsid_list;
        _create_class=create_class;
        _flag=flag;
    }
    const char* className() const
    {
     return _class_name;
    }
    std::vector<long> clsidList() const
    {
      return _clsid_list;
    }
    CreateClass  createClass() const
    {
    return _create_class;
    }
};

动态库初始化函数实现

我们要有个可以在外部识别的函数,这个函数的主要功能就是为了完成类的注册信息获取(可以一个类或者多个类)

基本函数实现

 //返回的是std::vector列表,如果是想C语言调用,还是返回普通的数组指针(主要考虑野指针的问题)
extern "C"  __declspec(dllexport) std::vector<ClassEntry> initClasses(const char* module_name) 
{
    static std::vector<ClassEntry*> module_class_list;
    //NormalObject<Sample>是之前提到的类;
    //sample类
    auto cls=new ClassEntry(module_name+"sampe",Sample::clsidList(),(CreateClass)&dan::NormalObject<Sample>::create)
    module_class_list.push_back(cls);
    //后面可以用同样的方法存储相同的类;
    ...
    return module_class_list;
}

上面的实现是个基本的逻辑,但是如果让写插件的人员去维护这个逻辑,还是挺麻烦的,因此我们可以使用宏的方式优化这个定义,即将这个函数分为三个宏(函数开头、函数中间实现部分、函数结尾)来实现

注册函数的优化
//定义注册函数的开头部分(包括一些不影响的全局变量、以及其他一些固定函数),输入模块名(这个模块名字一般跟插件的名字相同)
#define MODULE_BEGIN(module_name)  \
const char* char_module_name = name; \
OUTCAPI const char* moduleName() { return char_module_name; }\ //返回模块的名称;
extern "C"  __declspec(dllexport) void* initClasses() {\
#endif

//定义函数的中间部分(类的信息注册部分)
#define DEFINE_CLASSENTRY(cls)      \
    module_class_list.push_back(\
    new ClassEntry(char_module_name + "."+ #cls, \
             cls::clsidList(), (CreateClass)&NormalObject<cls>::create));
    
//定义函数的结束部分;
#define MODEL_END()  return module_class_list;}

上面的三个宏就解决了我们initclasses每个插件开发者自己编写这个函数实现注册的问题。

编写插件

插件的内核逻辑我们已经解决了,现在从头梳理下二次开发者开发插件的步骤

  • 新建一个普通的dll,编写接口文件(如果不需要新的接口,直接实现已有接口就可)
  • 根据接口定义自己的实现类(注意在类的开头要加上一个宏CLASS_DEFINE)
  • 新建一个register.cpp文件来编写以下代码
MODULE_BEGIN(SampleDll) \\函数开头
DEFINE_CLASSENTRY(Sample) \\类注册;
DEFINE_CLASSENTRY(...)//可以添加多个类
MODEL_END()  \\模块结束标记

怎么样是不是so easy!!!
微信公众号

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

揽月凡尘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值