背景
最近在实现基础的状态机逻辑,考虑到需要工厂来创建一个指定的状态机,而且调用要简单且可扩展,就想到了反射机制。
以前学习了文章:
https://blog.csdn.net/Scythe666/article/details/51718864
给的思路是通过定义回调函数指针类型的方法来实现的,也就是说反射出的对象需要显式地实现实例类创建的回调函数,然后在全局注册进去,虽然步骤已经很简单了,而且一个宏就搞定注册了。
不过我想要在扩展一个需反射对象时,对外暴露的“注册行为”更少,尽量减少一个对象实现时需要考虑过多的附带代码(毕竟拷贝多了很容易出错);另一个想法就是希望在用户调用注册时,不必在全局中实现,能够在任何地方的代码块中注册。
说到这里,充分考虑C++的各种支持,无疑多态跑不掉,毕竟有工厂的概念,不过为了能够把Create这个步骤交给内部处理,模板就发挥了它的效用了,也就是说,我只需要传类型进去,就可以实现注册了。
达到效果
在介绍实现之前,先放一个最终效果,也就是从用户扩展的角度,如何达到注册+创建的需求。
#include <stdlib.h>
#include <iostream>
#include <QApplication>
#include "../../FSMBase/Include/IStateBase.h"
#include "../../FSMBase/Include/FSMObjectCreator.h"
// IStateBase 就是我需要反射创建对象的基类
// CStateIDEL和CStateIDELNew是我测试扩展的子类对象,实现了两个
class CStateIDEL : public IStateBase
{
ISTATEBASE_META_OBJECT("CStateIDEL")
public:
CStateIDEL() {}
virtual ~CStateIDEL() {}
/*先省略一些与反射机制无关的接口实现*/
public:
std::string m_strName;
};
class CStateIDELNew : public IStateBase
{
ISTATEBASE_META_OBJECT("CStateIDELNew")
public:
CStateIDELNew() {}
virtual ~CStateIDELNew() {}
/*先省略一些与反射机制无关的接口实现*/
public:
std::string m_strName;
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
CFSMObjectCreator* pCreator = CFSMObjectCreatorSingleton::GetInstance();
pCreator->RegisterStateBaseType<CStateIDEL>(); // 注册扩展的 CStateIDEL类
pCreator->RegisterStateBaseType<CStateIDELNew>(); // 注册扩展的 CStateIDELNew类
CStateIDEL* pIDEL = dynamic_cast<CStateIDEL*>(pCreator->CreateStateBase("CStateIDEL", "111")); // 创建 CStateIDEL类对象
pCreator->UnregisterStateBaseType<CStateIDELNew>(); // 取消注册CStateIDELNew类
CStateIDELNew* pIDELNew = dynamic_cast<CStateIDELNew*>(pCreator->CreateStateBase("CStateIDELNew", "222")); // 创建CStateIDELNew类对象,但此时对象指针是空的
app.exec();
return 0;
}
实现过程
实例代码结构:
IStateBase就是我们需要反射对象的基类
- ISTATEBASE_META_OBJECT宏是重点,需要向CFSMObjectCreator提供的反射类名(类名要对应Create),对于用户创建一个新的子类,唯一关注的也就是这里了,可以理解为,和Qt的Q_OBJECT加入以支持信号槽机制是一个道理。
- Initialize和Uninitialize作为工厂调用初始化和反初始化的接口;
- 其它的是其它业务接口类,可以忽略;
#include "FSMBaseExport.h"
#define ISTATEBASE_META_OBJECT(RefClass) \
public:\
static std::string GetRefClass() {return RefClass;}
class IStateMachine;
class FSMBASE_EXPORT IStateBase
{
ISTATEBASE_META_OBJECT("IStateBase")
public:
virtual ~IStateBase() {}
virtual bool Initialize(const std::string& strName) = 0;
virtual void Uninitialize() = 0;
virtual std::string GetName() const = 0;
virtual bool Entry(IStateMachine* pFSM) = 0;
virtual bool Execute(IStateMachine* pFSM) = 0;
virtual bool Exit(IStateMachine* pFSM) = 0;
};
CFSMObjectCreator和CFSMObjectCreatorSingleton对外提供注册和反注册,创建和销毁IStateBase的单例工厂
- 对于外部调用来说,如何获取该全局唯一的工厂是第一步,这里的一个方式是用CFSMObjectCreatorSingleton封装了一层CFSMObjectCreator提供出去。
- 注意RegisterStateBaseType和UnregisterStateBaseType使用了模板关联反射类,这里就是把Create可以放到内部的关键。因为语法的原因,模板函数的实现写在了头文件内,不过m_mapClassName具体的insert和erase的逻辑是由RegisterType和UnregisterType封装了起来,只是单纯为了不把加锁的逻辑放在外面,仅在cpp内实现。
- 每个类对应的Create是通过CObjectRegisterAngent代理提供的Create接口实现的。
- std::map<std::string, CObjectRegisterAngent*> m_mapClassName; 用类名映射创建代理类实例。
class FSMBASE_EXPORT CFSMObjectCreator
{
public:
CFSMObjectCreator();
~CFSMObjectCreator();
template <typename T>
void RegisterStateBaseType()
{
CFSMObjectRegisterAngent<T>* pAngent = new CFSMObjectRegisterAngent<T>();
std::string strClassName = pAngent->GetRefName();
RegisterType(strClassName, pAngent);
}
template <typename T>
void UnregisterStateBaseType()
{
std::string strClassName = T::GetRefClass();
UnregisterType(strClassName);
}
IStateBase* CreateStateBase(const std::string& strClassName, const std::string& strName);
void DestroyStateBase(IStateBase* pStateBase);
private:
void RegisterType(const std::string& strClassName, CObjectRegisterAngent* pAngent);
void UnregisterType(const std::string& strClassName);
private:
// 有必要加锁
std::map<std::string, CObjectRegisterAngent*> m_mapClassName;
};
// ================================================
class FSMBASE_EXPORT CFSMObjectCreatorSingleton
{
public:
static CFSMObjectCreator* GetInstance();
private:
CFSMObjectCreatorSingleton() {}
~CFSMObjectCreatorSingleton() {}
};
最重点的实现:使用模板对应到Create
- 刚才我们说了,CObjectRegisterAngent对应每个注册进来的反射类所需要的Create,但是还没有真正把模板类T给用上;
- CFSMObjectRegisterAngent作为一个继承于CObjectRegisterAngent的模板类,就实现了virtual IStateBase* Create() = 0;的方法,而且对应到了注册进的类。
- CFSMObjectRegisterAngent提供了一个GetRefName的方法,获取刚才用宏实现的IStateBase提供反射类名的方法;
- 一个小细节:容器内没有办法使用模板类,所以必须使用CFSMObjectRegisterAngent的基类,也是多了一个CObjectRegisterAngent的主要原因。
class IStateBase;
class CObjectRegisterAngent
{
public:
virtual ~CObjectRegisterAngent() {}
virtual IStateBase* Create() = 0;
};
template <typename T>
class CFSMObjectRegisterAngent : public CObjectRegisterAngent
{
public:
CFSMObjectRegisterAngent() {}
~CFSMObjectRegisterAngent() {}
IStateBase* Create()
{
return new T;
}
std::string GetRefName() const
{
return T::GetRefClass();
}
};
CFSMObjectCreator内的实现过程
其实反射机制的主要内容都在CFSMObjectCreator的头文件内了,CPP内只是做了简单的m_mapClassName容器操作,其次是封装了锁,不暴露出去,也就是完成了一个最简化的工厂职责处理。
有兴趣可以参考下:
#include "../Include/FSMObjectCreator.h"
#include "../Include/IStateBase.h"
CFSMObjectCreator::CFSMObjectCreator()
{
// 锁初始化
}
CFSMObjectCreator::~CFSMObjectCreator()
{
// 锁销毁
}
IStateBase* CFSMObjectCreator::CreateStateBase(const std::string& strClassName, const std::string& strName)
{
// 加锁
std::map<std::string, CObjectRegisterAngent*>::const_iterator itr = m_mapClassName.find(strClassName);
if (itr == m_mapClassName.end())
{
return NULL;
}
IStateBase* pStateBase = itr->second->Create();
if (NULL == pStateBase)
{
return NULL;
}
bool bInit = pStateBase->Initialize(strName);
if (!bInit)
{
delete pStateBase;
return NULL;
}
return pStateBase;
}
void CFSMObjectCreator::DestroyStateBase(IStateBase* pStateBase)
{
if (NULL == pStateBase)
{
return;
}
pStateBase->Uninitialize();
delete pStateBase;
}
// =====
void CFSMObjectCreator::RegisterType(const std::string& strClassName, CObjectRegisterAngent* pAngent)
{
// 加锁
std::map<std::string, CObjectRegisterAngent*>::iterator itr = m_mapClassName.find(strClassName);
if (itr == m_mapClassName.end())
{
m_mapClassName.insert(std::make_pair(strClassName, pAngent));
}
else
{
// LOG ??
delete itr->second;
itr->second = pAngent;
}
}
void CFSMObjectCreator::UnregisterType(const std::string& strClassName)
{
// 加锁
std::map<std::string, CObjectRegisterAngent*>::const_iterator itr = m_mapClassName.find(strClassName);
if (itr == m_mapClassName.end())
{
return;
}
delete itr->second;
m_mapClassName.erase(itr);
}
// ======= Singleton
CFSMObjectCreator* CFSMObjectCreatorSingleton::GetInstance()
{
static CFSMObjectCreator g_FSMObjCreator;
return &g_FSMObjCreator;
}