From 94d49c42acfb3dfac495f9610008140d78aecec7 Mon Sep 17 00:00:00 2001 From: Shane Grant Date: Sat, 4 Apr 2015 18:19:22 -0700 Subject: [PATCH] is_in/out_serializable is now aware of specialization 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 --- include/cereal/cereal.hpp | 48 +++++++----- include/cereal/details/traits.hpp | 120 ++++++++++++++++-------------- sandbox/sandbox_vs.cpp | 2 +- unittests/structs_specialized.cpp | 70 +++++++++++++---- 4 files changed, 149 insertions(+), 91 deletions(-) diff --git a/include/cereal/cereal.hpp b/include/cereal/cereal.hpp index f20c9b36b..040366db7 100644 --- a/include/cereal/cereal.hpp +++ b/include/cereal/cereal.hpp @@ -366,11 +366,18 @@ namespace cereal } //! Helper macro that expands the requirements for activating an overload - #define PROCESS_IF(name) \ - traits::EnableIf::value, \ - !traits::has_invalid_output_versioning::value, \ - (traits::is_specialized_##name::value || \ - traits::is_output_serializable::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::value, \ + !traits::has_invalid_output_versioning::value, \ + (traits::is_output_serializable::value && \ + (traits::is_specialized_##name::value || \ + !traits::is_specialized::value))> = traits::sfinae //! Member serialization template inline @@ -422,7 +429,6 @@ namespace cereal //! Empty class specialization template ::value, !traits::is_output_serializable::value, std::is_empty::value> = traits::sfinae> inline ArchiveType & processImpl(T const &) @@ -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 ::value || - (!traits::is_specialized::value && - !traits::is_output_serializable::value && + (!traits::is_output_serializable::value && (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty::value)))> = traits::sfinae> inline ArchiveType & processImpl(T const &) { @@ -588,7 +593,7 @@ namespace cereal itsPolymorphicTypeMap(), itsVersionedTypes() { } - + InputArchive & operator=( InputArchive const & ) = delete; //! Serializes all passed in data @@ -729,11 +734,18 @@ namespace cereal } //! Helper macro that expands the requirements for activating an overload - #define PROCESS_IF(name) \ - traits::EnableIf::value, \ - !traits::has_invalid_input_versioning::value, \ - (traits::is_specialized_##name::value || \ - traits::is_input_serializable::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::value, \ + !traits::has_invalid_input_versioning::value, \ + (traits::is_input_serializable::value && \ + (traits::is_specialized_##name::value || \ + !traits::is_specialized::value))> = traits::sfinae //! Member serialization template inline @@ -791,7 +803,6 @@ namespace cereal //! Empty class specialization template ::value, !traits::is_input_serializable::value, std::is_empty::value> = traits::sfinae> inline ArchiveType & processImpl(T const &) @@ -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 ::value || - (!traits::is_specialized::value && - !traits::is_input_serializable::value && + (!traits::is_input_serializable::value && (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty::value)))> = traits::sfinae> inline ArchiveType & processImpl(T const &) { diff --git a/include/cereal/details/traits.hpp b/include/cereal/details/traits.hpp index 6dfaa5b68..b268f9ea7 100644 --- a/include/cereal/details/traits.hpp +++ b/include/cereal/details/traits.hpp @@ -884,54 +884,6 @@ namespace cereal (has_non_member_load::value && has_non_member_save::value) || (has_non_member_versioned_load::value && has_non_member_versioned_save::value)> {}; - // ###################################################################### - namespace detail - { - template - struct count_output_serializers : std::integral_constant::value + - has_non_member_save::value + - has_member_serialize::value + - has_non_member_serialize::value + - has_member_save_minimal::value + - has_non_member_save_minimal::value + - /*-versioned---------------------------------------------------------*/ - has_member_versioned_save::value + - has_non_member_versioned_save::value + - has_member_versioned_serialize::value + - has_non_member_versioned_serialize::value + - has_member_versioned_save_minimal::value + - has_non_member_versioned_save_minimal::value> {}; - } - - template - struct is_output_serializable : std::integral_constant::value == 1> {}; - - // ###################################################################### - namespace detail - { - template - struct count_input_serializers : std::integral_constant::value + - has_non_member_load::value + - has_member_serialize::value + - has_non_member_serialize::value + - has_member_load_minimal::value + - has_non_member_load_minimal::value + - /*-versioned---------------------------------------------------------*/ - has_member_versioned_load::value + - has_non_member_versioned_load::value + - has_member_versioned_serialize::value + - has_non_member_versioned_serialize::value + - has_member_versioned_load_minimal::value + - has_non_member_versioned_load_minimal::value> {}; - } - - template - struct is_input_serializable : std::integral_constant::value == 1> {}; - // ###################################################################### template struct has_invalid_output_versioning : std::integral_constant - struct is_specialized_error : std::integral_constant::value + - is_specialized_member_load_save::value + - is_specialized_member_load_save_minimal::value + - is_specialized_non_member_serialize::value + - is_specialized_non_member_load_save::value + - is_specialized_non_member_load_save_minimal::value) <= 1> {}; + struct count_specializations : std::integral_constant::value + + is_specialized_member_load_save::value + + is_specialized_member_load_save_minimal::value + + is_specialized_non_member_serialize::value + + is_specialized_non_member_load_save::value + + is_specialized_non_member_load_save_minimal::value> {}; } // namespace detail //! Check if any specialization exists for a type @@ -991,7 +943,7 @@ namespace cereal detail::is_specialized_non_member_load_save::value || detail::is_specialized_non_member_load_save_minimal::value> { - static_assert(detail::is_specialized_error::value, "More than one explicit specialization detected for type."); + static_assert(detail::count_specializations::value <= 1, "More than one explicit specialization detected for type."); }; //! Create the static assertion for some specialization @@ -1056,6 +1008,60 @@ namespace cereal !(is_specialized_member_serialize::value || is_specialized_member_load::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 + struct count_output_serializers : std::integral_constant::value ? count_specializations::value : + has_member_save::value + + has_non_member_save::value + + has_member_serialize::value + + has_non_member_serialize::value + + has_member_save_minimal::value + + has_non_member_save_minimal::value + + /*-versioned---------------------------------------------------------*/ + has_member_versioned_save::value + + has_non_member_versioned_save::value + + has_member_versioned_serialize::value + + has_non_member_versioned_serialize::value + + has_member_versioned_save_minimal::value + + has_non_member_versioned_save_minimal::value> {}; + } + + template + struct is_output_serializable : std::integral_constant::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 + struct count_input_serializers : std::integral_constant::value ? count_specializations::value : + has_member_load::value + + has_non_member_load::value + + has_member_serialize::value + + has_non_member_serialize::value + + has_member_load_minimal::value + + has_non_member_load_minimal::value + + /*-versioned---------------------------------------------------------*/ + has_member_versioned_load::value + + has_non_member_versioned_load::value + + has_member_versioned_serialize::value + + has_non_member_versioned_serialize::value + + has_member_versioned_load_minimal::value + + has_non_member_versioned_load_minimal::value> {}; + } + + template + struct is_input_serializable : std::integral_constant::value == 1> {}; + // ###################################################################### // Base Class Support namespace detail diff --git a/sandbox/sandbox_vs.cpp b/sandbox/sandbox_vs.cpp index 954d74c85..2ec989fcd 100644 --- a/sandbox/sandbox_vs.cpp +++ b/sandbox/sandbox_vs.cpp @@ -224,7 +224,7 @@ int main() std::cout << cereal::traits::detail::is_specialized_member_load_save::value << std::endl; std::cout << cereal::traits::detail::is_specialized_non_member_serialize::value << std::endl; std::cout << cereal::traits::detail::is_specialized_non_member_load_save::value << std::endl; - std::cout << cereal::traits::detail::is_specialized_error::value << std::endl; + std::cout << cereal::traits::detail::count_specializations::value << std::endl; std::cout << cereal::traits::is_specialized::value << std::endl; // array size diff --git a/unittests/structs_specialized.cpp b/unittests/structs_specialized.cpp index adbadeefe..bb26e6702 100644 --- a/unittests/structs_specialized.cpp +++ b/unittests/structs_specialized.cpp @@ -63,6 +63,14 @@ struct BogusBaseVersioned void load_minimal( Archive const &, int const &, const std::uint32_t ) {} }; +struct BogusBasePolymorphic +{ + template + void serialize( Archive & ) {} + + virtual void doesNothing() {} +}; + class SpecializedMSerialize : public BogusBase { public: @@ -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 + void save( Archive & ar ) const + { + ar( x ); + } + + template + void load( Archive & ar ) + { + ar( x ); + } +}; + class SpecializedMSplitMinimal : public BogusBase { public: @@ -310,6 +341,7 @@ namespace cereal template struct specialize {}; template struct specialize {}; + template struct specialize {}; template struct specialize {}; template struct specialize {}; @@ -326,6 +358,8 @@ namespace cereal cereal::specialization::non_member_load_save_minimal> {}; } +CEREAL_REGISTER_TYPE(SpecializedMSplitPolymorphic) + template void test_structs_specialized() { @@ -334,30 +368,34 @@ void test_structs_specialized() for(int ii=0; ii<100; ++ii) { - SpecializedMSerialize o_iser = { random_value(gen) }; - SpecializedMSerializeVersioned o_iserv = { random_value(gen) }; + SpecializedMSerialize o_iser = { random_value(gen) }; + SpecializedMSerializeVersioned o_iserv = { random_value(gen) }; + + SpecializedMSplit o_ispl = { random_value(gen) }; + SpecializedMSplitVersioned o_isplv = { random_value(gen) }; - SpecializedMSplit o_ispl = { random_value(gen) }; - SpecializedMSplitVersioned o_isplv = { random_value(gen) }; + // added re: issue #180 + std::shared_ptr o_shared_ispl = std::make_shared( random_value(gen) ); - SpecializedMSplitMinimal o_isplm = { random_value(gen) }; - SpecializedMSplitVersionedMinimal o_isplvm = { random_value(gen) }; + SpecializedMSplitMinimal o_isplm = { random_value(gen) }; + SpecializedMSplitVersionedMinimal o_isplvm = { random_value(gen) }; - SpecializedNMSerialize o_eser = { random_value(gen) }; - SpecializedNMSerializeVersioned o_eserv = { random_value(gen) }; + SpecializedNMSerialize o_eser = { random_value(gen) }; + SpecializedNMSerializeVersioned o_eserv = { random_value(gen) }; - SpecializedNMSplit o_espl = { random_value(gen) }; - SpecializedNMSplitVersioned o_esplv = { random_value(gen) }; + SpecializedNMSplit o_espl = { random_value(gen) }; + SpecializedNMSplitVersioned o_esplv = { random_value(gen) }; + + SpecializedNMSplitMinimal o_esplm = { random_value(gen) }; + SpecializedNMSplitVersionedMinimal o_esplvm = { random_value(gen) }; - SpecializedNMSplitMinimal o_esplm = { random_value(gen) }; - SpecializedNMSplitVersionedMinimal o_esplvm = { random_value(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, @@ -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; @@ -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, @@ -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);