Post

Run Time Type Reflection

I use Python alot in some of my projects, and one of the Python features I really like and wish that C++ had is “Reflection”.

What is reflection?

In computer science, reflective programming or reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.

Its a pretty powerful feature which should be taken with a grain f salt. one great use case for it is object serillization. assume you want to create a general function which takes any type of object, and serialize it to a text file. you can do this easily in python by using some of the built in functions like ‘dir’, ‘type’ and ‘getattr’.

a simple implementation looks like the below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TestClass:
    def __init__(self,name:str, age:int):
        self.name = name
        self.age = age

def seri(obj):
    with open("class.txt",mode="w") as file:
        file.write(f"ClassName={str(type(obj))}\n")
        for attr in dir(inst):
            if attr[0]!='_':
                file.write(f"{attr}={getattr(obj,attr)}\n")

class_inst = TestClass("Ibrahim",28)
seri(class_inst)

And this will create a text file with the following lines.

1
2
3
ClassName=<class '__main__.TestClass'>
age=28
name=Ibrahim

pretty nice right?

Now lets do the same in C++. Unofrtunately, C++ doesn’t have built in functions to achive the same. and we have to use a third party library. a nice third party library is RTTR.

RTTR (Run Time Type Reflection) is an open source library, which adds reflection to C++.

Lets see how we can use it. First thing, we need to manually register every class we want to have type reflection for. We can do this using the RTTR_REGISTRATION macro.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <rttr/registration>
using namespace rttr;

struct TestClass
{
    std::string name;
    int age;
};

RTTR_REGISTRATION
{
    // For each property in the class 
    // we define the name
    registration::class_<TestClass>("TestClass")
        .constructor<>()
        .property("name", &TestClass::name)
        .property("age", &TestClass::age);
}

Now lets write the serilization function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <typename T>
void seri(const T &obj)
{
    // Create the output txt file
    std::ofstream file;
    file.open("class_cpp.txt", std::ios::binary);
    type t = type::get<T>();  // Get the class type
    file << "ClassName=" << t.get_name() << std::endl; // get the class name
    for (auto &prop : t.get_properties())
    {
        // iterate over all class's properties.
        variant var_prop = prop.get_value(obj); // get the value of the property
        file << prop.get_name() << "=" << var_prop.to_string() << std::endl;
    }
}

then we can test it

1
2
3
4
5
6
int main(int argc, char const *argv[])
{
    TestClass class_inst{"Ibrahim", 28};
    seri(class_inst);
    return 0;
}

This will save a txt file with the following lines

1
2
3
ClassName=TestClass
name=Ibrahim
age=28

And that’s how we do type refelection in c++. It still not as straight forward as pyhon, because you still need to register every type manually. but it’s still a powerful tool which can save you alot of time.

Is this the only way to do type refelctino in C++? not really, there is other options like refl-cpp which offers a compile time refelection.

A nice advantage for the Runtime reflection vs Compile time, is that you can work with types, where you don’t even have the headers available. Just think about plugins, where it is not necessary anymore to derive from some common base class and implement standard interfaces. but of course this will come with performance cost.

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.