// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 #pragma once #include #include #include #include #include #include #include #include #include namespace Catch { namespace Matchers { class MatcherGenericBase : public MatcherUntypedBase { std::string describe() const override; public: constexpr MatcherGenericBase() = default; constexpr MatcherGenericBase(MatcherGenericBase const&) = default; constexpr MatcherGenericBase(MatcherGenericBase&&) = default; MatcherGenericBase& operator=(MatcherGenericBase const&) = delete; MatcherGenericBase& operator=(MatcherGenericBase&&) = delete; }; namespace Detail { template constexpr 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 constexpr 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 constexpr 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; } template static constexpr bool is_generic_matcher_v = std::is_base_of< Catch::Matchers::MatcherGenericBase, std::remove_cv_t> >::value; template static constexpr bool are_generic_matchers_v = Catch::Detail::conjunction>...>::value; template static constexpr bool is_matcher_v = std::is_base_of< Catch::Matchers::MatcherUntypedBase, std::remove_cv_t> >::value; template constexpr bool match_all_of( Arg&&, std::array const&, std::index_sequence<> ) { return true; } template constexpr 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 constexpr bool match_any_of( Arg&&, std::array const&, std::index_sequence<> ) { return false; } template constexpr 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 class MatchAllOfGeneric final : public MatcherGenericBase { public: MatchAllOfGeneric(MatchAllOfGeneric const&) = delete; MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete; constexpr MatchAllOfGeneric( MatchAllOfGeneric&& ) = default; constexpr MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; constexpr MatchAllOfGeneric( MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) : m_matchers{ {std::addressof(matchers)...} } {} constexpr explicit MatchAllOfGeneric( std::array matchers ): m_matchers{ matchers } {} template constexpr 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{}); } // Has to be public to enable the concatenating operators // below, because they are not friend of the RHS, only LHS, // and thus cannot access private fields of RHS std::array m_matchers; //! Avoids type nesting for `GenericAllOf && GenericAllOf` case template constexpr friend MatchAllOfGeneric operator && ( MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAllOf && some matcher` case template constexpr friend std::enable_if_t< is_matcher_v, MatchAllOfGeneric> operator && ( MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(&rhs))}; } //! Avoids type nesting for `some matcher && GenericAllOf` case template constexpr friend std::enable_if_t< is_matcher_v, MatchAllOfGeneric> operator && ( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; template class MatchAnyOfGeneric final : public MatcherGenericBase { public: MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete; constexpr MatchAnyOfGeneric( MatchAnyOfGeneric&& ) = default; constexpr MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; constexpr MatchAnyOfGeneric( MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) : m_matchers{ {std::addressof(matchers)...} } {} constexpr explicit MatchAnyOfGeneric( std::array matchers ): m_matchers{ matchers } {} template constexpr 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{}); } // Has to be public to enable the concatenating operators // below, because they are not friend of the RHS, only LHS, // and thus cannot access private fields of RHS std::array m_matchers; //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template constexpr friend MatchAnyOfGeneric operator||( MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAnyOf || some matcher` case template constexpr friend std::enable_if_t< is_matcher_v, MatchAnyOfGeneric> operator || ( MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } //! Avoids type nesting for `some matcher || GenericAnyOf` case template constexpr friend std::enable_if_t< is_matcher_v, MatchAnyOfGeneric> operator || ( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; template class MatchNotOfGeneric final : public MatcherGenericBase { MatcherT const& m_matcher; public: MatchNotOfGeneric(MatchNotOfGeneric const&) = delete; MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete; constexpr MatchNotOfGeneric( MatchNotOfGeneric&& ) = default; constexpr MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; constexpr explicit MatchNotOfGeneric( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) : m_matcher{matcher} {} template constexpr 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 constexpr friend MatcherT const& operator!( MatchNotOfGeneric const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return matcher.m_matcher; } }; } // namespace Detail // compose only generic matchers template constexpr std::enable_if_t< Detail::are_generic_matchers_v, Detail::MatchAllOfGeneric> operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template constexpr std::enable_if_t< Detail::are_generic_matchers_v, Detail::MatchAnyOfGeneric> operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } //! Wrap provided generic matcher in generic negator template constexpr std::enable_if_t, Detail::MatchNotOfGeneric> operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOfGeneric{matcher}; } // compose mixed generic and non-generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric>> operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAllOfGeneric, MatcherRHS>> operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric>> operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric, MatcherRHS>> operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } } // namespace Matchers } // namespace Catch