Skip to content

Commit

Permalink
is_in/out_serializable is now aware of specialization
Browse files Browse the repository at this point in the history
count_in/out_serializers will now count the number of specializations if a type is
specialized, otherwise it will count the number of non-specialized serialization functions.

as a result of this, is_output/input_serializable now works as you would expect from the name
and will return true for correctly configured specialized types too.

this caused some logic changes to need to happen in cereal.hpp, mostly within the PROCESS_IF macro.

added some tests related to this change and #180

fixes #180
  • Loading branch information
AzothAmmo committed Apr 5, 2015
1 parent b385b39 commit 94d49c4
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 91 deletions.
48 changes: 29 additions & 19 deletions include/cereal/cereal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,18 @@ namespace cereal
}

//! Helper macro that expands the requirements for activating an overload
#define PROCESS_IF(name) \
traits::EnableIf<traits::has_##name<T, ArchiveType>::value, \
!traits::has_invalid_output_versioning<T, ArchiveType>::value, \
(traits::is_specialized_##name<T, ArchiveType>::value || \
traits::is_output_serializable<T, ArchiveType>::value)> = traits::sfinae
/*! Requirements:
Has the requested serialization function
Does not have version and unversioned at the same time
Is output serializable AND
is specialized for this type of function OR
has no specialization at all */
#define PROCESS_IF(name) \
traits::EnableIf<traits::has_##name<T, ArchiveType>::value, \
!traits::has_invalid_output_versioning<T, ArchiveType>::value, \
(traits::is_output_serializable<T, ArchiveType>::value && \
(traits::is_specialized_##name<T, ArchiveType>::value || \
!traits::is_specialized<T, ArchiveType>::value))> = traits::sfinae

//! Member serialization
template <class T, PROCESS_IF(member_serialize)> inline
Expand Down Expand Up @@ -422,7 +429,6 @@ namespace cereal

//! Empty class specialization
template <class T, traits::EnableIf<(Flags & AllowEmptyClassElision),
!traits::is_specialized<T, ArchiveType>::value,
!traits::is_output_serializable<T, ArchiveType>::value,
std::is_empty<T>::value> = traits::sfinae> inline
ArchiveType & processImpl(T const &)
Expand All @@ -432,11 +438,10 @@ namespace cereal

//! No matching serialization
/*! Invalid if we have invalid output versioning or
we have no specialization, are not output serializable, and either
we are not output serializable, and either
don't allow empty class ellision or allow it but are not serializing an empty class */
template <class T, traits::EnableIf<traits::has_invalid_output_versioning<T, ArchiveType>::value ||
(!traits::is_specialized<T, ArchiveType>::value &&
!traits::is_output_serializable<T, ArchiveType>::value &&
(!traits::is_output_serializable<T, ArchiveType>::value &&
(!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty<T>::value)))> = traits::sfinae> inline
ArchiveType & processImpl(T const &)
{
Expand Down Expand Up @@ -588,7 +593,7 @@ namespace cereal
itsPolymorphicTypeMap(),
itsVersionedTypes()
{ }

InputArchive & operator=( InputArchive const & ) = delete;

//! Serializes all passed in data
Expand Down Expand Up @@ -729,11 +734,18 @@ namespace cereal
}

//! Helper macro that expands the requirements for activating an overload
#define PROCESS_IF(name) \
traits::EnableIf<traits::has_##name<T, ArchiveType>::value, \
!traits::has_invalid_input_versioning<T, ArchiveType>::value, \
(traits::is_specialized_##name<T, ArchiveType>::value || \
traits::is_input_serializable<T, ArchiveType>::value)> = traits::sfinae
/*! Requirements:
Has the requested serialization function
Does not have version and unversioned at the same time
Is input serializable AND
is specialized for this type of function OR
has no specialization at all */
#define PROCESS_IF(name) \
traits::EnableIf<traits::has_##name<T, ArchiveType>::value, \
!traits::has_invalid_input_versioning<T, ArchiveType>::value, \
(traits::is_input_serializable<T, ArchiveType>::value && \
(traits::is_specialized_##name<T, ArchiveType>::value || \
!traits::is_specialized<T, ArchiveType>::value))> = traits::sfinae

//! Member serialization
template <class T, PROCESS_IF(member_serialize)> inline
Expand Down Expand Up @@ -791,7 +803,6 @@ namespace cereal

//! Empty class specialization
template <class T, traits::EnableIf<(Flags & AllowEmptyClassElision),
!traits::is_specialized<T, ArchiveType>::value,
!traits::is_input_serializable<T, ArchiveType>::value,
std::is_empty<T>::value> = traits::sfinae> inline
ArchiveType & processImpl(T const &)
Expand All @@ -801,11 +812,10 @@ namespace cereal

//! No matching serialization
/*! Invalid if we have invalid input versioning or
we have no specialization, are not input serializable, and either
we are not input serializable, and either
don't allow empty class ellision or allow it but are not serializing an empty class */
template <class T, traits::EnableIf<traits::has_invalid_input_versioning<T, ArchiveType>::value ||
(!traits::is_specialized<T, ArchiveType>::value &&
!traits::is_input_serializable<T, ArchiveType>::value &&
(!traits::is_input_serializable<T, ArchiveType>::value &&
(!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty<T>::value)))> = traits::sfinae> inline
ArchiveType & processImpl(T const &)
{
Expand Down
120 changes: 63 additions & 57 deletions include/cereal/details/traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,54 +884,6 @@ namespace cereal
(has_non_member_load<T, InputArchive>::value && has_non_member_save<T, OutputArchive>::value) ||
(has_non_member_versioned_load<T, InputArchive>::value && has_non_member_versioned_save<T, OutputArchive>::value)> {};

// ######################################################################
namespace detail
{
template <class T, class OutputArchive>
struct count_output_serializers : std::integral_constant<int,
has_member_save<T, OutputArchive>::value +
has_non_member_save<T, OutputArchive>::value +
has_member_serialize<T, OutputArchive>::value +
has_non_member_serialize<T, OutputArchive>::value +
has_member_save_minimal<T, OutputArchive>::value +
has_non_member_save_minimal<T, OutputArchive>::value +
/*-versioned---------------------------------------------------------*/
has_member_versioned_save<T, OutputArchive>::value +
has_non_member_versioned_save<T, OutputArchive>::value +
has_member_versioned_serialize<T, OutputArchive>::value +
has_non_member_versioned_serialize<T, OutputArchive>::value +
has_member_versioned_save_minimal<T, OutputArchive>::value +
has_non_member_versioned_save_minimal<T, OutputArchive>::value> {};
}

template <class T, class OutputArchive>
struct is_output_serializable : std::integral_constant<bool,
detail::count_output_serializers<T, OutputArchive>::value == 1> {};

// ######################################################################
namespace detail
{
template <class T, class InputArchive>
struct count_input_serializers : std::integral_constant<int,
has_member_load<T, InputArchive>::value +
has_non_member_load<T, InputArchive>::value +
has_member_serialize<T, InputArchive>::value +
has_non_member_serialize<T, InputArchive>::value +
has_member_load_minimal<T, InputArchive>::value +
has_non_member_load_minimal<T, InputArchive>::value +
/*-versioned---------------------------------------------------------*/
has_member_versioned_load<T, InputArchive>::value +
has_non_member_versioned_load<T, InputArchive>::value +
has_member_versioned_serialize<T, InputArchive>::value +
has_non_member_versioned_serialize<T, InputArchive>::value +
has_member_versioned_load_minimal<T, InputArchive>::value +
has_non_member_versioned_load_minimal<T, InputArchive>::value> {};
}

template <class T, class InputArchive>
struct is_input_serializable : std::integral_constant<bool,
detail::count_input_serializers<T, InputArchive>::value == 1> {};

// ######################################################################
template <class T, class OutputArchive>
struct has_invalid_output_versioning : std::integral_constant<bool,
Expand Down Expand Up @@ -970,15 +922,15 @@ namespace cereal

#undef CEREAL_MAKE_IS_SPECIALIZED_IMPL

//! Considered an error if specialization exists for more than one type
//! Number of specializations detected
template <class T, class A>
struct is_specialized_error : std::integral_constant<bool,
(is_specialized_member_serialize<T, A>::value +
is_specialized_member_load_save<T, A>::value +
is_specialized_member_load_save_minimal<T, A>::value +
is_specialized_non_member_serialize<T, A>::value +
is_specialized_non_member_load_save<T, A>::value +
is_specialized_non_member_load_save_minimal<T, A>::value) <= 1> {};
struct count_specializations : std::integral_constant<int,
is_specialized_member_serialize<T, A>::value +
is_specialized_member_load_save<T, A>::value +
is_specialized_member_load_save_minimal<T, A>::value +
is_specialized_non_member_serialize<T, A>::value +
is_specialized_non_member_load_save<T, A>::value +
is_specialized_non_member_load_save_minimal<T, A>::value> {};
} // namespace detail

//! Check if any specialization exists for a type
Expand All @@ -991,7 +943,7 @@ namespace cereal
detail::is_specialized_non_member_load_save<T, A>::value ||
detail::is_specialized_non_member_load_save_minimal<T, A>::value>
{
static_assert(detail::is_specialized_error<T, A>::value, "More than one explicit specialization detected for type.");
static_assert(detail::count_specializations<T, A>::value <= 1, "More than one explicit specialization detected for type.");
};

//! Create the static assertion for some specialization
Expand Down Expand Up @@ -1056,6 +1008,60 @@ namespace cereal
!(is_specialized_member_serialize<T, InputArchive>::value ||
is_specialized_member_load<T, InputArchive>::value))> {};

// ######################################################################
namespace detail
{
//! The number of output serialization functions available
/*! If specialization is being used, we'll count only those; otherwise we'll count everything */
template <class T, class OutputArchive>
struct count_output_serializers : std::integral_constant<int,
count_specializations<T, OutputArchive>::value ? count_specializations<T, OutputArchive>::value :
has_member_save<T, OutputArchive>::value +
has_non_member_save<T, OutputArchive>::value +
has_member_serialize<T, OutputArchive>::value +
has_non_member_serialize<T, OutputArchive>::value +
has_member_save_minimal<T, OutputArchive>::value +
has_non_member_save_minimal<T, OutputArchive>::value +
/*-versioned---------------------------------------------------------*/
has_member_versioned_save<T, OutputArchive>::value +
has_non_member_versioned_save<T, OutputArchive>::value +
has_member_versioned_serialize<T, OutputArchive>::value +
has_non_member_versioned_serialize<T, OutputArchive>::value +
has_member_versioned_save_minimal<T, OutputArchive>::value +
has_non_member_versioned_save_minimal<T, OutputArchive>::value> {};
}

template <class T, class OutputArchive>
struct is_output_serializable : std::integral_constant<bool,
detail::count_output_serializers<T, OutputArchive>::value == 1> {};

// ######################################################################
namespace detail
{
//! The number of input serialization functions available
/*! If specialization is being used, we'll count only those; otherwise we'll count everything */
template <class T, class InputArchive>
struct count_input_serializers : std::integral_constant<int,
count_specializations<T, InputArchive>::value ? count_specializations<T, InputArchive>::value :
has_member_load<T, InputArchive>::value +
has_non_member_load<T, InputArchive>::value +
has_member_serialize<T, InputArchive>::value +
has_non_member_serialize<T, InputArchive>::value +
has_member_load_minimal<T, InputArchive>::value +
has_non_member_load_minimal<T, InputArchive>::value +
/*-versioned---------------------------------------------------------*/
has_member_versioned_load<T, InputArchive>::value +
has_non_member_versioned_load<T, InputArchive>::value +
has_member_versioned_serialize<T, InputArchive>::value +
has_non_member_versioned_serialize<T, InputArchive>::value +
has_member_versioned_load_minimal<T, InputArchive>::value +
has_non_member_versioned_load_minimal<T, InputArchive>::value> {};
}

template <class T, class InputArchive>
struct is_input_serializable : std::integral_constant<bool,
detail::count_input_serializers<T, InputArchive>::value == 1> {};

// ######################################################################
// Base Class Support
namespace detail
Expand Down
2 changes: 1 addition & 1 deletion sandbox/sandbox_vs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ int main()
std::cout << cereal::traits::detail::is_specialized_member_load_save<T, Archive>::value << std::endl;
std::cout << cereal::traits::detail::is_specialized_non_member_serialize<T, Archive>::value << std::endl;
std::cout << cereal::traits::detail::is_specialized_non_member_load_save<T, Archive>::value << std::endl;
std::cout << cereal::traits::detail::is_specialized_error<T, Archive>::value << std::endl;
std::cout << cereal::traits::detail::count_specializations<T, Archive>::value << std::endl;
std::cout << cereal::traits::is_specialized<T, Archive>::value << std::endl;

// array size
Expand Down
70 changes: 56 additions & 14 deletions unittests/structs_specialized.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ struct BogusBaseVersioned
void load_minimal( Archive const &, int const &, const std::uint32_t ) {}
};

struct BogusBasePolymorphic
{
template <class Archive>
void serialize( Archive & ) {}

virtual void doesNothing() {}
};

class SpecializedMSerialize : public BogusBase
{
public:
Expand Down Expand Up @@ -143,6 +151,29 @@ class SpecializedMSplitVersioned : public BogusBaseVersioned
}
};

class SpecializedMSplitPolymorphic : public BogusBasePolymorphic
{
public:
SpecializedMSplitPolymorphic() = default;
SpecializedMSplitPolymorphic( int xx ) : x(xx) {}

int x;

private:
friend class cereal::access;
template <class Archive>
void save( Archive & ar ) const
{
ar( x );
}

template <class Archive>
void load( Archive & ar )
{
ar( x );
}
};

class SpecializedMSplitMinimal : public BogusBase
{
public:
Expand Down Expand Up @@ -310,6 +341,7 @@ namespace cereal

template <class Archive> struct specialize<Archive, SpecializedMSplit, cereal::specialization::member_load_save> {};
template <class Archive> struct specialize<Archive, SpecializedMSplitVersioned, cereal::specialization::member_load_save> {};
template <class Archive> struct specialize<Archive, SpecializedMSplitPolymorphic, cereal::specialization::member_load_save> {};

template <class Archive> struct specialize<Archive, SpecializedMSplitMinimal, cereal::specialization::member_load_save_minimal> {};
template <class Archive> struct specialize<Archive, SpecializedMSplitVersionedMinimal, cereal::specialization::member_load_save_minimal> {};
Expand All @@ -326,6 +358,8 @@ namespace cereal
cereal::specialization::non_member_load_save_minimal> {};
}

CEREAL_REGISTER_TYPE(SpecializedMSplitPolymorphic)

template <class IArchive, class OArchive>
void test_structs_specialized()
{
Expand All @@ -334,30 +368,34 @@ void test_structs_specialized()

for(int ii=0; ii<100; ++ii)
{
SpecializedMSerialize o_iser = { random_value<int>(gen) };
SpecializedMSerializeVersioned o_iserv = { random_value<int>(gen) };
SpecializedMSerialize o_iser = { random_value<int>(gen) };
SpecializedMSerializeVersioned o_iserv = { random_value<int>(gen) };

SpecializedMSplit o_ispl = { random_value<int>(gen) };
SpecializedMSplitVersioned o_isplv = { random_value<int>(gen) };

SpecializedMSplit o_ispl = { random_value<int>(gen) };
SpecializedMSplitVersioned o_isplv = { random_value<int>(gen) };
// added re: issue #180
std::shared_ptr<BogusBasePolymorphic> o_shared_ispl = std::make_shared<SpecializedMSplitPolymorphic>( random_value<int>(gen) );

SpecializedMSplitMinimal o_isplm = { random_value<int>(gen) };
SpecializedMSplitVersionedMinimal o_isplvm = { random_value<int>(gen) };
SpecializedMSplitMinimal o_isplm = { random_value<int>(gen) };
SpecializedMSplitVersionedMinimal o_isplvm = { random_value<int>(gen) };

SpecializedNMSerialize o_eser = { random_value<int>(gen) };
SpecializedNMSerializeVersioned o_eserv = { random_value<int>(gen) };
SpecializedNMSerialize o_eser = { random_value<int>(gen) };
SpecializedNMSerializeVersioned o_eserv = { random_value<int>(gen) };

SpecializedNMSplit o_espl = { random_value<int>(gen) };
SpecializedNMSplitVersioned o_esplv = { random_value<int>(gen) };
SpecializedNMSplit o_espl = { random_value<int>(gen) };
SpecializedNMSplitVersioned o_esplv = { random_value<int>(gen) };

SpecializedNMSplitMinimal o_esplm = { random_value<int>(gen) };
SpecializedNMSplitVersionedMinimal o_esplvm = { random_value<int>(gen) };

SpecializedNMSplitMinimal o_esplm = { random_value<int>(gen) };
SpecializedNMSplitVersionedMinimal o_esplvm = { random_value<int>(gen) };

std::ostringstream os;
{
OArchive oar(os);

oar( o_iser, o_iserv,
o_ispl, o_isplv,
o_ispl, o_isplv, o_shared_ispl,
o_isplm, o_isplvm,
o_eser, o_eserv,
o_espl, o_esplv,
Expand All @@ -370,6 +408,8 @@ void test_structs_specialized()
decltype(o_ispl) i_ispl;
decltype(o_isplv) i_isplv;

decltype(o_shared_ispl) i_shared_ispl;

decltype(o_isplm) i_isplm;
decltype(o_isplvm) i_isplvm;

Expand All @@ -387,7 +427,7 @@ void test_structs_specialized()
IArchive iar(is);

iar( i_iser, i_iserv,
i_ispl, i_isplv,
i_ispl, i_isplv, i_shared_ispl,
i_isplm, i_isplvm,
i_eser, i_eserv,
i_espl, i_esplv,
Expand All @@ -400,6 +440,8 @@ void test_structs_specialized()
BOOST_CHECK(i_ispl.x == o_ispl.x);
BOOST_CHECK(i_isplv.x == o_isplv.x);

BOOST_CHECK_EQUAL(((SpecializedMSplitPolymorphic*)i_shared_ispl.get())->x, ((SpecializedMSplitPolymorphic*)o_shared_ispl.get())->x);

BOOST_CHECK(i_isplm.x == o_isplm.x);
BOOST_CHECK(i_isplvm.x == o_isplvm.x);

Expand Down

0 comments on commit 94d49c4

Please sign in to comment.