Kigs Framework Introduction (2/8) - CoreModifiable

 
Rate me!
5.00 (4 votes)
 
22 Feb 2020MIT
Kigs framework is a multi purpose, cross-platform, free and open source C++ framework. This article will focus on the main base class of the framework: the CoreModifiable class.
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.

Kigs Logo

Table of Contents

Introduction

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.

Class Type and Name

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 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;
};

Instead of DECLARE_CLASS_INFODECLARE_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 a helper parameter.

Instead of DECLARE_INLINE_CONSTRUCTORDECLARE_CONSTRUCTOR can be used, 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 initialization was OK
    if (_isInit)
    {
        // initialize things
    }
}

`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();

Instance Type

Testing instance type is possible using `isSubType` method:

// test if a cast can be done
if(simpleclass1->isSubType("SimpleSampleClass"))
{
       SimpleSampleClass* castSimpleClass=simpleclass1->as<SimpleSampleClass>();
}

Reference Counting and Instances Tree

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 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.
  • `GetRef` increases ref count of the instance by 1.
  • `Destroy` decreases ref count of the instance by 1 and if ref count reaches 0, recursively removes all son instances and calls destructor.

Smart Pointers

SmartPointer classes are used to easily manage ref counting. CMSP is a typedef of SmartPointer<CoreModifiable>.
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 in two different ways:

// smartpointer with no ref count increase
SmartPointer<SimpleSampleClassBase> sp(instance1,StealRefTag{});

or:

// smartpointer with ref count increase
SmartPointer<SimpleSampleClassBase> sp(instance1,GetRefTag{});

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 Tree

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");
// 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);

It is then easy to retrieve instances in the tree using `GetInstanceByPath` method:

// retrieve instances in the instances tree using "path"
CoreModifiable* simpleclass2 = 
     GetInstanceByPath("SimpleSampleClass:simpleclass1/simpleclass2");
CoreModifiable* simpleclass1 = 
     simpleclass2->GetInstanceByPath("/Sample2/SimpleSampleClass:simpleclass1");
CoreModifiable* 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.

Searching Instances by Name or Type

Search can be done in sons:

// retrieve all instances named "simpleclass1" in sons list
std::set<CoreModifiable*> 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());
}

Or at a global scope:

// retrieve all instances named "simpleclass1" at global scope
GetInstancesByName("CoreModifiable", "simpleclass1", instances);
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
GetInstances("SimpleSampleClass", instances);
printf("GetInstances result :\n");
for (auto i : instances)
{
    printf("found instance named : %s\n", i->getName().c_str());
}

Attributes

The CoreModifiable attributes will be detailed in a future article.

Here is just a brief overview.

Declaration

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, "");
};

Access

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);

Dynamic Attributes

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.

Virtual Methods

Initialization / Un-initialization

Two methods are useful to overload to manage initialization of an instance:

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

// 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 initialization
    if (_isInit)
    {
        bool somethingWentWrong=false;
        // here is some initialization 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 the class or add some specific destruction code.

Add / Remove Sons or Parents

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

// 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.

Attribute Set Notification

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);

Methods

Faster Way (But More Restrictive)

The detailed specifications of the CoreModifiable methods will be described in a future article.

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,kstl::vector<CoreModifiableAttribute*>& params,
                   void* privateParams);

Helpers macro are available to facilitate things: DECLARE_METHOD(methodName)DECLARE_VIRTUAL_METHOD(methodName) or DECLARE_PURE_VIRTUAL_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)) // if first param value can be get as float
    {
        // increment value
        params[0]->setValue(val + 1.0f);
    }
    return true;
}

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;
}

Easier Way

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");

Aggregates

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.

Serialization

Export

Export is only available when project is built in StaticDebug or StaticReleaseTools configuration. In StaticReleaseKigsID (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

Import is always available (in all build configurations).

// import instances from file "Sample1.xml"
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

  1. Kigs Framework Introduction (1/8) - Overview
  2. Kigs Framework Introduction (2/8) - CoreModifiable
  3. Kigs Framework Introduction (3/8) - Attributes
  4. Kigs Framework Introduction (4/8) - Methods
  5. Kigs Framework Introduction (5/8) - CoreItem

History

  • 31st January, 2020: Initial version
  • 2nd February, 2020: Small fix in addItem / removeItem prototype
  • 07th February, 2020: Added latest published article in the series
  • 14th February, 2020: Article (4/8) added to the series
  • 21th February, 2020: Article (5/8) added to the series and little bug fix in code

[출처] https://www.codeproject.com/Articles/5257387/Kigs-framework-introduction-2-8-CoreModifiable

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
28 [Linux programming][turbo c] The libXbgi Library file 졸리운_곰 2023.02.05 248
27 [unix][linux][compiler] Yacc와 Lex 시작하기 file 졸리운_곰 2021.09.08 261
26 [Visual Studio 2019] Visual Studio로 Linux 원격 개발하기(Ubuntu 설치부터 SSH 서버접속까지) file 졸리운_곰 2021.08.29 235
25 [unix][emacs] 이맥스와 함께하는 개발환경 . emacs 튜토리얼 file 졸리운_곰 2021.08.18 319
24 [Linux][C/C++] cmake 사용법과 활용 file 졸리운_곰 2020.11.08 552
23 [CMake] Linux C/C++ cmake 시작하기 졸리운_곰 2020.11.08 398
22 [C++] CMake Build System file 졸리운_곰 2020.11.08 328
21 Windows 10에 한국어 입출력이 가능한 리눅스 데스크톱 설치하기 file 졸리운_곰 2020.06.29 289
20 wxwidgets 과 codeblock 설치(리눅스) 졸리운_곰 2019.12.25 228
19 ubuntu 18.04.2 LTS 개발환경 세팅 졸리운_곰 2019.12.25 223
18 Ubuntu 16.04 개발환경 세팅하기! 졸리운_곰 2017.03.21 356
17 tcl/tk 자료 secret 졸리운_곰 2017.02.23 0
16 CentOS xrdp 설정 (원격데스크탑) 졸리운_곰 2017.02.01 453
15 Five lightweight Linux desktop worlds for extreme open-sourcers file 졸리운_곰 2016.05.12 1022
14 Logging every shell command 졸리운_곰 2016.05.11 581
13 How to keep a detailed audit trail of what’s being done on your Linux systems file 졸리운_곰 2016.05.11 467
12 Docker의 소개와 간단한 사용법 file 졸리운_곰 2015.11.24 804
11 Make Self-Extracting Archives with makeself.sh 졸리운_곰 2015.11.07 363
10 리눅스 간단 배포 Linux simple deploy 제작 : make self file 졸리운_곰 2015.11.07 352
9 Joinc: Linux 커널에서의 디바이스 드라이버 작성 졸리운_곰 2015.06.16 833
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED