Loading polymorphig shared_ptrs works

This commit is contained in:
Randolph Voorhies
2013-06-28 17:23:30 -07:00
parent 3c9554f8a2
commit d9493f02ca
6 changed files with 272 additions and 66 deletions

View File

@@ -167,11 +167,11 @@ namespace cereal
} } // end namespaces } } // end namespaces
//! The base output archive class //! The base output archive class
template<class ArchiveType, uint32_t Flags = 0> template<class ArchiveType, std::uint32_t Flags = 0>
class OutputArchive : public detail::OutputArchiveBase class OutputArchive : public detail::OutputArchiveBase
{ {
public: public:
OutputArchive(ArchiveType * const self) : self(self), itsCurrentPointerId(1) OutputArchive(ArchiveType * const self) : self(self), itsCurrentPointerId(1), itsCurrentPolymorphicTypeId(1)
{ } { }
//! Serializes all passed in data //! Serializes all passed in data
@@ -183,7 +183,7 @@ namespace cereal
} }
//! Registers a pointer with the archive //! 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 // Handle null pointers by just returning 0
if(addr == 0) return 0; if(addr == 0) return 0;
@@ -199,6 +199,20 @@ namespace cereal
return id->second; 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: private:
template <class T> inline template <class T> inline
void process( T && head ) void process( T && head )
@@ -307,15 +321,21 @@ namespace cereal
std::unordered_set<traits::detail::base_class_id, traits::detail::base_class_id_hash> itsBaseClassSet; std::unordered_set<traits::detail::base_class_id, traits::detail::base_class_id_hash> itsBaseClassSet;
//! Maps from addresses to pointer ids //! Maps from addresses to pointer ids
std::unordered_map<void const *, std::size_t> itsSharedPointerMap; std::unordered_map<void const *, std::uint32_t> itsSharedPointerMap;
//! The id to be given to the next pointer //! 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<char const *, std::uint32_t> itsPolymorphicTypeMap;
//! The id to be given to the next polymorphic type name
std::uint32_t itsCurrentPolymorphicTypeId;
}; // class OutputArchive }; // class OutputArchive
// ###################################################################### // ######################################################################
//! The base input archive class //! The base input archive class
template<class ArchiveType, uint32_t Flags = 0> template<class ArchiveType, std::uint32_t Flags = 0>
class InputArchive : public detail::InputArchiveBase class InputArchive : public detail::InputArchiveBase
{ {
public: public:
@@ -329,7 +349,7 @@ namespace cereal
return *self; return *self;
} }
std::shared_ptr<void> getSharedPointer(uint32_t const id) std::shared_ptr<void> getSharedPointer(std::uint32_t const id)
{ {
if(id == 0) return std::shared_ptr<void>(nullptr); if(id == 0) return std::shared_ptr<void>(nullptr);
@@ -341,12 +361,27 @@ namespace cereal
return ptr->second; return ptr->second;
} }
void registerSharedPointer(uint32_t const id, std::shared_ptr<void> ptr) void registerSharedPointer(std::uint32_t const id, std::shared_ptr<void> 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} ); 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: private:
template <class T> inline template <class T> inline
@@ -455,8 +490,12 @@ namespace cereal
//! A set of all base classes that have been serialized //! A set of all base classes that have been serialized
std::unordered_set<traits::detail::base_class_id, traits::detail::base_class_id_hash> itsBaseClassSet; std::unordered_set<traits::detail::base_class_id, traits::detail::base_class_id_hash> itsBaseClassSet;
//! Maps from addresses to pointer ids //! Maps from pointer ids to addresses
std::unordered_map<std::size_t, std::shared_ptr<void>> itsSharedPointerMap; std::unordered_map<std::uint32_t, std::shared_ptr<void>> itsSharedPointerMap;
//! Maps from name ids to names
std::unordered_map<std::uint32_t, std::string> itsPolymorphicTypeMap;
}; // class InputArchive }; // class InputArchive
} // namespace cereal } // namespace cereal

View File

@@ -43,6 +43,7 @@
#include <cereal/details/static_object.hpp> #include <cereal/details/static_object.hpp>
#include <cereal/types/memory.hpp> #include <cereal/types/memory.hpp>
#include <cereal/types/string.hpp>
#include <typeindex> #include <typeindex>
#include <map> #include <map>
@@ -72,6 +73,7 @@ namespace cereal
template <class T> template <class T>
struct binding_name {}; struct binding_name {};
//! A structure holding a map from type_indices to output serializer functions
template <class Archive> template <class Archive>
struct OutputBindingMap struct OutputBindingMap
{ {
@@ -89,19 +91,61 @@ namespace cereal
std::map<std::type_index, Serializers> map; std::map<std::type_index, Serializers> map;
}; };
//! A structure holding a map from type name strings to input serializer functions
template <class Archive>
struct InputBindingMap
{
//! A serializer function
typedef std::function<void(void*, std::shared_ptr<void> & )> SharedSerializer;
typedef std::function<void(void*, std::unique_ptr<void> & )> 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<std::string, Serializers> map;
};
//! An empty noop deleter //! An empty noop deleter
template<class T> struct EmptyDeleter { void operator()(T *) const {} }; template<class T> struct EmptyDeleter { void operator()(T *) const {} };
struct InputArchiveBase; struct InputArchiveBase;
struct OutputArchiveBase; struct OutputArchiveBase;
template <class Archive, class T> struct InputBinding template <class Archive, class T> struct InputBindingCreator
{ {
InputBindingCreator()
{
typename InputBindingMap<Archive>::Serializers serializers;
serializers.shared_ptr =
[](void * arptr, std::shared_ptr<void> & dptr)
{
Archive & ar = *static_cast<Archive*>(arptr);
std::shared_ptr<T> ptr;
ar( make_ptr_wrapper(ptr) );
dptr = ptr;
};
serializers.unique_ptr =
[](void * arptr, std::unique_ptr<void> & dptr)
{
};
StaticObject<InputBindingMap<Archive>>::getInstance().map.insert( { std::string(binding_name<T>::name()), serializers } );
}
}; };
template <class Archive, class T> struct OutputBinding template <class Archive, class T> struct OutputBindingCreator
{ {
OutputBinding( ) OutputBindingCreator()
{ {
typename OutputBindingMap<Archive>::Serializers serializers; typename OutputBindingMap<Archive>::Serializers serializers;
@@ -111,6 +155,20 @@ namespace cereal
Archive & ar = *static_cast<Archive*>(arptr); Archive & ar = *static_cast<Archive*>(arptr);
std::shared_ptr<T const> const ptr(static_cast<T const *>(dptr), EmptyDeleter<T const>()); std::shared_ptr<T const> const ptr(static_cast<T const *>(dptr), EmptyDeleter<T const>());
// Register the polymorphic type name with the archive, and get the id
char const * name = binding_name<T>::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) ); ar( detail::make_ptr_wrapper(ptr) );
}; };
@@ -135,16 +193,16 @@ namespace cereal
template <class Archive, class T> template <class Archive, class T>
struct create_bindings struct create_bindings
{ {
static const InputBinding<Archive, T> & static const InputBindingCreator<Archive, T> &
load(std::true_type) load(std::true_type)
{ {
return cereal::detail::StaticObject<InputBinding<Archive, T>>::getInstance(); return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance();
} }
static const OutputBinding<Archive, T> & static const OutputBindingCreator<Archive, T> &
save(std::true_type) save(std::true_type)
{ {
return cereal::detail::StaticObject<OutputBinding<Archive, T>>::getInstance(); return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance();
} }
inline static void load(std::false_type) {} inline static void load(std::false_type) {}

View File

@@ -27,31 +27,6 @@
#ifndef CEREAL_DETAILS_STATIC_OBJECT_HPP_ #ifndef CEREAL_DETAILS_STATIC_OBJECT_HPP_
#define CEREAL_DETAILS_STATIC_OBJECT_HPP_ #define CEREAL_DETAILS_STATIC_OBJECT_HPP_
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
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<class T> inline
std::string demangledName()
{ return demangle(typeid(T).name()); }
}
namespace cereal namespace cereal
{ {
namespace detail namespace detail
@@ -74,7 +49,6 @@ namespace cereal
static T & create() static T & create()
{ {
static T t; static T t;
std::cout << demangledName<T>() << std::endl;
instantiate(instance); instantiate(instance);
return t; return t;
} }

View File

@@ -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 <COPYRIGHT HOLDER> 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 <typeinfo>
#include <cxxabi.h>
#include <string>
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<class T> inline
std::string demangledName()
{ return demangle(typeid(T).name()); }
}
}
#endif // CEREAL_DETAILS_UTIL_HPP_

View File

@@ -30,14 +30,19 @@
#include <cereal/cereal.hpp> #include <cereal/cereal.hpp>
#include <cereal/types/memory.hpp> #include <cereal/types/memory.hpp>
#include <cereal/details/polymorphic_impl.hpp> #include <cereal/details/polymorphic_impl.hpp>
#include <cereal/details/util.hpp>
#define CEREAL_REGISTER_TYPE(T) \ #include <iostream>
namespace cereal { \
namespace detail { \ #define CEREAL_REGISTER_TYPE(T) \
template <> \ namespace cereal { \
struct binding_name<T> \ namespace detail { \
{ static const char * name = #T; }; \ template <> \
} } /* end namespaces */ \ struct binding_name<T> \
{ \
static constexpr char const * name() { return #T; }; \
}; \
} } /* end namespaces */ \
CEREAL_BIND_TO_ARCHIVES(T); CEREAL_BIND_TO_ARCHIVES(T);
#define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name)\ #define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name)\
@@ -45,7 +50,7 @@
namespace detail { \ namespace detail { \
template <> \ template <> \
struct binding_name<T> \ struct binding_name<T> \
{ static const char * name = #Name; }; \ { static constexpr char const * name() { return Name; }; }; \
} } /* end namespaces */ \ } } /* end namespaces */ \
CEREAL_BIND_TO_ARCHIVES(T); CEREAL_BIND_TO_ARCHIVES(T);
@@ -58,7 +63,8 @@ namespace cereal
{ {
if(!ptr) if(!ptr)
{ {
//ar (detail::null_ptr()); // same behavior as nullptr in memory implementation
ar( std::uint32_t(0) );
return; return;
} }
@@ -66,16 +72,45 @@ namespace cereal
auto binding = bindingMap.find(std::type_index(typeid(*ptr.get()))); auto binding = bindingMap.find(std::type_index(typeid(*ptr.get())));
if(binding == bindingMap.end()) 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()); 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 <class Archive, class T> inline template <class Archive, class T> inline
typename std::enable_if<std::is_polymorphic<T>::value, void>::type typename std::enable_if<std::is_polymorphic<T>::value, void>::type
load( Archive & ar, std::shared_ptr<T> & ptr ) load( Archive & ar, std::shared_ptr<T> & 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<detail::InputBindingMap<Archive>>::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<void> result;
binding->second.shared_ptr(&ar, result);
ptr = std::static_pointer_cast<T>(result);
} }
//! Saving std::weak_ptr for polymorphic types //! Saving std::weak_ptr for polymorphic types
@@ -83,6 +118,13 @@ namespace cereal
typename std::enable_if<std::is_polymorphic<T>::value, void>::type typename std::enable_if<std::is_polymorphic<T>::value, void>::type
save( Archive & ar, std::weak_ptr<T> const & ptr ) save( Archive & ar, std::weak_ptr<T> const & ptr )
{ {
if(!ptr)
{
// same behavior as nullptr in memory implementation
ar( std::uint32_t(0) );
return;
}
} }
//! Loading std::weak_ptr for polymorphic types //! Loading std::weak_ptr for polymorphic types
@@ -97,6 +139,13 @@ namespace cereal
typename std::enable_if<std::is_polymorphic<T>::value, void>::type typename std::enable_if<std::is_polymorphic<T>::value, void>::type
save( Archive & ar, std::unique_ptr<T, D> const & ptr ) save( Archive & ar, std::unique_ptr<T, D> 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 //! Loading std::unique_ptr, case when user provides load_and_allocate for polymorphic types

View File

@@ -29,15 +29,23 @@
#include <cereal/archives/binary.hpp> #include <cereal/archives/binary.hpp>
#include <cereal/types/polymorphic.hpp> #include <cereal/types/polymorphic.hpp>
#include <sstream> #include <sstream>
#include <fstream>
#include <iostream>
struct Base struct Base
{ {
virtual void foo() = 0; virtual void foo() = 0;
template<class Archive> template<class Archive>
void serialize(Archive & ar) void save(Archive & ar) const
{ {
std::cout << "Serializing Base" << std::endl; std::cout << "Saving Base" << std::endl;
}
template<class Archive>
void load(Archive & ar)
{
std::cout << "Loading Base" << std::endl;
} }
}; };
@@ -46,24 +54,44 @@ struct MyType : public Base
void foo() {} void foo() {}
template<class Archive> template<class Archive>
void serialize(Archive & ar) void save(Archive & ar) const
{ {
std::cout << "Serializing MyType" << std::endl; std::cout << "Saving MyType" << std::endl;
}
template<class Archive>
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 <class T> void nop(T&&t) {} template <class T> void nop(T&&t) {}
int main() int main()
{ {
std::stringstream stream; {
cereal::BinaryOutputArchive archive(stream); std::ofstream ostream("rtti.txt");
cereal::BinaryOutputArchive oarchive(ostream);
std::shared_ptr<Base> ptr = std::make_shared<MyType>(); std::shared_ptr<Base> ptr = std::make_shared<MyType>();
archive(ptr);
oarchive(ptr);
}
{
std::ifstream istream("rtti.txt");
cereal::BinaryInputArchive iarchive(istream);
std::shared_ptr<Base> ptr;
std::shared_ptr<Base> ptr2;
iarchive(ptr);
}
std::unique_ptr<int> xxx(nullptr);
archive(xxx);
} }