-
Notifications
You must be signed in to change notification settings - Fork 22
CoreModifiable
- Class type and name
- Reference counting and instances tree
- Attributes
- Virtual methods
- Methods
- Aggregates
- Serialization
All high level classes have to inherit CoreModifiable, or another CoreModifiable inherited class, in order to have access to instance factory, reference counting, attributes...
Here is a basic example of class declaration :
// this class inherit 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 initialisation method called explicitly with "Init()" or implicitly when importing from xml for example
void InitModifiable() override;
};
Instead of DECLARE_CLASS_INFO, DECLARE_ABSTRACT_CLASS_INFO can be use 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 use, associated with IMPLEMENT_CONSTRUCTOR (probably in the .cpp file).
Then the .cpp file will look like that :
// Helper macro, setup implementation
IMPLEMENT_CLASS_INFO(SimpleSampleClass)
// override InitModifiable method
void SimpleSampleClass::InitModifiable()
{
// call parent InitModifiable method
ParentClassType::InitModifiable();
// check if parent initialisation was OK
if (_isInit)
{
// initialize things
}
}
ParentClassType
is an helper typedef used to call methods on parent class.
_isInit
is also an helper macro used to test if the class was correctly initialized...
Then, in the initialisation 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 retrieve with the getName() method :
std::string name=simpleclass1->getName();
Testing instance type is possible using isSubType
method :
// test if a cast can be done
if(simpleclass1->isSubType("SimpleSampleClass"))
{
SimpleSampleClass* castSimpleClass=simpleclass1->as<SimpleSampleClass>();
}
So now an instance of the class SimpleSampleClass can be asked to the instance factory :
// ask for a SimpleSampleClassBase instance named simpleclass1
CMSP simpleclass1 = KigsCore::GetInstanceOf("simpleclass1", "SimpleSampleClass");
When created, the instance have a ref count of 1.
-
addItem
increase ref count of the added instance by 1 -
removeItem
decrease ref count of the removed instance by 1
SmartPointer class are used to easily manage ref counting. CMSP is a class inheriting SmartPointer. SP and SmartPointer are equivalent.
{
// 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"
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.
// ask for a SimpleSampleClassBase instance named simpleclass1
CMSP simpleclass1 = KigsCore::GetInstanceOf("simpleclass1", "SimpleSampleClass");
// Initialise 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);
It is then easy to retrieve instances in the tree using GetInstanceByPath
method :
// 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");
- 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 :
// retreive 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();
// retreive 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();
// retreive 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();
// retreive 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());
}
Or at a global scope :
// retreive 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();
// retreive 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());
}
The detailed specifications of the CoreModifiable attributes are available on this specific wiki page.
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:
// unsigned int attribute "Version"
maUInt m_version = BASE_ATTRIBUTE(Version, 0);
// string attribute "Description"
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:
// unsigned int attribute "Version"
u32 mVersion = 0;
// string attribute "Description"
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 initialisation of an instance :
// 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 is called by Init() method.
Here is a classic way to overload 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 initialisation
if (_isInit)
{
bool somethingWentWrong=false;
// here is some initialisation code for this
...
// check if something went wrong
if(somethingWentWrong)
{
// call Uninit
UnInit();
return;
}
}
}
Of course, it's also a good thing to add a virtual destructor to free all allocations done by your 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 :
// 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)
The Update method of an instance is called at each application loop if the instance was added to auto update :
// add instanceToAutoUpdate to application auto update system
KigsCore::GetCoreApplication()->AddAutoUpdate(instanceToAutoUpdate);
Of course, the instance is automatically removed from auto update when destroyed or manually by calling RemoveAutoUpdate :
// remove instanceToAutoUpdate from application auto update system
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.
// 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);
Check detailed CoreModifiable attributes section to learn more about it.
The detailed specifications of the CoreModifiable methods are available on this specific wiki page.
Here is just a brief overview.
The class CoreModifiable allows to define 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 :
// method that add 1 to the given parameter
DECLARE_METHOD(incrementParam);
and then in cpp file, class definition :
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;
}
The list of CoreModifiable methods must be declared in the class header using COREMODIFIABLE_METHODS(method1,method2...) helper macro.
// declare all CoreModifiable methods
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)
{
// 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;
}
Any CoreModifiable member method can be accessed by its name using WRAP_METHODS helper macro :
// simple method
void printMessage();
// ask possible call by name
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:
// RGB color
maVect3DF m_Color = BASE_ATTRIBUTE(Color,1.0,0.0,0.0);
// shininess
maFloat m_Shininess = BASE_ATTRIBUTE(Shininess, 0.5);
};
If an instance of material is aggregate with an instance of SimpleSampleClass :
// create an instance of SimpleMaterialClass
CMSP material= KigsCore::GetInstanceOf("material", "SimpleMaterialClass");
// manage simpleclass3 and material as one unique object
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 build 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.
// 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
Corresponding Sample1.xml file will look like this :
<?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 is always available (in all build configuration).
// import instances from file "Sample1.xml"
CMSP imported=CoreModifiable::Import("Sample1.xml");
Find all the sample code from this wiki section in Sample2 project (browse the code)
Kigs framework Wiki - kigs-framework.org
-
3.1. CoreModifiable
3.1.1. Upgrador
3.2. Attributes
3.3. Methods
3.4. CoreItem
3.6. Lua Binding
-
Other Modules
4.1. FileManager
4.2. Timer
4.3. 2DLayers
4.4. Collision
4.5. GUI
4.6. Input
4.7. SceneGraph
4.8. Renderer