#ifndef TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED #define TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED #include #include #include #include #include #include #include #include #include namespace Catch { namespace Matchers { namespace Impl { template struct MatchAllOfGeneric; template struct MatchAnyOfGeneric; template struct MatchNotOfGeneric; struct MatcherGenericBase : MatcherUntypedBase { virtual ~MatcherGenericBase(); }; 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::Impl::MatcherGenericBase, typename std::remove_cv::type>::type >; template using are_generic_matchers = conjunction...>; template using is_matcher = std::is_base_of< Catch::Matchers::Impl::MatcherUntypedBase, typename std::remove_cv::type>::type >; 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 : MatcherGenericBase { 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; }; template struct MatchAnyOfGeneric : MatcherGenericBase { 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; }; template struct MatchNotOfGeneric : MatcherGenericBase { 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(); } MatcherT const& m_matcher; }; // compose only generic matchers template typename std::enable_if::value, MatchAllOfGeneric>::type operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { return {lhs, rhs}; } template typename std::enable_if::value, MatchAnyOfGeneric>::type operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { return {lhs, rhs}; } template typename std::enable_if::value, MatchNotOfGeneric>::type operator ! (MatcherT const& matcher) { return MatchNotOfGeneric{matcher}; } // compose mixed generic and non-generic matchers template typename std::enable_if::value, MatchAllOfGeneric>>::type operator && (MatcherLHS const& lhs, MatcherBase const& rhs) { return {lhs, rhs}; } template typename std::enable_if::value, MatchAllOfGeneric, MatcherRHS>>::type operator && (MatcherBase const& lhs, MatcherRHS const& rhs) { return {lhs, rhs}; } template typename std::enable_if::value, MatchAnyOfGeneric>>::type operator || (MatcherLHS const& lhs, MatcherBase const& rhs) { return {lhs, rhs}; } template typename std::enable_if::value, MatchAnyOfGeneric, MatcherRHS>>::type operator || (MatcherBase const& lhs, MatcherRHS const& rhs) { return {lhs, rhs}; } // avoid deep nesting of matcher templates template MatchAllOfGeneric operator && (MatchAllOfGeneric && lhs, MatchAllOfGeneric && rhs) { return MatchAllOfGeneric{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))}; } template typename std::enable_if::value, MatchAllOfGeneric>::type operator && (MatchAllOfGeneric && lhs, MatcherRHS const& rhs) { return MatchAllOfGeneric{array_cat(std::move(lhs.m_matchers), static_cast(&rhs))}; } template typename std::enable_if::value, MatchAllOfGeneric>::type operator && (MatcherLHS const& lhs, MatchAllOfGeneric && rhs) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), std::move(rhs.m_matchers))}; } template MatchAnyOfGeneric operator || (MatchAnyOfGeneric && lhs, MatchAnyOfGeneric && rhs) { return MatchAnyOfGeneric{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))}; } template typename std::enable_if::value, MatchAnyOfGeneric>::type operator || (MatchAnyOfGeneric && lhs, MatcherRHS const& rhs) { return MatchAnyOfGeneric{array_cat(std::move(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } template typename std::enable_if::value, MatchAnyOfGeneric>::type operator || (MatcherLHS const& lhs, MatchAnyOfGeneric && rhs) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), std::move(rhs.m_matchers))}; } template MatcherT const& operator ! (MatchNotOfGeneric const& matcher) { return matcher.m_matcher; } } // namespace Impl } // namespace Matchers using namespace Matchers; using Matchers::Impl::MatcherGenericBase; } // namespace Catch #endif //TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED