diff --git a/include/cereal/archives/adapters.hpp b/include/cereal/archives/adapters.hpp new file mode 100644 index 00000000..b82bacf3 --- /dev/null +++ b/include/cereal/archives/adapters.hpp @@ -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 +#include + +namespace cereal +{ + // Forward declaration for friend access + template 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 myReference; + }; + + struct MyClass + { + // Note the raw pointer parameter + MyClass( int xx, int * rawP ); + + int x; + + template + void serialize( Archive & ar ) + { ar( x ); } + + template + static void load_and_construct( Archive & ar, cereal::construct & 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( ar ).myRawPointer ); + } + }; + + int main() + { + { + MyUserData md; + md.myRawPointer = &something; + md.myReference = someInstanceOfType; + + std::ifstream is( "data.xml" ); + cereal::UserDataAdapter ar( md, is ); + + std::unique_ptr 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 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 + UserDataAdapter( UserData & ud, Args && ... args ) : + Archive( std::forward( args )... ), + userdata( ud ) + { } + + private: + //! Overload the rtti function to enable dynamic_cast + void rtti() {} + friend UserData & get_user_data( 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 + UserData & get_user_data( Archive & ar ) + { + try + { + return dynamic_cast &>( 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_ diff --git a/include/cereal/details/helpers.hpp b/include/cereal/details/helpers.hpp index 6a8773b3..9bc89680 100644 --- a/include/cereal/details/helpers.hpp +++ b/include/cereal/details/helpers.hpp @@ -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 struct polymorphic_serialization_support; diff --git a/unittests/user_data_adapters.cpp b/unittests/user_data_adapters.cpp new file mode 100644 index 00000000..0bb80841 --- /dev/null +++ b/unittests/user_data_adapters.cpp @@ -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 +#include + +struct SomeStruct {}; + +struct UserData +{ + UserData( SomeStruct * pp, SomeStruct & r ) : + p(pp), ref(r) {} + + SomeStruct * p; + std::reference_wrapper 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 + void serialize( Archive & ar ) + { + ar( i32 ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + std::int32_t ii; + ar( ii ); + auto & data = cereal::get_user_data( ar ); + construct( ii, data.p, data.ref.get() ); + } +}; + +template +void test_user_data_adapters() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rng = [&](){ return random_value(gen); }; + + for(int ii=0; ii<100; ++ii) + { + SomeStruct ss; + std::unique_ptr 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 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(); +} + +BOOST_AUTO_TEST_CASE( portable_binary_tuple ) +{ + test_user_data_adapters(); +} + +BOOST_AUTO_TEST_CASE( xml_tuple ) +{ + test_user_data_adapters(); +} + +BOOST_AUTO_TEST_CASE( json_tuple ) +{ + test_user_data_adapters(); +} +