cpp
#ifndef EDWMODULE_H
#define EDWMODULE_H
#include <modules/iedwmodule.h>
#include <modules/iedwprocess.h>
#include <module/classmacro.h>
#include "ibaseplugin.h"
class EDWModelModule
: public IEDWModule, public IEDWProcess
{
BLUE_BEGIN_CLASS_DECLARE (EDWModelModule, clsidEDWModel)
BLUE_DEFINE_INTERFACE_ENTRY (IEDWModule)
BLUE_DEFINE_INTERFACE_ENTRY (IEDWProcess)
BLUE_END_CLASS_DECLARE()
public:
EDWModelModule();
virtual ~EDWModelModule();
public:
virtual QAbstractItemModel *getmodel (const char *);
virtual void processEDWMsg (char *msgEDW);
};
#endif // EDWMODULE_H
cpp
BLUE_BEGIN_DEFINE_MODULE()
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton (EDWModelModule)
BLUE_END_DEFINE_MODULE_DLL()
EDWModelModule *hInstance = NULL;
OUTAPI bool bInitializePlugin()
{
return true;
}
OUTAPI std::string bUninitializePlugin()
{
return clsidEDWModel;
}
类定义(edwmodelmodule.h)
`EDWModelModule` 同时继承两个接口:`IEDWModule` (给 UI 取模型用)和`IEDWProcess` (给数据处理方推送消息用)
用宏`BLUE_BEGIN_CLASS_DECLARE(EDWModelModule, clsidEDWModel)` 声明,注册了这两个接口条目。
插件框架下如何被创建
cpp
BLUE_BEGIN_DEFINE_MODULE()
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton (EDWModelModule)
BLUE_END_DEFINE_MODULE_DLL()
- 关键就是`BLUE_DEFINE_CLASSMAP_ENTRY_Singleton` :
这个 DLL 被插件框架加载后,框架通过 类映射表(ClassMap)+ CLSID(`clsidEDWModel` ) 来实例化它,且是 单例 ------其他插件/模块向框架请求`clsidEDWModel` 时拿到的是同一个实例(通常是通过`IEDWModule` 或`IEDWProcess` 接口查询)。
- `bInitializePlugin` /`bUninitializePlugin` 是 DLL 导出的插件初始化/卸载入口,目前基本是空的。
iedwmodule.h
cpp
#ifndef COMPONENT_IEDWMODEL_H
#define COMPONENT_IEDWMODEL_H
#include <objectptr.h>
#include <portability/bpfport.h>
#include <QAbstractTableModel>
const char *const clsidEDWModel = "B42BA7B1-2BD8-T445TT-5555-2344Fdsfsd7";
// EDWModule接口,根据名字返回需要的model
class IEDWModule : public bpf::IBObject
{
BLUE_DEFINE_IID (IEDWModule);
virtual QAbstractItemModel *getmodel (const char *name) = 0;
};
#endif
这个文件就是`EDWModelModule` 对外暴露的 服务契约
1.CLSID------框架里查找这个单例的"身份证"
cpp
const char *const clsidEDWModel = "B42BA7B1-2BD8-T445TT-5555-2344Fdsfsd7";
其他插件不直接`#include` 实现类、也不链接 EDWModule 的库,而是拿着这个字符串向框架的对象管理器请求对象(对应 cpp 里`BLUE_DEFINE_CLASSMAP_ENTRY_Singleton` 的注册)。
2.接口类本身 iedwmodule.h
cpp
BLUE_DEFINE_IID (IEDWModule); // 接口ID,用于 QueryInterface 式查询
-
继承`bpf::IBObject` ,这是框架的COM 式根基类(一般带引用计数 + 接口查询),`BLUE_DEFINE_IID` 给接口一个 IID。
-
全是纯虚函数------这就是为什么 cpp 里`EDWModelModule` 必须逐个实现。
-
典型调用方写法(UI 插件侧)大致是:
用`clsidEDWModel` 创建/取得对象 → 查询`IEDWModule` 接口 →
调`getmodel(EDW_Filter_Name)` 拿到`QAbstractItemModel*` 塞给 Qt 视图。
你可以帮我想想,有没有办法拥有EDWModelModule的两个对象,我不想要单例了,你分析一下你的思路。
路线A:双 CLSID**,**各挂一个单例(改动最小,推荐先考虑)
很多这类COM 风格框架的 "Singleton" 语义其实是" 每个 CLSID 一个实例 "。如果是这样,再注册一个`clsidEDWModel2` 映射到同一个类,就天然得到两个互不干扰的实例,生命周期仍由框架托管,最安全。
-
调用方按需要用**`clsidEDWModel` 或`clsidEDWModel2`** 取对象;
-
数据推送方(`IEDWProcess` )和取模型方(`IEDWModule` )各自明确自己服务哪个实例;
-
不确定点:`BLUE_BEGIN_CLASS_DECLARE(EDWModelModule, clsidEDWModel)`把 CLSID 写在类声明宏里了,同一个类能否在ClassMap 里注册两个 CLSID,要看宏实现。
已确认的机制
CLSID 在编译期和类绑定死了
cpp
#define BLUE_BEGIN_CLASS_DECLARE(Cls, clsid) \
static const char* _getClassID() { return clsid; } \
static const char* _getClassName() { return #Cls; } \
static bool _queryObject(...)
`_getClassID()` 是个静态函数,直接返回宏参数`clsid` 。这意味着 同一个 C++ 类只有一个编译期 CLSID ------所以路线A"一个类注册两个 CLSID"不能简单靠ClassMap 加一行实现,要么改这个宏(比如让类支持第二个 clsid),要么用一个新的派生/包装类带第二个 CLSID。
一个类不能直接登记两个 CLSID------但有干净的绕法
注意 ClassMap 表项的 CLSID 不是宏参数,而是写死取`cls::_getClassID()` 。而`_getClassID()` 是 classmacro.h 里编译期绑定的静态函数。所以 不能在表里把`EDWModelModule` 写两遍配两个 CLSID (会变成两条相同 CLSID 的冲突表项)。
路线A的正确落地方式是一个极薄的派生类:
cpp
// 可以直接放在 edwmodelmodule.cpp 里,不必新增头文件
class EDWModelModule2 : public EDWModelModule
{
BLUE_BEGIN_CLASS_DECLARE(EDWModelModule2, clsidEDWModel2)
BLUE_DEFINE_INTERFACE_ENTRY(IEDWModule)
BLUE_DEFINE_INTERFACE_ENTRY(IEDWProcess)
BLUE_END_CLASS_DECLARE()
public:
EDWModelModule2() : EDWModelModule() {}
};
然后注册表里登记两条:
cpp
BLUE_BEGIN_DEFINE_MODULE()
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton(EDWModelModule) // clsidEDWModel
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton(EDWModelModule2) // clsidEDWModel2
BLUE_END_DEFINE_MODULE_DLL()
因为单例表是按CLSID 区分的,这就得到 两个互不干扰的单例 。新 CLSID 仿照 iedwmodule.h#L9 加一个`clsidEDWModel2` 常量即可。
构造逻辑完全复用父类,两套模型链各自独立。
好,先假定我们走路线a,能不能优化优化,先写一个EDWModelModule类,再写两个派生类
可以,而且框架宏刚好为这种优化提供了现成手段------还记得 classmacro.h#L16 那个不带 CLSID 的`BLUE_BEGIN_CLASS_DECLARE0` 吗?它就是给"不注册、只做基类"用的(不生成`_getClassID()` )。配合`BLUE_USE_INTERFACE_ENTRY` 转发接口查询,设计可以非常干净。
cpp
#define BLUE_BEGIN_CLASS_DECLARE0(Cls) \
public: \
static bool _queryObject(const Cls* self, long iid, bpf::IBObject** p) \
{
#define BLUE_BEGIN_CLASS_DECLARE(Cls, clsid) \
public: \
static const char* _getClassID() { return clsid; } \
static const char* _getClassName() { return #Cls; } \
static bool _queryObject(const Cls* self, long iid, bpf::IBObject** p) \
{
优化后的结构
- 基类:只保留逻辑,不绑定 CLSID
cpp
class EDWModelModule
: public IEDWModule, public IEDWProcess
{
BLUE_BEGIN_CLASS_DECLARE0(EDWModelModule) // 原来是 DECLARE(..., clsidEDWModel)
BLUE_DEFINE_INTERFACE_ENTRY(IEDWModule)
BLUE_DEFINE_INTERFACE_ENTRY(IEDWProcess)
BLUE_END_CLASS_DECLARE()
protected:
EDWModelModule(); // 改成 protected:基类不可直接实例化,只有派生类能构造
public:
virtual ~EDWModelModule();
public:
// 下面所有方法、成员完全不动......
要点:
-
`DECLARE0` 不生成`_getClassID()` ,所以 基类无法出现在 ClassMap里 ,从机制上保证"逻辑基类不被注册"。
-
构造函数改`protected` ,框架创建的是派生类(派生类构造函数 public,可正常调用基类保护构造)。
-
宏注入的全是 静态函数 ,不增加虚函数、不改变对象布局,成员和全部实现原样留在基类。
- 两个派生类:只差一个 CLSID(直接写在 cpp 里,不新增文件)
外部插件从来都是"CLSID + 接口"访问,没人需要引用这两个具体类,所以放 edwmodelmodule.cpp 顶部即可:
cpp
// 实例1:沿用原 CLSID,现有调用方零改动
class EDWModelModule1 : public EDWModelModule
{
BLUE_BEGIN_CLASS_DECLARE(EDWModelModule1, clsidEDWModel)
BLUE_USE_INTERFACE_ENTRY(EDWModelModule) // 接口查询转发给基类,多重继承指针偏移由基类统一处理
BLUE_END_CLASS_DECLARE()
public:
EDWModelModule1() = default;
~EDWModelModule1() override = default;
};
// 实例2:新 CLSID
class EDWModelModule2 : public EDWModelModule
{
BLUE_BEGIN_CLASS_DECLARE(EDWModelModule2, clsidEDWModel2)
BLUE_USE_INTERFACE_ENTRY(EDWModelModule)
BLUE_END_CLASS_DECLARE()
public:
EDWModelModule2() = default;
~EDWModelModule2() override = default;
};
BLUE_BEGIN_DEFINE_MODULE()
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton(EDWModelModule1)
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton(EDWModelModule2)
BLUE_END_DEFINE_MODULE_DLL()
为什么`BLUE_USE_INTERFACE_ENTRY(EDWModelModule)` 是安全的:它展开成`EDWModelModule::_queryObject(self, ...)` ,`self` 隐式向上转型为基类指针,基类内部再完成向**`IEDWModule*` /`IEDWProcess*`** 的转型------多重继承的指针偏移由编译器在基类那一处统一算对,派生类零负担。将来接口增删也只改基类一处。
cpp
#define BLUE_USE_INTERFACE_ENTRY(_BaseClass) \
if (_BaseClass::_queryObject(self, iid, p)) \
{ \
return true; \
}
我按照这个方案试过了,确实可行。
这是我的测试示例:
cpp
EDWModelModule::EDWModelModule()
{
filterEDWForViewModel = new FilterEDWForViewModel();
qDebug() << __FILE__ << __LINE__ << filterEDWForViewModel;
}
cpp
{
bpf::BObject<IEDWModule> edwModule(clsidEDWModel);
if (edwModule)
{
qDebug() << __FILE__ << __LINE__ << edwModule->getmodel(EDW_Filter_For_View_Name);
}
}
{
bpf::BObject<IEDWModule> edwModule(clsidEDWModel2);
if (edwModule)
{
qDebug() << __FILE__ << __LINE__ << edwModule->getmodel(EDW_Filter_For_View_Name);
}
}
cpp
../../../src/Components/EDWModule/edwmodelmodule.cpp 39 FilterEDWForViewModel(0x28ef50e0, name = "filter_edw_for_view")
../../../src/Plugins/EDWTableViewPlugin/edwtableview.cpp 42 FilterEDWForViewModel(0x28ef50e0, name = "filter_edw_for_view")
../../../src/Components/EDWModule/edwmodelmodule.cpp 39 FilterEDWForViewModel(0x290a93f0, name = "filter_edw_for_view")
../../../src/Plugins/EDWTableViewPlugin/edwtableview.cpp 49 FilterEDWForViewModel(0x290a93f0, name = "filter_edw_for_view")
可以看到,有两个不同的FilterEDWForViewModel对象。
我试过了,你这个方案是可行的,但我觉得逻辑上有点怪怪的,因为实际上我需要的其实是两个EDWModelModule的对象,而不是两个派生类,虽然现在功能上实现了,但有点奇怪。
你的直觉是对的。"两个 EDWModelModule 对象"和"两个派生壳"确实是不同的语义------派生类方案是为了"调用方零改动 + 保留单例语义"而打的补丁,功能上能用,但本质上是在框架"一个类一个 CLSID"的限制下绕路。
回到你真正想要的:"一个类、两个该类的对象",语义最直白的其实是 路线B:
把 EDWModelModule 注册成 NormalObject(非单例) 。
cpp
// 只改这一行宏
BLUE_DEFINE_CLASSMAP_ENTRY(EDWModelModule) // 原来是 _Singleton
cpp
#define BLUE_DEFINE_CLASSMAP_ENTRY(cls) \
bpf::ClassEntry(1, "NormalObject<" #cls ">", cls::_getClassID(), \
(bpf::ObjectCreator)(&bpf::NormalObject<cls>::create), \
(bpf::HASIID)(&bpf::NormalObject<cls>::hasInterface)),
原来的宏:
cpp
BLUE_DEFINE_CLASSMAP_ENTRY_Singleton (EDWModelModule)
cpp
#define BLUE_DEFINE_CLASSMAP_ENTRY_Singleton(cls) \
bpf::ClassEntry(bpf::MIN_SINGLETON_TYPE, \
"SingletonObject<" #cls ">", cls::_getClassID(), \
(bpf::ObjectCreator)(&bpf::SingletonObject<cls>::create), \
(bpf::HASIID)(&bpf::SingletonObject<cls>::hasInterface)),
这样每个调用方`create` 拿到的就是货真价实的`EDWModelModule` ,不是派生类,想要两个就是两个,想要 N 个就是 N 个。类只有一个,CLSID 也只有一个,语义干净。
normalobject.h
cpp
static IBObject* create(long iid)
{
NormalObject<Cls>* p = new NormalObject<Cls>(); // 每次都 new
p->queryObject(iid, &ret); // 查询成功会 retain
p->releaseObject(); // 抵消构造时的 _refcount(1)
return ret;
}
先到这里,我有点看不懂这部分代码了。