This article will focus on the main base class of the framework: the CoreModifiable class. It looks at Reference Counting and Instances Tree, Attributes, Virtual Methods, Aggregates, and Serialization.

Table of Contents
In the first article of this series, we have offered a general overview of the Kigs framework. This article will focus on the main base class of the framework: the CoreModifiable
class.
All high level classes have to inherit CoreModifiable
, or another CoreModifiable
inherited class, in order to have access to instance factory, reference counting, serialization, attributes...
Here is a basic example of class declaration:
class SimpleSampleClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleSampleClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleSampleClass) {}
protected:
void InitModifiable() override;
};
Instead of DECLARE_CLASS_INFO
, DECLARE_ABSTRACT_CLASS_INFO
can be used to create a base class that can't be directly instantiated. Parameters are class name, parent class name and module name. Module name parameter is just an helper parameter.
Instead of DECLARE_INLINE_CONSTRUCTOR
, DECLARE_CONSTRUCTOR
can be used, associated with IMPLEMENT_CONSTRUCTOR
(probably in the .cpp file).
Then the .cpp file will look like that:
IMPLEMENT_CLASS_INFO(SimpleSampleClass)
void SimpleSampleClass::InitModifiable()
{
ParentClassType::InitModifiable();
if (_isInit)
{
}
}
`ParentClassType
` is a helper typedef
used to call methods on parent class.
`_isInit
` is also a helper macro used to test if the class was correctly initialized...
Then, in the initialization of the module or application, classes must be declared (to factory):
DECLARE_FULL_CLASS_INFO(KigsCore::Instance(),
SimpleSampleClass, SimpleSampleClass, Application);
Parameters are: the current singleton instance of KigsCore
, the name of the class to instantiate, the name given to the instance factory, and the Module name.
The name of an instance can be retrieved with the getName()
method:
std::string name=simpleclass1->getName();
Testing instance type is possible using `isSubType
` method:
if(simpleclass1->isSubType("SimpleSampleClass"))
{
SimpleSampleClass* castSimpleClass=simpleclass1->as<SimpleSampleClass>();
}
So now an instance of the class SimpleSampleClass
can be asked to the instance factory:
CMSP simpleclass1 = KigsCore::GetInstanceOf("simpleclass1", "SimpleSampleClass");
When created, the instance has a ref count of 1.
- `
addItem
` increases ref count of the added instance by 1. - `
removeItem
` decreases ref count of the removed instance by 1.
SmartPointer
classes are used to easily manage ref counting. CMSP is a class inheriting SmartPointer<CoreModifiable>
.
SP
and SmartPointer
are equivalent.
{
SmartPointer<SimpleSampleClassBase> sp=KigsCore::GetInstanceOf
("simpleclass1", "SimpleSampleClass");
}
Pointers on CoreModifiable
inherited instances can be wrapped in SmartPointer
using SharedFromThis method
SmartPointer<SimpleSampleClassBase> sp=instance1->SharedFromThis();
Operator `->
` is used to access functionality of the instance in the SmartPointer
:
float test;
sp->getValue("test",test);
And retrieving the instance pointer itself is done using get()
method:
SimpleSampleClassBase* simpleinstance = sp.get();
Instances inheriting CoreModifiable
class maintain lists of their parents and sons instances (not in an inheritance point of view). So it is possible to construct trees of instances.
CMSP simpleclass1 = KigsCore::GetInstanceOf("simpleclass1", "SimpleSampleClass");
simpleclass1->Init();
CMSP simpleclass2 = KigsCore::GetInstanceOf("simpleclass2", "SimpleSampleClass");
simpleclass2->Init();
CMSP simpleclass3 = KigsCore::GetInstanceOf("simpleclass3", "SimpleSampleClass");
simpleclass3->Init();
simpleclass1->addItem(simpleclass2); simpleclass1->addItem(simpleclass3);
addItem(simpleclass1);
It is then easy to retrieve instances in the tree using `GetInstanceByPath
` method:
CMSP simpleclass2 =
GetInstanceByPath("SimpleSampleClass:simpleclass1/simpleclass2");
CMSP simpleclass1 =
simpleclass2->GetInstanceByPath("/Sample2/SimpleSampleClass:simpleclass1");
CMSP simpleclass3 = simpleclass2->GetInstanceByPath("../simpleclass3");
simpleclass3 = GetInstanceByPath("*/simpleclass3");
- If path starts with `
/
`, then start search by root parents (parents of this without parent). - If path contains `
../
`, then continue search from parents instance. - If path contains `
*/
`, then search all sons instance at this level in path.
Search can be done in sons:
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();
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();
GetSonInstancesByType("CoreModifiable", instances);
printf("GetSonInstancesByType result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
instances.clear();
GetSonInstancesByType("SimpleSampleClass", instances,true);
printf("Recursive GetSonInstancesByType result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
Or at a 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();
instances = GetInstances("SimpleSampleClass");
printf("GetInstances result :\n");
for (auto i : instances)
{
printf("found instance named : %s\n", i->getName().c_str());
}
The CoreModifiable
attributes are detailed in a next article.
Here is just a brief overview.
CoreModifiable
can have "compile time" attributes that can be declared in the class as below:
class SimpleSampleClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleSampleClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleSampleClass) {}
protected:
maUInt m_version = BASE_ATTRIBUTE(Version, 0);
maString m_desc = BASE_ATTRIBUTE(Description, "");
};
Another way to declare several attributes, mapped on member variables is to use WRAP_ATTRIBUTES macro:
class SimpleSampleClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleSampleClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleSampleClass) {}
protected:
u32 mVersion = 0;
std::string mDescription = "";
WRAP_ATTRIBUTES(mVersion,mDescription);
};
WRAP_ATTRIBUTES macro remove the first character (here 'm' prefix) of the attribute to define the name of the attribute (used by getters/setters, serialization...)
Then, on an instance of SimpleSampleClass
, the attributes can be accessed with getValue
/ setValue
methods:
simpleclass1->setValue("Version",5);
std::string desc;
simpleclass1->getValue("Description",desc);
It's also possible to add or remove attributes dynamically:
simpleclass1->AddDynamicAttribute(ATTRIBUTE_TYPE::BOOL, "isON");
simpleclass1->RemoveDynamicAttribute("isON");
All the attributes of an instance are serialized to and from XML files when the instance is serialized.
Two methods are useful to overload to manage initialization of an instance:
virtual void InitModifiable();
virtual void UninitModifiable();
InitModifiable
is called by Init()
method.
Here is a classic way to overload InitModifiable
:
void SimpleSampleClass::InitModifiable()
{
if (_isInit)
{
return;
}
ParentClassType::InitModifiable();
if (_isInit)
{
bool somethingWentWrong=false;
...
if(somethingWentWrong)
{
UnInit();
return;
}
}
}
Of course, it's also a good thing to add a virtual destructor to free all allocations done by the class or add some specific destruction code.
If a special behaviour is needed when an instance is added to another, for example to check if an instance of a specific type is added to another, the following methods can be overloaded:
virtual void addUser(CoreModifiable* user);
virtual void removeUser(CoreModifiable* user);
virtual bool addItem(const CMSP& item, ItemPosition pos = Last);
virtual bool removeItem(const CMSP& item);
virtual void Update(const Timer& timer, void* addParam);
The Update
method of an instance is called at each application loop if the instance was added to auto update:
KigsCore::GetCoreApplication()->AddAutoUpdate(instanceToAutoUpdate);
Of course, the instance is automatically removed from auto update when destroyed or manually by calling RemoveAutoUpdate
:
KigsCore::GetCoreApplication()->RemoveAutoUpdate(instanceToAutoUpdate);
Update
method can also be called manually by CallUpdate
method or RecursiveUpdate
method.
CoreModifiable
attributes can notify their owners when they change (when accessed by "setValue
"), calling the "NotifyUpdate
" method with their ID.
virtual void NotifyUpdate(const u32 labelid);
The detailed specifications of the CoreModifiable
methods are be described in a next article.
Here is just a brief overview.
The class CoreModifiable
allows defining methods callable by their name (string
) with a fixed prototype:
bool methodName(CoreModifiable* sender,std::vector<CoreModifiableAttribute*>& params,
void* privateParams);
Helpers macro are available to facilitate things: DECLARE_METHOD(methodName)
, DECLARE_VIRTUAL_METHOD(methodName)
, DECLARE_PURE_VIRTUAL_METHOD(methodName),
DECLARE_OVERRIDE_METHOD(methodname)
,DEFINE_METHOD(classtype,methodName)
.
In class declaration:
DECLARE_METHOD(incrementParam);
and then in cpp file, class definition:
DEFINE_METHOD(SimpleSampleBaseClass, incrementParam)
{
float val=0;
if (params[0]->getValue(val,this)) {
params[0]->setValue(val + 1.0f,this);
}
return true;
}
The list of CoreModifiable methods must be declared in the class header using COREMODIFIABLE_METHODS(method1,method2...) helper macro.
COREMODIFIABLE_METHODS(incrementParam);
The method can then be called on a CoreModifiable
instance pointer (without knowing the exact instance type):
CoreModifiableAttribute* param = item->getAttribute("CountWhenAdded");
if (param)
{
std::vector<CoreModifiableAttribute*> sendParams;
sendParams.push_back(param);
item->CallMethod("incrementParam", sendParams);
std::cout << item->getName() << " parameter CountWhenAdded = "
<< item->getValue<int>("CountWhenAdded") << std::endl;
}
Any CoreModifiable
member method can be accessed by its name using WRAP_METHODS
helper macro:
void printMessage();
WRAP_METHODS(printMessage);
WRAP_METHODS
can take several coma separated parameters.
Then the method can be called with SimpleCall
method:
simpleclass1->SimpleCall("printMessage");
For methods with parameters and return value, the SimpleCall
method will be used like this:
int returnedValue = instance->SimpleCall<int>("DoSomethingFun",42,"yes");
Two or more CoreModifiable
instances of different types can be aggregated all together.
For example, let's define a material class managing Color
and Shininess
:
class SimpleMaterialClass : public CoreModifiable
{
public:
DECLARE_CLASS_INFO(SimpleMaterialClass, CoreModifiable, Application);
DECLARE_INLINE_CONSTRUCTOR(SimpleMaterialClass)
{ std::cout << "SimpleMaterialClass constructor" << std::endl; }
protected:
maVect3DF m_Color = BASE_ATTRIBUTE(Color,1.0,0.0,0.0);
maFloat m_Shininess = BASE_ATTRIBUTE(Shininess, 0.5);
};
If an instance of material is aggregate with an instance of SimpleSampleClass
:
CMSP material= KigsCore::GetInstanceOf("material", "SimpleMaterialClass");
simpleclass3->aggregateWith(material);
It's then possible to directly get or set "Shininess
" or "Color
" values on simpleclass3
:
float shine=0.0f;
simpleclass3->getValue("Shininess", shine);
std::cout << simpleclass3->getName() << " has Shininess value of "
<< shine << " thanks to aggregate with SimpleMaterialClass " << std::endl;
The opposite is also true, it's possible to retrieve SimpleSampleClass
values from "material
" instance. And calling CoreModifiable
methods is also available the same way.
Export is only available when project is built in StaticDebug
or StaticReleaseTools
configuration. In StaticRelease
, KigsID
(used as map key for instances name...) are optimized and std::string
used to construct them are not preserved.
#ifdef KIGS_TOOLS
CoreModifiable::Export("Sample1.xml", simpleclass.get(), true);
#endif // KIGS_TOOLS
Corresponding Sample1.xml file will look like this:
="1.0"="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
is always available (in all build configurations).
CMSP imported=CoreModifiable::Import("Sample1.xml");
Find all the sample code from this article in Sample2 project on GitHub (browse the code).
Already Published in this Series
- Kigs Framework Introduction (1/8) - Overview
- Kigs Framework Introduction (2/8) - CoreModifiable
- Kigs Framework Introduction (3/8) - Attributes
- Kigs Framework Introduction (4/8) - Methods
- Kigs Framework Introduction (5/8) - CoreItem
- Kigs Framework Introduction (6/8) - Signal, Slot, Notification
- Kigs Framework Introduction (7/8) - Lua Binding
- Kigs Framework Introduction (8/8) - Data Driven Application
- 31st January, 2020: Initial version
- 2nd February, 2020: Small fix in
addItem
/ removeItem
prototype - 7th February, 2020: Added latest published article in the series
- 14th February, 2020: Article (4/8) added to the series
- 21st February, 2020: Article (5/8) added to the series and little bug fix in code
- 2nd March, 2020: Article (6/8) added to the series
- 19th March, 2020: Article (7/8) added to the series, and fix GetInstances methods prototype in samples
- 17th June, 2020 : Added final article of the series
- 1st March, 2023: Update after framework refactoring