A lightweight, header-only C++ runtime reflection system
- Runtime type introspection
- Properties backed by class members, getter/setter methods, and getter/setter lambdas
- Class inheritance support
- Nested object support
std::vectorcontainersstd::optionalvalues- Property value change callbacks
- Works across DLL boundaries without duplicate static definition issues
- Custom property metadata
- Skybolt Widgets provides useful Qt widgets, including a property editor backed by Skybolt Reflect.
- Skybolt Engine uses Skybolt Reflect to reflect entity component properties.
The library may be installed in one of two ways:
Define the SKYBOLT_REFLECT_IMPL macro in exactly one cpp file in your project, and include src/SkyboltReflect/SkyboltReflect.h. E.g.
#define SKYBOLT_REFLECT_IMPL
#include "SkyboltReflect.h"
Compile both src/SkyboltReflect/SkyboltReflect.h and src/SkyboltReflect/SkyboltReflect.cpp into a library and link to it. Do not define SKYBOLT_REFLECT_IMPL in your code.
#define SKYBOLT_REFLECT_IMPL // Must be defined in exactly one cpp file
#include "SkyboltReflect/Reflection.h"
using namespace skybolt::refl;
struct Vec2
{
float x = 0, y = 0;
};
SKYBOLT_REFLECT(Vec2)
{
registry.type<Vec2>("Vec2")
.property("x", &Vec2::x)
.property("y", &Vec2::y);
}
TEST_CASE("Get and set class member properties")
{
TypeRegistry registry;
auto type = registry.getTypeByName("Vec2");
auto property = type->getProperty("x");
Vec2 v{1.f, 2,f};
auto instance = makeRefInstance(registry, &v);
// Get value
float value = property->getValue(instance).cast<float>();
CHECK(value == 1.f);
// Set value
property->setValue(instance, makeValueInstance(registry, 2.f));
CHECK(value == 2.f);
}Reflected types may be registered in either a .cpp or a header file using SKYBOLT_REFLECT(MyModuleName). MyModuleName can be any name you like, so long as each SKYBOLT_REFLECT definition has a unique name.
If you use the SKYBOLT_REFLECT macro within a .cpp file, you must place SKYBOLT_REFLECT_EXTERN(MyModuleName) in the corresponding header. This creates a symbolic dependency that ensures the module is correctly linked into the final executable. This is a mechanism to bypass a standard C++ linker optimization: if an object file contains only staticlly initialized objects and no functions called by the main application, the linker may discard the file as "dead code." The SKYBOLT_REFLECT_EXTERN macro forces the linker to include the module, ensuring your types are registered at startup.
- Property getter/setters
- Property categories
- Property metadata
- Containers
- Class hierarchies
- Property value change callbacks
When you build an application using shared libraries on Windows (i.e. DLL files), each shared library normally gets its own private "island" of memory. This means that by default, types registered in shared libraries are not visible to other parts of your application.
To fix this and ensure all parts of your application share a single TypeRegistry, call the following in your library initialization code:
addStaticallyRegisteredTypes(myTypeRegistry)
This adds local statically registered types into your central TypeRegistry used by the rest of your application.
| Concept | Description |
|---|---|
TypeRegistry |
Registry of all reflected types |
Type |
Information about a reflected C++ type |
Property |
Base class for properties of types |
Instance |
A type-safe wrapper around raw property values |
TypeBuilder |
Builder API for defining types and properties |
SKYBOLT_REFLECT_IMPL- defines implementation for header-only usageSKYBOLT_REFLECT(moduleName)– defines a reflection registration blockSKYBOLT_REFLECT_EXTERN(moduleName)– exposes a .cpp reflection definition block in a header to ensure correct linkage
MIT License
To submit a bug report, please raise an issue on the GitHub repository.