-
Notifications
You must be signed in to change notification settings - Fork 150
DevolopPlugin
在一个H文件中定义接口,此接口从 x3::IObject 派生,包含纯虚函数,使用 X3DEFINE_IID
(接口名) 定义接口ID。例如下面定义了有两个纯虚函数的接口 ISimple:
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;
};
X3DEFINE_IID
和 x3::IObject
是在 iobject.h 中定义的,通常包含 objptr.h 更方便些,下面是接口文件的完整形式:
#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
其中 #ifndef
的“头文件卫士”形式适用于VC++和GCC等绝大多数编译器,在VC++中也可以使用 #pragma once
的简单形式代替。
既可以在每个H文件只定义一个接口,也可以在一个H文件中定义多个接口,根据复杂程度和喜好决定。接口文件名和接口命名风格根据自己需要决定。
一个组件类如果要让外部模块能创建对象实例,需要指定组件类的全局唯一标识信息,一般采用GUID串来标识组件类。下面的例子定义了一个类UID常量clsidSimple:
const char* const clsidSimple = "94071767-ba6b-4769-9eb4-2ebf469289f3";
既可以在单独的H文件中定义类UID,也可以与接口定义在同一个文件中。通常习惯:如果一个组件类支持多个接口,则将类ID定义在单独的H文件,然后每个接口文件包含此类ID文件;如果一个组件类仅支持一个接口(除IObject外),为方便起见则将类UID和接口定义在同一个文件中。
注意:类UID不可重复(复制已有接口文件时容易发生),否则将会覆盖已向管理器注册的类,导致其他模块无法创建原来类的接口对象。
实现接口就是定义一个类,从接口派生并实现其接口函数。由于没有使用C++的RTTI机制,所以使用 X3BEGIN_CLASS_DECLARE
等宏来指定一个类实现了哪些接口,这样就可以从一种接口转换到另一种接口。这些宏是在 module/classmacro.h 中定义的。
#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
上面是CSimple实现类的例子。其中构造函数和析构函数申明为保护函数,所实现的接口函数申明为私有函数,这是推荐方式,但不是必须的。这样做是为了明确表明不允许直接实例化和delete销毁(当然也不可能实例化,因为IObject函数还没有实现),也不允许直接调用实现类的接口函数。
一个实现类可以实现一个或多个接口,还可以从已有的实现类派生以便继承所有的接口实现(实现继承),或者重载已实现的接口函数。下面是实现继承的一个例子:
#ifndef X3_EXAMPLE_SIMPLE_IMPL2_H
#define X3_EXAMPLE_SIMPLE_IMPL2_H
#include "plsimple.h" // 包含基实现类
#include <plsimple/isimple2.h> // 包含附加接口
#include <plsimple/isimple3.h>
class CSimple2
: public CSimple // 从基实现类派生
, public ISimple2 // 从附加接口派生
, public ISimple3
{
X3BEGIN_CLASS_DECLARE(CSimple2, clsidSimple)
X3DEFINE_INTERFACE_ENTRY(ISimple2) // 指定实现的接口
X3DEFINE_INTERFACE_ENTRY(ISimple3)
X3USE_INTERFACE_ENTRY(CSimple) // 继承已实现的所有接口
X3END_CLASS_DECLARE()
protected:
CSimple2() {}
private:
virtual int add(const std::vector<int>& nums) const;
virtual x3::AnyObject createSimple();
};
#endif
插件中的实现类一般不直接用于实例化对象,而是通过 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/plugininc.h>
#include <module/pluginimpl.h>
#include <module/modulemacro.h>
XDEFINE_EMPTY_MODULE()