Add possibility to serialize of boost::variant with non default constructible types.

This commit is contained in:
Vol-Alex 2018-07-03 14:48:21 +03:00 committed by Shane Grant
parent cfbb7b0f25
commit 36ab0c1f89
5 changed files with 102 additions and 39 deletions

View File

@ -111,6 +111,7 @@ namespace cereal
// forward decl for construct
//! @cond PRIVATE_NEVERDEFINED
namespace memory_detail{ template <class Ar, class T> struct LoadAndConstructLoadWrapper; }
namespace boost_variant_detail{ template <class Ar, class T> struct LoadAndConstructLoadWrapper; }
//! @endcond
//! Used to construct types with no default constructor
@ -203,7 +204,8 @@ namespace cereal
}
private:
template <class A, class B> friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper;
template <class Ar, class TT> friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper;
template <class Ar, class TT> friend struct ::cereal::boost_variant_detail::LoadAndConstructLoadWrapper;
construct( T * p ) : itsPtr( p ), itsEnableSharedRestoreFunction( [](){} ), itsValid( false ) {}
construct( T * p, std::function<void()> enableSharedFunc ) : // g++4.7 ice with default lambda to std func

View File

@ -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_

View File

@ -31,8 +31,8 @@
#define CEREAL_TYPES_BOOST_VARIANT_HPP_
#include "cereal/cereal.hpp"
#include <boost/variant.hpp>
#include <boost/mpl/size.hpp>
#include <boost/variant/variant_fwd.hpp>
#include <boost/variant/static_visitor.hpp>
namespace cereal
{
@ -53,34 +53,58 @@ namespace cereal
Archive & ar;
};
//! @internal
template<int N, class Variant, class ... Args, class Archive>
typename std::enable_if<N == boost::mpl::size<typename Variant::types>::value, void>::type
load_variant(Archive & /*ar*/, int /*target*/, Variant & /*variant*/)
{
throw ::cereal::Exception("Error traversing variant during load");
}
//! @internal
template<int N, class Variant, class H, class ... T, class Archive>
typename std::enable_if<N < boost::mpl::size<typename Variant::types>::value, void>::type
load_variant(Archive & ar, int target, Variant & variant)
template<class T, class Variant, class Archive>
typename std::enable_if<std::is_default_constructible<T>::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<N+1, Variant, T...>(ar, target, variant);
variant = std::move(value);
}
template <class Archive, class T>
struct LoadAndConstructLoadWrapper
{
using ST = typename std::aligned_storage<sizeof(T), CEREAL_ALIGNOF(T)>::type;
LoadAndConstructLoadWrapper() :
construct( reinterpret_cast<T *>( &st ) )
{ }
~LoadAndConstructLoadWrapper()
{
if (construct.itsValid)
{
construct->~T();
}
}
void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
{
::cereal::detail::Construct<T, Archive>::load_andor_construct( ar, construct );
}
ST st;
::cereal::construct<T> construct;
};
template<class T, class Variant, class Archive>
typename std::enable_if<!std::is_default_constructible<T>::value>::type
load_variant(Archive & ar, Variant & variant)
{
LoadAndConstructLoadWrapper<Archive, T> loadWrapper;
ar( CEREAL_NVP_("data", loadWrapper) );
variant = std::move(*loadWrapper.construct.ptr());
}
} // namespace boost_variant_detail
//! Saving for boost::variant
template <class Archive, typename VariantType1, typename... VariantTypes> inline
void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, boost::variant<VariantType1, VariantTypes...> const & variant )
template <class Archive, typename... VariantTypes> inline
void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, boost::variant<VariantTypes...> const & variant )
{
int32_t which = variant.which();
ar( CEREAL_NVP_("which", which) );
@ -89,17 +113,19 @@ namespace cereal
}
//! Loading for boost::variant
template <class Archive, typename VariantType1, typename... VariantTypes> inline
void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, boost::variant<VariantType1, VariantTypes...> & variant )
template <class Archive, typename... VariantTypes> inline
void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, boost::variant<VariantTypes...> & variant )
{
typedef typename boost::variant<VariantType1, VariantTypes...>::types types;
int32_t which;
ar( CEREAL_NVP_("which", which) );
if(which >= boost::mpl::size<types>::value)
using LoadFuncType = void(*)(Archive &, boost::variant<VariantTypes...> &);
constexpr LoadFuncType loadFuncArray[] = {&boost_variant_detail::load_variant<VariantTypes>...};
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...>, VariantType1, VariantTypes...>(ar, which, variant);
loadFuncArray[which](ar, variant);
}
} // namespace cereal

View File

@ -34,13 +34,6 @@
#include <memory>
#include <cstring>
// 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_

View File

@ -29,6 +29,34 @@
#include "../common.hpp"
#include <cereal/types/boost_variant.hpp>
#include <boost/variant.hpp>
struct NonDefaultConstructible
{
NonDefaultConstructible(int i) : index(i)
{}
template <class Archive>
void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
{
ar( index );
}
int index;
};
namespace cereal
{
template <> struct LoadAndConstruct<NonDefaultConstructible>
{
template <class Archive>
static void load_and_construct( Archive & ar, cereal::construct<NonDefaultConstructible> & construct )
{
int i;
ar( i );
construct( i );
}
};
} // end namespace cereal
template <class IArchive, class OArchive> inline
void test_boost_variant()
@ -36,9 +64,12 @@ void test_boost_variant()
std::random_device rd;
std::mt19937 gen(rd());
boost::variant<int, double, std::string> o_bv1 = random_value<int>(gen);
boost::variant<int, double, std::string> o_bv2 = random_value<double>(gen);
boost::variant<int, double, std::string> o_bv3 = random_basic_string<char>(gen);
using VariantType = boost::variant<int, double, std::string, NonDefaultConstructible>;
VariantType o_bv1 = random_value<int>(gen);
VariantType o_bv2 = random_value<double>(gen);
VariantType o_bv3 = random_basic_string<char>(gen);
VariantType o_bv4 = NonDefaultConstructible(random_value<int>(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<int>(i_bv1), boost::get<int>(o_bv1) );
CHECK_EQ( boost::get<double>(i_bv2), doctest::Approx(boost::get<double>(o_bv2)).epsilon(1e-5) );
CHECK_EQ( boost::get<std::string>(i_bv3), boost::get<std::string>(o_bv3) );
CHECK_EQ( boost::get<NonDefaultConstructible>(i_bv4).index, boost::get<NonDefaultConstructible>(o_bv4).index );
}
#endif // CEREAL_TEST_BOOST_VARIANT_H_