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);