Merge branch 'issue_46' into develop relates #46

This commit is contained in:
Shane Grant 2014-10-04 19:15:05 -07:00
commit e054ec1b10
3 changed files with 295 additions and 2 deletions

View File

@ -0,0 +1,156 @@
/*! \file adapters.hpp
\brief Archive adapters that provide additional functionality
on top of an existing archive */
/*
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_ARCHIVES_ADAPTERS_HPP_
#define CEREAL_ARCHIVES_ADAPTERS_HPP_
#include <cereal/details/helpers.hpp>
#include <utility>
namespace cereal
{
// Forward declaration for friend access
template <class U, class A> U & get_user_data( A & );
//! Wraps an archive and gives access to user data
/*! This adapter is useful if you require access to
either raw pointers or references within your
serialization functions.
While cereal does not directly support serialization
raw pointers or references, it is sometimes the case
that you may want to supply something such as a raw
pointer or global reference to some constructor.
In this situation this adapter would likely be used
with the construct class to allow for non-default
constructors.
@code{.cpp}
struct MyUserData
{
int * myRawPointer;
std::reference_wrapper<MyOtherType> myReference;
};
struct MyClass
{
// Note the raw pointer parameter
MyClass( int xx, int * rawP );
int x;
template <class Archive>
void serialize( Archive & ar )
{ ar( x ); }
template <class Archive>
static void load_and_construct( Archive & ar, cereal::construct<MyClass> & construct )
{
int xx;
ar( xx );
// note the need to use get_user_data to retrieve user data from the archive
construct( xx, cereal::get_user_data<MyUserData>( ar ).myRawPointer );
}
};
int main()
{
{
MyUserData md;
md.myRawPointer = &something;
md.myReference = someInstanceOfType;
std::ifstream is( "data.xml" );
cereal::UserDataAdapter<MyUserData, cereal::XMLInputArchive> ar( md, is );
std::unique_ptr<MyClass> sc;
ar( sc ); // use as normal
}
return 0;
}
@endcode
@relates get_user_data
@tparam UserData The type to give the archive access to
@tparam Archive The archive to wrap */
template <class UserData, class Archive>
class UserDataAdapter : public Archive
{
public:
//! Construct the archive with some user data struct
/*! This will forward all arguments (other than the user
data) to the wrapped archive type. The UserDataAdapter
can then be used identically to the wrapped archive type
@tparam Args The arguments to pass to the constructor of
the archive. */
template <class ... Args>
UserDataAdapter( UserData & ud, Args && ... args ) :
Archive( std::forward<Args>( args )... ),
userdata( ud )
{ }
private:
//! Overload the rtti function to enable dynamic_cast
void rtti() {}
friend UserData & get_user_data<UserData>( Archive & ar );
UserData & userdata; //!< The actual user data
};
//! Retrieves user data from an archive wrapped by UserDataAdapter
/*! This will attempt to retrieve the user data associated with
some archive wrapped by UserDataAdapter. If this is used on
an archive that is not wrapped, a run-time exception will occur.
@note The correct use of this function cannot be enforced at compile
time.
@relates UserDataAdapter
@tparam UserData The data struct contained in the archive
@tparam Archive The archive, which should be wrapped by UserDataAdapter
@param ar The archive
@throws Exception if the archive this is used upon is not wrapped with
UserDataAdapter. */
template <class UserData, class Archive>
UserData & get_user_data( Archive & ar )
{
try
{
return dynamic_cast<UserDataAdapter<UserData, Archive> &>( ar ).userdata;
}
catch( std::bad_cast const & )
{
throw ::cereal::Exception("Attempting to get user data from archive not wrapped in UserDataAdapter");
}
}
} // namespace cereal
#endif // CEREAL_ARCHIVES_ADAPTERS_HPP_

View File

@ -220,8 +220,11 @@ namespace cereal
namespace detail
{
// base classes for type checking
struct OutputArchiveBase {};
struct InputArchiveBase {};
/* The rtti virtual function only exists to enable an archive to
be used in a polymorphic fashion, if necessary. See the
archive adapters for an example of this */
class OutputArchiveBase { private: virtual void rtti(){} };
class InputArchiveBase { private: virtual void rtti(){} };
// forward decls for polymorphic support
template <class Archive, class T> struct polymorphic_serialization_support;

View File

@ -0,0 +1,134 @@
/*
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 AND 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.
*/
#include "common.hpp"
#include <cereal/archives/adapters.hpp>
#include <boost/test/unit_test.hpp>
struct SomeStruct {};
struct UserData
{
UserData( SomeStruct * pp, SomeStruct & r ) :
p(pp), ref(r) {}
SomeStruct * p;
std::reference_wrapper<SomeStruct> ref;
};
struct UserStruct
{
UserStruct( std::int32_t i,
SomeStruct * pointer,
SomeStruct & reference ) :
i32( i ),
p( pointer ),
ref( reference )
{ }
std::int32_t i32;
SomeStruct const * p;
SomeStruct & ref;
template <class Archive>
void serialize( Archive & ar )
{
ar( i32 );
}
template <class Archive>
static void load_and_construct( Archive & ar, cereal::construct<UserStruct> & construct )
{
std::int32_t ii;
ar( ii );
auto & data = cereal::get_user_data<UserData>( ar );
construct( ii, data.p, data.ref.get() );
}
};
template <class IArchive, class OArchive>
void test_user_data_adapters()
{
std::random_device rd;
std::mt19937 gen(rd());
auto rng = [&](){ return random_value<int>(gen); };
for(int ii=0; ii<100; ++ii)
{
SomeStruct ss;
std::unique_ptr<UserStruct> o_ptr( new UserStruct( rng(), &ss, ss ) );
std::ostringstream os;
{
OArchive oar(os);
oar(o_ptr);
}
decltype( o_ptr ) i_ptr;
std::istringstream is(os.str());
{
UserData ud(&ss, ss);
cereal::UserDataAdapter<UserData, IArchive> iar(ud, is);
iar(i_ptr);
}
BOOST_CHECK_EQUAL( i_ptr->p == o_ptr->p, true );
BOOST_CHECK_EQUAL( std::addressof(i_ptr->ref) == std::addressof(o_ptr->ref), true );
BOOST_CHECK_EQUAL( i_ptr->i32, o_ptr->i32 );
std::istringstream bad_is(os.str());
{
IArchive iar(bad_is);
BOOST_CHECK_THROW( iar(i_ptr), ::cereal::Exception )
}
}
}
BOOST_AUTO_TEST_CASE( binary_tuple )
{
test_user_data_adapters<cereal::BinaryInputArchive, cereal::BinaryOutputArchive>();
}
BOOST_AUTO_TEST_CASE( portable_binary_tuple )
{
test_user_data_adapters<cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive>();
}
BOOST_AUTO_TEST_CASE( xml_tuple )
{
test_user_data_adapters<cereal::XMLInputArchive, cereal::XMLOutputArchive>();
}
BOOST_AUTO_TEST_CASE( json_tuple )
{
test_user_data_adapters<cereal::JSONInputArchive, cereal::JSONOutputArchive>();
}