upgrade cereal
This commit is contained in:
@@ -68,8 +68,10 @@ namespace cereal
|
||||
namespace detail
|
||||
{
|
||||
struct NameValuePairCore {}; //!< Traits struct for NVPs
|
||||
struct DeferredDataCore {}; //!< Traits struct for DeferredData
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
//! For holding name value pairs
|
||||
/*! This pairs a name (some string) with some value such that an archive
|
||||
can potentially take advantage of the pairing.
|
||||
@@ -210,7 +212,7 @@ namespace cereal
|
||||
{
|
||||
//! Internally store the pointer as a void *, keeping const if created with
|
||||
//! a const pointer
|
||||
using PT = typename std::conditional<std::is_const<typename std::remove_pointer<T>::type>::value,
|
||||
using PT = typename std::conditional<std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value,
|
||||
const void *,
|
||||
void *>::type;
|
||||
|
||||
@@ -220,6 +222,43 @@ namespace cereal
|
||||
uint64_t size; //!< size in bytes
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
//! A wrapper around data that should be serialized after all non-deferred data
|
||||
/*! This class is used to demarcate data that can only be safely serialized after
|
||||
any data not wrapped in this class.
|
||||
|
||||
@internal */
|
||||
template <class T>
|
||||
class DeferredData : detail::DeferredDataCore
|
||||
{
|
||||
private:
|
||||
// If we get passed an array, keep the type as is, otherwise store
|
||||
// a reference if we were passed an l value reference, else copy the value
|
||||
using Type = typename std::conditional<std::is_array<typename std::remove_reference<T>::type>::value,
|
||||
typename std::remove_cv<T>::type,
|
||||
typename std::conditional<std::is_lvalue_reference<T>::value,
|
||||
T,
|
||||
typename std::decay<T>::type>::type>::type;
|
||||
|
||||
// prevent nested nvps
|
||||
static_assert( !std::is_base_of<detail::DeferredDataCore, T>::value,
|
||||
"Cannot defer DeferredData" );
|
||||
|
||||
DeferredData & operator=( DeferredData const & ) = delete;
|
||||
|
||||
public:
|
||||
//! Constructs a new NameValuePair
|
||||
/*! @param v The value to defer. Ideally this should be an l-value reference so that
|
||||
the value can be both loaded and saved to. If you pass an r-value reference,
|
||||
the DeferredData will store a copy of it instead of a reference. Thus you should
|
||||
only pass r-values in cases where this makes sense, such as the result of some
|
||||
size() call.
|
||||
@internal */
|
||||
DeferredData( T && v ) : value(std::forward<T>(v)) {}
|
||||
|
||||
Type value;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
namespace detail
|
||||
{
|
||||
@@ -256,7 +295,7 @@ namespace cereal
|
||||
struct adl_tag;
|
||||
|
||||
// used during saving pointers
|
||||
static const int32_t msb_32bit = 0x80000000;
|
||||
static const uint32_t msb_32bit = 0x80000000;
|
||||
static const int32_t msb2_32bit = 0x40000000;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,14 @@
|
||||
#include <set>
|
||||
#include <stack>
|
||||
|
||||
//! Helper macro to omit unused warning
|
||||
#if defined(__GNUC__)
|
||||
// GCC / clang don't want the function
|
||||
#define CEREAL_BIND_TO_ARCHIVES_UNUSED_FUNCTION
|
||||
#else
|
||||
#define CEREAL_BIND_TO_ARCHIVES_UNUSED_FUNCTION static void unused() { (void)b; }
|
||||
#endif
|
||||
|
||||
//! Binds a polymorhic type to all registered archives
|
||||
/*! This binds a polymorphic type to all compatible registered archives that
|
||||
have been registered with CEREAL_REGISTER_ARCHIVE. This must be called
|
||||
@@ -67,7 +75,7 @@
|
||||
template<> \
|
||||
struct init_binding<__VA_ARGS__> { \
|
||||
static bind_to_archives<__VA_ARGS__> const & b; \
|
||||
static void unused() { (void)b; } \
|
||||
CEREAL_BIND_TO_ARCHIVES_UNUSED_FUNCTION \
|
||||
}; \
|
||||
bind_to_archives<__VA_ARGS__> const & init_binding<__VA_ARGS__>::b = \
|
||||
::cereal::detail::StaticObject< \
|
||||
@@ -115,8 +123,10 @@ namespace cereal
|
||||
all registered mappings between base and derived types. */
|
||||
struct PolymorphicCasters
|
||||
{
|
||||
//! Maps from a derived type index to a set of chainable casters
|
||||
using DerivedCasterMap = std::unordered_map<std::type_index, std::vector<PolymorphicCaster const *>>;
|
||||
//! Maps from base type index to a map from derived type index to caster
|
||||
std::map<std::type_index, std::map<std::type_index, std::vector<PolymorphicCaster const*>>> map;
|
||||
std::unordered_map<std::type_index, DerivedCasterMap> map;
|
||||
|
||||
std::multimap<std::type_index, std::type_index> reverseMap;
|
||||
|
||||
@@ -127,24 +137,26 @@ namespace cereal
|
||||
"Make sure you either serialize the base class at some point via cereal::base_class or cereal::virtual_base_class.\n" \
|
||||
"Alternatively, manually register the association with CEREAL_REGISTER_POLYMORPHIC_RELATION.");
|
||||
|
||||
//! Checks if the mapping object that can perform the upcast or downcast
|
||||
//! Checks if the mapping object that can perform the upcast or downcast exists, and returns it if so
|
||||
/*! Uses the type index from the base and derived class to find the matching
|
||||
registered caster. If no matching caster exists, returns false. */
|
||||
static bool exists( std::type_index const & baseIndex, std::type_index const & derivedIndex )
|
||||
registered caster. If no matching caster exists, the bool in the pair will be false and the vector
|
||||
reference should not be used. */
|
||||
static std::pair<bool, std::vector<PolymorphicCaster const *> const &>
|
||||
lookup_if_exists( std::type_index const & baseIndex, std::type_index const & derivedIndex )
|
||||
{
|
||||
// First phase of lookup - match base type index
|
||||
auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
|
||||
auto baseIter = baseMap.find( baseIndex );
|
||||
if (baseIter == baseMap.end())
|
||||
return false;
|
||||
return {false, {}};
|
||||
|
||||
// Second phase - find a match from base to derived
|
||||
auto & derivedMap = baseIter->second;
|
||||
auto const & derivedMap = baseIter->second;
|
||||
auto derivedIter = derivedMap.find( derivedIndex );
|
||||
if (derivedIter == derivedMap.end())
|
||||
return false;
|
||||
return {false, {}};
|
||||
|
||||
return true;
|
||||
return {true, derivedIter->second};
|
||||
}
|
||||
|
||||
//! Gets the mapping object that can perform the upcast or downcast
|
||||
@@ -162,7 +174,7 @@ namespace cereal
|
||||
exceptionFunc();
|
||||
|
||||
// Second phase - find a match from base to derived
|
||||
auto & derivedMap = baseIter->second;
|
||||
auto const & derivedMap = baseIter->second;
|
||||
auto derivedIter = derivedMap.find( derivedIndex );
|
||||
if( derivedIter == derivedMap.end() )
|
||||
exceptionFunc();
|
||||
@@ -176,8 +188,8 @@ namespace cereal
|
||||
{
|
||||
auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } );
|
||||
|
||||
for( auto const * map : mapping )
|
||||
dptr = map->downcast( dptr );
|
||||
for( auto const * dmap : mapping )
|
||||
dptr = dmap->downcast( dptr );
|
||||
|
||||
return static_cast<Derived const *>( dptr );
|
||||
}
|
||||
@@ -213,6 +225,14 @@ namespace cereal
|
||||
#undef UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION
|
||||
};
|
||||
|
||||
#ifdef CEREAL_OLDER_GCC
|
||||
#define CEREAL_EMPLACE_MAP(map, key, value) \
|
||||
map.insert( std::make_pair(std::move(key), std::move(value)) );
|
||||
#else // NOT CEREAL_OLDER_GCC
|
||||
#define CEREAL_EMPLACE_MAP(map, key, value) \
|
||||
map.emplace( key, value );
|
||||
#endif // NOT_CEREAL_OLDER_GCC
|
||||
|
||||
//! Strongly typed derivation of PolymorphicCaster
|
||||
template <class Base, class Derived>
|
||||
struct PolymorphicVirtualCaster : PolymorphicCaster
|
||||
@@ -229,18 +249,16 @@ namespace cereal
|
||||
// First insert the relation Base->Derived
|
||||
const auto lock = StaticObject<PolymorphicCasters>::lock();
|
||||
auto & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
|
||||
auto lb = baseMap.lower_bound(baseKey);
|
||||
|
||||
{
|
||||
auto & derivedMap = baseMap.insert( lb, {baseKey, {}} )->second;
|
||||
auto lbd = derivedMap.lower_bound(derivedKey);
|
||||
auto & derivedVec = derivedMap.insert( lbd, { std::move(derivedKey), {}} )->second;
|
||||
auto & derivedMap = baseMap.insert( {baseKey, PolymorphicCasters::DerivedCasterMap{}} ).first->second;
|
||||
auto & derivedVec = derivedMap.insert( {derivedKey, {}} ).first->second;
|
||||
derivedVec.push_back( this );
|
||||
}
|
||||
|
||||
// Insert reverse relation Derived->Base
|
||||
auto & reverseMap = StaticObject<PolymorphicCasters>::getInstance().reverseMap;
|
||||
reverseMap.insert( {derivedKey, baseKey} );
|
||||
CEREAL_EMPLACE_MAP(reverseMap, derivedKey, baseKey);
|
||||
|
||||
// Find all chainable unregistered relations
|
||||
/* The strategy here is to process only the nodes in the class hierarchy graph that have been
|
||||
@@ -254,28 +272,40 @@ namespace cereal
|
||||
// Checks whether there is a path from parent->child and returns a <dist, path> pair
|
||||
// dist is set to MAX if the path does not exist
|
||||
auto checkRelation = [](std::type_index const & parentInfo, std::type_index const & childInfo) ->
|
||||
std::pair<size_t, std::vector<PolymorphicCaster const *>>
|
||||
std::pair<size_t, std::vector<PolymorphicCaster const *> const &>
|
||||
{
|
||||
if( PolymorphicCasters::exists( parentInfo, childInfo ) )
|
||||
auto result = PolymorphicCasters::lookup_if_exists( parentInfo, childInfo );
|
||||
if( result.first )
|
||||
{
|
||||
auto const & path = PolymorphicCasters::lookup( parentInfo, childInfo, [](){} );
|
||||
auto const & path = result.second;
|
||||
return {path.size(), path};
|
||||
}
|
||||
else
|
||||
return {std::numeric_limits<size_t>::max(), {}};
|
||||
return {(std::numeric_limits<size_t>::max)(), {}};
|
||||
};
|
||||
|
||||
std::stack<std::type_index> parentStack; // Holds the parent nodes to be processed
|
||||
std::set<std::type_index> dirtySet; // Marks child nodes that have been changed
|
||||
std::set<std::type_index> processedParents; // Marks parent nodes that have been processed
|
||||
std::stack<std::type_index> parentStack; // Holds the parent nodes to be processed
|
||||
std::vector<std::type_index> dirtySet; // Marks child nodes that have been changed
|
||||
std::unordered_set<std::type_index> processedParents; // Marks parent nodes that have been processed
|
||||
|
||||
// Checks if a child has been marked dirty
|
||||
auto isDirty = [&](std::type_index const & c)
|
||||
{
|
||||
auto const dirtySetSize = dirtySet.size();
|
||||
for( size_t i = 0; i < dirtySetSize; ++i )
|
||||
if( dirtySet[i] == c )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Begin processing the base key and mark derived as dirty
|
||||
parentStack.push( baseKey );
|
||||
dirtySet.insert( derivedKey );
|
||||
dirtySet.emplace_back( derivedKey );
|
||||
|
||||
while( !parentStack.empty() )
|
||||
{
|
||||
using Relations = std::multimap<std::type_index, std::pair<std::type_index, std::vector<PolymorphicCaster const *>>>;
|
||||
using Relations = std::unordered_multimap<std::type_index, std::pair<std::type_index, std::vector<PolymorphicCaster const *>>>;
|
||||
Relations unregisteredRelations; // Defer insertions until after main loop to prevent iterator invalidation
|
||||
|
||||
const auto parent = parentStack.top();
|
||||
@@ -285,7 +315,7 @@ namespace cereal
|
||||
for( auto const & childPair : baseMap[parent] )
|
||||
{
|
||||
const auto child = childPair.first;
|
||||
if( dirtySet.count( child ) && baseMap.count( child ) )
|
||||
if( isDirty( child ) && baseMap.count( child ) )
|
||||
{
|
||||
auto parentChildPath = checkRelation( parent, child );
|
||||
|
||||
@@ -340,11 +370,11 @@ namespace cereal
|
||||
{
|
||||
auto & derivedMap = baseMap.find( it.first )->second;
|
||||
derivedMap[it.second.first] = it.second.second;
|
||||
reverseMap.insert( {it.second.first, it.first} );
|
||||
CEREAL_EMPLACE_MAP(reverseMap, it.second.first, it.first );
|
||||
}
|
||||
|
||||
// Mark current parent as modified
|
||||
dirtySet.insert( parent );
|
||||
dirtySet.emplace_back( parent );
|
||||
|
||||
// Insert all parents of the current parent node that haven't yet been processed
|
||||
auto parentRange = reverseMap.equal_range( parent );
|
||||
@@ -361,6 +391,8 @@ namespace cereal
|
||||
} // end chainable relations
|
||||
} // end PolymorphicVirtualCaster()
|
||||
|
||||
#undef CEREAL_EMPLACE_MAP
|
||||
|
||||
//! Performs the proper downcast with the templated types
|
||||
void const * downcast( void const * const ptr ) const override
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
# define CEREAL_DLL_EXPORT __declspec(dllexport)
|
||||
# define CEREAL_USED
|
||||
#else // clang or gcc
|
||||
# define CEREAL_DLL_EXPORT
|
||||
# define CEREAL_DLL_EXPORT __attribute__ ((visibility("default")))
|
||||
# define CEREAL_USED __attribute__ ((__used__))
|
||||
#endif
|
||||
|
||||
@@ -67,13 +67,12 @@ namespace cereal
|
||||
class CEREAL_DLL_EXPORT StaticObject
|
||||
{
|
||||
private:
|
||||
//! Forces instantiation at pre-execution time
|
||||
static void instantiate( T const & ) {}
|
||||
|
||||
static T & create()
|
||||
{
|
||||
static T t;
|
||||
instantiate(instance);
|
||||
//! Forces instantiation at pre-execution time
|
||||
(void)instance;
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -95,6 +94,7 @@ namespace cereal
|
||||
std::unique_lock<std::mutex> lock;
|
||||
#else
|
||||
public:
|
||||
LockGuard(LockGuard const &) = default; // prevents implicit copy ctor warning
|
||||
~LockGuard() CEREAL_NOEXCEPT {} // prevents variable not used
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -800,6 +800,18 @@ namespace cereal
|
||||
|
||||
See notes from member load_minimal implementation.
|
||||
|
||||
Note that there should be an additional const check on load_minimal after the valid check,
|
||||
but this currently interferes with many valid uses of minimal serialization. It has been
|
||||
removed (see #565 on github) and previously was:
|
||||
|
||||
@code
|
||||
static_assert( check::const_valid || !check::exists,
|
||||
"cereal detected an invalid serialization type parameter in non-member " #test_name ". "
|
||||
#test_name " non-member functions must accept their serialization type by non-const reference" );
|
||||
@endcode
|
||||
|
||||
See #132, #436, #263, and #565 on https://github.com/USCiLab/cereal for more details.
|
||||
|
||||
@param test_name The name to give the test (e.g. load_minimal or versioned_load_minimal)
|
||||
@param save_name The corresponding name the save test would have (e.g. save_minimal or versioned_save_minimal)
|
||||
@param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
|
||||
@@ -847,9 +859,6 @@ namespace cereal
|
||||
static_assert( check::valid || !check::exists, "cereal detected different types in corresponding non-member " \
|
||||
#test_name " and " #save_name " functions. \n " \
|
||||
"the paramater to " #test_name " must be a constant reference to the type that " #save_name " returns." ); \
|
||||
static_assert( check::const_valid || !check::exists, \
|
||||
"cereal detected an invalid serialization type parameter in non-member " #test_name ". " \
|
||||
#test_name " non-member functions must accept their serialization type by non-const reference" ); \
|
||||
}; \
|
||||
} /* namespace detail */ \
|
||||
\
|
||||
@@ -869,17 +878,29 @@ namespace cereal
|
||||
#undef CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST
|
||||
|
||||
// ######################################################################
|
||||
namespace detail
|
||||
{
|
||||
// const stripped away before reaching here, prevents errors on conversion from
|
||||
// construct<const T> to construct<T>
|
||||
template<typename T, typename A>
|
||||
struct has_member_load_and_construct_impl : std::integral_constant<bool,
|
||||
std::is_same<decltype( access::load_and_construct<T>( std::declval<A&>(), std::declval< ::cereal::construct<T>&>() ) ), void>::value>
|
||||
{ };
|
||||
|
||||
template<typename T, typename A>
|
||||
struct has_member_versioned_load_and_construct_impl : std::integral_constant<bool,
|
||||
std::is_same<decltype( access::load_and_construct<T>( std::declval<A&>(), std::declval< ::cereal::construct<T>&>(), 0 ) ), void>::value>
|
||||
{ };
|
||||
} // namespace detail
|
||||
|
||||
//! Member load and construct check
|
||||
template<typename T, typename A>
|
||||
struct has_member_load_and_construct : std::integral_constant<bool,
|
||||
std::is_same<decltype( access::load_and_construct<T>( std::declval<A&>(), std::declval< ::cereal::construct<T>&>() ) ), void>::value>
|
||||
struct has_member_load_and_construct : detail::has_member_load_and_construct_impl<typename std::remove_const<T>::type, A>
|
||||
{ };
|
||||
|
||||
// ######################################################################
|
||||
//! Member load and construct check (versioned)
|
||||
template<typename T, typename A>
|
||||
struct has_member_versioned_load_and_construct : std::integral_constant<bool,
|
||||
std::is_same<decltype( access::load_and_construct<T>( std::declval<A&>(), std::declval< ::cereal::construct<T>&>(), 0 ) ), void>::value>
|
||||
struct has_member_versioned_load_and_construct : detail::has_member_versioned_load_and_construct_impl<typename std::remove_const<T>::type, A>
|
||||
{ };
|
||||
|
||||
// ######################################################################
|
||||
@@ -901,7 +922,8 @@ namespace cereal
|
||||
}; \
|
||||
} /* end namespace detail */ \
|
||||
template <class T, class A> \
|
||||
struct has_non_member_##test_name : std::integral_constant<bool, detail::has_non_member_##test_name##_impl<T, A>::value> {};
|
||||
struct has_non_member_##test_name : \
|
||||
std::integral_constant<bool, detail::has_non_member_##test_name##_impl<typename std::remove_const<T>::type, A>::value> {};
|
||||
|
||||
// ######################################################################
|
||||
//! Non member load and construct check
|
||||
|
||||
Reference in New Issue
Block a user