To supplement the new custom serialization feature, we also would like the ability to use standard reflection-based serialization, but also to add some extra data in on the side. This is now possible as auxiliary serialized data. A class can define auxiliary data serializers which will add additional data to the serialized record generated by the usual reflection-based serializers.
178 lines
6.4 KiB
C++
Executable file
178 lines
6.4 KiB
C++
Executable file
#pragma once
|
|
#include <fc/reflect/reflect.hpp>
|
|
#include <fc/variant_object.hpp>
|
|
|
|
namespace fc
|
|
{
|
|
template<typename T>
|
|
void to_variant( const T& o, variant& v, uint32_t max_depth );
|
|
template<typename T>
|
|
void from_variant( const variant& v, T& o, uint32_t max_depth );
|
|
|
|
|
|
template<typename T>
|
|
class to_variant_visitor
|
|
{
|
|
public:
|
|
to_variant_visitor( mutable_variant_object& mvo, const T& v, uint32_t max_depth )
|
|
:vo(mvo),val(v),_max_depth(max_depth - 1) {
|
|
_FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" );
|
|
}
|
|
|
|
template<typename Member, class Class, Member (Class::*member)>
|
|
void operator()( const char* name )const
|
|
{
|
|
this->add(vo,name,(val.*member));
|
|
}
|
|
|
|
private:
|
|
template<typename M>
|
|
void add( mutable_variant_object& vo, const char* name, const optional<M>& v )const
|
|
{
|
|
if( v.valid() )
|
|
vo(name, variant( *v, _max_depth ));
|
|
}
|
|
template<typename M>
|
|
void add( mutable_variant_object& vo, const char* name, const M& v )const
|
|
{ vo(name, variant( v, _max_depth )); }
|
|
|
|
mutable_variant_object& vo;
|
|
const T& val;
|
|
const uint32_t _max_depth;
|
|
};
|
|
|
|
template<typename T>
|
|
class from_variant_visitor
|
|
{
|
|
public:
|
|
from_variant_visitor( const variant_object& _vo, T& v, uint32_t max_depth )
|
|
:vo(_vo),val(v),_max_depth(max_depth - 1) {
|
|
_FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" );
|
|
}
|
|
|
|
template<typename Member, class Class, Member (Class::*member)>
|
|
void operator()( const char* name )const
|
|
{
|
|
auto itr = vo.find(name);
|
|
if( itr != vo.end() )
|
|
from_variant( itr->value(), val.*member, _max_depth );
|
|
}
|
|
|
|
const variant_object& vo;
|
|
T& val;
|
|
const uint32_t _max_depth;
|
|
};
|
|
|
|
template<typename IsEnum=fc::false_type>
|
|
struct if_enum
|
|
{
|
|
template<typename T>
|
|
static inline void to_variant( const T& v, fc::variant& vo, uint32_t max_depth )
|
|
{
|
|
mutable_variant_object mvo;
|
|
fc::reflector<T>::visit( to_variant_visitor<T>( mvo, v, max_depth ) );
|
|
vo = fc::move(mvo);
|
|
}
|
|
template<typename T>
|
|
static inline void from_variant( const fc::variant& v, T& o, uint32_t max_depth )
|
|
{
|
|
const variant_object& vo = v.get_object();
|
|
fc::reflector<T>::visit( from_variant_visitor<T>( vo, o, max_depth ) );
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct if_enum<fc::true_type>
|
|
{
|
|
template<typename T>
|
|
static inline void to_variant( const T& o, fc::variant& v, uint32_t max_depth = 1 )
|
|
{
|
|
v = fc::reflector<T>::to_fc_string(o);
|
|
}
|
|
template<typename T>
|
|
static inline void from_variant( const fc::variant& v, T& o, uint32_t max_depth = 1 )
|
|
{
|
|
if( v.is_string() )
|
|
o = fc::reflector<T>::from_string( v.get_string().c_str() );
|
|
else
|
|
o = fc::reflector<T>::from_int( v.as_int64() );
|
|
}
|
|
};
|
|
|
|
namespace detail {
|
|
template<typename...> using void_t = void;
|
|
|
|
template<typename C, typename=void>
|
|
struct has_auxiliary_variant_data : std::false_type {};
|
|
template<typename C>
|
|
struct has_auxiliary_variant_data<C,
|
|
void_t<decltype(std::declval<const C>().fc_auxiliary_to_variant(std::declval<variant&>(), 1)),
|
|
decltype(std::declval<C>().fc_auxiliary_from_variant(std::declval<const variant&>(), 1))>>
|
|
: std::true_type {};
|
|
|
|
template<typename T, std::enable_if_t<has_auxiliary_variant_data<T>::value, bool> = true>
|
|
void auxiliary_to_variant( const T& o, variant& v, uint32_t max_depth ) {
|
|
variant auxiliary;
|
|
o.fc_auxiliary_to_variant(auxiliary, max_depth);
|
|
v = mutable_variant_object(std::move(v)).set("Auxiliary Data", std::move(auxiliary));
|
|
}
|
|
template<typename T, std::enable_if_t<!has_auxiliary_variant_data<T>::value, bool> = true>
|
|
void auxiliary_to_variant( const T&, variant&, uint32_t ) { /* Do nothing */ }
|
|
template<typename T, std::enable_if_t<has_auxiliary_variant_data<T>::value, bool> = true>
|
|
void auxiliary_from_variant( const variant& v, T& o, uint32_t max_depth ) {
|
|
auto vo = v.get_object();
|
|
auto itr = vo.find("Auxiliary Data");
|
|
if (itr != vo.end())
|
|
o.fc_auxiliary_from_variant(itr->value(), max_depth);
|
|
}
|
|
template<typename T, std::enable_if_t<!has_auxiliary_variant_data<T>::value, bool> = true>
|
|
void auxiliary_from_variant( const variant&, T&, uint32_t ) { /* Do nothing */ }
|
|
|
|
template<typename C, typename=void>
|
|
struct has_custom_variant_conversion : std::false_type {};
|
|
template<typename C>
|
|
struct has_custom_variant_conversion<C,
|
|
void_t<decltype(std::declval<const C>().fc_to_variant(std::declval<variant&>(), 1)),
|
|
decltype(std::declval<C>().fc_from_variant(std::declval<const variant&>(), 1))>>
|
|
: std::true_type {};
|
|
|
|
template<typename T, std::enable_if_t<!has_custom_variant_conversion<T>::value, bool> = true>
|
|
void to_variant( const T& o, variant& v, uint32_t max_depth )
|
|
{
|
|
if_enum<T>::to_variant( o, v, max_depth );
|
|
auxiliary_to_variant( o, v, max_depth - 1 );
|
|
}
|
|
template<typename T, std::enable_if_t<has_custom_variant_conversion<T>::value, bool> = true>
|
|
void to_variant( const T& o, variant& v, uint32_t max_depth )
|
|
{
|
|
static_assert(!has_auxiliary_variant_data<T>::value,
|
|
"Classes with custom variant serialization must not have auxiliary variant data. "
|
|
"Include auxiliary data in custom serializer.");
|
|
o.fc_to_variant(v, max_depth);
|
|
}
|
|
|
|
template<typename T, std::enable_if_t<!has_custom_variant_conversion<T>::value, bool> = true>
|
|
void from_variant( const variant& v, T& o, uint32_t max_depth )
|
|
{
|
|
if_enum<T>::from_variant( v, o, max_depth );
|
|
auxiliary_from_variant( v, o, max_depth - 1 );
|
|
}
|
|
template<typename T, std::enable_if_t<has_custom_variant_conversion<T>::value, bool> = true>
|
|
void from_variant( const variant& v, T& o, uint32_t max_depth )
|
|
{
|
|
o.fc_from_variant(v, max_depth);
|
|
}
|
|
} // namespace detail
|
|
|
|
template<typename T>
|
|
void to_variant( const T& o, variant& v, uint32_t max_depth )
|
|
{
|
|
detail::to_variant(o, v, max_depth);
|
|
}
|
|
|
|
template<typename T>
|
|
void from_variant( const variant& v, T& o, uint32_t max_depth )
|
|
{
|
|
detail::from_variant(v, o, max_depth);
|
|
}
|
|
}
|