From d9493f02cab96acfaefc81b537040240faf96cc4 Mon Sep 17 00:00:00 2001 From: Randolph Voorhies Date: Fri, 28 Jun 2013 17:23:30 -0700 Subject: [PATCH] Loading polymorphig shared_ptrs works --- include/cereal/cereal.hpp | 61 +++++++++++++---- include/cereal/details/polymorphic_impl.hpp | 72 +++++++++++++++++++-- include/cereal/details/static_object.hpp | 26 -------- include/cereal/details/util.hpp | 58 +++++++++++++++++ include/cereal/types/polymorphic.hpp | 71 ++++++++++++++++---- sandbox_rtti.cpp | 50 ++++++++++---- 6 files changed, 272 insertions(+), 66 deletions(-) create mode 100644 include/cereal/details/util.hpp diff --git a/include/cereal/cereal.hpp b/include/cereal/cereal.hpp index fcac5baf..ed797caa 100644 --- a/include/cereal/cereal.hpp +++ b/include/cereal/cereal.hpp @@ -167,11 +167,11 @@ namespace cereal } } // end namespaces //! The base output archive class - template + template class OutputArchive : public detail::OutputArchiveBase { public: - OutputArchive(ArchiveType * const self) : self(self), itsCurrentPointerId(1) + OutputArchive(ArchiveType * const self) : self(self), itsCurrentPointerId(1), itsCurrentPolymorphicTypeId(1) { } //! Serializes all passed in data @@ -183,7 +183,7 @@ namespace cereal } //! Registers a pointer with the archive - uint32_t registerSharedPointer( void const * addr ) + std::uint32_t registerSharedPointer( void const * addr ) { // Handle null pointers by just returning 0 if(addr == 0) return 0; @@ -199,6 +199,20 @@ namespace cereal return id->second; } + //! Registers a polymorphic type name with the archive + 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: template inline void process( T && head ) @@ -307,15 +321,21 @@ namespace cereal std::unordered_set itsBaseClassSet; //! Maps from addresses to pointer ids - std::unordered_map itsSharedPointerMap; + std::unordered_map itsSharedPointerMap; //! The id to be given to the next pointer - std::size_t itsCurrentPointerId; + 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; }; // class OutputArchive // ###################################################################### //! The base input archive class - template + template class InputArchive : public detail::InputArchiveBase { public: @@ -329,7 +349,7 @@ namespace cereal return *self; } - std::shared_ptr getSharedPointer(uint32_t const id) + std::shared_ptr getSharedPointer(std::uint32_t const id) { if(id == 0) return std::shared_ptr(nullptr); @@ -341,12 +361,27 @@ namespace cereal return ptr->second; } - void registerSharedPointer(uint32_t const id, std::shared_ptr ptr) + void registerSharedPointer(std::uint32_t const id, std::shared_ptr ptr) { - uint32_t const stripped_id = id & ~detail::msb_32bit; + std::uint32_t const stripped_id = id & ~detail::msb_32bit; itsSharedPointerMap.insert( {stripped_id, ptr} ); } + 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; + } + + 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: template inline @@ -455,8 +490,12 @@ namespace cereal //! A set of all base classes that have been serialized std::unordered_set itsBaseClassSet; - //! Maps from addresses to pointer ids - std::unordered_map> itsSharedPointerMap; + //! Maps from pointer ids to addresses + std::unordered_map> itsSharedPointerMap; + + //! Maps from name ids to names + std::unordered_map itsPolymorphicTypeMap; + }; // class InputArchive } // namespace cereal diff --git a/include/cereal/details/polymorphic_impl.hpp b/include/cereal/details/polymorphic_impl.hpp index e763ce6c..6d513e12 100644 --- a/include/cereal/details/polymorphic_impl.hpp +++ b/include/cereal/details/polymorphic_impl.hpp @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -72,6 +73,7 @@ namespace cereal template struct binding_name {}; + //! A structure holding a map from type_indices to output serializer functions template struct OutputBindingMap { @@ -89,19 +91,61 @@ namespace cereal std::map map; }; + //! A structure holding a map from type name strings to input serializer functions + template + struct InputBindingMap + { + //! A serializer function + typedef std::function & )> SharedSerializer; + typedef std::function & )> UniqueSerializer; + + //! Struct containing the serializer functions for all pointer types + struct Serializers + { + SharedSerializer shared_ptr; //!< Serializer function for shared/weak pointers + UniqueSerializer unique_ptr; //!< Serializer function for unique pointers + }; + + //! A map of serializers for pointers of all registered types + std::map map; + }; + //! An empty noop deleter template struct EmptyDeleter { void operator()(T *) const {} }; struct InputArchiveBase; struct OutputArchiveBase; - template struct InputBinding + template struct InputBindingCreator { + InputBindingCreator() + { + typename InputBindingMap::Serializers serializers; + + serializers.shared_ptr = + [](void * arptr, std::shared_ptr & dptr) + { + Archive & ar = *static_cast(arptr); + std::shared_ptr ptr; + + ar( make_ptr_wrapper(ptr) ); + + dptr = ptr; + + }; + + serializers.unique_ptr = + [](void * arptr, std::unique_ptr & dptr) + { + }; + + StaticObject>::getInstance().map.insert( { std::string(binding_name::name()), serializers } ); + } }; - template struct OutputBinding + template struct OutputBindingCreator { - OutputBinding( ) + OutputBindingCreator() { typename OutputBindingMap::Serializers serializers; @@ -111,6 +155,20 @@ namespace cereal Archive & ar = *static_cast(arptr); std::shared_ptr const ptr(static_cast(dptr), EmptyDeleter()); + // Register the polymorphic type name with the archive, and get the id + char const * name = binding_name::name(); + std::uint32_t id = ar.registerPolymorphicType(name); + + // Serialize the id + ar( id ); + + // If the msb of the id is 1, then the type name is new, and we should serialize it + if( id & detail::msb_32bit ) + { + std::string namestring(name); + ar(namestring); + } + ar( detail::make_ptr_wrapper(ptr) ); }; @@ -135,16 +193,16 @@ namespace cereal template struct create_bindings { - static const InputBinding & + static const InputBindingCreator & load(std::true_type) { - return cereal::detail::StaticObject>::getInstance(); + return cereal::detail::StaticObject>::getInstance(); } - static const OutputBinding & + static const OutputBindingCreator & save(std::true_type) { - return cereal::detail::StaticObject>::getInstance(); + return cereal::detail::StaticObject>::getInstance(); } inline static void load(std::false_type) {} diff --git a/include/cereal/details/static_object.hpp b/include/cereal/details/static_object.hpp index 87d767e2..257e9367 100644 --- a/include/cereal/details/static_object.hpp +++ b/include/cereal/details/static_object.hpp @@ -27,31 +27,6 @@ #ifndef CEREAL_DETAILS_STATIC_OBJECT_HPP_ #define CEREAL_DETAILS_STATIC_OBJECT_HPP_ -#include -#include -#include - -namespace cereal -{ - inline std::string demangle(std::string mangledName) - { - int status = 0; - char *demangledName = NULL; - std::size_t len; - - demangledName = abi::__cxa_demangle(mangledName.c_str(), 0, &len, &status); - - std::string retName(demangledName); - free(demangledName); - - return retName; - } - - template inline - std::string demangledName() - { return demangle(typeid(T).name()); } -} - namespace cereal { namespace detail @@ -74,7 +49,6 @@ namespace cereal static T & create() { static T t; - std::cout << demangledName() << std::endl; instantiate(instance); return t; } diff --git a/include/cereal/details/util.hpp b/include/cereal/details/util.hpp new file mode 100644 index 00000000..c7f18ba6 --- /dev/null +++ b/include/cereal/details/util.hpp @@ -0,0 +1,58 @@ +/* + Copyright (c) 2013, 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 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_DETAILS_UTIL_HPP_ +#define CEREAL_DETAILS_UTIL_HPP_ + +#include +#include +#include + +namespace cereal +{ + namespace util + { + inline std::string demangle(std::string mangledName) + { + int status = 0; + char *demangledName = NULL; + std::size_t len; + + demangledName = abi::__cxa_demangle(mangledName.c_str(), 0, &len, &status); + + std::string retName(demangledName); + free(demangledName); + + return retName; + } + + template inline + std::string demangledName() + { return demangle(typeid(T).name()); } + } +} + +#endif // CEREAL_DETAILS_UTIL_HPP_ diff --git a/include/cereal/types/polymorphic.hpp b/include/cereal/types/polymorphic.hpp index d9932d20..07887ce0 100644 --- a/include/cereal/types/polymorphic.hpp +++ b/include/cereal/types/polymorphic.hpp @@ -30,14 +30,19 @@ #include #include #include +#include -#define CEREAL_REGISTER_TYPE(T) \ - namespace cereal { \ - namespace detail { \ - template <> \ - struct binding_name \ - { static const char * name = #T; }; \ - } } /* end namespaces */ \ +#include + +#define CEREAL_REGISTER_TYPE(T) \ + namespace cereal { \ + namespace detail { \ + template <> \ + struct binding_name \ + { \ + static constexpr char const * name() { return #T; }; \ + }; \ + } } /* end namespaces */ \ CEREAL_BIND_TO_ARCHIVES(T); #define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name)\ @@ -45,7 +50,7 @@ namespace detail { \ template <> \ struct binding_name \ - { static const char * name = #Name; }; \ + { static constexpr char const * name() { return Name; }; }; \ } } /* end namespaces */ \ CEREAL_BIND_TO_ARCHIVES(T); @@ -58,7 +63,8 @@ namespace cereal { if(!ptr) { - //ar (detail::null_ptr()); + // same behavior as nullptr in memory implementation + ar( std::uint32_t(0) ); return; } @@ -66,16 +72,45 @@ namespace cereal auto binding = bindingMap.find(std::type_index(typeid(*ptr.get()))); if(binding == bindingMap.end()) - throw cereal::Exception("Trying to serialize unregistered polymorphic type"); + throw cereal::Exception("Trying to save an unregistered polymorphic type (" + cereal::util::demangle(typeid(*ptr.get()).name()) + ")"); binding->second.shared_ptr(&ar, ptr.get()); } - //! Loading std::shared_ptr, case when user load and allocate for polymorphic types + //! Loading std::shared_ptr for polymorphic types template inline typename std::enable_if::value, void>::type load( Archive & ar, std::shared_ptr & ptr ) { + std::uint32_t nameid; + ar( nameid ); + + if(nameid == 0) + { + ptr.reset(); + return; + } + + std::string name; + if(nameid & detail::msb_32bit) + { + ar( name ); + ar.registerPolymorphicName(nameid, name); + } + else + { + name = ar.getPolymorphicName(nameid); + } + + auto & bindingMap = detail::StaticObject>::getInstance().map; + + auto binding = bindingMap.find(name); + if(binding == bindingMap.end()) + throw cereal::Exception("Trying to load an unregistered polymorphic type (" + name + ")"); + + std::shared_ptr result; + binding->second.shared_ptr(&ar, result); + ptr = std::static_pointer_cast(result); } //! Saving std::weak_ptr for polymorphic types @@ -83,6 +118,13 @@ namespace cereal typename std::enable_if::value, void>::type save( Archive & ar, std::weak_ptr const & ptr ) { + if(!ptr) + { + // same behavior as nullptr in memory implementation + ar( std::uint32_t(0) ); + return; + } + } //! Loading std::weak_ptr for polymorphic types @@ -97,6 +139,13 @@ namespace cereal typename std::enable_if::value, void>::type save( Archive & ar, std::unique_ptr const & ptr ) { + if(!ptr) + { + // same behavior as nullptr in memory implementation + ar( std::uint32_t(0) ); + return; + } + } //! Loading std::unique_ptr, case when user provides load_and_allocate for polymorphic types diff --git a/sandbox_rtti.cpp b/sandbox_rtti.cpp index 304bcaac..bf0439d0 100644 --- a/sandbox_rtti.cpp +++ b/sandbox_rtti.cpp @@ -29,15 +29,23 @@ #include #include #include +#include +#include struct Base { virtual void foo() = 0; template - void serialize(Archive & ar) + void save(Archive & ar) const { - std::cout << "Serializing Base" << std::endl; + std::cout << "Saving Base" << std::endl; + } + + template + void load(Archive & ar) + { + std::cout << "Loading Base" << std::endl; } }; @@ -46,24 +54,44 @@ struct MyType : public Base void foo() {} template - void serialize(Archive & ar) + void save(Archive & ar) const { - std::cout << "Serializing MyType" << std::endl; + std::cout << "Saving MyType" << std::endl; + } + + template + void load(Archive & ar) + { + std::cout << "Loading MyType" << std::endl; } }; -CEREAL_BIND_TO_ARCHIVES(MyType); +CEREAL_REGISTER_TYPE(MyType); +//CEREAL_REGISTER_TYPE_WITH_NAME(MyType, "cool beans"); template void nop(T&&t) {} int main() { - std::stringstream stream; - cereal::BinaryOutputArchive archive(stream); + { + std::ofstream ostream("rtti.txt"); + cereal::BinaryOutputArchive oarchive(ostream); - std::shared_ptr ptr = std::make_shared(); - archive(ptr); + std::shared_ptr ptr = std::make_shared(); + + oarchive(ptr); + } + + + + { + std::ifstream istream("rtti.txt"); + cereal::BinaryInputArchive iarchive(istream); + + std::shared_ptr ptr; + std::shared_ptr ptr2; + + iarchive(ptr); + } - std::unique_ptr xxx(nullptr); - archive(xxx); }