1. 4FIPS
  4. APPS
  5. CODE
  7. ABOUT


Forums dedicated to the projects hosted at 4FipS.com

Skip to content

C++ reflection - The simplest way

> Coding, hacking, graphics, game dev, and such...

C++ reflection - The simplest way

Postby fips on Tue Jul 24, 2012 8:09 pm

First, I’m not going to show anything super-sophisticated here. Have a look at Boost Fusion if you are looking for a robust industrial solution for reflection in C++. I’d rather talk about something simple and efficient, for those who appreciate keeping their code-base clean and flexible, without external dependencies, for those like me ;) who like fast iteration cycles and easy debugging.

So, I’ve been recently in need of a simple reflection system that would allow me to pass my POD structures through a generic interface as BLOBs, without loosing the type information about the POD attributes on the way.

After a few iterations, I’ve come up with quite a simple system, similar to this:

VIEW THE CODE BELOW IN FULL-SCREEN (reflection_sample.cpp)
(c) 2012 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=724

#include <array>
#include <vector>
#include <string>
#include <algorithm>
#include <cstdio> // printf
#include <cstdint> // uint16_t, ...
#include <cstddef> // offsetof
#include <cassert>

const uint16_t c_invalid_index = uint16_t(-1);
struct Vector4 { float x, y, z, w; };

/// Describes various type characteristics.
struct Type
    std::string name;
    uint16_t byte_size;

    Type(const std::string &name, uint16_t byte_size):
    name(name), byte_size(byte_size)

/// Manages a table of types, indexed by uint16_t.
class Type_table

    /// Defines a basic set of types, more can be added later using add().
        add("float", sizeof(float));
        add("Vector4", sizeof(Vector4));

    /// Adds a new type and returns its index.
    uint16_t add(const char *type_name, uint16_t byte_size)
        assert(type_name && byte_size > 0);
        _types.emplace_back(Type(type_name, byte_size));
        return static_cast<uint16_t>(_types.size() - 1);

    /// A direct read-only access to the internal 'Type' can't harm ;)
    const Type & get(uint16_t type_index) const
        assert(type_index < _types.size());
        return _types[type_index];

    /// Finds a type by name, returns 'c_invalid_index' if not found.
    uint16_t find(const char *type_name) const

        const auto it = std::find_if(
         _types.begin(), _types.end(),
         [&type_name](const Type &type) { return type.name == type_name; }

         it != _types.end() ?
         static_cast<uint16_t>(std::distance(_types.begin(), it)) :


    std::vector<Type> _types;

/// Describes a single struct attribute.
struct Struct_attribute
    uint16_t type_index;
    uint16_t byte_offset;

    type_index(c_invalid_index), byte_offset(0)

    Struct_attribute(uint16_t type_index, uint16_t byte_offset):
    type_index(type_index), byte_offset(byte_offset)

/// Describes a whole struct (made of attributes) in a generic way.
struct Struct_descriptor
    const void *data;
    size_t byte_size;
    const Struct_attribute *attribs_begin;

    Struct_descriptor(const void *data, size_t byte_size, const Struct_attribute *attribs_begin):
    data(data), byte_size(byte_size), attribs_begin(attribs_begin)

/// A sample class with a 'Data' block that supports reflection.
class Material

    Material(const Type_table &type_table)
        const Attribute_array data_attribs =
            Struct_attribute(type_table.find("Vector4"), offsetof(Data, inner_color)),
            Struct_attribute(type_table.find("Vector4"), offsetof(Data, outer_color)),
            Struct_attribute(type_table.find("float"), offsetof(Data, point_size)),
            Struct_attribute() // terminator
        // one unnecessary copy of 'data_attribs' here is worth the initialization through {}
        _data_attribs = data_attribs;

    Struct_descriptor data_descriptor() const
    { return Struct_descriptor(&_data, sizeof(_data), _data_attribs.data()); }

    const Vector4 & inner_color() { return _data.inner_color; }
    void set_inner_color(const Vector4 &color) { _data.inner_color = color; }


    /// The actual reflected structure.
    struct Data
        Vector4 inner_color;
        Vector4 outer_color;
        float point_size;

    } _data;

    typedef std::array<Struct_attribute, 4> Attribute_array;
    Attribute_array _data_attribs;

/// A function that demonstrates how to work with a 'reflected' structure in a generic way.
void dump(const Type_table &type_table, const Struct_descriptor &desc)
    assert(desc.byte_size > 0);

    printf("struct_byte_size=%d\n", desc.byte_size);
    for(const Struct_attribute *attrib = desc.attribs_begin; attrib->type_index != c_invalid_index; ++attrib)
        const Type &type = type_table.get(attrib->type_index);
         "type_index=%d, byte_offset=%d, type_size='%s', type_size=%d\n",
         attrib->type_index, attrib->byte_offset, type.name.c_str(), type.byte_size

int main()
    const Type_table type_table;

    const Material material(type_table);
    dump(type_table, material.data_descriptor());

    const Material material2 = material; // note that copying is almost for free, with no heap allocations!
    dump(type_table, material2.data_descriptor());

    return 0;

// output:
// struct_byte_size=36
// type_index=1, byte_offset=0, type_name='Vector4', type_size=16
// type_index=1, byte_offset=16, type_name='Vector4', type_size=16
// type_index=0, byte_offset=32, type_name='float', type_size=4
// struct_byte_size=36
// type_index=1, byte_offset=0, type_name='Vector4', type_size=16
// type_index=1, byte_offset=16, type_name='Vector4', type_size=16
// type_index=0, byte_offset=32, type_name='float', type_size=4

It’s worth mentioning that the reflection info itself is being kept in std::array, so it’s directly embedded in the host class and there’re no heap allocations involved = big win, cheap to copy, no sharing needed!
User avatar
Site Admin
Posts: 163
Joined: Wed Nov 12, 2008 9:49 pm
Location: Prague

C++ reflection - The simplest way



Return to General Discussion, Tech Talk & Blog

Who is online

Users browsing this forum: No registered users and 2 guests