Skip to content

Small, flexible, single-header library for runtime reflection and meta data in C++11.

License

Notifications You must be signed in to change notification settings

stevinz/reflect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

baa437e · Feb 7, 2023

History

24 Commits
Mar 24, 2021
Mar 25, 2021
Feb 7, 2023
Mar 20, 2021
Mar 22, 2021
Feb 7, 2023
Feb 7, 2023

Repository files navigation

Reflect

Small, flexible, single-header library for aggregate (struct / class) runtime reflection and meta data using C++11 features. Minimal STL usage, no other dependencies.


Installation

  • Copy 'reflect.h' to project

  • In ONE cpp file, define REGISTER_REFLECTION:

#define REGISTER_REFLECTION
#include "reflect.h"
#include "my_struct_1.h"
#include "my_struct_2.h"
#include etc...
  • Classes / Structs should be simple aggregate types (standard layout)

  • BEFORE using reflection functions, make one call to

InitializeReflection();

Usage

Registration in Header File, example: "transform.h"

#ifndef TRANSFORM2D_H
#define TRANSFORM2D_H

#include "reflect.h"
struct Transform2D {
    int width;
    int height;
    std::vector<double> position;
    std::string text;
    REFLECT();
}
#ifdef REGISTER_REFLECTION
    REFLECT_CLASS(Transform2D)
    REFLECT_MEMBER(width)
    REFLECT_MEMBER(height)
    REFLECT_MEMBER(position)
    REFLECT_MEMBER(text)
    REFLECT_END(Transform2D)
#endif

#endif // TRANSFORM2D_H

In Code

Initialize class instance

Transform2D t { };
t.width = 100;
t.height = 100;
t.position = std::vector<double>({1.0, 2.0, 3.0});
t.text = "Hello world!";

TypeData Object

// Class TypeData
TypeData data = ClassData<Transform2D>();           // By class type
TypeData data = ClassData(t);                       // By class instance
TypeData data = ClassData(type_hash);               // By class type hash
TypeData data = ClassData("Transform2D");           // By class name

// Member TypeData
TypeData data = MemberData<Transform2D>(0);         // By class type, member index
TypeData data = MemberData<Transform2D>("width");   // By class type, member name
TypeData data = MemberData(t, 0);                   // By class instance, member index
TypeData data = MemberData(t, "width");             // By class instance, member name
TypeData data = MemberData(type_hash, 0);           // By class type hash, member index
TypeData data = MemberData(type_hash, "width");     // By class type hash, member name

Get / Set Member Variables

  • Use the ClassMember<member_type>(class_instance, member_data) function to return a reference to a member variable. This function requires the return type, a class instance (can be void* or class type), and a member variable TypeData object. Before calling ClassMember<>(), member variable type can be checked by comparing to types using helper function TypeHashID<type_to_check>()
// Member Variable by Index
TypeData member = MemberData(t, 0);
if (member.type_hash == TypeHashID<int>()) {
    // Create reference to member
    int& width = ClassMember<int>(&t, member);
    // Can now set member variable directly
    width = 120;
}

// Member Variable by Name
TypeData member = MemberData(t, "position");
if (member.type_hash == TypeHashID<std::vector<double>>()) {
    // Create reference to member
    std::vector<double>& position = ClassMember<std::vector<double>>(&t, member);
    // Can now set member variable directly
    position = { 2.0, 4.0, 6.0 };
}

Iterating Members / Properties

int member_count = ClassData(t).member_count;
for (int index = 0; index < member_count; ++index) {
    TypeData member = MemberData(t, index);
    std::cout << " Index: " << member.index << ", ";
    std::cout << " Name: "  << member.name  << ", ";
    std::cout << " Title: " << member.title << ", ";
    std::cout << " Value: ";
    if (member.type_hash == TypeHashID<int>()) {
        std::cout << ClassMember<int>(&t, member);
    } else if (member.type_hash == TypeHashID<std::string>()) {
        std::cout << ClassMember<std::string>(&t, member);
    } else if (member.type_hash == TypeHashID<std::vector<double>>()) {
        std::vector<double>& vec = ClassMember<std::vector<double>>(&t, member);
        for (auto& number : vec) {
            std::cout << number << ", ";
        }
    }
}

Data from Unknown Class Type

  • If using with an entity component system, it's possible you may not have access to class type at runtime. Often a collection of components are stored in a container of void pointers. Somewhere in your code when your class is initialized, store the component class TypeHash:
TypeHash saved_hash = ClassData(t).type_hash;
void* class_pointer = (void*)(&t);
  • Later (if your components are stored as void pointers in an array / vector / etc. with other components) you may still access the member variables of the component without casting the component back to the original type. This is done by using the saved_hash from earlier:
using vec = std::vector<double>;
TypeData member = MemberData(saved_hash, 3);
if (member.type_hash == TypeHashID<vec>()) {
    vec& rotation = ClassMember<vec>(class_pointer, member);
    std::cout << "  Rotation X: " << rotation[0];
    std::cout << ", Rotation Y: " << rotation[1];
    std::cout << ", Rotation Z: " << rotation[2];
}

User Meta Data

Registration in Header File, example: "transform.h"

  • Meta data can by stored as std::string within a class or member type. Set user meta data at compile time using CLASS_META_DATA and MEMBER_META_DATA in the class header file with (int, string) or (sting, string) pairs:
#ifdef REGISTER_REFLECTION
    REFLECT_CLASS(Transform2D)
        CLASS_META_DATA(META_DATA_DESCRIPTION, "Describes object in 2D space.")
        CLASS_META_DATA("icon", "assets/transform.png")
    REFLECT_MEMBER(width)
        MEMBER_META_DATA(META_DATA_DESCRIPTION, "Width of this object.")
    REFLECT_MEMBER(height)
        MEMBER_META_DATA(META_DATA_DESCRIPTION, "Height of this object.")
    REFLECT_MEMBER(position)
        MEMBER_META_DATA(META_DATA_HIDDEN, "true")
        MEMBER_META_DATA("tooltip", "Pos")
    REFLECT_END(Transform2D)
#endif

Get / Set Meta Data

  • BY REFERENCE, pass a TypeData object (class or member, this can be retrieved many different ways as shown earlier) to the meta data functions to get / set meta data at runtime:
// TypeData from class
TypeData& type_data = ClassData<Transform2D>();
// or from member variable
TypeData& type_data = MemberData<Transform2D>("width");

// Get meta data
std::string description = GetMetaData(type_data, META_DATA_DESCRIPTION);
std::string icon_file   = GetMetaData(type_data, "icon");

// Set meta data
SetMetaData(type_data, META_DATA_DESCRIPTION, description);
SetMetaData(type_data, "icon", icon_file);

Portability

Tested and compiled with:

  • GCC 4.2.1
  • Emscripten 1.38.43

About

Small, flexible, single-header library for runtime reflection and meta data in C++11.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published