• 插件框架内核的设计


    1、应用接口定义

    没有基类,没有显式的接口ID,全部由纯虚函数组成,例如:

    interface Ix_MyObj
    {
        virtual void foo() = 0;
    };

     

    2、实现接口

    接口实现类(也叫组件类):从接口派生,不需要特殊基类,没有显式的组件类ID,例如:

    #include "Ix_MyObj.h"

    class Cx_MyObj : public Ix_MyObj
    {
    protected:
        Cx_MyObj();
        virtual ~Cx_MyObj();

        virtual void foo();
    };

    为了针对接口编程、让使用者不依赖于具体实现类,该类应当不能直接实例化,所以可以将构造函数设置为保护成员。

    一个实现类可以实现多个接口,其方法是从多个接口派生,然后实现接口的函数。

    实现类可以继承,这样就允许多个类复用相同的实现部分。

     

     

    3、组件类ID的形式

    要实例化一个组件类,需要知道其组件类ID。通常组件类ID有三种形式:

    1)在类定义中使用__declspec(uuid())关键字指定,使用__uuidof引用该组件类ID,该方法在COM中广泛使用;

    2)直接使用GUID字符串定义一个常量名,需要用到组件类ID时使用该常量名,不依赖于编译环境;

    3)使用DWORD值定义唯一的模块ID,组件类ID在模块ID的基础上进行1到255的偏移,该方法在飞腾创艺产品中使用。

    考虑到尽可能通用、编译环境的uuid复杂性,选择了第二种方法。

     

    专门在一个H文件中定义可供外部实例化的组件类的ID,这样使用者不需要知道该类如何实现就能使用该组件类ID,例如:

    const LPCWSTR ClsID_MyObj        = L"80313e22-597a-4216-a282-b8ed85722c9c";
    const LPCWSTR ClsID_OtherObj    = L"d57720ee-f816-4f40-a47b-44ea47b15321";

    4、公共接口 Ix_Object

    所有接口都可转换到Ix_Object接口,该接口使用引用技术机制管理对象生命周期。

    Ix_Object的继承关系如下图所示:

    image

    interface Ix_Object
    {
        virtual void AddRef() = 0;
        virtual void Release() = 0;
    };

    在模板类Cx_Object中实现了Ix_Object接口,实现了对象创建函数(静态成员)CreateObject。

    5、单实例对象

    用模板类Cx_SingletonObject标记单实例类,Cx_SingletonObject在第一次创建对象时将记下该对象,下次再创建时直接返回该对象的引用,同时将该对象添加到全局链表中,以便模块卸载时能销毁该对象。

    Cx_ModuleItem类负责管理一个模块内的单实例对象,采用了链表技术。其ClearModuleItems函数在插件模块卸载时调用。

    6、标记一个模块有哪些可实例化的组件类

    在一个模块中标记出有哪些组件类可实例化、组件类对应的类ID、组件类创建类型(普通类、单实例类、支持特殊接口的单实例类等),在一个CPP文件中统一指定这些信息便于管理,避免分散在各个CPP文件中。这些信息记录到一个_XCLASSMETA_ENTRY 静态数组中,数组元素包含组件类ID、类名、创建对象的模板类等信息。

    _XCLASSMETA_ENTRY 的 clsid 为组件类ID(如ClsID_MyObj),pfnCreator 为对象创建函数的地址(如&Cx_Object<Cx_MyObj>::CreateObject)。该模块的所有可实例化组件类的信息都放到 _XCLASSMETA_ENTRY::s_classes 数组中。

    struct _XCLASSMETA_ENTRY
    {
        LPCWSTR                clsid;
        PFNXObjectCreator    pfnCreator;

        static const _XCLASSMETA_ENTRY s_classes[];
    };

    下面是一个模块的组件类数组信息的例子:

    const _XCLASSMETA_ENTRY _XCLASSMETA_ENTRY::s_classes[] = {
        { ClsID_MyObj, &Cx_Object<Cx_MyObj>::CreateObject },
        { L"", NULL }
    };

    为了方便于使用,利用宏来简化上面的代码,简化后的结果为:

    XBEGIN_DEFINE_MODULE()
        XDEFINE_CLASSMAP_ENTRY(ClsID_MyObj, Cx_MyObj)
    XEND_DEFINE_MODULE()

    需要下列宏:

    XBEGIN_DEFINE_MODULE()

        XDEFINE_CLASSMAP_ENTRY(clsid, cls)

        XDEFINE_CLASSMAP_ENTRY_Singleton(clsid, cls)

        XDEFINE_SPECIAL_INTERFACE_ENTRY_Singleton(clsid, iid, cls)

    XEND_DEFINE_MODULE()

    7、创建对象的原理

    使用 xCreateInstance 函数创建对象,创建一个对象的内部过程为:根据 clsid 在 _XCLASSMETA_ENTRY::s_classes 中查找对象创建函数地址,创建出一个对象,得到 Ix_Object 指针,然后动态转换为特定接口。

    对于扩越多个插件的对象创建,是由插件管理器来进行的。由插件管理器将所有插件的 clsid 对应的对象创建函数地址管理起来,当在一个插件内部无法创建对象时,进入插件管理器继续创建对象。

    使用智能指针类Cx_Interface和Cx_Ptr来进行对象创建、对象生命期管理。

    8、插件管理器原理

    将所有插件管理起来,使得插件模块之间可以相互使用接口。具体内容和以前版本相似,省略。

  • 相关阅读:
    form表单回车提交
    Mac os x下配置nginx + php
    Mac下git命令自动补全
    关于javascript中的操作符&&和||的最终返回值
    ARM 裸机程序学习 01 点亮LED
    LINUX SHELL 中 2>&1 重定向的问题
    项目经理到底关心项目的什么?——有关外包项目成本的计算
    ARM 裸机程序学习 03 发送SOS信号(汇编 + C)
    ARM 裸机程序学习 02 按响BEEP
    备忘录 Linux及其内核杂项知识
  • 原文地址:https://www.cnblogs.com/rhcad/p/1605950.html
Copyright © 2020-2023  润新知