polymorphic.hpp
Go to the documentation of this file.
/*
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_TYPES_POLYMORPHIC_HPP_
#define CEREAL_TYPES_POLYMORPHIC_HPP_
#include <cereal/cereal.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/details/util.hpp>
#include <cereal/details/helpers.hpp>
#include <cereal/details/traits.hpp>
#include <cereal/details/polymorphic_impl.hpp>
#ifdef _MSC_VER
#define STATIC_CONSTEXPR static
#else
#define STATIC_CONSTEXPR static constexpr
#endif
#define CEREAL_REGISTER_TYPE(T) \
namespace cereal { \
namespace detail { \
template <> \
struct binding_name<T> \
{ \
STATIC_CONSTEXPR char const * name() { return #T; } \
}; \
} } /* end namespaces */ \
CEREAL_BIND_TO_ARCHIVES(T)
#define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name) \
namespace cereal { \
namespace detail { \
template <> \
struct binding_name<T> \
{ STATIC_CONSTEXPR char const * name() { return Name; } }; \
} } /* end namespaces */ \
CEREAL_BIND_TO_ARCHIVES(T)
#define CEREAL_REGISTER_DYNAMIC_INIT(LibName) \
namespace cereal { \
namespace detail { \
void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName() {} \
} } /* end namespaces */
#define CEREAL_FORCE_DYNAMIC_INIT(LibName) \
namespace cereal { \
namespace detail { \
void dynamic_init_dummy_##LibName(); \
} /* end detail */ \
namespace { \
void dynamic_init_##LibName() \
{ \
::cereal::detail::dynamic_init_dummy_##LibName(); \
} \
} } /* end namespaces */
#ifdef _MSC_VER
#undef CONSTEXPR
#endif
namespace cereal
{
namespace polymorphic_detail
{
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name) \
throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n" \
"Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive " \
"you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n" \
"If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT.");
template<class Archive> inline
typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
{
// If the nameid is zero, we serialized a null pointer
if(nameid == 0)
{
typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr) { ptr.reset(); };
emptySerializers.unique_ptr = [](void*, std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> & ptr) { ptr.reset( nullptr ); };
return emptySerializers;
}
std::string name;
if(nameid & detail::msb_32bit)
{
ar( CEREAL_NVP_("polymorphic_name", 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())
UNREGISTERED_POLYMORPHIC_EXCEPTION(load, name)
return binding->second;
}
template<class Archive, class T> inline
typename std::enable_if<(traits::is_default_constructible<T>::value
|| traits::has_load_and_construct<T, Archive>::value)
&& !std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
{
if(nameid & detail::msb2_32bit)
{
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
return true;
}
return false;
}
template<class Archive, class T, class D> inline
typename std::enable_if<(traits::is_default_constructible<T>::value
|| traits::has_load_and_construct<T, Archive>::value)
&& !std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
{
if(nameid & detail::msb2_32bit)
{
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
return true;
}
return false;
}
template<class Archive, class T> inline
typename std::enable_if<(!traits::is_default_constructible<T>::value
&& !traits::has_load_and_construct<T, Archive>::value)
|| std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
{
if(nameid & detail::msb2_32bit)
throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
return false;
}
template<class Archive, class T, class D> inline
typename std::enable_if<(!traits::is_default_constructible<T>::value
&& !traits::has_load_and_construct<T, Archive>::value)
|| std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
{
if(nameid & detail::msb2_32bit)
throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
return false;
}
} // polymorphic_detail
// ######################################################################
// Pointer serialization for polymorphic types
template <class Archive, class T> inline
typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
{
if(!ptr)
{
// same behavior as nullptr in memory implementation
ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
return;
}
std::type_info const & ptrinfo = typeid(*ptr.get());
// ptrinfo can never be equal to T info since we can't have an instance
// of an abstract object
// this implies we need to do the lookup
auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
auto binding = bindingMap.find(std::type_index(ptrinfo));
if(binding == bindingMap.end())
UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
binding->second.shared_ptr(&ar, ptr.get());
}
template <class Archive, class T> inline
typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
{
if(!ptr)
{
// same behavior as nullptr in memory implementation
ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
return;
}
std::type_info const & ptrinfo = typeid(*ptr.get());
static std::type_info const & tinfo = typeid(T);
if(ptrinfo == tinfo)
{
// The 2nd msb signals that the following pointer does not need to be
// cast with our polymorphic machinery
ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
return;
}
auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
auto binding = bindingMap.find(std::type_index(ptrinfo));
if(binding == bindingMap.end())
UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
binding->second.shared_ptr(&ar, ptr.get());
}
template <class Archive, class T> inline
typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
{
std::uint32_t nameid;
ar( CEREAL_NVP_("polymorphic_id", nameid) );
// Check to see if we can skip all of this polymorphism business
if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
return;
auto binding = polymorphic_detail::getInputBinding(ar, nameid);
std::shared_ptr<void> result;
binding.shared_ptr(&ar, result);
ptr = std::static_pointer_cast<T>(result);
}
template <class Archive, class T> inline
typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
{
auto const sptr = ptr.lock();
ar( CEREAL_NVP_("locked_ptr", sptr) );
}
template <class Archive, class T> inline
typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
{
std::shared_ptr<T> sptr;
ar( CEREAL_NVP_("locked_ptr", sptr) );
ptr = sptr;
}
template <class Archive, class T, class D> inline
typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
{
if(!ptr)
{
// same behavior as nullptr in memory implementation
ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
return;
}
std::type_info const & ptrinfo = typeid(*ptr.get());
// ptrinfo can never be equal to T info since we can't have an instance
// of an abstract object
// this implies we need to do the lookup
auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
auto binding = bindingMap.find(std::type_index(ptrinfo));
if(binding == bindingMap.end())
UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
binding->second.unique_ptr(&ar, ptr.get());
}
template <class Archive, class T, class D> inline
typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
{
if(!ptr)
{
// same behavior as nullptr in memory implementation
ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
return;
}
std::type_info const & ptrinfo = typeid(*ptr.get());
static std::type_info const & tinfo = typeid(T);
if(ptrinfo == tinfo)
{
// The 2nd msb signals that the following pointer does not need to be
// cast with our polymorphic machinery
ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
return;
}
auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
auto binding = bindingMap.find(std::type_index(ptrinfo));
if(binding == bindingMap.end())
UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
binding->second.unique_ptr(&ar, ptr.get());
}
template <class Archive, class T, class D> inline
typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
{
std::uint32_t nameid;
ar( CEREAL_NVP_("polymorphic_id", nameid) );
// Check to see if we can skip all of this polymorphism business
if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
return;
auto binding = polymorphic_detail::getInputBinding(ar, nameid);
std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
binding.unique_ptr(&ar, result);
ptr.reset(static_cast<T*>(result.release()));
}
#undef UNREGISTERED_POLYMORPHIC_EXCEPTION
} // namespace cereal
#endif // CEREAL_TYPES_POLYMORPHIC_HPP_
cereal::traits::has_load_and_construct
Has either a member or non member allocate.
Definition: traits.hpp:1192
CEREAL_NVP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:197
cereal::polymorphic_detail::serialize_wrapper
std::enable_if<(traits::is_default_constructible< T >::value||traits::has_load_and_construct< T, Archive >::value)&&!std::is_abstract< T >::value, bool >::type serialize_wrapper(Archive &ar, std::shared_ptr< T > &ptr, std::uint32_t const nameid)
Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the poin...
Definition: polymorphic.hpp:217
cereal::polymorphic_detail::getInputBinding
typename::cereal::detail::InputBindingMap< Archive >::Serializers getInputBinding(Archive &ar, std::uint32_t const nameid)
Get an input binding from the given archive by deserializing the type meta data.
Definition: polymorphic.hpp:178
UNREGISTERED_POLYMORPHIC_EXCEPTION
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)
Error message used for unregistered polymorphic types.
Definition: polymorphic.hpp:169
traits.hpp
Internal type trait support.
helpers.hpp
Internal helper functionality.
cereal
Definition: access.hpp:39
util.hpp
Internal misc utilities.
cereal.hpp
Main cereal functionality.
CEREAL_LOAD_FUNCTION_NAME
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:58
polymorphic_impl.hpp
Internal polymorphism support.
CEREAL_SAVE_FUNCTION_NAME
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:65
cereal::detail::StaticObject
A static, pre-execution object.
Definition: static_object.hpp:61
memory.hpp
Support for types found in <memory>
cereal::Exception
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48