Constructible Trait
Exposing the destructor and constructors of a type.
You may find it useful while working with type-erased data to be able to create copies of the data, destroy it, or move it around. The ConstructibleTrait trait exposes the size, alignment, destructor and constructors of a type.
Lets say you have a type Scale, which you want to be able to reflect, with a default value of 1.0:
#include <cubos/core/reflection/reflect.hpp> struct Scale { CUBOS_REFLECT; float value = 1.0F; Scale() = default; Scale(float value) : value(value) { } };
We're going to add the ConstructibleTrait trait to it, so that we can create instances of it at runtime:
#include <cubos/core/reflection/external/primitives.hpp> #include <cubos/core/reflection/traits/constructible.hpp> #include <cubos/core/reflection/type.hpp> using cubos::core::reflection::ConstructibleTrait; using cubos::core::reflection::Type; CUBOS_REFLECT_IMPL(Scale) { return Type::create("Scale").with( ConstructibleTrait::typed<Scale>().withDefaultConstructor().withCustomConstructor<float>({"value"}).build()); }
Now, we can access the trait from the reflected type:
const auto& scaleType = reflect<Scale>(); CUBOS_ASSERT(scaleType.has<ConstructibleTrait>()); const auto& constructible = scaleType.get<ConstructibleTrait>();
Imagine for a moment that you don't know the type of the data you're working, and you only have access to its reflection data through scaleType. If you want to create a default instance of the type, you can call the default constructor stored in the trait:
// Allocate memory for the instance and default-construct it. void* instance = operator new(constructible.size()); constructible.defaultConstruct(instance); CUBOS_ASSERT(static_cast<Scale*>(instance)->value == 1.0F);
Don't forget to destroy the instance manually when you're done with it:
// Destroy the instance and deallocate its memory. constructible.destruct(instance);
The constructible trait always stores the destructor of a type, and may optionally contain any of the basic constructors (default, copy and move), or even arbitrary custom constructors, which receive arguments of reflectable types. For example, below, we're calling the custom constructor we previously reflected.
CUBOS_ASSERT(constructible.customConstructorCount() == 1); CUBOS_ASSERT(constructible.customConstructor(0).argCount() == 1); CUBOS_ASSERT(constructible.customConstructor(0).argName(0) == "value"); CUBOS_ASSERT(constructible.customConstructor(0).argType(0).is<float>()); float value = 2.0F; void* args[] = {&value}; constructible.customConstruct(0, instance, args); CUBOS_ASSERT(static_cast<Scale*>(instance)->value == 2.0F);