Add support for auxiliary serialization data
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.
This commit is contained in:
parent
ac40f449f8
commit
8b50451373
4 changed files with 129 additions and 6 deletions
|
|
@ -533,6 +533,30 @@ namespace fc {
|
|||
}
|
||||
};
|
||||
|
||||
template<typename...> using void_t = void;
|
||||
|
||||
template<typename T, typename=void>
|
||||
struct has_auxiliary_packing : std::false_type {};
|
||||
template<typename T>
|
||||
struct has_auxiliary_packing<T,
|
||||
void_t<decltype(std::declval<const T>().fc_pack_auxiliary(std::declval<fc::datastream<size_t>&>(), 1)),
|
||||
decltype(std::declval<T>().fc_unpack_auxiliary(std::declval<fc::datastream<size_t>&>(), 1))>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename Stream, typename T, std::enable_if_t<has_auxiliary_packing<T>::value, bool> = true>
|
||||
static inline void pack_auxiliary( Stream& s, const T& v, uint32_t _max_depth ) {
|
||||
v.fc_pack_auxiliary(s, _max_depth );
|
||||
}
|
||||
template<typename Stream, typename T, std::enable_if_t<!has_auxiliary_packing<T>::value, bool> = true>
|
||||
static inline void pack_auxiliary( Stream&, const T&, uint32_t ) { /* Do nothing */ }
|
||||
|
||||
template<typename Stream, typename T, std::enable_if_t<has_auxiliary_packing<T>::value, bool> = true>
|
||||
static inline void unpack_auxiliary( Stream& s, T& v, uint32_t _max_depth ) {
|
||||
v.fc_unpack_auxiliary(s, _max_depth );
|
||||
}
|
||||
template<typename Stream, typename T, std::enable_if_t<!has_auxiliary_packing<T>::value, bool> = true>
|
||||
static inline void unpack_auxiliary( Stream&, T&, uint32_t ) { /* Do nothing */ }
|
||||
|
||||
template<typename IsReflected=std::false_type>
|
||||
struct if_reflected {
|
||||
template<typename Stream, typename T>
|
||||
|
|
@ -552,17 +576,17 @@ namespace fc {
|
|||
static inline void pack( Stream& s, const T& v, uint32_t _max_depth ) {
|
||||
FC_ASSERT( _max_depth > 0 );
|
||||
if_enum<T>::pack( s, v, _max_depth - 1 );
|
||||
pack_auxiliary( s, v, _max_depth - 1 );
|
||||
}
|
||||
|
||||
template<typename Stream, typename T>
|
||||
static inline void unpack( Stream& s, T& v, uint32_t _max_depth ) {
|
||||
FC_ASSERT( _max_depth > 0 );
|
||||
if_enum<T>::unpack( s, v, _max_depth - 1 );
|
||||
unpack_auxiliary( s, v, _max_depth - 1 );
|
||||
}
|
||||
};
|
||||
|
||||
template<typename...> using void_t = void;
|
||||
|
||||
template<typename T, typename=void>
|
||||
struct has_custom_packing : std::false_type {};
|
||||
template<typename T>
|
||||
|
|
@ -574,6 +598,8 @@ namespace fc {
|
|||
template<typename Stream, typename T, std::enable_if_t<has_custom_packing<T>::value, bool> = true>
|
||||
static inline void pack( Stream& s, const T& v, uint32_t _max_depth ) {
|
||||
FC_ASSERT( _max_depth > 0 );
|
||||
static_assert(!has_auxiliary_packing<T>::value,
|
||||
"A class with custom packing must not have auxiliary packing. Pack auxiliary data in custom packer.");
|
||||
v.fc_pack( s, _max_depth - 1 );
|
||||
}
|
||||
template<typename Stream, typename T, std::enable_if_t<!has_custom_packing<T>::value, bool> = true>
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ namespace fc {
|
|||
|
||||
template<typename T> inline std::vector<char> pack( const T& v, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
|
||||
template<typename T> inline T unpack( const std::vector<char>& s, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
|
||||
template<typename T> inline void unpack( const std::vector<char>& s, T& tmp, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
|
||||
template<typename T> inline T unpack( const char* d, uint32_t s, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
|
||||
template<typename T> inline void unpack( const char* d, uint32_t s, T& v, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -100,6 +100,32 @@ namespace fc
|
|||
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>
|
||||
|
|
@ -108,23 +134,28 @@ namespace fc
|
|||
decltype(std::declval<C>().fc_from_variant(std::declval<const variant&>(), 1))>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename T, std::enable_if_t<!detail::has_custom_variant_conversion<T>::value, bool> = true>
|
||||
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<detail::has_custom_variant_conversion<T>::value, bool> = true>
|
||||
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<!detail::has_custom_variant_conversion<T>::value, bool> = true>
|
||||
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<detail::has_custom_variant_conversion<T>::value, bool> = true>
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,38 @@ struct custom_serialized_type {
|
|||
}
|
||||
};
|
||||
|
||||
// Default-serialized type with auxiliary data to serialize
|
||||
struct auxiliary_serialized_type {
|
||||
int a = 1;
|
||||
std::string b = "Hi!";
|
||||
bool c = true;
|
||||
|
||||
// Define auxiliary data
|
||||
template<typename Stream>
|
||||
void fc_pack_auxiliary(Stream& s, uint32_t) const {
|
||||
fc::raw::pack(s, 7);
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
template<typename Stream>
|
||||
void fc_unpack_auxiliary(Stream& s, uint32_t) {
|
||||
int data;
|
||||
fc::raw::unpack(s, data);
|
||||
BOOST_CHECK_EQUAL(data, 7);
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
|
||||
void fc_auxiliary_to_variant(fc::variant& v, uint32_t) const {
|
||||
v = 7;
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
void fc_auxiliary_from_variant(const fc::variant& v, uint32_t) {
|
||||
BOOST_CHECK_EQUAL(v.as_int64(), 7);
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
};
|
||||
|
||||
FC_REFLECT(custom_serialized_type, (a)(b)(c))
|
||||
FC_REFLECT(auxiliary_serialized_type, (a)(b)(c))
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(fc_serialization)
|
||||
|
||||
|
|
@ -154,6 +185,7 @@ BOOST_AUTO_TEST_CASE( unpack_recursion_test )
|
|||
|
||||
BOOST_AUTO_TEST_CASE( custom_serialization )
|
||||
{ try {
|
||||
custom_serialization_used = false;
|
||||
// Create our custom serialized type and serialize it; check that only our fake serializer ran
|
||||
custom_serialized_type obj;
|
||||
auto packed = fc::raw::pack(obj);
|
||||
|
|
@ -194,4 +226,37 @@ BOOST_AUTO_TEST_CASE( custom_serialization )
|
|||
BOOST_CHECK(obj.c);
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( auxiliary_serialization )
|
||||
{ try {
|
||||
custom_serialization_used = false;
|
||||
// Just serialize and deserialize, text and binary. The class does the checks itself -- we just make sure they ran.
|
||||
auxiliary_serialized_type obj;
|
||||
auto packed = fc::raw::pack(obj);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
custom_serialization_used = false;
|
||||
obj.a = 0;
|
||||
obj.b = "Something else";
|
||||
obj.c = false;
|
||||
// Why I need this cast to get it to build, I may never know... Nor will I get that hour of my life back.
|
||||
fc::raw::unpack((const std::vector<char>&)packed, obj);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
BOOST_CHECK_EQUAL(obj.a, 1);
|
||||
BOOST_CHECK_EQUAL(obj.b, "Hi!");
|
||||
BOOST_CHECK_EQUAL(obj.c, true);
|
||||
custom_serialization_used = false;
|
||||
|
||||
fc::variant v;
|
||||
fc::to_variant(obj, v, 10);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
custom_serialization_used = false;
|
||||
obj.a = 0;
|
||||
obj.b = "Something else";
|
||||
obj.c = false;
|
||||
fc::from_variant(v, obj, 10);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
BOOST_CHECK_EQUAL(obj.a, 1);
|
||||
BOOST_CHECK_EQUAL(obj.b, "Hi!");
|
||||
BOOST_CHECK_EQUAL(obj.c, true);
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
Loading…
Reference in a new issue