Skip to content

Properties

The code for this tutorial is available on GitHub

https://github.com/ADAMMA-CDHI-ETH-Zurich/CLAIDTutorial02

Adding properties to our Module

Now that we are able to instantiate Modules from XML configurations, we can also add properties to our Module. This properties (e.g., settings, configuration variables, ...) can be set from the XML configuration and will be loaded at Runtime before the Module's initialize function is called. To add properties, we simply need to add a public Reflect function to the Module.

Consider the following code (alternation of the class MyModule).

MyModule with properties
#include "CLAID.hpp"
#include <string>

namespace HelloWorld
{
    // When creating a Module, inheriting from claid::Module is mandatory.
    class MyModule : public claid::Module
    {
        private:
            int myInteger;
            float myFloat;
            std::string myString;

            void initialize()
            {
                printf("MyInteger is %d\n", myInteger);
                printf("MyFloat is %f\n", myFloat);
                printf("MyString is \"%s\"\n", myString.c_str());
            }


        // Add reflection to public part of the class definition.
        public:

            Reflect(MyModule,
                reflectMember(myInteger);
                reflectMember(myFloat);
                reflectMember(myString);
            )
    };
}
import JavaCLAID.Module;

// When creating a Module, inheriting from Module is mandatory.
class MyModule extends Module
{
    private int myInteger;
    private float myFloat;

    // Make sure to initialize non-primitive properties!
    private String myString = "";

    void initialize()
    {
        printf("MyInteger is %d\n", myInteger);
        printf("MyFloat is %f\n", myFloat);
        printf("MyString is \"%s\"\n", myString.c_str());
    }

    void reflect(Reflector r)
    {
        r.reflect("myInteger", myInteger);
        r.reflect("myFloat", myFloat);
        r.reflect("myString", myString);
    }            
};

As you can see, we added 3 properties to the Module ("myInteger", "myFloat" and "myString"). We can now change our previous XML configuration to include values for these properties:

Updated XML configuration
<root>
    <Module class = "HelloWorld::MyModule">
        <myInteger>42</myInteger>
        <myFloat>13.37</myFloat>
        <myString>This is a test</myString>
    </Module>
</root>
Caution

When adding properties to a Module, all those properties HAVE to be specified in the XML configuration. If you forget to specify properties of a Module in the configuration, an error will be thrown at startup.

Running the code

If you run the example again, you can expect the following output:

Expected output
sh-3.2$ ./MyApplication 
MyInteger is 42
MyFloat is 13.370000
MyString is "This is a test"

Quick hits: Examples for properties

In general, properties can be any data type that you like, as long as the data type provides a Reflect function. This function be either directly be part of the class (i.e., member function) OR defined in the global namespace (see non-invasive reflection for more details). Reflection can be added to any data type, even to those whose class definition cannot be altered (e.g., data types from other libraries) using non-invasive reflection. Hence, almost all data types can be used as properties for Modules. Even pointers and polymorphic objects/pointers are supported.

For primitives (such as int, float, double, long long, ...) and data types that are part of the standard library (std::string, std::vector, std::map, ...), CLAID already provides the required reflection mechanisms. If you want to add reflection to your own data type, check the examples here. If you want to add reflection to third party data types, check the examples for non-invasive reflection here. In the following, we provide some common examples for data types that are typically used as properties for Modules.

Vector
#include <vector>
#include <string>

namespace HelloWorld
{
    class MyModule : public claid::Module
    {
        private:
            std::vector<int> intVector;
            std::vector<std::string> stringVector;

            void initialize()
            {
                for(size_t i = 0; i < intVector.size(); i++)
                {
                    printf("intVector item %d is %d\n", i, intVector[i]);
                }

                for(size_t i = 0; i < stringVector.size(); i++)
                {
                    printf("stringVector item %d is \"%s\"\n", i, stringVector[i].c_str());
                }
            }


        // Add reflection to public part of the class definition.
        public:

            Reflect(MyModule,
                reflectMember(intVector);
                reflectMember(stringVector);
            )
    };
}
// This will add the Module to the ClassFactory. 
// It allows instantiating this Module from an XML configuration.
REGISTER_MODULE(HelloWorld::MyModule)
<root>
    <Module class = "HelloWorld::MyModule">
        <intVector>
            <item>42</item>
            <item>1337</item>
            <item>3735928559</item>
        </intVector>

        <stringVector>
            <item>This is the first entry.</item>
            <item>This is the second entry.</item>
            <item>This is another entry.</item>
        </stringVector>
    </Module>
</root>
sh-3.2$ ./MyApplication 
intVector item 0 is 42
intVector item 1 is 1337
intVector item 2 is 48879
stringVector item 0 is "This is the first entry."
stringVector item 1 is "This is the second entry."
stringVector item 2 is "This is another entry."
Complex class
#include <vector>
#include <string>

namespace HelloWorld
{
    class Profession
    {
        public:
            std::string name;
            std::string startDate;
            std::string endDate;

            int salary;

            Reflect(Profession,
                reflectMember(name);
                reflectMember(startDate);
                reflectMember(endDate);
                reflectMember(salary);
            )
    };
    // Class or struct, does not matter.
    class Person
    {
        public:
            std::string name;
            int age;

            std::vector<Profession> workingExperience;

            Reflect(Person,
                reflectMember(name);
                reflectMember(age);
                reflectMember(workingExperience);
            )
    };


    class MyModule : public claid::Module
    {
        private:
            std::vector<Person> persons;

            void initialize()
            {
                printf("%d persons were registered.\n", persons.size());
                for(size_t i = 0; i < persons.size(); i++)
                {
                    const Person& person = persons[i];

                    printf("Person %d \"%s\" is of age %d\n", i, person.name.c_str(), person.age);
                    printf("He has the following working experience:\n\n");
                    for(size_t j = 0; j < person.workingExperience.size(); j++)
                    {
                        const Profession& profession = person.workingExperience[j];

                        printf("\tFrom %s to %s, he worked as \"%s\" and earned %d per year.\n", 
                        profession.startDate.c_str(), profession.endDate.c_str(), 
                        profession.name.c_str(), profession.salary);
                    }
                    printf("\n");
                }                       
            }


        // Add reflection to public part of the class definition.
        public:

            Reflect(MyModule,
                reflectMember(persons);
            )
    };
}
// This will add the Module to the ClassFactory. 
// It allows instantiating this Module from an XML configuration.
REGISTER_MODULE(HelloWorld::MyModule)
<root>
    <Module class = "HelloWorld::MyModule">
        <persons>
            <item>
                <name>John Doe</name>
                <age>42</age>
                <workingExperience>

                    <item>
                        <startDate>01.01.2012</startDate>
                        <endDate>now</endDate>
                        <name>Doctoral researcher</name>
                        <salary>60000</salary>
                    </item>
                    <item>
                        <startDate>01.01.2010</startDate>
                        <endDate>11.11.2011</endDate>
                        <name>Research engineer</name>
                        <salary>80000</salary>
                    </item>
                </workingExperience>
            </item>
            <item>
                <name>Dr Droid</name>
                <age>14</age>
                <workingExperience>
                    <item>
                        <startDate>23.08.2008</startDate>
                        <endDate>now</endDate>
                        <name>Content Manager</name>
                        <salary>180000</salary>
                    </item>
                </workingExperience>
            </item>
        </persons>
    </Module>
</root>
sh-3.2$ ./MyApplication 
2 persons were registered.
Person 0 "John Doe" is of age 42
He has the following working experience:

        From 01.01.2012 to now, he worked as "Doctoral researcher" and earned 60000 per year.
        From 01.01.2010 to 11.11.2011, he worked as "Research engineer" and earned 80000 per year.

Person 1 "Dr Droid" is of age 14
He has the following working experience:

        From 23.08.2008 to now, he worked as "Content Manager" and earned 180000 per year.
Polymorphic Vector

TODO