Skip to content

A prototype member introspection reflection system for C++

Notifications You must be signed in to change notification settings

LucasDower/Reflection

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reflection

This is a prototype reflection system built for C++ that supports member variable introspection. It is header-only and requires no extra external tooling.

Usage

Note: The included images show an example application how a reflection system can be visualised in a GUI. This is not included in the base library inside /Include but can be viewed inside `/Examples

Adding reflection to a class is easy. Just add the following macros and ensure your class structure inherits from RObject.

#include "Reflection.hpp"

class RPlayer : public RObject
{
    REFLECTION_CLASS_BEGIN(RPlayer, RObject)
        REFLECTION_PROPERTIES_BEGIN
            REFLECTION_PROPERTY(m_Health)
            REFLECTION_PROPERTY(m_Mana)
            REFLECTION_PROPERTY(m_Level)
            REFLECTION_PROPERTY(m_Experience)
        REFLECTION_PROPERTIES_END
    REFLECTION_CLASS_END

public:
    int m_Health = 100;
    int m_Mana = 200;
};

REFLECTION_CLASS_BEGIN expects the first argument to be the name of the class being reflected and the second argument as the parent RClass. Properties that should be reflected require the corresponding REFLECTION_PROPERTY.

All classes with reflection steup have include a static RClass accessible statically with RClass::StaticGetClass() or dynamically with GetClass().

// Statically (RPlayer::Class is derived from RClass)
RPlayer::Class& PlayerClass = RPlayer::StaticGetClass();
// or

// Dynamically
std::shared_ptr<RClass> SomeObject = std::make_shared<RPlayer>();
const RClass& SomeClass = SomeObject->GetClass();

You can query the inheritance structure with GetParent():

static_assert(RPlayer::StaticGetClass().GetParent() == RClass::StaticGetClass());

An RObject's class can be compared with IsA<T>(), for example:

if (SomeClass.GetClass().IsA<RPlayer>())
{
    std::shared_ptr<RPlayer> Player = std::static_pointer_cast<RPlayer>(SomeObject);
    std::cout << "PlayerObject is a RPlayer object, it's health is " << Player._MHealth << std::endl;
}

You can inspect a class's properties statically or dynamically with RClass::StaticGetProperties() or SomeClass.GetProperties() respectively. Once you have a class's set of properties, you can identify types and get/set values.

// Static
for (const RProperty& Prop : RPlayer::StaticGetClass().StaticGetProperties())
{
    std::cout << Prop.name << "\n";
    std::cout << "  type = " << Prop.type << "\n";
    std::cout << "  size = " << Prop.size << "\n";
    std::cout << "  offset = " << Prop.offset << "\n";
}

// Dynamic
std::unique_ptr<RPlayer> Player = std::make_unique<RPlayer>();
const std::vector<RProperty>& Properties = Player->GetClass().GetProperties();
std::unique_ptr<RPlayer> Player = std::make_unique<RPlayer>();
const RProperty& HealthProperty = Player->GetClass().GetPropertyChecked("m_Health");
Player->GetClass().SetPropertyValueChecked(*Player, HealthProperty, 0);

Currently there is reflection support for the following data types, (though this is trivial to expand):

  • Bool,
  • Char,
  • Short,
  • Int,
  • Long,
  • LongLong,
  • UnsignedChar,
  • UnsignedShort,
  • UnsignedInt,
  • UnsignedLong,
  • UnsignedLongLong,
  • Float,
  • Double

Reflected classes can be derived from other RObjects, for example:

class RPlayerWithLevels : public RObject
{
    REFLECTION_CLASS_BEGIN(RPlayerWithLevels, RPlayer)
        REFLECTION_PROPERTIES_BEGIN
            REFLECTION_PROPERTY(m_Level)
            REFLECTION_PROPERTY(m_Experience)
        REFLECTION_PROPERTIES_END
    REFLECTION_CLASS_END

public:
    int m_Level = 0;
    float m_Experience = 0.0f;
};

You can also use RObject's as properties, for example:

class RVector2f : public RObject
{
    REFLECTION_CLASS_BEGIN(RVector2f, RObject)
        REFLECTION_PROPERTIES_BEGIN
            REFLECTION_PROPERTY(m_X)
            REFLECTION_PROPERTY(m_Y)
        REFLECTION_PROPERTIES_END
    REFLECTION_CLASS_END

public:
    float m_X = 0;
    float m_Y = 0;
};

class RPhysicsBody : public RObject
{
    REFLECTION_CLASS_BEGIN(RPhysicsBody, RObject)
        REFLECTION_PROPERTIES_BEGIN
            REFLECTION_PROPERTY(m_Position)
            REFLECTION_PROPERTY(m_Velocity)
        REFLECTION_PROPERTIES_END
    REFLECTION_CLASS_END

public:
    RVector2f m_Position;
    RVector2f m_Velocity;
};

About

A prototype member introspection reflection system for C++

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published