polymorphic_impl.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.
*/
/* This code is heavily inspired by the boost serialization implementation by the following authors
(C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
See http://www.boost.org for updates, documentation, and revision history.
(C) Copyright 2006 David Abrahams - http://www.boost.org.
See /boost/serialization/export.hpp and /boost/archive/detail/register_archive.hpp for their
implementation.
*/
#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
#define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
#include <cereal/details/static_object.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/string.hpp>
#include <functional>
#include <typeindex>
#include <map>
#define CEREAL_BIND_TO_ARCHIVES(T) \
namespace cereal { \
namespace detail { \
template<> \
struct init_binding<T> { \
static bind_to_archives<T> const & b; \
static void unused() { (void)b; } \
}; \
bind_to_archives<T> const & init_binding<T>::b = \
::cereal::detail::StaticObject< \
bind_to_archives<T> \
>::getInstance().bind(); \
}} /* end namespaces */
namespace cereal
{
namespace detail
{
template <class T>
struct binding_name {};
template <class Archive>
struct OutputBindingMap
{
typedef std::function<void(void*, void const *)> Serializer;
struct Serializers
{
Serializer shared_ptr,
unique_ptr;
};
std::map<std::type_index, Serializers> map;
};
template<class T> struct EmptyDeleter { void operator()(T *) const {} };
template <class Archive>
struct InputBindingMap
{
typedef std::function<void(void*, std::shared_ptr<void> & )> SharedSerializer;
typedef std::function<void(void*, std::unique_ptr<void, EmptyDeleter<void>> & )> UniqueSerializer;
struct Serializers
{
SharedSerializer shared_ptr;
UniqueSerializer unique_ptr;
};
std::map<std::string, Serializers> map;
};
// forward decls for archives from cereal.hpp
class InputArchiveBase;
class OutputArchiveBase;
template <class Archive, class T> struct InputBindingCreator
{
InputBindingCreator()
{
auto & map = StaticObject<InputBindingMap<Archive>>::getInstance().map;
auto key = std::string(binding_name<T>::name());
auto lb = map.lower_bound(key);
if (lb != map.end() && lb->first == key)
return;
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( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
dptr = ptr;
};
serializers.unique_ptr =
[](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr)
{
Archive & ar = *static_cast<Archive*>(arptr);
std::unique_ptr<T> ptr;
ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
dptr.reset(ptr.release());
};
map.insert( lb, { std::move(key), std::move(serializers) } );
}
};
template <class Archive, class T> struct OutputBindingCreator
{
static void writeMetadata(Archive & ar)
{
// 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( CEREAL_NVP_("polymorphic_id", 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( CEREAL_NVP_("polymorphic_name", namestring) );
}
}
class PolymorphicSharedPointerWrapper
{
public:
PolymorphicSharedPointerWrapper( void const * dptr ) : refCount()
{
#ifdef _LIBCPP_VERSION
// libc++ needs this hacky workaround, see http://llvm.org/bugs/show_bug.cgi?id=18843
wrappedPtr = std::shared_ptr<T const>(
std::const_pointer_cast<T const>(
std::shared_ptr<T>( refCount, static_cast<T *>(const_cast<void *>(dptr) ))));
#else // NOT _LIBCPP_VERSION
wrappedPtr = std::shared_ptr<T const>( refCount, static_cast<T const *>(dptr) );
#endif // _LIBCPP_VERSION
}
inline std::shared_ptr<T const> const & operator()() const { return wrappedPtr; }
private:
std::shared_ptr<void> refCount;
std::shared_ptr<T const> wrappedPtr;
};
static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::true_type /* has_shared_from_this */ )
{
::cereal::memory_detail::EnableSharedStateHelper<T> state( static_cast<T *>(const_cast<void *>(dptr)) );
PolymorphicSharedPointerWrapper psptr( dptr );
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
}
static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ )
{
PolymorphicSharedPointerWrapper psptr( dptr );
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
}
OutputBindingCreator()
{
auto & map = StaticObject<OutputBindingMap<Archive>>::getInstance().map;
auto key = std::type_index(typeid(T));
auto lb = map.lower_bound(key);
if (lb != map.end() && lb->first == key)
return;
typename OutputBindingMap<Archive>::Serializers serializers;
serializers.shared_ptr =
[&](void * arptr, void const * dptr)
{
Archive & ar = *static_cast<Archive*>(arptr);
writeMetadata(ar);
#ifdef _MSC_VER
savePolymorphicSharedPtr( ar, dptr, ::cereal::traits::has_shared_from_this<T>::type() ); // MSVC doesn't like typename here
#else // not _MSC_VER
savePolymorphicSharedPtr( ar, dptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
#endif // _MSC_VER
};
serializers.unique_ptr =
[&](void * arptr, void const * dptr)
{
Archive & ar = *static_cast<Archive*>(arptr);
writeMetadata(ar);
std::unique_ptr<T const, EmptyDeleter<T const>> const ptr(static_cast<T const *>(dptr));
ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
};
map.insert( { std::move(key), std::move(serializers) } );
}
};
struct adl_tag {};
namespace { struct polymorphic_binding_tag {}; }
template <class Archive, class T>
struct create_bindings
{
static const InputBindingCreator<Archive, T> &
load(std::true_type)
{
return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance();
}
static const OutputBindingCreator<Archive, T> &
save(std::true_type)
{
return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance();
}
inline static void load(std::false_type) {}
inline static void save(std::false_type) {}
};
template <void(*)()>
struct instantiate_function {};
template <class Archive, class T>
struct polymorphic_serialization_support
{
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
virtual CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
#else // NOT _MSC_VER
static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
typedef instantiate_function<instantiate> unused;
#endif // _MSC_VER
};
// instantiate implementation
template <class Archive, class T>
CEREAL_DLL_EXPORT void polymorphic_serialization_support<Archive,T>::instantiate()
{
create_bindings<Archive,T>::save( std::integral_constant<bool,
std::is_base_of<detail::OutputArchiveBase, Archive>::value &&
traits::is_output_serializable<T, Archive>::value>{} );
create_bindings<Archive,T>::load( std::integral_constant<bool,
std::is_base_of<detail::InputArchiveBase, Archive>::value &&
traits::is_input_serializable<T, Archive>::value>{} );
}
template <class T, class Tag = polymorphic_binding_tag>
struct bind_to_archives
{
void bind(std::false_type) const
{
instantiate_polymorphic_binding((T*) 0, 0, Tag{}, adl_tag{});
}
void bind(std::true_type) const
{ }
bind_to_archives const & bind() const
{
static_assert( std::is_polymorphic<T>::value,
"Attempting to register non polymorphic type" );
bind( std::is_abstract<T>() );
return *this;
}
};
template <class T, class Tag = polymorphic_binding_tag>
struct init_binding;
template <class T, typename BindingTag>
void instantiate_polymorphic_binding( T*, int, BindingTag, adl_tag ) {}
} // namespace detail
} // namespace cereal
#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
CEREAL_NVP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:197
cereal::detail::instantiate_polymorphic_binding
void instantiate_polymorphic_binding(T *, int, BindingTag, adl_tag)
Base case overload for instantiation.
Definition: polymorphic_impl.hpp:433
cereal::detail::OutputBindingCreator::savePolymorphicSharedPtr
static void savePolymorphicSharedPtr(Archive &ar, void const *dptr, std::true_type)
Does the actual work of saving a polymorphic shared_ptr.
Definition: polymorphic_impl.hpp:257
cereal::detail::polymorphic_serialization_support
Definition: helpers.hpp:232
cereal::detail::InputBindingCreator
Creates a binding (map entry) between an input archive type and a polymorphic type.
Definition: polymorphic_impl.hpp:148
cereal::detail::OutputBindingMap::Serializers
Struct containing the serializer functions for all pointer types.
Definition: polymorphic_impl.hpp:97
cereal::detail::instantiate_function
When specialized, causes the compiler to instantiate its parameter.
Definition: polymorphic_impl.hpp:350
cereal::detail::init_binding
Used to hide the static object used to bind T to registered archives.
Definition: polymorphic_impl.hpp:418
cereal::memory_detail::EnableSharedStateHelper
Definition: memory.hpp:117
cereal::detail::OutputBindingMap::Serializers::unique_ptr
Serializer unique_ptr
Serializer function for unique pointers.
Definition: polymorphic_impl.hpp:99
cereal::detail::OutputBindingCreator::PolymorphicSharedPointerWrapper
Holds a properly typed shared_ptr to the polymorphic type.
Definition: polymorphic_impl.hpp:214
cereal::detail::OutputBindingCreator::PolymorphicSharedPointerWrapper::operator()
std::shared_ptr< T const > const & operator()() const
Get the wrapped shared_ptr */.
Definition: polymorphic_impl.hpp:242
cereal::detail::bind_to_archives::bind
bind_to_archives const & bind() const
Binds the type T to all registered archives.
Definition: polymorphic_impl.hpp:407
cereal::traits::has_shared_from_this
Determine if T or any base class of T has inherited from std::enable_shared_from_this.
Definition: traits.hpp:1140
cereal::detail::bind_to_archives::bind
void bind(std::false_type) const
Binding for non abstract types.
Definition: polymorphic_impl.hpp:395
cereal
Definition: access.hpp:39
cereal::detail::OutputBindingMap::Serializers::shared_ptr
Serializer shared_ptr
Serializer function for shared/weak pointers.
Definition: polymorphic_impl.hpp:99
cereal::detail::InputBindingMap::Serializers::shared_ptr
SharedSerializer shared_ptr
Serializer function for shared/weak pointers.
Definition: polymorphic_impl.hpp:131
cereal::detail::InputBindingMap::Serializers
Struct containing the serializer functions for all pointer types.
Definition: polymorphic_impl.hpp:129
cereal::detail::InputBindingMap::UniqueSerializer
std::function< void(void *, std::unique_ptr< void, EmptyDeleter< void >> &)> UniqueSerializer
Unique ptr serializer function.
Definition: polymorphic_impl.hpp:126
cereal::detail::OutputBindingCreator::OutputBindingCreator
OutputBindingCreator()
Initialize the binding.
Definition: polymorphic_impl.hpp:279
cereal::detail::InputBindingMap::map
std::map< std::string, Serializers > map
A map of serializers for pointers of all registered types.
Definition: polymorphic_impl.hpp:136
cereal::detail::polymorphic_serialization_support::instantiate
static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED
Definition: polymorphic_impl.hpp:375
cereal::traits::is_output_serializable
Definition: traits.hpp:1035
cereal::detail::adl_tag
Definition: polymorphic_impl.hpp:322
cereal::detail::OutputBindingCreator
Creates a binding (map entry) between an output archive type and a polymorphic type.
Definition: polymorphic_impl.hpp:193
string.hpp
Support for types found in <string>
cereal::detail::create_bindings
Causes the static object bindings between an archive type and a serializable type T...
Definition: polymorphic_impl.hpp:330
cereal::detail::OutputBindingCreator::writeMetadata
static void writeMetadata(Archive &ar)
Writes appropriate metadata to the archive for this polymorphic type.
Definition: polymorphic_impl.hpp:196
cereal::detail::EmptyDeleter
An empty noop deleter.
Definition: polymorphic_impl.hpp:108
cereal::detail::InputBindingCreator::InputBindingCreator
InputBindingCreator()
Initialize the binding.
Definition: polymorphic_impl.hpp:151
cereal::detail::OutputBindingMap::map
std::map< std::type_index, Serializers > map
A map of serializers for pointers of all registered types.
Definition: polymorphic_impl.hpp:104
cereal::detail::polymorphic_serialization_support::unused
instantiate_function< instantiate > unused
This typedef causes the compiler to instantiate this static function.
Definition: polymorphic_impl.hpp:369
cereal::detail::binding_name
Binds a compile time type with a user defined string.
Definition: polymorphic_impl.hpp:79
cereal::detail::OutputBindingCreator::PolymorphicSharedPointerWrapper::PolymorphicSharedPointerWrapper
PolymorphicSharedPointerWrapper(void const *dptr)
Definition: polymorphic_impl.hpp:229
cereal::detail::OutputArchiveBase
Definition: helpers.hpp:228
cereal::detail::OutputBindingMap::Serializer
std::function< void(void *, void const *)> Serializer
A serializer function.
Definition: polymorphic_impl.hpp:94
CEREAL_DLL_EXPORT
#define CEREAL_DLL_EXPORT
Prevent link optimization from removing non-referenced static objects.
Definition: static_object.hpp:45
cereal::detail::InputBindingMap
A structure holding a map from type name strings to input serializer functions.
Definition: polymorphic_impl.hpp:116
cereal::detail::bind_to_archives
Begins the binding process of a type to all registered archives.
Definition: polymorphic_impl.hpp:392
cereal::detail::InputArchiveBase
Definition: helpers.hpp:229
cereal::detail::InputBindingMap::SharedSerializer
std::function< void(void *, std::shared_ptr< void > &)> SharedSerializer
Shared ptr serializer function.
Definition: polymorphic_impl.hpp:124
cereal::traits::is_input_serializable
Definition: traits.hpp:1062
cereal::detail::InputBindingMap::Serializers::unique_ptr
UniqueSerializer unique_ptr
Serializer function for unique pointers.
Definition: polymorphic_impl.hpp:132
cereal::detail::StaticObject
A static, pre-execution object.
Definition: static_object.hpp:61
cereal::detail::OutputBindingCreator::savePolymorphicSharedPtr
static void savePolymorphicSharedPtr(Archive &ar, void const *dptr, std::false_type)
Does the actual work of saving a polymorphic shared_ptr.
Definition: polymorphic_impl.hpp:272
cereal::detail::OutputBindingMap
A structure holding a map from type_indices to output serializer functions.
Definition: polymorphic_impl.hpp:87
memory.hpp
Support for types found in <memory>
static_object.hpp
Internal polymorphism static object support.
cereal::detail::bind_to_archives::bind
void bind(std::true_type) const
Binding for abstract types.
Definition: polymorphic_impl.hpp:401