Skip to content

File serialization_defaults.hpp

File List > backends > cxx > include > zmbt > reflect > serialization_defaults.hpp

Go to the documentation of this file

#ifndef ZMBT_CORE_SERIALIZATION_DEFAULTS_HPP_
#define ZMBT_CORE_SERIALIZATION_DEFAULTS_HPP_

#include <functional>
#include <type_traits>

#include <boost/describe.hpp>
#include <boost/json.hpp>

#include "serialization.hpp"



namespace zmbt {
namespace reflect {
namespace detail {

// DESCRIBED ENUM SERIALIZER

template <class T>
struct default_serialization<T, first_if_t<void,
    boost::describe::has_describe_enumerators<T>
>>
{
    static boost::json::value json_from(T const& t) {
        boost::json::value v;
        using Descr = boost::describe::describe_enumerators<T>;
        using underlying_t = std::underlying_type_t<T>;

        // first assign numeric value if nominal representation does not exist.
        if (std::is_signed<underlying_t>::value) {
            v.emplace_int64() = static_cast<std::underlying_type_t<T>>(t);
        }
        else {
            v.emplace_uint64() = static_cast<std::underlying_type_t<T>>(t);
        }

        boost::mp11::mp_for_each<Descr>([&](auto descr) {
            if (t == descr.value) {
                v.emplace_string() = descr.name;
            }
        });

        return v;
    }

    static T dejsonize(boost::json::value const& v)
    {
        using Descr = boost::describe::describe_enumerators<T>;
        T t = reflect::signal_traits<T>::init();

        if (v.is_number()) {
            using underlying_t = std::underlying_type_t<T>;
            if (std::is_signed<underlying_t>::value) {

                std::int64_t value = v.as_int64();
                std::int64_t test_value = static_cast<std::int64_t>(static_cast<underlying_t>(value));
                if (value != test_value) {
                    throw serialization_error("narrowing enum conversion, can't represent " + std::to_string(value));
                }
                t = static_cast<T>(value);
            }
            else {

                std::uint64_t value = v.as_uint64();
                if (value > std::numeric_limits<underlying_t>::max()) {
                    throw serialization_error("narrowing enum conversion, can't represent " + std::to_string(value));
                }
                t = static_cast<T>(value);
            }
        }
        else if (v.is_string())
        {
            auto const &value = boost::json::string_view {v.as_string()};
            bool found = false;

            boost::mp11::mp_for_each<Descr>([&](auto descr) {
                if (not found and value == boost::json::string_view(descr.name)) {
                    t = T(descr.value);
                    found = true;
                }
            });

            if (not found) {
                throw serialization_error("invalid enum value: %s", value);
            }

        }

        return t;
    }
};


// DESCRIBED STRUCT SERIALIZER


template <class T>
struct default_serialization<T, first_if_t<void,
    boost::describe::has_describe_members<T>
>>
{

    static boost::json::value json_from(T const& t)
    {

        static_assert(not std::is_union<T>::value, "union types are not supported by default, provide a specialization");

        using Descr = boost::describe::describe_members<T, boost::describe::mod_public | boost::describe::mod_inherited | boost::describe::mod_protected | boost::describe::mod_private>;

        boost::json::value v;

        auto& obj = v.emplace_object();

        boost::mp11::mp_for_each<Descr>([&](auto descr) {
            obj[descr.name] = zmbt::json_from(t.*descr.pointer);
        });

        return v;
    }

    static T dejsonize(boost::json::value const& v)
    {

        static_assert(not std::is_union<T>::value, "union types are not supported by default, provide a specialization");

        using Descr = boost::describe::describe_members<T, boost::describe::mod_public | boost::describe::mod_inherited | boost::describe::mod_protected | boost::describe::mod_private>;

        auto const& obj = v.as_object();

        T t = reflect::signal_traits<T>::init();

        boost::mp11::mp_for_each<Descr>([&](auto descr) {

            using TT = std::remove_reference_t<decltype(t.*descr.pointer)>;
            t.*descr.pointer = zmbt::dejsonize<TT>(obj.at(descr.name));
        });

        return t;
    }
};


template <class T>
struct default_serialization<T, first_if_t<void,
    is_json_convertible<T>,
    mp_not<boost::describe::has_describe_members<T>>
>>
{

    static boost::json::value json_from(T const& t)
    {
        return t.operator boost::json::value();
    }

    static T dejsonize(boost::json::value const& v)
    {
        return T(v);
    }
};

} // namespace detail
} // namespace reflect
} // namespace zmbt


#endif // ZMBT_CORE_SERIALIZATION_DEFAULTS_HPP_