diff --git a/include/cereal/cereal.hpp b/include/cereal/cereal.hpp index 648052ebb..c599382f1 100644 --- a/include/cereal/cereal.hpp +++ b/include/cereal/cereal.hpp @@ -100,7 +100,50 @@ namespace cereal // ###################################################################### //! Marks data for deferred serialization - /*! @relates DeferredData + /*! cereal performs a recursive depth-first traversal of data it serializes. When + serializing smart pointers to large, nested, or cyclical data structures, it + is possible to encounter a stack overflow from excessive recursion when following + a chain of pointers. + + Deferment can help in these situations if the data can be serialized separately from + the pointers used to traverse the structure. For example, a graph structure can have its + nodes serialized before its edges: + + @code{.cpp} + struct MyEdge + { + std::shared_ptr connection; + int some_value; + + template + void serialize(Archive & archive) + { + // when we serialize an edge, we'll defer serializing the associated node + archive( cereal::defer( connection ), + some_value ); + } + }; + + struct MyGraphStructure + { + std::vector edges; + std::vector nodes; + + template + void serialize(Archive & archive) + { + // because of the deferment, we ensure all nodes are fully serialized + // before any connection pointers to those nodes are serialized + archive( edges, nodes ); + + // we have to explicitly inform the archive when it is safe to serialize + // the deferred data + archive.serializeDeferments(); + } + }; + @endcode + + @relates DeferredData @ingroup Utility */ template inline DeferredData defer( T && value ) @@ -269,6 +312,15 @@ namespace cereal return *self; } + //! Serializes any data marked for deferment + /*! This will cause any data wrapped in DeferredData to be immediately serialized + @relates defer */ + void serializeDeferments() + { + for( auto & deferment : itsDeferments ) + deferment(); + } + /*! @name Boost Transition Layer Functionality that mirrors the syntax for Boost. This is useful if you are transitioning a large project from Boost to cereal. The preferred interface for cereal is using operator(). */ @@ -357,12 +409,6 @@ namespace cereal return id->second; } - void serializeDeferments() - { - for( auto & deferment : itsDeferments ) - deferment(); - } - private: //! Serializes data after calling prologue, then calls epilogue template inline @@ -655,6 +701,15 @@ namespace cereal return *self; } + //! Serializes any data marked for deferment + /*! This will cause any data wrapped in DeferredData to be immediately serialized + @relates defer */ + void serializeDeferments() + { + for( auto & deferment : itsDeferments ) + deferment(); + } + /*! @name Boost Transition Layer Functionality that mirrors the syntax for Boost. This is useful if you are transitioning a large project from Boost to cereal. The preferred interface for cereal is using operator(). */ @@ -702,6 +757,7 @@ namespace cereal /*! This is used to retrieve a previously registered shared_ptr which has already been loaded. + @internal @param id The unique id that was serialized for the pointer @return A shared pointer to the data @throw Exception if the id does not exist */ @@ -720,6 +776,7 @@ namespace cereal /*! After a shared pointer has been allocated for the first time, it should be registered with its loaded id for future references to it. + @internal @param id The unique identifier for the shared pointer @param ptr The actual shared pointer */ inline void registerSharedPointer(std::uint32_t const id, std::shared_ptr ptr) @@ -732,6 +789,7 @@ namespace cereal /*! This is used to retrieve a string previously registered during a polymorphic load. + @internal @param id The unique id that was serialized for the polymorphic type @return The string identifier for the tyep */ inline std::string getPolymorphicName(std::uint32_t const id) @@ -748,6 +806,7 @@ namespace cereal /*! After a polymorphic type has been loaded for the first time, it should be registered with its loaded id for future references to it. + @internal @param id The unique identifier for the polymorphic type @param name The name associated with the tyep */ inline void registerPolymorphicName(std::uint32_t const id, std::string const & name) @@ -756,12 +815,6 @@ namespace cereal itsPolymorphicTypeMap.insert( {stripped_id, name} ); } - void serializeDeferments() - { - for( auto & deferment : itsDeferments ) - deferment(); - } - private: //! Serializes data after calling prologue, then calls epilogue template inline diff --git a/include/cereal/details/helpers.hpp b/include/cereal/details/helpers.hpp index 5162c08ce..7b4c4ad39 100644 --- a/include/cereal/details/helpers.hpp +++ b/include/cereal/details/helpers.hpp @@ -223,6 +223,11 @@ namespace cereal }; // ###################################################################### + //! 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 DeferredData : detail::DeferredDataCore {