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:
Nathaniel 2022-03-16 15:47:17 -05:00
parent 23fd3a3a41
commit 907dc57fb6
No known key found for this signature in database
GPG key ID: B4344309A110851E
3 changed files with 132 additions and 4 deletions

View file

@ -556,12 +556,34 @@ namespace fc {
};
template<>
struct if_reflected<std::true_type> {
template<typename Stream, typename T>
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<T>::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<T>::unpack( s, v, _max_depth - 1 );

View file

@ -97,16 +97,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<T>::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<T>::from_variant( v, o, max_depth );
detail::from_variant(v, o, max_depth);
}
}

View file

@ -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 {
@ -40,6 +42,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 )
@ -121,4 +152,46 @@ BOOST_AUTO_TEST_CASE( unpack_recursion_test )
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()