Kigs框架介绍 (2/8) - CoreModifiable






3.61/5 (8投票s)
Kigs 框架是一个多用途、跨平台、免费开源的 C++ 框架。本文将重点介绍该框架的主要基类:CoreModifiable 类。
目录
引言
在本系列的第一篇文章中,我们概述了 Kigs 框架。本文将重点介绍该框架的主要基类:CoreModifiable
类。
类类型和名称
所有高级类都必须继承 CoreModifiable
或另一个继承自 CoreModifiable
的类,以便访问实例工厂、引用计数、序列化、属性等。
这是一个类声明的基本示例
// this class inherits CoreModifiable directly
class SimpleSampleClass : public CoreModifiable
{
public:
// helper Macro to setup everything needed
DECLARE_CLASS_INFO(SimpleSampleClass, CoreModifiable, Application);
// helper Macro to declare an inline constructor (empty here)
DECLARE_INLINE_CONSTRUCTOR(SimpleSampleClass) {}
protected:
// override initialization method called explicitly with "Init()"
// or implicitly when importing from XML for example
void InitModifiable() override;
};
可以使用 DECLARE_ABSTRACT_CLASS_INFO
来创建不能直接实例化的基类,而不是使用 DECLARE_CLASS_INFO
。参数是类名、父类名和模块名。模块名参数只是一个辅助参数。
可以使用 DECLARE_CONSTRUCTOR
结合 IMPLEMENT_CONSTRUCTOR
(可能在 .cpp 文件中)来代替 DECLARE_INLINE_CONSTRUCTOR
。
然后 .cpp 文件将如下所示
// Helper macro, setup implementation
IMPLEMENT_CLASS_INFO(SimpleSampleClass)
// override InitModifiable method
void SimpleSampleClass::InitModifiable()
{
// call parent InitModifiable method
ParentClassType::InitModifiable();
// check if parent initialization was OK
if (_isInit)
{
// initialize things
}
}
`ParentClassType
` 是一个辅助 typedef
,用于调用父类的方法。
`_isInit
` 也是一个辅助宏,用于测试类是否已正确初始化...
然后,在模块或应用程序的初始化中,必须声明(注册到工厂)类
DECLARE_FULL_CLASS_INFO(KigsCore::Instance(),
SimpleSampleClass, SimpleSampleClass, Application);
参数是:当前的 KigsCore
单例实例、要实例化的类名、赋予实例工厂的名称以及模块名。
可以使用 getName()
方法获取实例的名称
std::string name=simpleclass1->getName();
实例类型
可以使用 `isSubType
` 方法测试实例类型
// test if a cast can be done
if(simpleclass1->isSubType("SimpleSampleClass"))
{
SimpleSampleClass* castSimpleClass=simpleclass1->as<SimpleSampleClass>();
}
引用计数和实例树
这样就可以从实例工厂请求 SimpleSampleClass
类的实例了
// ask for a SimpleSampleClassBase instance named simpleclass1
CMSP simpleclass1 = KigsCore::GetInstanceOf("simpleclass1", "SimpleSampleClass");
创建时,实例的引用计数为 1。
- `
addItem
` 将添加实例的引用计数加 1。 - `
removeItem
` 将移除实例的引用计数减 1。
智能指针
SmartPointer
类用于轻松管理引用计数。CMSP 是继承 SmartPointer<CoreModifiable>
的一个类。
SP
和 SmartPointer
是等效的。
{
// sp is a smart pointer on an instance of SimpleSampleClass named "simpleclass1"
SmartPointer<SimpleSampleClassBase> sp=KigsCore::GetInstanceOf
("simpleclass1", "SimpleSampleClass");
} // exiting the block scope will automatically delete the instance "simpleclass1"
可以利用 SharedFromThis 方法将指向继承 CoreModifiable
的实例的指针包装到 SmartPointer
中
// smartpointer with no ref count increase
SmartPointer<SimpleSampleClassBase> sp=instance1->SharedFromThis();
运算符 `->
` 用于在 SmartPointer
中访问实例的功能
float test;
sp->getValue("test",test);
通过 get()
方法可以获取实例指针本身
SimpleSampleClassBase* simpleinstance = sp.get();
实例树
继承 CoreModifiable
类的实例会维护其父节点和子节点实例的列表(非继承意义上的)。因此,可以构建实例树。
// ask for a SimpleSampleClassBase instance named simpleclass1
CMSP simpleclass1 = KigsCore::GetInstanceOf("simpleclass1", "SimpleSampleClass");
// Initialize class
simpleclass1->Init();
// ask for two other instances
CMSP simpleclass2 = KigsCore::GetInstanceOf("simpleclass2", "SimpleSampleClass");
simpleclass2->Init();
CMSP simpleclass3 = KigsCore::GetInstanceOf("simpleclass3", "SimpleSampleClass");
simpleclass3->Init();
// and add simpleclass2 and simpleclass3 to simpleclass1
simpleclass1->addItem(simpleclass2); // simpleclass2 count ref is now 2
simpleclass1->addItem(simpleclass3); // simpleclass3 count ref is now 2
// add simpleclass1 to this
addItem(simpleclass1);
然后可以使用 `GetInstanceByPath
` 方法轻松地在树中检索实例
// retrieve instances in the instances tree using "path"
CMSP simpleclass2 =
GetInstanceByPath("SimpleSampleClass:simpleclass1/simpleclass2");
CMSP simpleclass1 =
simpleclass2->GetInstanceByPath("/Sample2/SimpleSampleClass:simpleclass1");
CMSP simpleclass3 = simpleclass2->GetInstanceByPath("../simpleclass3");
simpleclass3 = GetInstanceByPath("*/simpleclass3");
- 如果路径以 `
/
` 开头,则从根父节点(没有父节点的父节点)开始搜索。 - 如果路径包含 `
../
`,则从父实例继续搜索。 - 如果路径包含 `
*/
`,则搜索路径中该级别下的所有子实例。
按名称或类型搜索实例
可以在子节点中进行搜索
// retrieve all instances named "simpleclass1" in sons list
std::vector<CMSP> instances;
GetSonInstancesByName("CoreModifiable", "simpleclass1",instances);
printf("GetSonInstancesByName result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
instances.clear();
// retrieve all instances named "simpleclass2" recursively in sons list
GetSonInstancesByName("CoreModifiable", "simpleclass2", instances,true);
printf("Recursive GetSonInstancesByName result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
instances.clear();
// retrieve all instances of type CoreModifiable in sons list
GetSonInstancesByType("CoreModifiable", instances);
printf("GetSonInstancesByType result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
instances.clear();
// retrieve all instances of type SimpleSampleClass recursively in sons list
GetSonInstancesByType("SimpleSampleClass", instances,true);
printf("Recursive GetSonInstancesByType result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
或在全局范围内搜索
// retrieve all instances named "simpleclass1" at global scope
instances = GetInstancesByName("CoreModifiable", "simpleclass1");
printf("GetInstancesByName result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
instances.clear();
// retrieve all instances of type SimpleSampleClass at global scope
instances = GetInstances("SimpleSampleClass");
printf("GetInstances result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
属性
CoreModifiable
属性将在下一篇文章中详细介绍。
这里只是一个简要概述。
声明
CoreModifiable
可以拥有“编译时”属性,这些属性可以在类中声明,如下所示
class SimpleSampleClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleSampleClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleSampleClass) {}
protected:
// unsigned int attribute "Version"
maUInt m_version = BASE_ATTRIBUTE(Version, 0);
// string attribute "Description"
maString m_desc = BASE_ATTRIBUTE(Description, "");
};
另一种声明多个属性(映射到成员变量)的方法是使用 WRAP_ATTRIBUTES 宏
class SimpleSampleClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleSampleClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleSampleClass) {}
protected:
// unsigned int attribute "Version"
u32 mVersion = 0;
// string attribute "Description"
std::string mDescription = "";
WRAP_ATTRIBUTES(mVersion,mDescription);
};
WRAP_ATTRIBUTES 宏会移除属性的第一个字符(此处为 'm' 前缀),以定义属性的名称(由 getter/setter、序列化等使用)。
访问
然后在 SimpleSampleClass
的实例上,可以使用 getValue
/ setValue
方法访问属性
simpleclass1->setValue("Version",5);
std::string desc;
simpleclass1->getValue("Description",desc);
动态属性
还可以动态添加或删除属性
simpleclass1->AddDynamicAttribute(ATTRIBUTE_TYPE::BOOL, "isON");
simpleclass1->RemoveDynamicAttribute("isON");
实例的所有属性在实例被序列化时会被序列化到 XML 文件中,并从 XML 文件中读取。
虚方法
初始化/反初始化
有两个有用的方法可以重载以管理实例的初始化
// Init the modifiable and set the _isInit flag if OK.
// Need to call ParentClassType::InitModifiable() when overriding !
virtual void InitModifiable();
// Called when init has failed.
// Need to call ParentClassType::UninitModifiable() when overriding !
virtual void UninitModifiable();
InitModifiable
由 Init()
方法调用。
这是一个重载 InitModifiable
的经典方法
// InitModifiable overload sample code
void SimpleSampleClass::InitModifiable()
{
// check for multiple init
if (_isInit)
{
// init was already done, just return
return;
}
// call parent class InitModifiable
ParentClassType::InitModifiable();
// if everything is OK, do this initialization
if (_isInit)
{
bool somethingWentWrong=false;
// here is some initialization code for this
...
// check if something went wrong
if(somethingWentWrong)
{
// call Uninit
UnInit();
return;
}
}
}
当然,添加一个虚析构函数来释放类分配的所有内存或添加一些特定的销毁代码也是一个好主意。
添加/删除子节点或父节点
如果当一个实例被添加到另一个实例时需要特殊的行为,例如检查是否添加了特定类型的实例到另一个实例,则可以重载以下方法
// add the given parent to list. Need to call ParentClassType::addUser(...) when overriding !
virtual void addUser(CoreModifiable* user);
// remove the given parent from list.
// Need to call ParentClassType::removeUser(...) when overriding !
virtual void removeUser(CoreModifiable* user);
// add a son. Need to call ParentClassType::addItem(...) when overriding !
virtual bool addItem(const CMSP& item, ItemPosition pos = Last);
// remove a son. Need to call ParentClassType::removeItem(...) when overriding !
virtual bool removeItem(const CMSP& item);
更新
// Update method. Call to ParentClassType::Update is not necessary when overriding
virtual void Update(const Timer& timer, void* addParam);
如果实例被添加到自动更新中,其 Update
方法将在每个应用程序循环中被调用
// add instanceToAutoUpdate to application auto update system
KigsCore::GetCoreApplication()->AddAutoUpdate(instanceToAutoUpdate);
当然,当实例被销毁或通过调用 RemoveAutoUpdate
手动移除时,它会自动从自动更新中移除
// remove instanceToAutoUpdate from application auto update system
KigsCore::GetCoreApplication()->RemoveAutoUpdate(instanceToAutoUpdate);
Update
方法也可以通过 CallUpdate
方法或 RecursiveUpdate
方法手动调用。
属性设置通知
CoreModifiable
属性可以在它们发生变化时(通过“setValue
”访问时)通知它们的宿主,并使用其 ID 调用“NotifyUpdate
”方法。
// Called when an attribute that has its notification level set to Owner is modified.
// Need to call ParentClassType::NotifyUpdate(...) when overriding !
virtual void NotifyUpdate(const u32 labelid);
方法
更快捷的方式(但更受限制)
CoreModifiable
方法的详细规格将在下一篇文章中介绍。
这里只是一个简要概述。
CoreModifiable
类允许定义可以通过名称(string
)调用的方法,这些方法具有固定的原型
bool methodName(CoreModifiable* sender,std::vector<CoreModifiableAttribute*>& params,
void* privateParams);
提供了辅助宏来简化操作:DECLARE_METHOD(methodName)
、DECLARE_VIRTUAL_METHOD(methodName)
、DECLARE_PURE_VIRTUAL_METHOD(methodName)
、
DECLARE_OVERRIDE_METHOD(methodname)
、DEFINE_METHOD(classtype,methodName)
。
在类声明中
// method that add 1 to the given parameter
DECLARE_METHOD(incrementParam);
然后在 cpp 文件中,定义类
DEFINE_METHOD(SimpleSampleBaseClass, incrementParam)
{
float val=0;
// access first param (we could check for param name here)
if (params[0]->getValue(val,this)) // if first param value can be get as float
{
// increment value
params[0]->setValue(val + 1.0f,this);
}
return true;
}
CoreModifiable
方法列表必须在类头文件中使用 COREMODIFIABLE_METHODS(method1,method2...) 辅助宏声明。
// declare all CoreModifiable methods
COREMODIFIABLE_METHODS(incrementParam);
然后可以在 CoreModifiable
实例指针上调用该方法(无需知道确切的实例类型)
CoreModifiableAttribute* param = item->getAttribute("CountWhenAdded");
if (param)
{
// call incrementParam method
std::vector<CoreModifiableAttribute*> sendParams;
sendParams.push_back(param);
item->CallMethod("incrementParam", sendParams);
std::cout << item->getName() << " parameter CountWhenAdded = "
<< item->getValue<int>("CountWhenAdded") << std::endl;
}
更简单的方式
可以使用 WRAP_METHODS
辅助宏按名称访问任何 CoreModifiable
成员方法
// simple method
void printMessage();
// ask possible call by name
WRAP_METHODS(printMessage);
WRAP_METHODS
可以接受多个逗号分隔的参数。
然后可以使用 SimpleCall
方法调用该方法
simpleclass1->SimpleCall("printMessage");
对于带有参数和返回值的函数,SimpleCall
方法的用法如下
int returnedValue = instance->SimpleCall<int>("DoSomethingFun",42,"yes");
聚合
可以聚合两个或多个不同类型的 CoreModifiable
实例。
例如,定义一个管理 Color
和 Shininess
的材质类
class SimpleMaterialClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleMaterialClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleMaterialClass)
{ std::cout << "SimpleMaterialClass constructor" << std::endl; }
protected:
// RGB color
maVect3DF m_Color = BASE_ATTRIBUTE(Color,1.0,0.0,0.0);
// shininess
maFloat m_Shininess = BASE_ATTRIBUTE(Shininess, 0.5);
};
如果材质实例与 SimpleSampleClass
的实例聚合
// create an instance of SimpleMaterialClass
CMSP material= KigsCore::GetInstanceOf("material", "SimpleMaterialClass");
// manage simpleclass3 and material as one unique object
simpleclass3->aggregateWith(material);
然后可以直接在 simpleclass3
上获取或设置“Shininess
”或“Color
”值
float shine=0.0f;
simpleclass3->getValue("Shininess", shine);
std::cout << simpleclass3->getName() << " has Shininess value of "
<< shine << " thanks to aggregate with SimpleMaterialClass " << std::endl;
反之亦然,可以从“material
”实例中检索 SimpleSampleClass
的值。并且同样可以使用这种方式调用 CoreModifiable
方法。
序列化
导出
导出仅在项目以 StaticDebug
或 StaticReleaseTools
配置构建时可用。在 StaticRelease
中,KigsID
(用作实例名称的映射键...)经过优化,用于构造它们的 std::string
已不再保留。
// only if export is available
#ifdef KIGS_TOOLS
// export Sample1 and its sons in Sample1.xml file
CoreModifiable::Export("Sample1.xml", simpleclass.get(), true);
#endif // KIGS_TOOLS
相应的 Sample1.xml 文件将如下所示
<?xml version="1.0" encoding="utf-8"?>
<Inst N="simpleclass" T="SimpleSampleClass">
<Inst N="localtimer" T="Timer">
<Attr N="Time" V="0.001191"/>
<Attr T="float" N="floatValue" V="12.000000" Dyn="yes"/>
</Inst>
</Inst>
导入
Import
始终可用(在所有构建配置中)。
// import instances from file "Sample1.xml"
CMSP imported=CoreModifiable::Import("Sample1.xml");
在 GitHub 上的 Sample2 项目中查找本文的所有示例代码(浏览代码)。
本系列已发布内容:
- Kigs框架介绍 (1/8) - 概述
- Kigs框架介绍 (2/8) - CoreModifiable
- Kigs框架介绍 (3/8) - 属性
- Kigs框架介绍 (4/8) - 方法
- Kigs框架介绍 (5/8) - CoreItem
- Kigs框架介绍 (6/8) - 信号,槽,通知
- Kigs框架介绍 (7/8) - Lua绑定
- Kigs框架介绍 (8/8) - 数据驱动应用程序
历史
- 2020 年 1 月 31 日:初始版本
- 2020 年 2 月 2 日:在
addItem
/removeItem
原型中进行的小修复 - 2020 年 2 月 7 日:添加了系列中最新发布的文章
- 2020年2月14日:将文章(4/8)添加到本系列
- 2020 年 2 月 21 日:文章 (5/8) 添加到系列中,并在代码中进行了小错误修复
- 2020年3月2日:将文章(6/8)添加到本系列
- 2020 年 3 月 19 日:文章 (7/8) 添加到系列中,并修复了示例中的 GetInstances 方法原型
- 2020年6月17日:添加了该系列的最后一篇文章
- 2023 年 3 月 1 日:框架重构后更新