Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++11

A General C++ Reflection Engine Based on C++11 and Meta Programming

4.92/5 (12 votes)
28 Jun 2016Apache2 min read 44.8K   886  
This is a cross-platform general C++ engine, including a general runtime and a reflection engine.

Introduction

This general C++ engine is a cross-platform general C++ engine, including a general runtime and a reflection engine by now. Its main purpose is to provide reflection capabilities to C++ language. Based on C++11 and Meta Programming, this reflection engine implements a serial of general reflection functionalities for C++.

Background

As we all know, C++ RTTI has very limited ability to make reflection, which definitely becomes more and more important in modern programming. So it’s quite critical to improve the reflection ability for C++. Unfortunately, we cannot add reflection ability to C++ implictly, except creating a brand new compiler. So we have to reflect by adding macros without new compiler. Fortunately, C++11 and Meta Programming provide very easy ways to explore reflection.

Features

  • Examine normal and static fields
  • Examine methods
  • Set and get field values without including any exact definition
  • Invoke normal, virtual and static methods and get return value dynamically
  • Examine class inheritance dynamically
  • Create instance without any header files
  • Support template class reflection as well
  • Serialization for complex object

Compilers and Platforms

  • Linux: g++ version 4.8.3 or higher, 64-bit
  • Windows: Microsoft Visual Studio 2010 or higher

C++ 11 features required for compiler:

  • Auto and nullptr keyword
  • Lambda expression
  • Default template type
  • Right value reference
  • Template traits

Builds

For g++:

  • gce/trunk/builds/so/make.sh  -> libgcrt.so libreflect.so

For Visual Studio:

  • gce/trunk/builds/dll/gcrt/gcrt.vcxproj
  • gce/trunk/builds/dll/reflect/reflect.vcxproj

Using the Code

It’s quite easy to use gce::reflection.

Demo

C++
#include <src/reflect/reflect.h>
#include <stdio.h>
#include <string>

//must inherit from gce::reflection::reflectable
class Base : public gce::reflection::reflectable<Base>
{
protected:
       //declare reflectable property
       member(unsigned long long, length);
       //declare reflectable property with mutable key word
       member_mutable(std::string, name);
       //declare reflectable property as array, with size 10
       member_array(char, buf1, 10);
       //declare reflectable property as array, with size 10 and mutable key word
       member_array_mutable(char, buf2, 10);
       //declare reflectable static property
       member_static(int, level);
       //declare reflectable static property as array, with size 10
       member_static_array(double, ds, 10);

public:
       //declare and implement reflectable method
       method(void, set_length, (long long l))
       {
           length = l;
       }

       //declare and implement reflectable method
       method(void, set_name, (const std::string& str))
       {
           name = str;
       }

       //declare and implement reflectable method
       method(unsigned long long, get_length, ())
       {
           return length;
       }

       //declare reflectable method
       method(std::string, get_name, ());

       //declare and implement reflectable static method
       method(static int, get_level, ())
       {
           return level;
       }

       //declare and implement reflectable virtual method
       method(virtual void, do_something, ())
       {
           std::cout<<"Base class do_something invoked"<<std::endl;
       }   

       //pointer and reference as parameters or return value
       method(std::string*, func1, (std::string* str, int& val))
       {
           std::cout<<"str:"<<*str<<" 
           val:"<<val<<std::endl;
           return str;
       }

       //std::shared_ptr as return value
       method(std::shared_ptr<std::string>, func2, ())
       {
           return std::shared_ptr<std::string>
           (new std::string("hello, shared_ptr"));
       }
};

//define static property
int Base::level = 0;
double Base::ds[10];

//implement reflectable method out of its class body
std::string Base::get_name()
{
    return name;
}

int main()
{
       Base base;
       //get the class of Base
       auto& base_class = base.get_class();
       //print the class name
       std::cout<<"class name:"<<base_class.get_name()<<std::endl;
       //print the class size
       std::cout<<"size:"<<base_class.get_size()<<std::endl;

       //list all properties
       auto& members = base_class.members();
       std::cout<<"property list:"<<std::endl;
       for(auto it=members.begin(); it!=members.end(); ++it)
       {
              auto& member_class = it->second.get_class();
              //must use get_total_size here
              std::cout<<it->first<<", 
              type name:"<<member_class.get_name()<<", 
              size:"<<member_class. get_total_size()<<std::endl;
       }

       try //try-catch is required
       {
              //invoke method, we need to specify the return type as template type.
              //numeric 10 refers to int implicitly, 
              //so this argument must be specified unsigned long long explicitly
              base_class.get_method("set_length").invoke<void>(&base, (unsigned long long)10);
              std::string name = "Tom";
              base_class.get_method("set_name").invoke<void, Base, std::string>(&base, name);

              //invoke method and print return value
              std::cout<<"invoke get_length:"<<base_class.get_method
              ("get_length").invoke<unsigned long long>(&base)<<std::endl;
              std::cout<<"invoke get_name:"<<base_class.get_method
              ("get_name").invoke<std::string>(&base)<<std::endl;

              std::string str = "string pointer";
              int val = 10;
              //return type is string pointer
              std::cout<<"invoke func1:"<<*base_class.get_method
              ("func1").invoke<std::string*>(&base, &str, val)<<std::endl;
              //return type is std::shared_ptr<std::string>
              std::cout<<"invoke func2:"<<*base_class.get_method
              ("func2").invoke<std::shared_ptr<std::string> >(&base)<<std::endl;

              //invoke static method, instead of passing instance, 
              //we just need to pass nullptr, which must be interpreted to Base type explicitly.
              std::cout<<"invoke static get_level:"<<base_class.get_method
              	("get_level").invoke<int>((Base*)nullptr)<<std::endl;
       }
       catch(std::exception& e)
       {
              std::cout<<e.what()<<std::endl;
       }
       return 0;
}

The outputs of this program would be:

There are a few more demo codes to show what this engine provides:

  • Demo 2 (dynamic invoke)
  • Demo 3 (single inheritance)
  • Demo 4 (multiple inheritances, i.e., diamond hierarchy)
  • Demo 5 (template class)
  • Demo 6 (dynamical instance)
  • Demo 7 (serialization)

For more details and demo codes, please download the release file and document.

This release is a beta version, any problems or feedback are very welcome.

Thanks very much!

Bug History

According to the bug reports, here's the list of the bug fixing history. Please make sure to download the latest version. Thanks!

version 0.53:

fix '__call_constructor’ was not declared in this scope

fix name conflict

version 0.51:

fix demo code

 

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0