diff --git a/include/cereal/cereal.hpp b/include/cereal/cereal.hpp index 05c98017..75f44f4a 100644 --- a/include/cereal/cereal.hpp +++ b/include/cereal/cereal.hpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include namespace cereal @@ -284,6 +284,15 @@ namespace cereal return *self; } + //! Serialization of a base_class wrapper + /*! \sa base_class */ + template inline + ArchiveType & processImpl(base_class b) + { + self->processImpl( *b.base_ptr ); + return *self; + } + //! Member serialization template inline typename std::enable_if() || @@ -495,6 +504,16 @@ namespace cereal return *self; } + //! Serialization of a base_class wrapper + /*! \sa base_class */ + template inline + ArchiveType & processImpl(base_class b) + { + self->processImpl( *b.base_ptr ); + return *self; + } + + //! Member serialization template inline typename std::enable_if() || diff --git a/include/cereal/types/virtual_base_class.hpp b/include/cereal/types/base_class.hpp similarity index 62% rename from include/cereal/types/virtual_base_class.hpp rename to include/cereal/types/base_class.hpp index 30b64779..ff010b0a 100644 --- a/include/cereal/types/virtual_base_class.hpp +++ b/include/cereal/types/base_class.hpp @@ -24,53 +24,118 @@ (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_VIRTUAL_BASE_CLASS_HPP_ -#define CEREAL_TYPES_VIRTUAL_BASE_CLASS_HPP_ +#ifndef CEREAL_TYPES_BASE_CLASS_HPP_ +#define CEREAL_TYPES_BASE_CLASS_HPP_ namespace cereal { - //! Casts a derived class to its base class in a way that allows cereal to track inheritance + //! 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. + + \sa virtual_base_class + + @code{.cpp} + struct MyBase + { + int x; + + virtual void foo() = 0; + + template + void serialize( Archive & ar ) + { + ar( x ); + } + }; + + struct MyDerived : public MyBase //<-- Note non-virtual inheritance + { + int y; + + virtual void foo() {}; + + template + void serialize( Archive & ar ) + { + ar( cereal::base_class(this) ); + ar( y ); + } + }; + @endcode */ + template + struct base_class + { + template + base_class(Derived const * derived) : + base_ptr(const_cast(static_cast(derived))) + { } + + Base * base_ptr; + }; + + //! Casts a derived class to its virtual base class in a way that allows cereal to track inheritance /*! This should be used in cases when a derived type features virtual inheritance from some base type. This allows cereal to track the inheritance and to avoid making duplicate copies during serialization. It is safe to use virtual_base_class in all circumstances for serializing base classes, even in cases where virtual inheritance does not take place, though it may be slightly faster to utilize - static_cast<> if you do not need to worry about virtual inheritance + cereal::base_class<> if you do not need to worry about virtual inheritance. + + \sa base_class @code{.cpp} struct MyBase - { }; + { + int x; - struct MyLeft : virtual MyBase - { template void serialize( Archive & ar ) { - ar( cereal::base_clas( this ) ); + ar( x ); + } + }; + + struct MyLeft : virtual MyBase //<-- Note the virtual inheritance + { + int y; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_class( this ) ); + ar( y ); } }; struct MyRight : virtual MyBase { + int z; + template void serialize( Archive & ar ) { - ar( cereal::base_clas( this ) ); + ar( cereal::virtual_base_clas( this ) ); + ar( z ); } }; - // diamond virtual inheritance; contains one copy if each base class + // diamond virtual inheritance; contains one copy of each base class struct MyDerived : virtual MyLeft, virtual MyRight { + int a; + template void serialize( Archive & ar ) { ar( cereal::virtual_base_class( this ) ); // safely serialize data members in MyLeft ar( cereal::virtual_base_class( this ) ); // safely serialize data members in MyRight + ar( a ); // Because we used virtual_base_class, cereal will ensure that only one instance of MyBase is - // serialized as we traverse the inheritance heirarchy. + // serialized as we traverse the inheritance heirarchy. This means that there will be one copy + // each of the variables x, y, z, and a // If we had chosen to use static_cast<> instead, cereal would perform no tracking and // assume that every base class should be serialized (in this case leading to a duplicate @@ -86,5 +151,6 @@ namespace cereal Base * base_ptr; }; + } // namespace cereal -#endif // CEREAL_TYPES_VIRTUAL_BASE_CLASS_HPP_ +#endif // CEREAL_TYPES_BASE_CLASS_HPP_ diff --git a/sandbox.cpp b/sandbox.cpp index e09f729b..34c776bb 100644 --- a/sandbox.cpp +++ b/sandbox.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/sandbox_rtti.cpp b/sandbox_rtti.cpp index 07c8a6b2..9a75aa49 100644 --- a/sandbox_rtti.cpp +++ b/sandbox_rtti.cpp @@ -34,18 +34,21 @@ struct Base { + int y; virtual void foo() = 0; template void save(Archive & ar) const { std::cout << "Saving Base" << std::endl; + ar( y ); } template void load(Archive & ar) { std::cout << "Loading Base" << std::endl; + ar( y ); } }; @@ -59,12 +62,14 @@ struct MyType : public Base void save(Archive & ar) const { std::cout << "Saving MyType" << std::endl; + ar( cereal::virtual_base_class( this ) ); } template void load(Archive & ar) { std::cout << "Loading MyType" << std::endl; + ar( cereal::base_class( this ) ); } }; CEREAL_REGISTER_TYPE(MyType);