/*! \file cereal.hpp \brief Main cereal functionality */ /* Copyright (c) 2014, Randolph Voorhies, Shane Grant All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of cereal nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CEREAL_CEREAL_HPP_ #define CEREAL_CEREAL_HPP_ #include #include #include #include #include #include #include #include #include #include #include "cereal/macros.hpp" #include "cereal/details/traits.hpp" #include "cereal/details/helpers.hpp" #include "cereal/types/base_class.hpp" namespace cereal { // ###################################################################### //! Creates a name value pair /*! @relates NameValuePair @ingroup Utility */ template inline NameValuePair make_nvp( std::string const & name, T && value ) { return {name.c_str(), std::forward(value)}; } //! Creates a name value pair /*! @relates NameValuePair @ingroup Utility */ template inline NameValuePair make_nvp( const char * name, T && value ) { return {name, std::forward(value)}; } //! Creates a name value pair for the variable T with the same name as the variable /*! @relates NameValuePair @ingroup Utility */ #define CEREAL_NVP(T) ::cereal::make_nvp(#T, T) // ###################################################################### //! Convenience function to create binary data for both const and non const pointers /*! @param data Pointer to beginning of the data @param size The size in bytes of the data @relates BinaryData @ingroup Utility */ template inline BinaryData binary_data( T && data, size_t size ) { return {std::forward(data), size}; } // ###################################################################### //! Creates a size tag from some variable. /*! Will normally be used to serialize size (e.g. size()) information for variable size containers. If you have a variable sized container, the very first thing it serializes should be its size, wrapped in a SizeTag. @relates SizeTag @ingroup Utility */ template inline SizeTag make_size_tag( T && sz ) { return {std::forward(sz)}; } // ###################################################################### //! Marks data for deferred serialization /*! 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 ) { return {std::forward(value)}; } // ###################################################################### //! Called before a type is serialized to set up any special archive state //! for processing some type /*! If designing a serializer that needs to set up any kind of special state or output extra information for a type, specialize this function for the archive type and the types that require the extra information. @ingroup Internal */ template inline void prologue( Archive & /* archive */, T const & /* data */) { } //! Called after a type is serialized to tear down any special archive state //! for processing some type /*! @ingroup Internal */ template inline void epilogue( Archive & /* archive */, T const & /* data */) { } // ###################################################################### //! Special flags for archives /*! AllowEmptyClassElision This allows for empty classes to be serialized even if they do not provide a serialization function. Classes with no data members are considered to be empty. Be warned that if this is enabled and you attempt to serialize an empty class with improperly formed serialize or load/save functions, no static error will occur - the error will propogate silently and your intended serialization functions may not be called. You can manually ensure that your classes that have custom serialization are correct by using the traits is_output_serializable and is_input_serializable in cereal/details/traits.hpp. @ingroup Internal */ enum Flags { AllowEmptyClassElision = 1 }; // ###################################################################### //! Registers a specific Archive type with cereal /*! This registration should be done once per archive. A good place to put this is immediately following the definition of your archive. Archive registration is only strictly necessary if you wish to support pointers to polymorphic data types. All archives that come with cereal are already registered. @ingroup Internal */ #define CEREAL_REGISTER_ARCHIVE(Archive) \ namespace cereal { namespace detail { \ template \ typename polymorphic_serialization_support::type \ instantiate_polymorphic_binding( T*, Archive*, BindingTag, adl_tag ); \ } } /* end namespaces */ //! Helper macro to omit unused warning #if defined(__GNUC__) // GCC / clang don't want the function #define CEREAL_UNUSED_FUNCTION #else #define CEREAL_UNUSED_FUNCTION static void unused() { (void)version; } #endif // ###################################################################### //! Defines a class version for some type /*! Versioning information is optional and adds some small amount of overhead to serialization. This overhead will occur both in terms of space in the archive (the version information for each class will be stored exactly once) as well as runtime (versioned serialization functions must check to see if they need to load or store version information). Versioning is useful if you plan on fundamentally changing the way some type is serialized in the future. Versioned serialization functions cannot be used to load non-versioned data. By default, all types have an assumed version value of zero. By using this macro, you may change the version number associated with some type. cereal will then use this value as a second parameter to your serialization functions. The interface for the serialization functions is nearly identical to non-versioned serialization with the addition of a second parameter, const std::uint32_t version, which will be supplied with the correct version number. Serializing the version number on a save happens automatically. Versioning cannot be mixed with non-versioned serialization functions. Having both types will result result in a compile time error. Data serialized without versioning cannot be loaded by a serialization function with added versioning support. Example interface for versioning on a non-member serialize function: @code{cpp} CEREAL_CLASS_VERSION( Mytype, 77 ); // register class version template void serialize( Archive & ar, Mytype & t, const std::uint32_t version ) { // When performing a load, the version associated with the class // is whatever it was when that data was originally serialized // // When we save, we'll use the version that is defined in the macro if( version >= some_number ) // do this else // do that } @endcode Interfaces for other forms of serialization functions is similar. This macro should be placed at global scope. @ingroup Utility */ #define CEREAL_CLASS_VERSION(TYPE, VERSION_NUMBER) \ namespace cereal { namespace detail { \ template <> struct Version \ { \ static const std::uint32_t version; \ static std::uint32_t registerVersion() \ { \ ::cereal::detail::StaticObject::getInstance().mapping.emplace( \ std::type_index(typeid(TYPE)).hash_code(), VERSION_NUMBER ); \ return VERSION_NUMBER; \ } \ CEREAL_UNUSED_FUNCTION \ }; /* end Version */ \ const std::uint32_t Version::version = \ Version::registerVersion(); \ } } // end namespaces // ###################################################################### //! The base output archive class /*! This is the base output archive for all output archives. If you create a custom archive class, it should derive from this, passing itself as a template parameter for the ArchiveType. The base class provides all of the functionality necessary to properly forward data to the correct serialization functions. Individual archives should use a combination of prologue and epilogue functions together with specializations of serialize, save, and load to alter the functionality of their serialization. @tparam ArchiveType The archive type that derives from OutputArchive @tparam Flags Flags to control advanced functionality. See the Flags enum for more information. @ingroup Internal */ template class OutputArchive : public detail::OutputArchiveBase { public: //! Construct the output archive /*! @param derived A pointer to the derived ArchiveType (pass this from the derived archive) */ OutputArchive(ArchiveType * const derived) : self(derived), itsCurrentPointerId(1), itsCurrentPolymorphicTypeId(1) { } OutputArchive & operator=( OutputArchive const & ) = delete; //! Serializes all passed in data /*! This is the primary interface for serializing data with an archive */ template inline ArchiveType & operator()( Types && ... args ) { self->process( std::forward( args )... ); return *self; } //! Serializes any data marked for deferment using defer /*! This will cause any data wrapped in DeferredData to be immediately serialized */ 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(). */ //! @{ //! Indicates this archive is not intended for loading /*! This ensures compatibility with boost archive types. If you are transitioning from boost, you can check this value within a member or external serialize function (i.e., Archive::is_loading::value) to disable behavior specific to loading, until you can transition to split save/load or save_minimal/load_minimal functions */ using is_loading = std::false_type; //! Indicates this archive is intended for saving /*! This ensures compatibility with boost archive types. If you are transitioning from boost, you can check this value within a member or external serialize function (i.e., Archive::is_saving::value) to enable behavior specific to loading, until you can transition to split save/load or save_minimal/load_minimal functions */ using is_saving = std::true_type; //! Serializes passed in data /*! This is a boost compatability layer and is not the preferred way of using cereal. If you are transitioning from boost, use this until you can transition to the operator() overload */ template inline ArchiveType & operator&( T && arg ) { self->process( std::forward( arg ) ); return *self; } //! Serializes passed in data /*! This is a boost compatability layer and is not the preferred way of using cereal. If you are transitioning from boost, use this until you can transition to the operator() overload */ template inline ArchiveType & operator<<( T && arg ) { self->process( std::forward( arg ) ); return *self; } //! @} //! Registers a shared pointer with the archive /*! This function is used to track shared pointer targets to prevent unnecessary saves from taking place if multiple shared pointers point to the same data. @internal @param addr The address (see shared_ptr get()) pointed to by the shared pointer @return A key that uniquely identifies the pointer */ inline std::uint32_t registerSharedPointer( void const * addr ) { // Handle null pointers by just returning 0 if(addr == 0) return 0; auto id = itsSharedPointerMap.find( addr ); if( id == itsSharedPointerMap.end() ) { auto ptrId = itsCurrentPointerId++; itsSharedPointerMap.insert( {addr, ptrId} ); return ptrId | detail::msb_32bit; // mask MSB to be 1 } else return id->second; } //! Registers a polymorphic type name with the archive /*! This function is used to track polymorphic types to prevent unnecessary saves of identifying strings used by the polymorphic support functionality. @internal @param name The name to associate with a polymorphic type @return A key that uniquely identifies the polymorphic type name */ inline std::uint32_t registerPolymorphicType( char const * name ) { auto id = itsPolymorphicTypeMap.find( name ); if( id == itsPolymorphicTypeMap.end() ) { auto polyId = itsCurrentPolymorphicTypeId++; itsPolymorphicTypeMap.insert( {name, polyId} ); return polyId | detail::msb_32bit; // mask MSB to be 1 } else return id->second; } private: //! Serializes data after calling prologue, then calls epilogue template inline void process( T && head ) { prologue( *self, head ); self->processImpl( head ); epilogue( *self, head ); } //! Unwinds to process all data template inline void process( T && head, Other && ... tail ) { self->process( std::forward( head ) ); self->process( std::forward( tail )... ); } //! Serialization of a virtual_base_class wrapper /*! \sa virtual_base_class */ template inline ArchiveType & processImpl(virtual_base_class const & b) { traits::detail::base_class_id id(b.base_ptr); if(itsBaseClassSet.count(id) == 0) { itsBaseClassSet.insert(id); self->processImpl( *b.base_ptr ); } return *self; } //! Serialization of a base_class wrapper /*! \sa base_class */ template inline ArchiveType & processImpl(base_class const & b) { self->processImpl( *b.base_ptr ); return *self; } std::vector> itsDeferments; template inline ArchiveType & processImpl(DeferredData const & d) { std::function deferment( [=](){ self->process( d.value ); } ); itsDeferments.emplace_back( std::move(deferment) ); return *self; } //! Helper macro that expands the requirements for activating an overload /*! Requirements: Has the requested serialization function Does not have version and unversioned at the same time Is output serializable AND is specialized for this type of function OR has no specialization at all */ #define PROCESS_IF(name) \ traits::EnableIf::value, \ !traits::has_invalid_output_versioning::value, \ (traits::is_output_serializable::value && \ (traits::is_specialized_##name::value || \ !traits::is_specialized::value))> = traits::sfinae //! Member serialization template inline ArchiveType & processImpl(T const & t) { access::member_serialize(*self, const_cast(t)); return *self; } //! Non member serialization template inline ArchiveType & processImpl(T const & t) { CEREAL_SERIALIZE_FUNCTION_NAME(*self, const_cast(t)); return *self; } //! Member split (save) template inline ArchiveType & processImpl(T const & t) { access::member_save(*self, t); return *self; } //! Non member split (save) template inline ArchiveType & processImpl(T const & t) { CEREAL_SAVE_FUNCTION_NAME(*self, t); return *self; } //! Member split (save_minimal) template inline ArchiveType & processImpl(T const & t) { self->process( access::member_save_minimal(*self, t) ); return *self; } //! Non member split (save_minimal) template inline ArchiveType & processImpl(T const & t) { self->process( CEREAL_SAVE_MINIMAL_FUNCTION_NAME(*self, t) ); return *self; } //! Empty class specialization template ::value, std::is_empty::value> = traits::sfinae> inline ArchiveType & processImpl(T const &) { return *self; } //! No matching serialization /*! Invalid if we have invalid output versioning or we are not output serializable, and either don't allow empty class ellision or allow it but are not serializing an empty class */ template ::value || (!traits::is_output_serializable::value && (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty::value)))> = traits::sfinae> inline ArchiveType & processImpl(T const &) { static_assert(traits::detail::count_output_serializers::value != 0, "cereal could not find any output serialization functions for the provided type and archive combination. \n\n " "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " "Serialize functions generally have the following signature: \n\n " "template \n " " void serialize(Archive & ar) \n " " { \n " " ar( member1, member2, member3 ); \n " " } \n\n " ); static_assert(traits::detail::count_output_serializers::value < 2, "cereal found more than one compatible output serialization function for the provided type and archive combination. \n\n " "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " "Use specialization (see access.hpp) if you need to disambiguate between serialize vs load/save functions. \n " "Note that serialization functions can be inherited which may lead to the aforementioned ambiguities. \n " "In addition, you may not mix versioned with non-versioned serialization functions. \n\n "); return *self; } //! Registers a class version with the archive and serializes it if necessary /*! If this is the first time this class has been serialized, we will record its version number and serialize that. @tparam T The type of the class being serialized */ template inline std::uint32_t registerClassVersion() { static const auto hash = std::type_index(typeid(T)).hash_code(); const auto insertResult = itsVersionedTypes.insert( hash ); const auto lock = detail::StaticObject::lock(); const auto version = detail::StaticObject::getInstance().find( hash, detail::Version::version ); if( insertResult.second ) // insertion took place, serialize the version number process( make_nvp("cereal_class_version", version) ); return version; } //! Member serialization /*! Versioning implementation */ template inline ArchiveType & processImpl(T const & t) { access::member_serialize(*self, const_cast(t), registerClassVersion()); return *self; } //! Non member serialization /*! Versioning implementation */ template inline ArchiveType & processImpl(T const & t) { CEREAL_SERIALIZE_FUNCTION_NAME(*self, const_cast(t), registerClassVersion()); return *self; } //! Member split (save) /*! Versioning implementation */ template inline ArchiveType & processImpl(T const & t) { access::member_save(*self, t, registerClassVersion()); return *self; } //! Non member split (save) /*! Versioning implementation */ template inline ArchiveType & processImpl(T const & t) { CEREAL_SAVE_FUNCTION_NAME(*self, t, registerClassVersion()); return *self; } //! Member split (save_minimal) /*! Versioning implementation */ template inline ArchiveType & processImpl(T const & t) { self->process( access::member_save_minimal(*self, t, registerClassVersion()) ); return *self; } //! Non member split (save_minimal) /*! Versioning implementation */ template inline ArchiveType & processImpl(T const & t) { self->process( CEREAL_SAVE_MINIMAL_FUNCTION_NAME(*self, t, registerClassVersion()) ); return *self; } #undef PROCESS_IF private: ArchiveType * const self; //! A set of all base classes that have been serialized std::unordered_set itsBaseClassSet; //! Maps from addresses to pointer ids std::unordered_map itsSharedPointerMap; //! The id to be given to the next pointer std::uint32_t itsCurrentPointerId; //! Maps from polymorphic type name strings to ids std::unordered_map itsPolymorphicTypeMap; //! The id to be given to the next polymorphic type name std::uint32_t itsCurrentPolymorphicTypeId; //! Keeps track of classes that have versioning information associated with them std::unordered_set itsVersionedTypes; }; // class OutputArchive // ###################################################################### //! The base input archive class /*! This is the base input archive for all input archives. If you create a custom archive class, it should derive from this, passing itself as a template parameter for the ArchiveType. The base class provides all of the functionality necessary to properly forward data to the correct serialization functions. Individual archives should use a combination of prologue and epilogue functions together with specializations of serialize, save, and load to alter the functionality of their serialization. @tparam ArchiveType The archive type that derives from InputArchive @tparam Flags Flags to control advanced functionality. See the Flags enum for more information. @ingroup Internal */ template class InputArchive : public detail::InputArchiveBase { public: //! Construct the output archive /*! @param derived A pointer to the derived ArchiveType (pass this from the derived archive) */ InputArchive(ArchiveType * const derived) : self(derived), itsBaseClassSet(), itsSharedPointerMap(), itsPolymorphicTypeMap(), itsVersionedTypes() { } InputArchive & operator=( InputArchive const & ) = delete; //! Serializes all passed in data /*! This is the primary interface for serializing data with an archive */ template inline ArchiveType & operator()( Types && ... args ) { process( std::forward( args )... ); return *self; } //! Serializes any data marked for deferment using defer /*! This will cause any data wrapped in DeferredData to be immediately serialized */ 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(). */ //! @{ //! Indicates this archive is intended for loading /*! This ensures compatibility with boost archive types. If you are transitioning from boost, you can check this value within a member or external serialize function (i.e., Archive::is_loading::value) to enable behavior specific to loading, until you can transition to split save/load or save_minimal/load_minimal functions */ using is_loading = std::true_type; //! Indicates this archive is not intended for saving /*! This ensures compatibility with boost archive types. If you are transitioning from boost, you can check this value within a member or external serialize function (i.e., Archive::is_saving::value) to disable behavior specific to loading, until you can transition to split save/load or save_minimal/load_minimal functions */ using is_saving = std::false_type; //! Serializes passed in data /*! This is a boost compatability layer and is not the preferred way of using cereal. If you are transitioning from boost, use this until you can transition to the operator() overload */ template inline ArchiveType & operator&( T && arg ) { self->process( std::forward( arg ) ); return *self; } //! Serializes passed in data /*! This is a boost compatability layer and is not the preferred way of using cereal. If you are transitioning from boost, use this until you can transition to the operator() overload */ template inline ArchiveType & operator>>( T && arg ) { self->process( std::forward( arg ) ); return *self; } //! @} //! Retrieves a shared pointer given a unique key for it /*! 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 */ inline std::shared_ptr getSharedPointer(std::uint32_t const id) { if(id == 0) return std::shared_ptr(nullptr); auto iter = itsSharedPointerMap.find( id ); if(iter == itsSharedPointerMap.end()) throw Exception("Error while trying to deserialize a smart pointer. Could not find id " + std::to_string(id)); return iter->second; } //! Registers a shared pointer to its unique identifier /*! 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) { std::uint32_t const stripped_id = id & ~detail::msb_32bit; itsSharedPointerMap[stripped_id] = ptr; } //! Retrieves the string for a polymorphic type given a unique key for it /*! 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) { auto name = itsPolymorphicTypeMap.find( id ); if(name == itsPolymorphicTypeMap.end()) { throw Exception("Error while trying to deserialize a polymorphic pointer. Could not find type id " + std::to_string(id)); } return name->second; } //! Registers a polymorphic name string to its unique identifier /*! 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) { std::uint32_t const stripped_id = id & ~detail::msb_32bit; itsPolymorphicTypeMap.insert( {stripped_id, name} ); } private: //! Serializes data after calling prologue, then calls epilogue template inline void process( T && head ) { prologue( *self, head ); self->processImpl( head ); epilogue( *self, head ); } //! Unwinds to process all data template inline void process( T && head, Other && ... tail ) { process( std::forward( head ) ); process( std::forward( tail )... ); } //! Serialization of a virtual_base_class wrapper /*! \sa virtual_base_class */ template inline ArchiveType & processImpl(virtual_base_class & b) { traits::detail::base_class_id id(b.base_ptr); if(itsBaseClassSet.count(id) == 0) { itsBaseClassSet.insert(id); self->processImpl( *b.base_ptr ); } return *self; } //! Serialization of a base_class wrapper /*! \sa base_class */ template inline ArchiveType & processImpl(base_class & b) { self->processImpl( *b.base_ptr ); return *self; } std::vector> itsDeferments; template inline ArchiveType & processImpl(DeferredData const & d) { std::function deferment( [=](){ self->process( d.value ); } ); itsDeferments.emplace_back( std::move(deferment) ); return *self; } //! Helper macro that expands the requirements for activating an overload /*! Requirements: Has the requested serialization function Does not have version and unversioned at the same time Is input serializable AND is specialized for this type of function OR has no specialization at all */ #define PROCESS_IF(name) \ traits::EnableIf::value, \ !traits::has_invalid_input_versioning::value, \ (traits::is_input_serializable::value && \ (traits::is_specialized_##name::value || \ !traits::is_specialized::value))> = traits::sfinae //! Member serialization template inline ArchiveType & processImpl(T & t) { access::member_serialize(*self, t); return *self; } //! Non member serialization template inline ArchiveType & processImpl(T & t) { CEREAL_SERIALIZE_FUNCTION_NAME(*self, t); return *self; } //! Member split (load) template inline ArchiveType & processImpl(T & t) { access::member_load(*self, t); return *self; } //! Non member split (load) template inline ArchiveType & processImpl(T & t) { CEREAL_LOAD_FUNCTION_NAME(*self, t); return *self; } //! Member split (load_minimal) template inline ArchiveType & processImpl(T & t) { using OutArchiveType = typename traits::detail::get_output_from_input::type; typename traits::has_member_save_minimal::type value; self->process( value ); access::member_load_minimal(*self, t, value); return *self; } //! Non member split (load_minimal) template inline ArchiveType & processImpl(T & t) { using OutArchiveType = typename traits::detail::get_output_from_input::type; typename traits::has_non_member_save_minimal::type value; self->process( value ); CEREAL_LOAD_MINIMAL_FUNCTION_NAME(*self, t, value); return *self; } //! Empty class specialization template ::value, std::is_empty::value> = traits::sfinae> inline ArchiveType & processImpl(T const &) { return *self; } //! No matching serialization /*! Invalid if we have invalid input versioning or we are not input serializable, and either don't allow empty class ellision or allow it but are not serializing an empty class */ template ::value || (!traits::is_input_serializable::value && (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty::value)))> = traits::sfinae> inline ArchiveType & processImpl(T const &) { static_assert(traits::detail::count_input_serializers::value != 0, "cereal could not find any input serialization functions for the provided type and archive combination. \n\n " "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " "Serialize functions generally have the following signature: \n\n " "template \n " " void serialize(Archive & ar) \n " " { \n " " ar( member1, member2, member3 ); \n " " } \n\n " ); static_assert(traits::detail::count_input_serializers::value < 2, "cereal found more than one compatible input serialization function for the provided type and archive combination. \n\n " "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " "Use specialization (see access.hpp) if you need to disambiguate between serialize vs load/save functions. \n " "Note that serialization functions can be inherited which may lead to the aforementioned ambiguities. \n " "In addition, you may not mix versioned with non-versioned serialization functions. \n\n "); return *self; } //! Befriend for versioning in load_and_construct template friend struct detail::Construct; //! Registers a class version with the archive and serializes it if necessary /*! If this is the first time this class has been serialized, we will record its version number and serialize that. @tparam T The type of the class being serialized */ template inline std::uint32_t loadClassVersion() { static const auto hash = std::type_index(typeid(T)).hash_code(); auto lookupResult = itsVersionedTypes.find( hash ); if( lookupResult != itsVersionedTypes.end() ) // already exists return lookupResult->second; else // need to load { std::uint32_t version; process( make_nvp("cereal_class_version", version) ); itsVersionedTypes.emplace_hint( lookupResult, hash, version ); return version; } } //! Member serialization /*! Versioning implementation */ template inline ArchiveType & processImpl(T & t) { const auto version = loadClassVersion(); access::member_serialize(*self, t, version); return *self; } //! Non member serialization /*! Versioning implementation */ template inline ArchiveType & processImpl(T & t) { const auto version = loadClassVersion(); CEREAL_SERIALIZE_FUNCTION_NAME(*self, t, version); return *self; } //! Member split (load) /*! Versioning implementation */ template inline ArchiveType & processImpl(T & t) { const auto version = loadClassVersion(); access::member_load(*self, t, version); return *self; } //! Non member split (load) /*! Versioning implementation */ template inline ArchiveType & processImpl(T & t) { const auto version = loadClassVersion(); CEREAL_LOAD_FUNCTION_NAME(*self, t, version); return *self; } //! Member split (load_minimal) /*! Versioning implementation */ template inline ArchiveType & processImpl(T & t) { using OutArchiveType = typename traits::detail::get_output_from_input::type; const auto version = loadClassVersion(); typename traits::has_member_versioned_save_minimal::type value; self->process(value); access::member_load_minimal(*self, t, value, version); return *self; } //! Non member split (load_minimal) /*! Versioning implementation */ template inline ArchiveType & processImpl(T & t) { using OutArchiveType = typename traits::detail::get_output_from_input::type; const auto version = loadClassVersion(); typename traits::has_non_member_versioned_save_minimal::type value; self->process(value); CEREAL_LOAD_MINIMAL_FUNCTION_NAME(*self, t, value, version); return *self; } #undef PROCESS_IF private: ArchiveType * const self; //! A set of all base classes that have been serialized std::unordered_set itsBaseClassSet; //! Maps from pointer ids to metadata std::unordered_map> itsSharedPointerMap; //! Maps from name ids to names std::unordered_map itsPolymorphicTypeMap; //! Maps from type hash codes to version numbers std::unordered_map itsVersionedTypes; }; // class InputArchive } // namespace cereal // This include needs to come after things such as binary_data, make_nvp, etc #include "cereal/types/common.hpp" #endif // CEREAL_CEREAL_HPP_