Examples » Core » Reflection » 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;
};

We're going to add the ConstructibleTrait trait to it, so that we can create instances of it at runtime:

#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().build());
}

Now, we can access the trait from the reflected type:

int main()
{
    using cubos::core::reflection::reflect;

    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);
    operator delete(instance);
}