Fixing issue #79

Added a set of trait classes that can be used to get an input archive
from an output archive.  Requires specializing a struct for each direction or
alternatively using the new macro CEREAL_SETUP_ARCHIVE_TRAITS(InArchive, OutArchive).
This has already been added for all built in archive types.  This is currently only
used for minimal serialization.

load_minimal type traits now correctly use the output archive to check the existence of
a corresponding save_minimal and get its return type, using the new get_input_from_output
type class.

Added a test for this case into the minimal structs test.

Sandbox_vs needed the new macro to become compliant.
This commit is contained in:
Shane Grant 2014-05-22 10:59:13 -07:00
parent 5305078096
commit f067ba6905
8 changed files with 110 additions and 17 deletions

View File

@ -44,7 +44,7 @@ namespace cereal
architectures with different endianness, use PortableBinaryOutputArchive.
When using a binary archive and a file stream, you must use the
std::ios::binary format flag to avoid having your data altered
std::ios::binary format flag to avoid having your data altered
inadvertently.
\ingroup Archives */
@ -77,9 +77,9 @@ namespace cereal
/* This archive does nothing to ensure that the endianness of the saved
and loaded data is the same. If you need to have portability over
architectures with different endianness, use PortableBinaryOutputArchive.
When using a binary archive and a file stream, you must use the
std::ios::binary format flag to avoid having your data altered
std::ios::binary format flag to avoid having your data altered
inadvertently.
\ingroup Archives */
@ -159,4 +159,7 @@ namespace cereal
CEREAL_REGISTER_ARCHIVE(cereal::BinaryOutputArchive)
CEREAL_REGISTER_ARCHIVE(cereal::BinaryInputArchive)
// tie input and output archives together
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::BinaryInputArchive, cereal::BinaryOutputArchive)
#endif // CEREAL_ARCHIVES_BINARY_HPP_

View File

@ -887,4 +887,7 @@ namespace cereal
CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive)
CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive)
// tie input and output archives together
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::JSONInputArchive, cereal::JSONOutputArchive)
#endif // CEREAL_ARCHIVES_JSON_HPP_

View File

@ -239,4 +239,7 @@ namespace cereal
CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryOutputArchive)
CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryInputArchive)
// tie input and output archives together
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive)
#endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_

View File

@ -825,4 +825,7 @@ namespace cereal
CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive)
CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive)
// tie input and output archives together
CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive)
#endif // CEREAL_ARCHIVES_XML_HPP_

View File

@ -756,7 +756,8 @@ namespace cereal
template <class T, PROCESS_IF(member_load_minimal)> inline
ArchiveType & processImpl(T & t)
{
typename traits::has_member_save_minimal<T, ArchiveType>::type value;
using OutArchiveType = typename traits::detail::get_output_from_input<ArchiveType>::type;
typename traits::has_member_save_minimal<T, OutArchiveType>::type value;
self->process( value );
access::member_load_minimal(*self, t, value);
return *self;
@ -766,7 +767,8 @@ namespace cereal
template <class T, PROCESS_IF(non_member_load_minimal)> inline
ArchiveType & processImpl(T & t)
{
typename traits::has_non_member_save_minimal<T, ArchiveType>::type value;
using OutArchiveType = typename traits::detail::get_output_from_input<ArchiveType>::type;
typename traits::has_non_member_save_minimal<T, OutArchiveType>::type value;
self->process( value );
CEREAL_LOAD_MINIMAL_FUNCTION_NAME(*self, t, value);
return *self;
@ -875,8 +877,9 @@ namespace cereal
template <class T, PROCESS_IF(member_versioned_load_minimal)> inline
ArchiveType & processImpl(T & t)
{
using OutArchiveType = typename traits::detail::get_output_from_input<ArchiveType>::type;
const auto version = loadClassVersion<T>();
typename traits::has_member_versioned_save_minimal<T, ArchiveType>::type value;
typename traits::has_member_versioned_save_minimal<T, OutArchiveType>::type value;
self->process(value);
access::member_load_minimal(*self, t, value, version);
return *self;
@ -887,8 +890,9 @@ namespace cereal
template <class T, PROCESS_IF(non_member_versioned_load_minimal)> inline
ArchiveType & processImpl(T & t)
{
using OutArchiveType = typename traits::detail::get_output_from_input<ArchiveType>::type;
const auto version = loadClassVersion<T>();
typename traits::has_non_member_versioned_save_minimal<T, ArchiveType>::type value;
typename traits::has_non_member_versioned_save_minimal<T, OutArchiveType>::type value;
self->process(value);
CEREAL_LOAD_MINIMAL_FUNCTION_NAME(*self, t, value, version);
return *self;

View File

@ -147,6 +147,32 @@ namespace cereal
template <bool ... Conditions>
using DisableIf = typename detail::DisableIfHelper<Conditions...>::type;
// ######################################################################
namespace detail
{
template <class InputArchive>
struct get_output_from_input : no
{
static_assert( detail::delay_static_assert<InputArchive>::value,
"Could not find an associated output archive for input archive." );
};
template <class OutputArchive>
struct get_input_from_output : no
{
static_assert( detail::delay_static_assert<OutputArchive>::value,
"Could not find an associated input archive for output archive." );
};
}
//! Sets up traits that relate an input archive to an output archive
#define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive) \
namespace cereal { namespace traits { namespace detail { \
template <> struct get_output_from_input<InputArchive> \
{ using type = OutputArchive; }; \
template <> struct get_input_from_output<OutputArchive> \
{ using type = InputArchive; }; } } } /* end namespaces */
// ######################################################################
//! Used to convert a MAKE_HAS_XXX macro into a versioned variant
#define CEREAL_MAKE_VERSIONED_TEST ,0
@ -678,11 +704,13 @@ namespace cereal
template <class T, class A>
struct has_member_load_minimal_wrapper<T, A, true>
{
static_assert( has_member_save_minimal<T, A>::value,
using AOut = typename detail::get_output_from_input<A>::type;
static_assert( has_member_save_minimal<T, AOut>::value,
"cereal detected member load_minimal but no valid member save_minimal. "
"cannot evaluate correctness of load_minimal without valid save_minimal." );
using SaveType = typename detail::get_member_save_minimal_type<T, A, true>::type;
using SaveType = typename detail::get_member_save_minimal_type<T, AOut, true>::type;
const static bool value = has_member_load_minimal_impl<T, A>::value;
const static bool valid = has_member_load_minimal_type_impl<T, A, SaveType>::value;
@ -739,11 +767,13 @@ namespace cereal
template <class T, class A>
struct has_member_versioned_load_minimal_wrapper<T, A, true>
{
static_assert( has_member_versioned_save_minimal<T, A>::value,
using AOut = typename detail::get_output_from_input<A>::type;
static_assert( has_member_versioned_save_minimal<T, AOut>::value,
"cereal detected member versioned load_minimal but no valid member versioned save_minimal. "
"cannot evaluate correctness of load_minimal without valid save_minimal." );
using SaveType = typename detail::get_member_versioned_save_minimal_type<T, A, true>::type;
using SaveType = typename detail::get_member_versioned_save_minimal_type<T, AOut, true>::type;
const static bool value = has_member_versioned_load_minimal_impl<T, A>::value;
const static bool valid = has_member_versioned_load_minimal_type_impl<T, A, SaveType>::value;
@ -794,11 +824,13 @@ namespace cereal
template <class T, class A>
struct has_non_member_load_minimal_wrapper<T, A, true>
{
static_assert( detail::has_non_member_save_minimal_impl<T, A>::valid,
using AOut = typename detail::get_output_from_input<A>::type;
static_assert( detail::has_non_member_save_minimal_impl<T, AOut>::valid,
"cereal detected non-member load_minimal but no valid non-member save_minimal. "
"cannot evaluate correctness of load_minimal without valid save_minimal." );
using SaveType = typename detail::get_non_member_save_minimal_type<T, A, true>::type;
using SaveType = typename detail::get_non_member_save_minimal_type<T, AOut, true>::type;
using check = has_non_member_load_minimal_impl<T, A, SaveType>;
static const bool value = check::exists;
@ -846,11 +878,13 @@ namespace cereal
template <class T, class A>
struct has_non_member_versioned_load_minimal_wrapper<T, A, true>
{
static_assert( detail::has_non_member_versioned_save_minimal_impl<T, A>::valid,
using AOut = typename detail::get_output_from_input<A>::type;
static_assert( detail::has_non_member_versioned_save_minimal_impl<T, AOut>::valid,
"cereal detected non-member versioned load_minimal but no valid non-member versioned save_minimal. "
"cannot evaluate correctness of load_minimal without valid save_minimal." );
using SaveType = typename detail::get_non_member_versioned_save_minimal_type<T, A, true>::type;
using SaveType = typename detail::get_non_member_versioned_save_minimal_type<T, AOut, true>::type;
using check = has_non_member_versioned_load_minimal_impl<T, A, SaveType>;
static const bool value = check::exists;

View File

@ -41,6 +41,7 @@
#include <functional>
struct Archive {};
CEREAL_SETUP_ARCHIVE_TRAITS(Archive, Archive)
struct Test
{
@ -226,6 +227,6 @@ int main()
std::cout << "\textra" << std::endl;
std::cout << cereal::traits::has_member_save_minimal<MemberMinimal, Archive>::value << std::endl;
std::cout << cereal::traits::has_member_load_minimal<MemberMinimal, Archive>::value << std::endl;
return 0;
return 0;
}

View File

@ -134,6 +134,41 @@ struct TestStruct
NonMemberMinimalVersioned nmmv;
};
struct Issue79Struct
{
Issue79Struct() = default;
Issue79Struct( std::int32_t xx ) : x(xx) {}
std::int32_t x;
};
template <class Archive, cereal::traits::DisableIf<std::is_same<Archive, cereal::BinaryOutputArchive>::value ||
std::is_same<Archive, cereal::PortableBinaryOutputArchive>::value> = cereal::traits::sfinae>
std::string save_minimal( Archive const &, Issue79Struct const & val )
{
return std::to_string( val.x );
}
template <class Archive, cereal::traits::DisableIf<std::is_same<Archive, cereal::BinaryInputArchive>::value ||
std::is_same<Archive, cereal::PortableBinaryInputArchive>::value> = cereal::traits::sfinae>
void load_minimal( Archive const &, Issue79Struct & val, std::string const & str )
{
val.x = std::stoi( str );
}
template <class Archive, cereal::traits::EnableIf<std::is_same<Archive, cereal::BinaryOutputArchive>::value ||
std::is_same<Archive, cereal::PortableBinaryOutputArchive>::value> = cereal::traits::sfinae>
std::int32_t save_minimal( Archive const &, Issue79Struct const & val )
{
return val.x;
}
template <class Archive, cereal::traits::EnableIf<std::is_same<Archive, cereal::BinaryInputArchive>::value ||
std::is_same<Archive, cereal::PortableBinaryInputArchive>::value> = cereal::traits::sfinae>
void load_minimal( Archive const &, Issue79Struct & val, std::int32_t const & xx )
{
val.x = xx;
}
template <class IArchive, class OArchive>
void test_structs_minimal()
{
@ -145,18 +180,23 @@ void test_structs_minimal()
TestStruct o_struct = { random_basic_string<char>(gen), random_value<double>(gen),
random_value<std::uint32_t>(gen), random_value<uint8_t>(gen) % 2 ? true : false };
Issue79Struct o_struct2 = { random_value<std::int32_t>(gen) };
std::ostringstream os;
{
OArchive oar(os);
oar( o_struct );
oar( o_struct2 );
}
decltype(o_struct) i_struct;
decltype(o_struct2) i_struct2;
std::istringstream is(os.str());
{
IArchive iar(is);
iar( i_struct );
iar( i_struct2 );
}
BOOST_CHECK(o_struct.mm.x == i_struct.mm.x);
@ -164,6 +204,8 @@ void test_structs_minimal()
BOOST_CHECK(o_struct.nmm.x == i_struct.nmm.x);
BOOST_CHECK(o_struct.nmmv.x == i_struct.nmmv.x);
BOOST_CHECK(o_struct2.x == i_struct2.x);
}
}