diff --git a/core/samples/CMakeLists.txt b/core/samples/CMakeLists.txt index fcb31042d8..1bade9e1ff 100644 --- a/core/samples/CMakeLists.txt +++ b/core/samples/CMakeLists.txt @@ -34,6 +34,7 @@ make_sample(DIR "reflection/traits/array") make_sample(DIR "reflection/traits/dictionary") make_sample(DIR "data/fs/embedded_archive" SOURCES "embed.cpp") make_sample(DIR "data/fs/standard_archive") +make_sample(DIR "data/ser/custom") make_sample(DIR "data/serialization") make_sample(DIR "ecs/events") make_sample(DIR "ecs/general") diff --git a/core/samples/data/page.md b/core/samples/data/page.md new file mode 100644 index 0000000000..cab52f11a2 --- /dev/null +++ b/core/samples/data/page.md @@ -0,0 +1,5 @@ +# Data {#examples-core-data} + +@brief Using the @ref core-data module. + +- @subpage examples-core-data-ser - @copybrief examples-core-data-ser diff --git a/core/samples/data/ser/custom/main.cpp b/core/samples/data/ser/custom/main.cpp new file mode 100644 index 0000000000..74f5a05dd3 --- /dev/null +++ b/core/samples/data/ser/custom/main.cpp @@ -0,0 +1,107 @@ +#include +#include + +using cubos::core::memory::Stream; + +/// [Include] +#include + +using cubos::core::data::Serializer; +using cubos::core::reflection::Type; +/// [Include] + +/// [Your own serializer] +class MySerializer : public Serializer +{ +public: + MySerializer(); + +protected: + virtual bool decompose(const Type& type, const void* value) override; +}; +/// [Your own serializer] + +/// [Setting up hooks] +#include + +using cubos::core::reflection::reflect; + +MySerializer::MySerializer() +{ + this->hook([](Serializer&, const Type&, const void* value) { + Stream::stdOut.print(*static_cast(value)); + return true; + }); +} +/// [Setting up hooks] + +/// [Decomposing types] +#include +#include +#include + +using cubos::core::reflection::ArrayTrait; +using cubos::core::reflection::FieldsTrait; + +bool MySerializer::decompose(const Type& type, const void* value) +{ + if (type.has()) + { + const auto& arrayTrait = type.get(); + + Stream::stdOut.put('['); + for (auto element : arrayTrait.view(value)) + { + if (!this->write(arrayTrait.elementType(), element)) + { + return false; + } + Stream::stdOut.print(", "); + } + Stream::stdOut.put(']'); + + return true; + } + /// [Decomposing types] + + /// [Decomposing types with fields] + if (type.has()) + { + Stream::stdOut.put('{'); + for (const auto& [field, fieldValue] : type.get().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; +} +/// [Decomposing types with fields] + +/// [Usage] +#include + +#include +#include + +int main() +{ + std::vector vec{{1, 2, 3}, {4, 5, 6}}; + + MySerializer ser{}; + ser.write(vec); +} +/// [Usage] + +/// [Output] +// [{x: 1, y: 2, z: 3, }, {x: 4, y: 5, z: 6, }, ] +/// [Output] diff --git a/core/samples/data/ser/custom/page.md b/core/samples/data/ser/custom/page.md new file mode 100644 index 0000000000..afb8803018 --- /dev/null +++ b/core/samples/data/ser/custom/page.md @@ -0,0 +1,55 @@ +# Custom Serializer {#examples-core-data-ser-custom} + +@brief Implementing your own @ref cubos::core::data::Serializer "Serializer". + +To define your own serializer type, you'll need to include +@ref core/data/ser/serializer.hpp. For simplicity, in this sample we'll use +the following aliases: + +@snippet data/ser/custom/main.cpp Include + +We'll define a serializer that will print the data to the standard output. + +@snippet data/ser/custom/main.cpp Your own serializer + +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. + +@snippet data/ser/custom/main.cpp Setting up hooks + +The only other thing you need to do is implement the @ref +cubos::core::data::Serializer::decompose "Serializer::decompose" method, which +acts as a catch-all for any type without a specific hook. + +Here, we can use traits such as @ref cubos::core::reflection::FieldsTrait +"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. + +@snippet data/ser/custom/main.cpp Decomposing types + +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. + +@snippet data/ser/custom/main.cpp Decomposing types with fields + +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 @ref +cubos::core::data::Serializer::write "Serializer::write" on the data we want to +serialize. + +In this case, we'll be serializing a `std::vector`, which is +an array of objects with three `int32_t` fields. + +@snippet data/ser/custom/main.cpp Usage + +This should output: + +@snippet data/ser/custom/main.cpp Output diff --git a/core/samples/data/ser/page.md b/core/samples/data/ser/page.md new file mode 100644 index 0000000000..7737e70b47 --- /dev/null +++ b/core/samples/data/ser/page.md @@ -0,0 +1,5 @@ +# Serialization {#examples-core-data-ser} + +@brief Using the @ref core-data-ser module. + +- @subpage examples-core-data-ser-custom - @copybrief examples-core-data-ser-custom diff --git a/docs/pages/3_examples/1_core/main.md b/docs/pages/3_examples/1_core/main.md index 9a48f9137e..761b5d59b6 100644 --- a/docs/pages/3_examples/1_core/main.md +++ b/docs/pages/3_examples/1_core/main.md @@ -10,3 +10,4 @@ The following examples have fully documented tutorials: - @subpage examples-core-logging - @copybrief examples-core-logging - @subpage examples-core-reflection - @copybrief examples-core-reflection +- @subpage examples-core-data - @copybrief examples-core-data