upgrade cereal

This commit is contained in:
Bassem Girgis
2020-01-22 15:55:26 -06:00
parent d5109aa647
commit 821d5b0a28
56 changed files with 3717 additions and 1624 deletions

View File

@@ -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;
}

View File

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

View File

@@ -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
};

View File

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