// Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef TWOBLUECUBES_CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED #define TWOBLUECUBES_CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED #include #include #include #include #include #include #include #include namespace Catch { namespace Matchers { struct MatcherGenericBase : MatcherUntypedBase { MatcherGenericBase() = default; virtual ~MatcherGenericBase(); // = default; MatcherGenericBase(MatcherGenericBase&) = default; MatcherGenericBase(MatcherGenericBase&&) = default; MatcherGenericBase& operator=(MatcherGenericBase const&) = delete; MatcherGenericBase& operator=(MatcherGenericBase&&) = delete; }; namespace Detail { template std::array array_cat(std::array && lhs, std::array && rhs) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); std::copy_n(rhs.begin(), M, arr.begin() + N); return arr; } template std::array array_cat(std::array && lhs, void const* rhs) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); arr[N] = rhs; return arr; } template std::array array_cat(void const* lhs, std::array && rhs) { std::array arr{ {lhs} }; std::copy_n(rhs.begin(), N, arr.begin() + 1); return arr; } #ifdef CATCH_CPP17_OR_GREATER using std::conjunction; #else // CATCH_CPP17_OR_GREATER template struct conjunction : std::true_type {}; template struct conjunction : std::integral_constant::value> {}; #endif // CATCH_CPP17_OR_GREATER template using is_generic_matcher = std::is_base_of< Catch::Matchers::MatcherGenericBase, std::remove_cv_t> >; template using are_generic_matchers = conjunction...>; template using is_matcher = std::is_base_of< Catch::Matchers::MatcherUntypedBase, std::remove_cv_t> >; template bool match_all_of(Arg&&, std::array const&, std::index_sequence<>) { return true; } template bool match_all_of(Arg&& arg, std::array const& matchers, std::index_sequence) { return static_cast(matchers[Idx])->match(arg) && match_all_of(arg, matchers, std::index_sequence{}); } template bool match_any_of(Arg&&, std::array const&, std::index_sequence<>) { return false; } template bool match_any_of(Arg&& arg, std::array const& matchers, std::index_sequence) { return static_cast(matchers[Idx])->match(arg) || match_any_of(arg, matchers, std::index_sequence{}); } std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end); template std::string describe_multi_matcher(StringRef combine, std::array const& matchers, std::index_sequence) { std::array descriptions {{ static_cast(matchers[Idx])->toString()... }}; return describe_multi_matcher(combine, descriptions.data(), descriptions.data() + descriptions.size()); } template struct MatchAllOfGeneric final : MatcherGenericBase { MatchAllOfGeneric(MatchAllOfGeneric const&) = delete; MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete; MatchAllOfGeneric(MatchAllOfGeneric&&) = default; MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} explicit MatchAllOfGeneric(std::array matchers) : m_matchers{matchers} {} template bool match(Arg&& arg) const { return match_all_of(arg, m_matchers, std::index_sequence_for{}); } std::string describe() const override { return describe_multi_matcher(" and "_sr, m_matchers, std::index_sequence_for{}); } std::array m_matchers; //! Avoids type nesting for `GenericAllOf && GenericAllOf` case template friend MatchAllOfGeneric operator && ( MatchAllOfGeneric&& lhs, MatchAllOfGeneric&& rhs) { return MatchAllOfGeneric{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAllOf && some matcher` case template friend std::enable_if_t::value, MatchAllOfGeneric> operator && ( MatchAllOfGeneric&& lhs, MatcherRHS const& rhs) { return MatchAllOfGeneric{array_cat(std::move(lhs.m_matchers), static_cast(&rhs))}; } //! Avoids type nesting for `some matcher && GenericAllOf` case template friend std::enable_if_t::value, MatchAllOfGeneric> operator && ( MatcherLHS const& lhs, MatchAllOfGeneric&& rhs) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), std::move(rhs.m_matchers))}; } }; template struct MatchAnyOfGeneric final : MatcherGenericBase { MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete; MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} explicit MatchAnyOfGeneric(std::array matchers) : m_matchers{matchers} {} template bool match(Arg&& arg) const { return match_any_of(arg, m_matchers, std::index_sequence_for{}); } std::string describe() const override { return describe_multi_matcher(" or "_sr, m_matchers, std::index_sequence_for{}); } std::array m_matchers; //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template friend MatchAnyOfGeneric operator || ( MatchAnyOfGeneric&& lhs, MatchAnyOfGeneric&& rhs) { return MatchAnyOfGeneric{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAnyOf || some matcher` case template friend std::enable_if_t::value, MatchAnyOfGeneric> operator || ( MatchAnyOfGeneric&& lhs, MatcherRHS const& rhs) { return MatchAnyOfGeneric{array_cat(std::move(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } //! Avoids type nesting for `some matcher || GenericAnyOf` case template friend std::enable_if_t::value, MatchAnyOfGeneric> operator || ( MatcherLHS const& lhs, MatchAnyOfGeneric&& rhs) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), std::move(rhs.m_matchers))}; } }; template struct MatchNotOfGeneric final : MatcherGenericBase { MatchNotOfGeneric(MatchNotOfGeneric const&) = delete; MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete; MatchNotOfGeneric(MatchNotOfGeneric&&) = default; MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {} template bool match(Arg&& arg) const { return !m_matcher.match(arg); } std::string describe() const override { return "not " + m_matcher.toString(); } //! Negating negation can just unwrap and return underlying matcher friend MatcherT const& operator ! (MatchNotOfGeneric const& matcher) { return matcher.m_matcher; } private: MatcherT const& m_matcher; }; } // namespace Detail // compose only generic matchers template std::enable_if_t::value, Detail::MatchAllOfGeneric> operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { return { lhs, rhs }; } template std::enable_if_t::value, Detail::MatchAnyOfGeneric> operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { return { lhs, rhs }; } //! Wrap provided generic matcher in generic negator template std::enable_if_t::value, Detail::MatchNotOfGeneric> operator ! (MatcherT const& matcher) { return Detail::MatchNotOfGeneric{matcher}; } // compose mixed generic and non-generic matchers template std::enable_if_t::value, Detail::MatchAllOfGeneric>> operator && (MatcherLHS const& lhs, MatcherBase const& rhs) { return { lhs, rhs }; } template std::enable_if_t::value, Detail::MatchAllOfGeneric, MatcherRHS>> operator && (MatcherBase const& lhs, MatcherRHS const& rhs) { return { lhs, rhs }; } template std::enable_if_t::value, Detail::MatchAnyOfGeneric>> operator || (MatcherLHS const& lhs, MatcherBase const& rhs) { return { lhs, rhs }; } template std::enable_if_t::value, Detail::MatchAnyOfGeneric, MatcherRHS>> operator || (MatcherBase const& lhs, MatcherRHS const& rhs) { return { lhs, rhs }; } } // namespace Matchers } // namespace Catch #endif //TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED