From d838f88b9c9a05d2879d29a5a54f864f823a1d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Tue, 12 May 2026 10:04:14 +0200 Subject: [PATCH] Constexpr matching support in generic Contains matchers --- docs/matchers.md | 2 ++ .../matchers/catch_matchers_contains.hpp | 14 ++++---- .../UsageTests/MatchersConstexpr.tests.cpp | 34 ++++++++++++------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/docs/matchers.md b/docs/matchers.md index b89006a9..5f912e44 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -502,6 +502,8 @@ matching. Currently, this includes: * `AnyMatch(Matcher element_matcher)` * `NoneMatch(Matcher element_matcher)` * `AllTrue()`, `AnyTrue()`, `NoneTrue()` +* `Contains(T&& target_element, Comparator = std::equal_to<>{})` +* `Contains(Matcher element_matcher)` --- diff --git a/src/catch2/matchers/catch_matchers_contains.hpp b/src/catch2/matchers/catch_matchers_contains.hpp index 90cfe641..85168030 100644 --- a/src/catch2/matchers/catch_matchers_contains.hpp +++ b/src/catch2/matchers/catch_matchers_contains.hpp @@ -23,7 +23,7 @@ namespace Catch { Equality m_eq; public: template - ContainsElementMatcher(T2&& target, Equality2&& predicate): + constexpr ContainsElementMatcher(T2&& target, Equality2&& predicate): m_desired(CATCH_FORWARD(target)), m_eq(CATCH_FORWARD(predicate)) {} @@ -33,7 +33,7 @@ namespace Catch { } template - bool match( RangeLike&& rng ) const { + constexpr bool match( RangeLike&& rng ) const { for ( auto&& elem : rng ) { if ( m_eq( elem, m_desired ) ) { return true; } } @@ -49,12 +49,12 @@ namespace Catch { // Note that we do a copy+move to avoid having to SFINAE this // constructor (and also avoid some perfect forwarding failure // cases) - ContainsMatcherMatcher(Matcher matcher): + constexpr ContainsMatcherMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return true; @@ -74,14 +74,14 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template - std::enable_if_t, + constexpr std::enable_if_t, ContainsElementMatcher>> Contains(T&& elem) { return { CATCH_FORWARD(elem), std::equal_to<>{} }; } //! Creates a matcher that checks whether a range contains element matching a matcher template - std::enable_if_t, + constexpr std::enable_if_t, ContainsMatcherMatcher> Contains(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } @@ -92,7 +92,7 @@ namespace Catch { * Uses `eq` to do the comparisons, the element is provided on the rhs */ template - ContainsElementMatcher Contains(T&& elem, Equality&& eq) { + constexpr ContainsElementMatcher Contains(T&& elem, Equality&& eq) { return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) }; } diff --git a/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp b/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp index 66fe7c8f..6cd687f8 100644 --- a/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp +++ b/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -47,6 +48,18 @@ namespace { constexpr MatchTrueMatcher MatchTrue() { return MatchTrueMatcher(); } + constexpr std::array compute_bools( int type ) { + switch ( type ) { + case 0: + return { true, true, true }; + case 1: + return { false, true, false }; + case 2: + return { false, false, false }; + default: + return { false, false, false }; + } + } } // namespace @@ -65,19 +78,6 @@ TEST_CASE( "IsEmpty and HasSize matchers can be used in constexpr contexts", STATIC_REQUIRE_THAT( arr, SizeIs( MatchAll() ) ); } -constexpr std::array compute_bools( int type ) { - switch ( type ) { - case 0: - return { true, true, true }; - case 1: - return { false, true, false }; - case 2: - return { false, false, false }; - default: - return { false, false, false }; - } -} - TEST_CASE( "Quantifier matchers can be used in constexpr contexts", "[constexpr][matchers][approvals]" ) { using namespace Catch::Matchers; @@ -90,6 +90,14 @@ TEST_CASE( "Quantifier matchers can be used in constexpr contexts", STATIC_REQUIRE_THAT( compute_bools( 2 ), NoneMatch( MatchTrue() ) ); } +TEST_CASE( "Generic Equals matchers can be used in constexpr contexts", + "[constexpr][matchers][approvals]" ) { + using Catch::Matchers::Contains; + STATIC_REQUIRE_THAT( compute_bools( 0 ), Contains( true ) ); + STATIC_REQUIRE_THAT( compute_bools( 1 ), Contains( MatchTrue() ) ); + STATIC_REQUIRE_THAT( compute_bools( 2 ), Contains( true, std::not_equal_to<>{} ) ); +} + // Combining matchers needs C++26 and P2738, so they are in separate preprocessor block # if __cpp_constexpr >= 202306L