Enable classes to customize their serialization
Add support for classes to define members to customize their serialization from the default from reflection.
This commit is contained in:
parent
6171e973c7
commit
9a1ea8a0bf
3 changed files with 153 additions and 5 deletions
|
|
@ -417,13 +417,35 @@ namespace fc {
|
|||
}
|
||||
};
|
||||
template<>
|
||||
struct if_reflected<fc::true_type> {
|
||||
template<typename Stream, typename T>
|
||||
struct if_reflected<std::true_type> {
|
||||
private:
|
||||
template<typename...> using void_t = void;
|
||||
|
||||
template<typename T, typename=void>
|
||||
struct has_custom_packing : std::false_type {};
|
||||
template<typename T>
|
||||
struct has_custom_packing<T,
|
||||
void_t<decltype(std::declval<const T>().fc_pack(std::declval<fc::datastream<size_t>&>(), 1)),
|
||||
decltype(std::declval<T>().fc_unpack(std::declval<fc::datastream<size_t>&>(), 1))>>
|
||||
: std::true_type {};
|
||||
public:
|
||||
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 );
|
||||
v.fc_pack( s, _max_depth - 1 );
|
||||
}
|
||||
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 );
|
||||
if_enum< typename fc::reflector<T>::is_enum >::pack( s, v, _max_depth - 1 );
|
||||
}
|
||||
template<typename Stream, typename T>
|
||||
|
||||
template<typename Stream, typename T, std::enable_if_t<has_custom_packing<T>::value, bool> = true>
|
||||
static inline void unpack( Stream& s, T& v, uint32_t _max_depth ) {
|
||||
FC_ASSERT( _max_depth > 0 );
|
||||
v.fc_unpack( s, _max_depth - 1 );
|
||||
}
|
||||
template<typename Stream, typename T, std::enable_if_t<!has_custom_packing<T>::value, bool> = true>
|
||||
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 );
|
||||
|
|
|
|||
|
|
@ -99,16 +99,49 @@ namespace fc
|
|||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<typename...> using void_t = void;
|
||||
|
||||
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<!detail::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 );
|
||||
}
|
||||
template<typename T, std::enable_if_t<detail::has_custom_variant_conversion<T>::value, bool> = true>
|
||||
void to_variant( const T& o, variant& v, uint32_t max_depth )
|
||||
{
|
||||
o.fc_to_variant(v, max_depth);
|
||||
}
|
||||
|
||||
template<typename T, std::enable_if_t<!detail::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 );
|
||||
}
|
||||
template<typename T, std::enable_if_t<detail::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 )
|
||||
{
|
||||
if_enum<typename fc::reflector<T>::is_enum>::to_variant( o, v, max_depth );
|
||||
detail::to_variant(o, v, max_depth);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void from_variant( const variant& v, T& o, uint32_t max_depth )
|
||||
{
|
||||
if_enum<typename fc::reflector<T>::is_enum>::from_variant( v, o, max_depth );
|
||||
detail::from_variant(v, o, max_depth);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <fc/container/flat.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <fc/io/json.hpp>
|
||||
#include <fc/reflect/variant.hpp>
|
||||
|
||||
namespace fc { namespace test {
|
||||
|
||||
|
|
@ -44,6 +46,35 @@ namespace fc { namespace test {
|
|||
FC_REFLECT( fc::test::item_wrapper, (v) );
|
||||
FC_REFLECT( fc::test::item, (level)(w) );
|
||||
|
||||
// These are used in the custom_serialization test
|
||||
|
||||
static bool custom_serialization_used = false;
|
||||
// Custom serialized type with a fake serializer that does nothing but set the above bool to true
|
||||
struct custom_serialized_type {
|
||||
int a = 1;
|
||||
std::string b = "Hi!";
|
||||
bool c = true;
|
||||
|
||||
// Override default serialization for reflected types
|
||||
template<typename Stream>
|
||||
void fc_pack(Stream&, uint32_t) const {
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
template<typename Stream>
|
||||
void fc_unpack(Stream&, uint32_t) {
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
|
||||
void fc_to_variant(fc::variant&, uint32_t) const {
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
void fc_from_variant(const fc::variant&, uint32_t) {
|
||||
custom_serialization_used = true;
|
||||
}
|
||||
};
|
||||
|
||||
FC_REFLECT(custom_serialized_type, (a)(b)(c))
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(fc_serialization)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( nested_objects_test )
|
||||
|
|
@ -105,4 +136,66 @@ BOOST_AUTO_TEST_CASE( nested_objects_test )
|
|||
|
||||
} FC_CAPTURE_LOG_AND_RETHROW ( (0) ) }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( unpack_recursion_test )
|
||||
{
|
||||
try
|
||||
{
|
||||
std::stringstream ss;
|
||||
int recursion_level = 100000;
|
||||
uint64_t allocation_per_level = 500000;
|
||||
|
||||
for ( int i = 0; i < recursion_level; i++ )
|
||||
{
|
||||
fc::raw::pack( ss, fc::unsigned_int( allocation_per_level ) );
|
||||
fc::raw::pack( ss, static_cast< uint8_t >( fc::variant::array_type ) );
|
||||
}
|
||||
|
||||
std::vector< fc::variant > v;
|
||||
BOOST_REQUIRE_THROW( fc::raw::unpack( ss, v ), fc::assert_exception );
|
||||
}
|
||||
FC_LOG_AND_RETHROW();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( custom_serialization )
|
||||
{ try {
|
||||
// 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);
|
||||
BOOST_CHECK(packed.empty());
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
|
||||
// Deserializer won't run on an empty vector; put a byte in so deserialization tests can run
|
||||
packed = {'a'};
|
||||
|
||||
// Reset the flag, alter the object, and deserialize it, again checking that only the fake serializer ran
|
||||
custom_serialization_used = false;
|
||||
obj.c = false;
|
||||
fc::raw::unpack(packed, obj);
|
||||
BOOST_CHECK(obj.c == false);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
|
||||
// Reset the flag, deserialize again without existing ref, check if fake serializer ran
|
||||
custom_serialization_used = false;
|
||||
auto obj2 = fc::raw::unpack<custom_serialized_type>(packed);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
|
||||
// All over again, but for variants instead of binary serialization
|
||||
fc::variant v(obj, 10);
|
||||
BOOST_CHECK(v.is_null());
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
custom_serialization_used = false;
|
||||
fc::to_variant(obj, v, 10);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
// JSON should be affected too. Check it is.
|
||||
custom_serialization_used = false;
|
||||
BOOST_CHECK_EQUAL(fc::json::to_string(obj), "null");
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
|
||||
custom_serialization_used = false;
|
||||
obj.c = true;
|
||||
fc::from_variant(v, obj, 10);
|
||||
BOOST_CHECK(custom_serialization_used);
|
||||
BOOST_CHECK(obj.c);
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
Loading…
Reference in a new issue