轻量级插件框架x3py

发布时间 2023-05-05 18:11:16作者: 敲什么敲

1、x3py用途

1)x3py 作为一个轻量级的C++插件框架,面向C++开发人员,首要目标是能快速容易的开发出中小型的软件、软件以插件形式模块化设计。其插件既可灵活组合到各个系统,又能单独拆开使用和测试。

2)x3py 可在Win/Linux/MacOS等平台上编译运行,可使用VS或GCC编译,具备基本的跨平台兼容性。框架力求最简化,核心设计目标为“实用、简洁”。

2、客户端引入x3py框架的目的

1)它可以实现类似COM组件开发效果,又具有跨平台兼容性。可以很方便的实现一个跨平台的组件化客户端开发框架。在引入x3py后,我们实现的核心框架层次从下到上可以如下所示。

  • FrameWork:最底层的基础框架层,提供最基本的公共函数,如日志、网络处理、基础ui框架等。x3py框架属于这一层。
  • MiddlePlugin:中间插件层,主要封装了提供给应用层使用的公共插件,提供公共的接口给应用层调用。在引入x3py使得客户端面向接口开发后,我们程序员的主要工作就在开发这些公共插件。
  • Application:应用层,应用层框架就比较简单了,主要负责插件的加载和接口调用,一些简单的ui处理等。

2)使用 x3LoadLibrary 函数加载动态库文件。使用此函数而不是WinAPI的 LoadLibraryA 函数,是为了能实现跨平台同时确保自动调用 x3InitPlugin 函数进行插件初始化。

3)使用 x3FreeLibrary 函数卸载动态库文件。使用此函数而不是WinAPI的 FreeLibrary 函数,同样也是是为了能实现跨平台同时确保自动调用 x3FreePlugin 函数进行插件注销。

3、如何实现插件

1)定义接口

首先定义接口类,此接口类从 x3::IObject 派生,包含纯虚函数,使用X3DEFINE_IID定义接口ID。

#ifndef X3_EXAMPLE_ISIMPLE_H
#define X3_EXAMPLE_ISIMPLE_H

#include <objptr.h>

class ISimple : public x3::IObject
{
X3DEFINE_IID(ISimple);
virtual int add(int a, int b) const = 0;
virtual int subtract(int a, int b) const = 0;
};

#endif

 2)定义类UID

一个插件类如果要让外部模块能创建对象实例,需要指定插件类的全局唯一标识信息,一般采用GUID串来标识插件类。通常一个插件类UID常量可以定义成如下形式:

const char* const clsidSimple = "94071767-ba6b-4769-9eb4-2ebf469289f3";

类UID既可以定义在单独的头文件中,也可以就定义在接口的头文件中。

我们的习惯做法是:如果一个插件类定义了多个接口文件,则将类UID定义在单独的头文件中,之后每个接口文件包含此头文件;如果一个插件类仅定义了一个接口,为方便起见则将类UID就定义在接口的头文件中。

注意:类UID不可重复,否则将会覆盖已向管理器注册的类,导致其他模块无法创建原来类的接口对象。

 3)实现接口

使用 X3BEGIN_CLASS_DECLARE 等宏来指定一个类实现了哪些接口。这里的CSimple是一个实现类的例子。其中构造函数和析构函数申明为保护函数,所有实现的接口函数申明为私有函数,这是推荐方式,但不是必须的。

这样做是为了明确表明不允许直接实例化和delete销毁,也不允许直接调用实现类的接口函数。

#ifndef X3_EXAMPLE_SIMPLE_IMPL_H
#define X3_EXAMPLE_SIMPLE_IMPL_H

#include <plsimple/isimple.h> // 包含接口定义

class CSimple : public ISimple // 从接口派生
{
X3BEGIN_CLASS_DECLARE(CSimple, clsidSimple) // 指定类ID
X3DEFINE_INTERFACE_ENTRY(ISimple) // 指定实现的接口
X3END_CLASS_DECLARE()
protected:
CSimple();
virtual ~CSimple();

private:
virtual int add(int a, int b) const;
virtual int subtract(int a, int b) const;
};

#endif

 4)注册实现类

因为插件中的实现类一般不直接用于实例化对象,而是通过 x3::Object 智能指针模板类来创建对象的。所以,我们需要在插件中注册可供实例化的类以及说明此类是否是单例。

注册实现类的通常做法是在工程的一个.cpp文件(通常为 module.cpp)中,包含 pluginimpl.h 和 modulemacro.h 文件,然后使用 XBEGIN_DEFINE_MODULE 等宏来申明注册哪些实现类。下面是示例:

#include <module/plugininc.h>
#include <module/pluginimpl.h> // 实现插件的导出函数
#include <module/modulemacro.h> // 注册实现类的宏定义

#include "plsimple.h" // 包含实现类

XBEGIN_DEFINE_MODULE()
XDEFINE_CLASSMAP_ENTRY(CSimple) // 注册普通实现类或单实例类
XDEFINE_CLASSMAP_ENTRY_Singleton(YourSingletonClass)
XEND_DEFINE_MODULE_DLL() // 插件动态库

OUTAPI bool x3InitializePlugin() // 插件加载时执行,用于额外初始化
{
return true;
}

OUTAPI void x3UninitializePlugin() // 插件卸载时执行,用于释放额外数据
{
}

 其中 x3InitializePlugin() 和 x3UninitializePlugin() 函数由自己实现,用于额外的初始化和释放操作。这两个函数可以在module.cpp或其他.cpp文件中实现,但是在同一个插件中只能实现一次。如果某个插件不实现任何接口,只使用其他插件的接口,则可以在module.cpp中使用下面更简单的形式:

#include <module/pluginimpl.h>
#include <module/modulemacro.h>
XDEFINE_EMPTY_MODULE()