breaking out access and base_class into their own headers

This commit is contained in:
Shane Grant
2013-06-21 12:11:01 -07:00
parent 6e5252c215
commit 56f0561bd7
6 changed files with 345 additions and 83 deletions

129
include/cereal/access.hpp Normal file
View File

@@ -0,0 +1,129 @@
/*
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_ACCESS_HPP_
#define CEREAL_ACCESS_HPP_
namespace cereal
{
//! A class that allows cereal to load smart pointers to types that have no default constructor
/*! If your class does not have a default constructor, cereal will not be able
to load any smart pointers to it unless you overload LoadAndAllocate
for your class, and provide an appropriate load_and_allocate method.
The specialization of LoadAndAllocate must be placed within the cereal namespace:
@code{.cpp}
struct MyType
{
MyType( int x ); // note: no default ctor
int myX;
// Define a serialize or save/load pair as you normally would
template <class Archive>
void serialize( Archive & ar )
{
ar( myX );
}
};
// Provide a specialization for LoadAndAllocate for your type
namespace cereal
{
template <> struct LoadAndAllocate<MyType>
{
// load_and_allocate will be passed the archive that you will be loading
// from and should return a raw pointer to a dynamically allocated instance
// of your type.
//
// This will be captured by a smart pointer of some type and you need not
// worry about managing the memory
template <class Archive>
static MyType * load_and_allocate( Archive & ar )
{
int x;
ar( x );
return new MyType( x );
}
};
} // end namespace cereal
@endcode
@tparam T The type to specialize for */
template <class T>
struct LoadAndAllocate
{
//! Called by cereal if no default constructor exists to load and allocate data simultaneously
/*! Overloads of this should return a pointer to T and expect an archive as a parameter */
static void load_and_allocate(...)
{ }
};
//! A class that can be made a friend to give cereal access to non public functions
/*! If you desire non-public serialization functions within a class, cereal can only
access these if you declare cereal::access a friend.
@code{.cpp}
class MyClass
{
private:
friend class cereal::access; // gives access to the private serialize
template <class Archive>
void serialize( Archive & ar )
{
// some code
}
};
@endcode */
class access
{
public:
template<class Archive, class T> inline
static auto member_serialize(Archive & ar, T & t) -> decltype(t.serialize(ar))
{ t.serialize(ar); }
template<class Archive, class T> inline
static auto member_save(Archive & ar, T const & t) -> decltype(t.save(ar))
{ t.save(ar); }
template<class Archive, class T> inline
static auto member_load(Archive & ar, T & t) -> decltype(t.load(ar))
{ t.load(ar); }
template <class T>
static void load_and_allocate(...)
{ }
template<class T, class Archive> inline
static auto load_and_allocate(Archive & ar) -> decltype(T::load_and_allocate(ar))
{
return T::load_and_allocate( ar );
}
};
} // namespace cereal
#endif // CEREAL_ACCESS_HPP_

View File

@@ -28,7 +28,6 @@
#define CEREAL_ARCHIVES_BINARY_HPP_
#include <cereal/cereal.hpp>
#include <cereal/types/common.hpp>
#include <stack>
#include <sstream>

View File

@@ -0,0 +1,91 @@
/*
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_BASE_CLASS_HPP_
#define CEREAL_BASE_CLASS_HPP_
namespace cereal
{
//! Casts a derived class to its 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 save to use base_cast 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 virtual inheritance
@code{.cpp}
struct MyBase
{ };
struct MyLeft : virtual MyBase
{
template <class Archive>
void serialize( Archive & ar )
{
ar( cereal::base_clas<MyBase>( this ) );
}
};
struct MyRight : virtual MyBase
{
template <class Archive>
void serialize( Archive & ar )
{
ar( cereal::base_clas<MyBase>( this ) );
}
};
// diamond virtual inheritance; contains one copy if each base class
struct MyDerived : virtual MyLeft, virtual MyRight
{
template <class Archive>
void serialize( Archive & ar )
{
ar( cereal::base_cast<MyLeft>( this ) ); // safely serialize data members in MyLeft
ar( cereal::base_cast<MyRight>( this ) ); // safely serialize data members in MyRight
// Because we used base_cast, cereal will ensure that only one instance of MyBase is
// serialized as we traverse the inheritance heirarchy.
// 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
// serialization of MyBase due to diamond inheritance
};
*/
template<class Base>
struct base_class
{
template<class Derived>
base_class(Derived const * derived) :
base_ptr(const_cast<Base*>(static_cast<Base const *>(derived)))
{ }
Base * base_ptr;
};
} // namespace cereal
#endif // CEREAL_BASE_CLASS_HPP_

View File

@@ -32,7 +32,9 @@
#include <unordered_set>
#include <cstddef>
#include <cereal/base_class.hpp>
#include <cereal/details/traits.hpp>
#include <cereal/types/common.hpp>
namespace cereal
{
@@ -115,20 +117,6 @@ namespace cereal
//! Creates a name value pair for the variable T, using the same name
#define CEREAL_NVP(T) ::cereal::make_nvp(#T, T)
// ######################################################################
//! Casts a derived class to its base class in a way that allows
//! cereal to track inheritance
template<class Base>
struct base_class
{
template<class Derived>
base_class(Derived const * derived) :
base_ptr(const_cast<Base*>(static_cast<Base const *>(derived)))
{ }
Base * base_ptr;
};
// ######################################################################
//! Called before a type is serialized to set up any special archive state
//! for processing some type
@@ -144,6 +132,16 @@ namespace cereal
// ######################################################################
//! Special flags for archives
/*! AllowEmptyClassElision
This allows for empty classes to be serialized even if they do not provide
a serialization function. Classes with no data members are considered to be
empty. Be warned that if this is enabled and you attempt to serialize an
empty class with improperly formed serialize or save/load functions, no
static error will occur - the error will propogate silently and your
intended serialization functions may not be called. You can manually
ensure that your classes that have custom serialization are correct
by using the traits is_output_serializable and is_input_serializable
in cereal/details/traits.hpp. */
enum Flags { AllowEmptyClassElision = 1 };
// ######################################################################

View File

@@ -31,41 +31,10 @@
#include <typeindex>
#include <memory>
#include <cereal/access.hpp>
namespace cereal
{
template <class T>
struct LoadAndAllocate
{
static void load_and_allocate(...)
{ }
};
class access
{
public:
template<class Archive, class T> inline
static auto member_serialize(Archive & ar, T & t) -> decltype(t.serialize(ar))
{ t.serialize(ar); }
template<class Archive, class T> inline
static auto member_save(Archive & ar, T const & t) -> decltype(t.save(ar))
{ t.save(ar); }
template<class Archive, class T> inline
static auto member_load(Archive & ar, T & t) -> decltype(t.load(ar))
{ t.load(ar); }
template <class T>
static void load_and_allocate(...)
{ }
template<class T, class Archive> inline
static auto load_and_allocate(Archive & ar) -> decltype(T::load_and_allocate(ar))
{
return T::load_and_allocate( ar );
}
};
namespace traits
{
template<typename> struct Void { typedef void type; };

View File

@@ -32,6 +32,7 @@
#include <cereal/types/memory.hpp>
#include <cereal/types/complex.hpp>
#include <cereal/types/boost_variant.hpp>
#include <cereal/base_class.hpp>
#include <cxxabi.h>
#include <sstream>
@@ -40,11 +41,49 @@
#include <complex>
#include <iostream>
class Base
{
private:
friend class cereal::access;
template <class Archive>
void serialize( Archive & ar )
{
std::cout << "Base serialize" << std::endl;
ar( x );
}
int x;
};
class Derived : public Base
{
public:
//friend class cereal::access;
template <class Archive>
void save( Archive & ar ) const
{
std::cout << "Derived save" << std::endl;
ar( y );
serialize( ar );
}
template <class Archive>
void load( Archive & ar )
{
std::cout << "Derived load" << std::endl;
ar( y );
}
int y;
};
//ar( cereal::base_class<Test1>(this) );
// ###################################
struct Test1
{
int a;
private:
friend class cereal::access;
template<class Archive>
void serialize(Archive & ar)
{
@@ -56,6 +95,8 @@ struct Test1
class Test2
{
public:
Test2() {}
Test2( int x ) : a( x ) {}
int a;
private:
@@ -209,44 +250,79 @@ namespace cereal
// ######################################################################
int main()
{
std::cout << std::boolalpha << std::endl;
std::cout << "Base" << std::endl;
std::cout << "ms " << cereal::traits::has_member_save<Base, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "nms " << cereal::traits::has_non_member_save<Base, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "ml " << cereal::traits::has_member_load<Base, cereal::BinaryInputArchive>() << std::endl;
std::cout << "nml " << cereal::traits::has_non_member_load<Base, cereal::BinaryInputArchive>() << std::endl;
std::cout << "mse " << cereal::traits::has_member_serialize<Base, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "nse " << cereal::traits::has_non_member_serialize<Base, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "iser " << cereal::traits::is_input_serializable<Base, cereal::BinaryInputArchive>() << std::endl;
std::cout << "oser " << cereal::traits::is_output_serializable<Base, cereal::BinaryOutputArchive>() << std::endl;
std::cout << std::endl;
int x[5];
std::cout << "Derived" << std::endl;
std::cout << "ms " << cereal::traits::has_member_save<Derived, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "nms " << cereal::traits::has_non_member_save<Derived, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "ml " << cereal::traits::has_member_load<Derived, cereal::BinaryInputArchive>() << std::endl;
std::cout << "nml " << cereal::traits::has_non_member_load<Derived, cereal::BinaryInputArchive>() << std::endl;
std::cout << "mse " << cereal::traits::has_member_serialize<Derived, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "nse " << cereal::traits::has_non_member_serialize<Derived, cereal::BinaryOutputArchive>() << std::endl;
std::cout << "iser " << cereal::traits::is_input_serializable<Derived, cereal::BinaryInputArchive>() << std::endl;
std::cout << "oser " << cereal::traits::is_output_serializable<Derived, cereal::BinaryOutputArchive>() << std::endl;
std::cout << std::endl;
//{
// int x[5];
// std::cout << "saving" << std::endl;
// std::stringstream os;
// cereal::BinaryOutputArchive archive(os);
// archive( x );
// Base b;
// archive( b );
// Derived d;
// archive( d );
// std::cout << "loading" << std::endl;
// cereal::BinaryInputArchive iarchive(os);
// iarchive( x );
// iarchive( b );
// iarchive( d );
//}
Everything e_out;
e_out.x = 99;
e_out.y = 100;
e_out.t1 = {1};
e_out.t2 = {2};
e_out.t3 = {3};
e_out.t4 = {4};
e_out.s = "Hello, World!";
Test2 t2 = {22};
{
std::ostringstream os;
std::ofstream os("out.txt");
cereal::BinaryOutputArchive archive(os);
archive( x );
archive(CEREAL_NVP(e_out));
archive(t2);
}
Everything e_in;
//Everything e_out;
//e_out.x = 99;
//e_out.y = 100;
//e_out.t1 = {1};
//e_out.t2 = {2};
//e_out.t3 = {3};
//e_out.t4 = {4};
//e_out.s = "Hello, World!";
{
std::ifstream is("out.txt");
cereal::BinaryInputArchive archive(is);
archive(CEREAL_NVP(e_in));
archive(t2);
}
//Test2 t2 = {22};
//{
// std::ofstream os("out.txt");
// cereal::BinaryOutputArchive archive(os);
// archive(CEREAL_NVP(e_out));
// archive(t2);
//}
//Everything e_in;
//{
// std::ifstream is("out.txt");
// cereal::BinaryInputArchive archive(is);
// archive(CEREAL_NVP(e_in));
// archive(t2);
//}
//assert(e_in == e_out);//
assert(e_in == e_out);//
//
//{ //
// std::ofstream os("pt//r.txt");