First pass at inline doc for new polymorphic casting

Also some changes to make sandbox things work with the new casting

Made it so base_class doesn't need to know about polymorphism to work

All relates to #188
This commit is contained in:
Shane Grant 2015-12-22 23:39:25 -08:00
parent 480de61173
commit 37380fa6ff
6 changed files with 77 additions and 12 deletions

View File

@ -77,18 +77,34 @@ namespace cereal
namespace detail
{
//! Base type for polymorphic void casting
/*! Contains functions for casting between registered base and derived types.
This class will be allocated as a StaticObject and only referenced by pointer,
allowing a templated derived version of it to define strongly typed functions
that cast between registered base and derived types.
This is necessary so that cereal can properly cast between polymorphic types
even though void pointers are used, which normally have no type information.
Runtime type information is used instead to index a compile-time made mapping
that can perform the proper cast.
*/
struct PolymorphicCaster
{
//! Casts to the proper derived type
//! Downcasts to the proper derived type
/*! This should be overrriden in derived types, which can
be templated to allow the proper base derived type
paring to be used, while allowing generic access
through a void pointer. */
virtual void const * downcast( void const * const ptr ) const = 0;
//! Upcast to proper base type
virtual void * upcast( void * const ptr ) const = 0;
//! Upcast to proper base type, shared_ptr version
virtual std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const = 0;
};
//! Holds registered mappings between base and derived types for casting
/*! This will be allocated as a StaticObject and holds a map containing
all registered mappings between base and derived types. */
struct PolymorphicCasters
{
//! Maps a derived type index to a polymorphic caster
@ -105,6 +121,10 @@ namespace cereal
"Alternatively, manually register the association with XXX.");
//! Gets the mapping object that can perform the upcast or downcast
/*! Uses the type info from the base and derived class to find the matching
registered caster. If no matching caster exists, calls the exception function.
The returned PolymorphicCaster is capable of upcasting or downcasting between the two types */
template <class F>
static PolymorphicCaster const * lookup( std::type_info const & baseInfo, std::type_info const & derivedInfo, F && exceptionFunc )
{
@ -125,7 +145,7 @@ namespace cereal
}
//! Performs a downcast to the derived type using a registered mapping
template <class Derived>
template <class Derived> inline
static const Derived * downcast( const void * const dptr, std::type_info const & baseInfo )
{
auto const * mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } );
@ -135,7 +155,7 @@ namespace cereal
//! Performs an upcast to the registered base type using the given a derived type
/*! The return is untyped because the final casting to the base type must happen in the polymorphic
serialization function, where the type is known at compile time */
template <class Derived>
template <class Derived> inline
static void * upcast( Derived * const dptr, std::type_info const & baseInfo )
{
auto const * mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } );
@ -143,7 +163,7 @@ namespace cereal
}
//! Upcasts for shared pointers
template <class Derived>
template <class Derived> inline
static std::shared_ptr<void> upcast( std::shared_ptr<Derived> const & dptr, std::type_info const & baseInfo )
{
auto const * mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } );
@ -153,12 +173,14 @@ namespace cereal
#undef UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION
};
//! Strongly typed derivation of PolymorphicCaster
template <class Base, class Derived>
struct PolymorphicVirtualCaster : PolymorphicCaster
{
//! Inserts an entry in the polymorphic casting map for this pairing
/*! This creates all possible paths from Base to Derived in the mapping
given currently registered pairings */
/*! Creates an explicit mapping between Base and Derived in both upwards and
downwards directions, allowing void pointers to either to be properly cast
assuming dynamic type information is available */
PolymorphicVirtualCaster()
{
auto & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
@ -181,17 +203,26 @@ namespace cereal
return dynamic_cast<Derived const*>( static_cast<Base const*>( ptr ) );
}
//! Performs the proper upcast with the templated types
void * upcast( void * const ptr ) const override
{
return dynamic_cast<Base*>( static_cast<Derived*>( ptr ) );
}
//! Performs the proper upcast with the templated types (shared_ptr version)
std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const override
{
return std::dynamic_pointer_cast<Base>( std::static_pointer_cast<Derived>( ptr ) );
}
};
//! Registers a polymorphic casting relation between a Base and Derived type
/*! Registering a relation allows cereal to properly cast between the two types
given runtime type information and void pointers.
Registration happens automatically via cereal::base_class and cereal::virtual_base_class
instantiations. For cases where neither is called, see the CEREAL_REGISTER_POLYMORPHIC_RELATION
macro */
template <class Base, class Derived>
struct RegisterPolymorphicCaster
{
@ -203,6 +234,8 @@ namespace cereal
static PolymorphicCaster const * bind( std::false_type /* is_polymorphic<Base> */ )
{ return nullptr; }
//! Performs registration (binding) between Base and Derived
/*! If the type is not polymorphic, nothing will happen */
static PolymorphicCaster const * bind()
{ return bind( typename std::is_polymorphic<Base>::type() ); }
};

View File

@ -49,14 +49,15 @@ namespace cereal
{
namespace detail
{
//! Forward declaration, see polymorphic_impl.hpp for more information
template <class Base, class Derived>
struct RegisterPolymorphicCaster;
//! Forward declaration, see polymorphic_impl.hpp for more information
struct PolymorphicCasters;
namespace { struct polymorphic_relation_tag {}; }
template <class Base, class Derived, class tag = polymorphic_relation_tag>
//! Forward declaration, see polymorphic_impl.hpp for more information
template <class Base, class Derived>
struct PolymorphicRelation;
} // namespace detail
} // namespace cereal

View File

@ -35,10 +35,31 @@
namespace cereal
{
namespace base_class_detail
{
template <class Base, class Derived, bool IsPolymorphic = std::is_polymorphic<Base>::value>
struct RegisterPolymorphicBaseClass
{
static void bind()
{ }
};
template <class Base, class Derived>
struct RegisterPolymorphicBaseClass<Base, Derived, true>
{
static void bind()
{ detail::RegisterPolymorphicCaster<Base, Derived>::bind(); }
};
}
//! Casts a derived class to its non-virtual base class in a way that safely supports abstract classes
/*! This should be used in cases when a derived type needs to serialize its base type. This is better than directly
using static_cast, as it allows for serialization of pure virtual (abstract) base classes.
This also automatically registers polymorphic relation between the base and derived class, assuming they
are indeed polymorphic. Note this is not the same as polymorphic type registration. For more information
see the documentation on polymorphism.
\sa virtual_base_class
@code{.cpp}
@ -77,7 +98,7 @@ namespace cereal
base_ptr(const_cast<Base*>(static_cast<Base const *>(derived)))
{
static_assert( std::is_base_of<Base, Derived>::value, "Can only use base_class on a valid base class" );
detail::RegisterPolymorphicCaster<Base, Derived>::bind();
base_class_detail::RegisterPolymorphicBaseClass<Base, Derived>::bind();
}
Base * base_ptr;
@ -92,6 +113,10 @@ namespace cereal
where virtual inheritance does not take place, though it may be slightly faster to utilize
cereal::base_class<> if you do not need to worry about virtual inheritance.
This also automatically registers polymorphic relation between the base and derived class, assuming they
are indeed polymorphic. Note this is not the same as polymorphic type registration. For more information
see the documentation on polymorphism.
\sa base_class
@code{.cpp}
@ -158,7 +183,10 @@ namespace cereal
template<class Derived>
virtual_base_class(Derived const * derived) :
base_ptr(const_cast<Base*>(static_cast<Base const *>(derived)))
{ static_assert( std::is_base_of<Base, Derived>::value, "Can only use base_class on a valid base class" ); }
{
static_assert( std::is_base_of<Base, Derived>::value, "Can only use virtual_base_class on a valid base class" );
base_class_detail::RegisterPolymorphicBaseClass<Base, Derived>::bind();
}
Base * base_ptr;
};

View File

@ -115,7 +115,7 @@
The Derived class should be the most derived type that will be serialized,
and the Base type any possible base that has not been covered under a base
class serialization
class serialization that will be used to store a Derived pointer.
Placement of this is the same as for CEREAL_REGISTER_TYPE. */
#define CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, Derived) \

View File

@ -97,7 +97,9 @@ struct YourType : public Base
ar( x );
}
};
CEREAL_REGISTER_TYPE(YourType)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, YourType)
struct OurBase
{

View File

@ -144,6 +144,7 @@ struct C
};
CEREAL_REGISTER_TYPE(B)
CEREAL_REGISTER_POLYMORPHIC_RELATION(A, B)
class MemberMinimal
{