From 36ab0c1f890262058bdceafa1f31d5f2669bcdd1 Mon Sep 17 00:00:00 2001 From: Vol-Alex Date: Tue, 3 Jul 2018 14:48:21 +0300 Subject: [PATCH] Add possibility to serialize of boost::variant with non default constructible types. --- include/cereal/access.hpp | 4 +- include/cereal/macros.hpp | 8 +++ include/cereal/types/boost_variant.hpp | 80 +++++++++++++++++--------- include/cereal/types/memory.hpp | 8 --- unittests/boost/boost_variant.hpp | 41 ++++++++++++- 5 files changed, 102 insertions(+), 39 deletions(-) diff --git a/include/cereal/access.hpp b/include/cereal/access.hpp index 4bdd1f37..d7917bdf 100644 --- a/include/cereal/access.hpp +++ b/include/cereal/access.hpp @@ -111,6 +111,7 @@ namespace cereal // forward decl for construct //! @cond PRIVATE_NEVERDEFINED namespace memory_detail{ template struct LoadAndConstructLoadWrapper; } + namespace boost_variant_detail{ template struct LoadAndConstructLoadWrapper; } //! @endcond //! Used to construct types with no default constructor @@ -203,7 +204,8 @@ namespace cereal } private: - template friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper; + template friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper; + template friend struct ::cereal::boost_variant_detail::LoadAndConstructLoadWrapper; construct( T * p ) : itsPtr( p ), itsEnableSharedRestoreFunction( [](){} ), itsValid( false ) {} construct( T * p, std::function enableSharedFunc ) : // g++4.7 ice with default lambda to std func diff --git a/include/cereal/macros.hpp b/include/cereal/macros.hpp index 6a098051..96ca7516 100644 --- a/include/cereal/macros.hpp +++ b/include/cereal/macros.hpp @@ -143,4 +143,12 @@ #define CEREAL_HAS_CPP14 #endif +// ###################################################################### +//! Defines the CEREAL_ALIGNOF macro to use instead of alignof +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define CEREAL_ALIGNOF __alignof +#else // not MSVC 2013 or older +#define CEREAL_ALIGNOF alignof +#endif // end MSVC check + #endif // CEREAL_MACROS_HPP_ diff --git a/include/cereal/types/boost_variant.hpp b/include/cereal/types/boost_variant.hpp index da047aa8..25ebb3b1 100644 --- a/include/cereal/types/boost_variant.hpp +++ b/include/cereal/types/boost_variant.hpp @@ -31,8 +31,8 @@ #define CEREAL_TYPES_BOOST_VARIANT_HPP_ #include "cereal/cereal.hpp" -#include -#include +#include +#include namespace cereal { @@ -53,34 +53,58 @@ namespace cereal Archive & ar; }; - //! @internal - template - typename std::enable_if::value, void>::type - load_variant(Archive & /*ar*/, int /*target*/, Variant & /*variant*/) - { - throw ::cereal::Exception("Error traversing variant during load"); - } //! @internal - template - typename std::enable_if::value, void>::type - load_variant(Archive & ar, int target, Variant & variant) + template + typename std::enable_if::value>::type + load_variant(Archive & ar, Variant & variant) { - if(N == target) - { - H value; + T value; ar( CEREAL_NVP_("data", value) ); - variant = value; - } - else - load_variant(ar, target, variant); + variant = std::move(value); + } + + template + struct LoadAndConstructLoadWrapper + { + using ST = typename std::aligned_storage::type; + + LoadAndConstructLoadWrapper() : + construct( reinterpret_cast( &st ) ) + { } + + ~LoadAndConstructLoadWrapper() + { + if (construct.itsValid) + { + construct->~T(); + } + } + + void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar ) + { + ::cereal::detail::Construct::load_andor_construct( ar, construct ); + } + + ST st; + ::cereal::construct construct; + }; + + template + typename std::enable_if::value>::type + load_variant(Archive & ar, Variant & variant) + { + LoadAndConstructLoadWrapper loadWrapper; + + ar( CEREAL_NVP_("data", loadWrapper) ); + variant = std::move(*loadWrapper.construct.ptr()); } } // namespace boost_variant_detail //! Saving for boost::variant - template inline - void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, boost::variant const & variant ) + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, boost::variant const & variant ) { int32_t which = variant.which(); ar( CEREAL_NVP_("which", which) ); @@ -89,17 +113,19 @@ namespace cereal } //! Loading for boost::variant - template inline - void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, boost::variant & variant ) + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, boost::variant & variant ) { - typedef typename boost::variant::types types; - int32_t which; ar( CEREAL_NVP_("which", which) ); - if(which >= boost::mpl::size::value) + + using LoadFuncType = void(*)(Archive &, boost::variant &); + constexpr LoadFuncType loadFuncArray[] = {&boost_variant_detail::load_variant...}; + + if(which >= int32_t(sizeof(loadFuncArray)/sizeof(loadFuncArray[0]))) throw Exception("Invalid 'which' selector when deserializing boost::variant"); - boost_variant_detail::load_variant<0, boost::variant, VariantType1, VariantTypes...>(ar, which, variant); + loadFuncArray[which](ar, variant); } } // namespace cereal diff --git a/include/cereal/types/memory.hpp b/include/cereal/types/memory.hpp index 78112683..0d86ab18 100644 --- a/include/cereal/types/memory.hpp +++ b/include/cereal/types/memory.hpp @@ -34,13 +34,6 @@ #include #include -// Work around MSVC not having alignof -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define CEREAL_ALIGNOF __alignof -#else // not MSVC 2013 or older -#define CEREAL_ALIGNOF alignof -#endif // end MSVC check - namespace cereal { namespace memory_detail @@ -423,5 +416,4 @@ namespace cereal // automatically include polymorphic support #include "cereal/types/polymorphic.hpp" -#undef CEREAL_ALIGNOF #endif // CEREAL_TYPES_SHARED_PTR_HPP_ diff --git a/unittests/boost/boost_variant.hpp b/unittests/boost/boost_variant.hpp index d189685d..67415010 100644 --- a/unittests/boost/boost_variant.hpp +++ b/unittests/boost/boost_variant.hpp @@ -29,6 +29,34 @@ #include "../common.hpp" #include +#include + +struct NonDefaultConstructible +{ + NonDefaultConstructible(int i) : index(i) + {} + + template + void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar ) + { + ar( index ); + } + int index; +}; + +namespace cereal +{ +template <> struct LoadAndConstruct +{ + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int i; + ar( i ); + construct( i ); + } +}; +} // end namespace cereal template inline void test_boost_variant() @@ -36,9 +64,12 @@ void test_boost_variant() std::random_device rd; std::mt19937 gen(rd()); - boost::variant o_bv1 = random_value(gen); - boost::variant o_bv2 = random_value(gen); - boost::variant o_bv3 = random_basic_string(gen); + using VariantType = boost::variant; + + VariantType o_bv1 = random_value(gen); + VariantType o_bv2 = random_value(gen); + VariantType o_bv3 = random_basic_string(gen); + VariantType o_bv4 = NonDefaultConstructible(random_value(gen)); std::ostringstream os; { @@ -47,11 +78,13 @@ void test_boost_variant() oar(o_bv1); oar(o_bv2); oar(o_bv3); + oar(o_bv4); } decltype(o_bv1) i_bv1; decltype(o_bv2) i_bv2; decltype(o_bv3) i_bv3; + decltype(o_bv4) i_bv4; std::istringstream is(os.str()); { @@ -60,11 +93,13 @@ void test_boost_variant() iar(i_bv1); iar(i_bv2); iar(i_bv3); + iar(i_bv4); } CHECK_EQ( boost::get(i_bv1), boost::get(o_bv1) ); CHECK_EQ( boost::get(i_bv2), doctest::Approx(boost::get(o_bv2)).epsilon(1e-5) ); CHECK_EQ( boost::get(i_bv3), boost::get(o_bv3) ); + CHECK_EQ( boost::get(i_bv4).index, boost::get(o_bv4).index ); } #endif // CEREAL_TEST_BOOST_VARIANT_H_