Skip to content

File serialization.hpp

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

Go to the documentation of this file

#ifndef ZMBT_CORE_SERIALIZATION_HPP_
#define ZMBT_CORE_SERIALIZATION_HPP_

#include <type_traits>

#include <boost/json.hpp>

#include "zmbt/core/preprocessor.hpp" // IWYU pragma: export
#include "zmbt/core.hpp"
#include "ducktyping_traits_pp.hpp"
#include "signal_traits.hpp"


#define ZMBT_INJECT_JSON_TAG_INVOKE using zmbt::reflect::tag_invoke;
#define ZMBT_INJECT_OSTREAM_OPERATOR using zmbt::reflect::operator<<;
#define ZMBT_INJECT_SERIALIZATION ZMBT_INJECT_JSON_TAG_INVOKE ZMBT_INJECT_OSTREAM_OPERATOR


#define ZMBT_INJECT_JSON_TAG_INVOKE_INTO(...) ZMBT_PP_INJECT_CODE_INTO_NS(ZMBT_INJECT_JSON_TAG_INVOKE, __VA_ARGS__)
#define ZMBT_INJECT_OSTREAM_OPERATOR_INTO(...) ZMBT_PP_INJECT_CODE_INTO_NS(ZMBT_INJECT_OSTREAM_OPERATOR, __VA_ARGS__)
#define ZMBT_INJECT_SERIALIZATION_INTO(...) ZMBT_PP_INJECT_CODE_INTO_NS(ZMBT_INJECT_SERIALIZATION, __VA_ARGS__)



namespace zmbt {
namespace reflect {

template <class T, class = void>
struct serialization;

template <class T, class = void>
struct custom_serialization;


namespace detail {


template <class T, class E = void>
struct default_serialization;

template<class T>
using default_serialization_t = decltype(default_serialization<T>::json_from(std::declval<T>()));

template<class T>
using has_default_serialization = mp_valid<default_serialization_t, T>;

template<class T>
using custom_serialization_t = decltype(custom_serialization<T>::json_from(std::declval<T>()));

template<class T>
using has_custom_serialization = mp_valid<custom_serialization_t, T>;


ZMBT_TRAIT_HAS_MEMBER(has_json_operator, operator boost::json::value)

template<class T>
using is_json_convertible = mp_and<
    is_constructible<T, boost::json::value>,
    has_json_operator<T>
>;


template <class T, class R = void>
using enable_for_default_serialization = first_if_t<R,
    mp_not<has_custom_serialization<T>>,
    has_default_serialization<T>
>;

template <class T, class R = void>
using enable_for_custom_serialization = first_if_t<R, has_custom_serialization<T>>;


template <class T, class R = void>
using enable_hermetic_serialization = first_if_any_t<R,
    has_custom_serialization<T>,
    has_default_serialization<T>
>;

template <class T, class R = void>
using disable_hermetic_serialization = first_if_none_t<R,
    has_custom_serialization<T>,
    has_default_serialization<T>
>;

} // namespace detail

template <class T>
struct serialization<T, detail::enable_for_default_serialization<T>> : detail::default_serialization<T> {};

template <class T>
struct serialization<T, detail::enable_for_custom_serialization<T>> : custom_serialization<T> {};

template <class T>
detail::enable_hermetic_serialization<T, void>
tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, T const& t)
{
    v = serialization<T>::json_from(t);
}

template <class T>
detail::enable_hermetic_serialization<T, T>
tag_invoke(boost::json::value_to_tag<T> const&, boost::json::value const& v)
{
    return serialization<T>::dejsonize(v);
}

template <class T>
detail::enable_hermetic_serialization<T, std::ostream&>
operator<< (std::ostream& os, T const& value)
{
    os << serialization<T>::json_from(value);
    return os;
}

} // namespace reflect

ZMBT_INJECT_SERIALIZATION


template<class T, class E = void>
struct has_serialization : std::false_type { };

template<class T>
struct has_serialization<T, void_t<typename reflect::serialization<T>::type>> : std::true_type { };


template <class T, class TT = remove_cvref_t<T>>
reflect::detail::disable_hermetic_serialization<TT, boost::json::value>
json_from(T&& t)
{
    return boost::json::value_from(std::forward<T>(t));
}

template <class T>
reflect::detail::disable_hermetic_serialization<T, T>
dejsonize(boost::json::value const& v)
{
    return boost::json::value_to<T>(v);
}

template <class T, class TT = remove_cvref_t<T>>
reflect::detail::enable_hermetic_serialization<TT, boost::json::value>
json_from(T&& t)
{
    return reflect::serialization<TT>::json_from(std::forward<T>(t));
}

template <class T>
reflect::detail::enable_hermetic_serialization<T, T>
dejsonize(boost::json::value const& v)
{
    return reflect::serialization<T>::dejsonize(v);
}

// Trivial conversions not handled by Boost JSON

inline boost::json::value json_from(std::tuple<>)
{
    return boost::json::array{};
}
template <>
inline std::tuple<> dejsonize<std::tuple<>>(boost::json::value const&)
{
    // TODO: check value
    return {};
}


template <> inline void dejsonize<void>(boost::json::value const&)
{
    // TODO: check value
    return;
}




template <class T, std::size_t N>
boost::json::value json_from_array(T const (&array)[N])
{

    boost::json::array out {};
    out.reserve(N);
    for (size_t i = 0; i < N; i++)
    {
        out.push_back(zmbt::json_from(array[i]));
    }
    return out;
}

template <class T, std::size_t N>
void dejsonize_array(boost::json::array const& jarr, T (&array)[N])
{

    if (jarr.size() != N)
    {
        throw serialization_error("JSON array size mismatch in deserialization of `%s[%d]`",
            zmbt::type_name<T>() , N);
    }
    auto jarr_it = jarr.cbegin();
    for (size_t i = 0; i < N; i++)
    {
        array[i] = zmbt::dejsonize<T>(*jarr_it++);
    }
}

} // namespace zmbt


#endif // ZMBT_CORE_SERIALIZATION_HPP_

#include "serialization_defaults.hpp"  // IWYU pragma: keep