File indexing completed on 2025-09-17 08:53:27
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef CATCH_DECOMPOSER_HPP_INCLUDED
0009 #define CATCH_DECOMPOSER_HPP_INCLUDED
0010
0011 #include <catch2/catch_tostring.hpp>
0012 #include <catch2/internal/catch_stringref.hpp>
0013 #include <catch2/internal/catch_compare_traits.hpp>
0014 #include <catch2/internal/catch_test_failure_exception.hpp>
0015 #include <catch2/internal/catch_logical_traits.hpp>
0016 #include <catch2/internal/catch_compiler_capabilities.hpp>
0017
0018 #include <type_traits>
0019 #include <iosfwd>
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 #ifdef _MSC_VER
0102 #pragma warning(push)
0103 #pragma warning(disable:4389)
0104 #pragma warning(disable:4018)
0105 #pragma warning(disable:4312)
0106 #pragma warning(disable:4180)
0107 #pragma warning(disable:4800)
0108 #endif
0109
0110 #ifdef __clang__
0111 # pragma clang diagnostic push
0112 # pragma clang diagnostic ignored "-Wsign-compare"
0113 # pragma clang diagnostic ignored "-Wnon-virtual-dtor"
0114 #elif defined __GNUC__
0115 # pragma GCC diagnostic push
0116 # pragma GCC diagnostic ignored "-Wsign-compare"
0117 # pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
0118 #endif
0119
0120 #if defined(CATCH_CPP20_OR_GREATER) && __has_include(<compare>)
0121 # include <compare>
0122 # if defined( __cpp_lib_three_way_comparison ) && \
0123 __cpp_lib_three_way_comparison >= 201907L
0124 # define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS
0125 # endif
0126 #endif
0127
0128 namespace Catch {
0129
0130 namespace Detail {
0131
0132 template <typename T>
0133 using RemoveCVRef_t = std::remove_cv_t<std::remove_reference_t<T>>;
0134 }
0135
0136
0137
0138
0139
0140 template <typename T>
0141 struct capture_by_value
0142 : std::integral_constant<bool, std::is_arithmetic<T>{}> {};
0143
0144 #if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
0145 template <>
0146 struct capture_by_value<std::strong_ordering> : std::true_type {};
0147 template <>
0148 struct capture_by_value<std::weak_ordering> : std::true_type {};
0149 template <>
0150 struct capture_by_value<std::partial_ordering> : std::true_type {};
0151 #endif
0152
0153 template <typename T>
0154 struct always_false : std::false_type {};
0155
0156 class ITransientExpression {
0157 bool m_isBinaryExpression;
0158 bool m_result;
0159
0160 protected:
0161 ~ITransientExpression() = default;
0162
0163 public:
0164 constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
0165 constexpr auto getResult() const -> bool { return m_result; }
0166
0167 virtual void streamReconstructedExpression( std::ostream& os ) const;
0168
0169 constexpr ITransientExpression( bool isBinaryExpression, bool result )
0170 : m_isBinaryExpression( isBinaryExpression ),
0171 m_result( result )
0172 {}
0173
0174 constexpr ITransientExpression( ITransientExpression const& ) = default;
0175 constexpr ITransientExpression& operator=( ITransientExpression const& ) = default;
0176
0177 friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
0178 expr.streamReconstructedExpression(out);
0179 return out;
0180 }
0181 };
0182
0183 void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
0184
0185 template<typename LhsT, typename RhsT>
0186 class BinaryExpr : public ITransientExpression {
0187 LhsT m_lhs;
0188 StringRef m_op;
0189 RhsT m_rhs;
0190
0191 void streamReconstructedExpression( std::ostream &os ) const override {
0192 formatReconstructedExpression
0193 ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
0194 }
0195
0196 public:
0197 constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
0198 : ITransientExpression{ true, comparisonResult },
0199 m_lhs( lhs ),
0200 m_op( op ),
0201 m_rhs( rhs )
0202 {}
0203
0204 template<typename T>
0205 auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0206 static_assert(always_false<T>::value,
0207 "chained comparisons are not supported inside assertions, "
0208 "wrap the expression inside parentheses, or decompose it");
0209 }
0210
0211 template<typename T>
0212 auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0213 static_assert(always_false<T>::value,
0214 "chained comparisons are not supported inside assertions, "
0215 "wrap the expression inside parentheses, or decompose it");
0216 }
0217
0218 template<typename T>
0219 auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0220 static_assert(always_false<T>::value,
0221 "chained comparisons are not supported inside assertions, "
0222 "wrap the expression inside parentheses, or decompose it");
0223 }
0224
0225 template<typename T>
0226 auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0227 static_assert(always_false<T>::value,
0228 "chained comparisons are not supported inside assertions, "
0229 "wrap the expression inside parentheses, or decompose it");
0230 }
0231
0232 template<typename T>
0233 auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0234 static_assert(always_false<T>::value,
0235 "chained comparisons are not supported inside assertions, "
0236 "wrap the expression inside parentheses, or decompose it");
0237 }
0238
0239 template<typename T>
0240 auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0241 static_assert(always_false<T>::value,
0242 "chained comparisons are not supported inside assertions, "
0243 "wrap the expression inside parentheses, or decompose it");
0244 }
0245
0246 template<typename T>
0247 auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0248 static_assert(always_false<T>::value,
0249 "chained comparisons are not supported inside assertions, "
0250 "wrap the expression inside parentheses, or decompose it");
0251 }
0252
0253 template<typename T>
0254 auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0255 static_assert(always_false<T>::value,
0256 "chained comparisons are not supported inside assertions, "
0257 "wrap the expression inside parentheses, or decompose it");
0258 }
0259 };
0260
0261 template<typename LhsT>
0262 class UnaryExpr : public ITransientExpression {
0263 LhsT m_lhs;
0264
0265 void streamReconstructedExpression( std::ostream &os ) const override {
0266 os << Catch::Detail::stringify( m_lhs );
0267 }
0268
0269 public:
0270 explicit constexpr UnaryExpr( LhsT lhs )
0271 : ITransientExpression{ false, static_cast<bool>(lhs) },
0272 m_lhs( lhs )
0273 {}
0274 };
0275
0276
0277 template<typename LhsT>
0278 class ExprLhs {
0279 LhsT m_lhs;
0280 public:
0281 explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
0282
0283 #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \
0284 template <typename RhsT> \
0285 constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
0286 -> std::enable_if_t< \
0287 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0288 Detail::negation<capture_by_value< \
0289 Detail::RemoveCVRef_t<RhsT>>>>::value, \
0290 BinaryExpr<LhsT, RhsT const&>> { \
0291 return { \
0292 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0293 } \
0294 template <typename RhsT> \
0295 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0296 -> std::enable_if_t< \
0297 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0298 capture_by_value<RhsT>>::value, \
0299 BinaryExpr<LhsT, RhsT>> { \
0300 return { \
0301 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0302 } \
0303 template <typename RhsT> \
0304 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0305 -> std::enable_if_t< \
0306 Detail::conjunction< \
0307 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0308 Detail::is_eq_0_comparable<LhsT>, \
0309 \
0310 Detail::disjunction<std::is_same<RhsT, int>, \
0311 std::is_same<RhsT, long>>>::value, \
0312 BinaryExpr<LhsT, RhsT>> { \
0313 if ( rhs != 0 ) { throw_test_failure_exception(); } \
0314 return { \
0315 static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
0316 } \
0317 template <typename RhsT> \
0318 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0319 -> std::enable_if_t< \
0320 Detail::conjunction< \
0321 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0322 Detail::is_eq_0_comparable<RhsT>, \
0323 \
0324 Detail::disjunction<std::is_same<LhsT, int>, \
0325 std::is_same<LhsT, long>>>::value, \
0326 BinaryExpr<LhsT, RhsT>> { \
0327 if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
0328 return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0329 }
0330
0331 CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == )
0332 CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != )
0333
0334 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR
0335
0336
0337 #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
0338 template <typename RhsT> \
0339 constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
0340 -> std::enable_if_t< \
0341 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0342 Detail::negation<capture_by_value< \
0343 Detail::RemoveCVRef_t<RhsT>>>>::value, \
0344 BinaryExpr<LhsT, RhsT const&>> { \
0345 return { \
0346 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0347 } \
0348 template <typename RhsT> \
0349 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0350 -> std::enable_if_t< \
0351 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0352 capture_by_value<RhsT>>::value, \
0353 BinaryExpr<LhsT, RhsT>> { \
0354 return { \
0355 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0356 } \
0357 template <typename RhsT> \
0358 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0359 -> std::enable_if_t< \
0360 Detail::conjunction< \
0361 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0362 Detail::is_##id##_0_comparable<LhsT>, \
0363 std::is_same<RhsT, int>>::value, \
0364 BinaryExpr<LhsT, RhsT>> { \
0365 if ( rhs != 0 ) { throw_test_failure_exception(); } \
0366 return { \
0367 static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
0368 } \
0369 template <typename RhsT> \
0370 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0371 -> std::enable_if_t< \
0372 Detail::conjunction< \
0373 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0374 Detail::is_##id##_0_comparable<RhsT>, \
0375 std::is_same<LhsT, int>>::value, \
0376 BinaryExpr<LhsT, RhsT>> { \
0377 if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
0378 return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0379 }
0380
0381 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < )
0382 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= )
0383 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > )
0384 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= )
0385
0386 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR
0387
0388
0389 #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \
0390 template <typename RhsT> \
0391 constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
0392 -> std::enable_if_t< \
0393 !capture_by_value<Detail::RemoveCVRef_t<RhsT>>::value, \
0394 BinaryExpr<LhsT, RhsT const&>> { \
0395 return { \
0396 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0397 } \
0398 template <typename RhsT> \
0399 constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0400 -> std::enable_if_t<capture_by_value<RhsT>::value, \
0401 BinaryExpr<LhsT, RhsT>> { \
0402 return { \
0403 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0404 }
0405
0406 CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
0407 CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
0408 CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^)
0409
0410 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR
0411
0412 template<typename RhsT>
0413 friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
0414 static_assert(always_false<RhsT>::value,
0415 "operator&& is not supported inside assertions, "
0416 "wrap the expression inside parentheses, or decompose it");
0417 }
0418
0419 template<typename RhsT>
0420 friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
0421 static_assert(always_false<RhsT>::value,
0422 "operator|| is not supported inside assertions, "
0423 "wrap the expression inside parentheses, or decompose it");
0424 }
0425
0426 constexpr auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
0427 return UnaryExpr<LhsT>{ m_lhs };
0428 }
0429 };
0430
0431 struct Decomposer {
0432 template <typename T,
0433 std::enable_if_t<!capture_by_value<Detail::RemoveCVRef_t<T>>::value,
0434 int> = 0>
0435 constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
0436 return ExprLhs<const T&>{ lhs };
0437 }
0438
0439 template <typename T,
0440 std::enable_if_t<capture_by_value<T>::value, int> = 0>
0441 constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
0442 return ExprLhs<T>{ value };
0443 }
0444 };
0445
0446 }
0447
0448 #ifdef _MSC_VER
0449 #pragma warning(pop)
0450 #endif
0451 #ifdef __clang__
0452 # pragma clang diagnostic pop
0453 #elif defined __GNUC__
0454 # pragma GCC diagnostic pop
0455 #endif
0456
0457 #endif