Custom Serializer
Implementing your own Serializer.
To define your own serializer type, you'll need to include core/
#include <cubos/core/data/ser/serializer.hpp> using cubos::core::data::Serializer; using cubos::core::reflection::Type;
We'll define a serializer that will print the data to the standard output.
class MySerializer : public Serializer { public: MySerializer(); protected: bool decompose(const Type& type, const void* value) override; };
In the constructor, we should set hooks to be called for serializing primitive types or any other type we want to handle specifically.
In this example, we'll only handle int32_t
, but usually you should at least cover all primitive types.
#include <cubos/core/reflection/external/primitives.hpp> using cubos::core::reflection::reflect; MySerializer::MySerializer() { this->hook<int32_t>([](const int32_t& value) { Stream::stdOut.print(value); return true; }); }
The only other thing you need to do is implement the Serializer::
Here, we can use traits such as FieldsTrait to get the fields of a type and print them.
In this sample, we'll only be handling fields and arrays, but you should try to cover as many kinds of data as possible.
#include <cubos/core/reflection/traits/array.hpp> #include <cubos/core/reflection/traits/fields.hpp> #include <cubos/core/reflection/type.hpp> using cubos::core::reflection::ArrayTrait; using cubos::core::reflection::FieldsTrait; bool MySerializer::decompose(const Type& type, const void* value) { if (type.has<ArrayTrait>()) { const auto& arrayTrait = type.get<ArrayTrait>(); Stream::stdOut.put('['); for (const auto* element : arrayTrait.view(value)) { if (!this->write(arrayTrait.elementType(), element)) { return false; } Stream::stdOut.print(", "); } Stream::stdOut.put(']'); return true; }
We start by checking if the type can be viewed as an array. If it can, we recurse into its elements. Otherwise, we'll fallback to the fields of the type.
if (type.has<FieldsTrait>()) { Stream::stdOut.put('{'); for (const auto& [field, fieldValue] : type.get<FieldsTrait>().view(value)) { Stream::stdOut.printf("{}: ", field->name()); if (!this->write(field->type(), fieldValue)) { return false; } Stream::stdOut.print(", "); } Stream::stdOut.put('}'); return true; } CUBOS_WARN("Cannot decompose {}", type.name()); return false; }
If the type has fields, we'll iterate over them and print them. Otherwise, we'll fail by returning false
.
Using our serializer is as simple as constructing it and calling Serializer::
In this case, we'll be serializing a std::vector<glm::ivec3>
, which is an array of objects with three int32_t
fields.
#include <glm/vec3.hpp> #include <cubos/core/reflection/external/glm.hpp> #include <cubos/core/reflection/external/vector.hpp> int main() { std::vector<glm::ivec3> vec{{1, 2, 3}, {4, 5, 6}}; MySerializer ser{}; ser.write(vec); }
This should output:
// [{x: 1, y: 2, z: 3, }, {x: 4, y: 5, z: 6, }, ]