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:
Nathaniel 2022-03-16 20:41:15 -05:00
parent 213fd5168f
commit e5aab9c58b
No known key found for this signature in database
GPG key ID: B4344309A110851E
4 changed files with 132 additions and 9 deletions

View file

@ -395,7 +395,31 @@ namespace fc {
}
};
template<typename IsReflected=fc::false_type>
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>
static inline void pack( Stream& s, const T& v, uint32_t _max_depth ) {
@ -413,18 +437,18 @@ namespace fc {
template<typename Stream, typename T>
static inline void pack( Stream& s, const T& v, uint32_t _max_depth ) {
FC_ASSERT( _max_depth > 0 );
if_enum< typename fc::reflector<T>::is_enum >::pack( s, v, _max_depth - 1 );
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< typename fc::reflector<T>::is_enum >::unpack( s, v, _max_depth - 1 );
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>
@ -436,6 +460,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>

View file

@ -119,6 +119,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 );
} }

View file

@ -102,6 +102,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>
@ -110,23 +136,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);

View file

@ -73,7 +73,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)
@ -158,6 +189,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);
@ -198,4 +230,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()