diff --git a/include/fc/io/raw.hpp b/include/fc/io/raw.hpp index 01db1bc..f6a2d81 100644 --- a/include/fc/io/raw.hpp +++ b/include/fc/io/raw.hpp @@ -533,6 +533,30 @@ namespace fc { } }; + template using void_t = void; + + template + struct has_auxiliary_packing : std::false_type {}; + template + struct has_auxiliary_packing().fc_pack_auxiliary(std::declval&>(), 1)), + decltype(std::declval().fc_unpack_auxiliary(std::declval&>(), 1))>> + : std::true_type {}; + + template::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::value, bool> = true> + static inline void pack_auxiliary( Stream&, const T&, uint32_t ) { /* Do nothing */ } + + template::value, bool> = true> + static inline void unpack_auxiliary( Stream& s, T& v, uint32_t _max_depth ) { + v.fc_unpack_auxiliary(s, _max_depth ); + } + template::value, bool> = true> + static inline void unpack_auxiliary( Stream&, T&, uint32_t ) { /* Do nothing */ } + template struct if_reflected { template @@ -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::pack( s, v, _max_depth - 1 ); + pack_auxiliary( s, v, _max_depth - 1 ); } template static inline void unpack( Stream& s, T& v, uint32_t _max_depth ) { FC_ASSERT( _max_depth > 0 ); if_enum::unpack( s, v, _max_depth - 1 ); + unpack_auxiliary( s, v, _max_depth - 1 ); } }; - template using void_t = void; - template struct has_custom_packing : std::false_type {}; template @@ -574,6 +598,8 @@ namespace fc { template::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::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::value, bool> = true> diff --git a/include/fc/io/raw_fwd.hpp b/include/fc/io/raw_fwd.hpp index ce85438..1ceb548 100644 --- a/include/fc/io/raw_fwd.hpp +++ b/include/fc/io/raw_fwd.hpp @@ -160,6 +160,7 @@ namespace fc { template inline std::vector pack( const T& v, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); template inline T unpack( const std::vector& s, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); + template inline void unpack( const std::vector& s, T& tmp, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); template inline T unpack( const char* d, uint32_t s, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); template inline void unpack( const char* d, uint32_t s, T& v, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); } } diff --git a/include/fc/reflect/variant.hpp b/include/fc/reflect/variant.hpp index 0656c38..9f81b4f 100644 --- a/include/fc/reflect/variant.hpp +++ b/include/fc/reflect/variant.hpp @@ -100,6 +100,32 @@ namespace fc namespace detail { template using void_t = void; + template + struct has_auxiliary_variant_data : std::false_type {}; + template + struct has_auxiliary_variant_data().fc_auxiliary_to_variant(std::declval(), 1)), + decltype(std::declval().fc_auxiliary_from_variant(std::declval(), 1))>> + : std::true_type {}; + + template::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::value, bool> = true> + void auxiliary_to_variant( const T&, variant&, uint32_t ) { /* Do nothing */ } + template::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::value, bool> = true> + void auxiliary_from_variant( const variant&, T&, uint32_t ) { /* Do nothing */ } + template struct has_custom_variant_conversion : std::false_type {}; template @@ -108,23 +134,28 @@ namespace fc decltype(std::declval().fc_from_variant(std::declval(), 1))>> : std::true_type {}; - template::value, bool> = true> + template::value, bool> = true> void to_variant( const T& o, variant& v, uint32_t max_depth ) { if_enum::to_variant( o, v, max_depth ); + auxiliary_to_variant( o, v, max_depth - 1 ); } - template::value, bool> = true> + template::value, bool> = true> void to_variant( const T& o, variant& v, uint32_t max_depth ) { + static_assert(!has_auxiliary_variant_data::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::value, bool> = true> + template::value, bool> = true> void from_variant( const variant& v, T& o, uint32_t max_depth ) { if_enum::from_variant( v, o, max_depth ); + auxiliary_from_variant( v, o, max_depth - 1 ); } - template::value, bool> = true> + template::value, bool> = true> void from_variant( const variant& v, T& o, uint32_t max_depth ) { o.fc_from_variant(v, max_depth); diff --git a/tests/serialization_test.cpp b/tests/serialization_test.cpp index 320985b..d76ad97 100644 --- a/tests/serialization_test.cpp +++ b/tests/serialization_test.cpp @@ -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 + void fc_pack_auxiliary(Stream& s, uint32_t) const { + fc::raw::pack(s, 7); + custom_serialization_used = true; + } + template + 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&)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()