File indexing completed on 2025-01-30 10:02:49
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
0017 #include <type_traits>
0018 #include <iosfwd>
0019
0020 #ifdef _MSC_VER
0021 #pragma warning(push)
0022 #pragma warning(disable:4389)
0023 #pragma warning(disable:4018)
0024 #pragma warning(disable:4312)
0025 #pragma warning(disable:4180)
0026 #pragma warning(disable:4800)
0027 #endif
0028
0029 #ifdef __clang__
0030 # pragma clang diagnostic push
0031 # pragma clang diagnostic ignored "-Wsign-compare"
0032 #elif defined __GNUC__
0033 # pragma GCC diagnostic push
0034 # pragma GCC diagnostic ignored "-Wsign-compare"
0035 #endif
0036
0037 namespace Catch {
0038
0039 template <typename T>
0040 struct always_false : std::false_type {};
0041
0042 class ITransientExpression {
0043 bool m_isBinaryExpression;
0044 bool m_result;
0045
0046 public:
0047 auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
0048 auto getResult() const -> bool { return m_result; }
0049 virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
0050
0051 ITransientExpression( bool isBinaryExpression, bool result )
0052 : m_isBinaryExpression( isBinaryExpression ),
0053 m_result( result )
0054 {}
0055
0056 ITransientExpression() = default;
0057 ITransientExpression(ITransientExpression const&) = default;
0058 ITransientExpression& operator=(ITransientExpression const&) = default;
0059
0060
0061
0062 virtual ~ITransientExpression();
0063
0064 friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
0065 expr.streamReconstructedExpression(out);
0066 return out;
0067 }
0068 };
0069
0070 void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
0071
0072 template<typename LhsT, typename RhsT>
0073 class BinaryExpr : public ITransientExpression {
0074 LhsT m_lhs;
0075 StringRef m_op;
0076 RhsT m_rhs;
0077
0078 void streamReconstructedExpression( std::ostream &os ) const override {
0079 formatReconstructedExpression
0080 ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
0081 }
0082
0083 public:
0084 BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
0085 : ITransientExpression{ true, comparisonResult },
0086 m_lhs( lhs ),
0087 m_op( op ),
0088 m_rhs( rhs )
0089 {}
0090
0091 template<typename T>
0092 auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0093 static_assert(always_false<T>::value,
0094 "chained comparisons are not supported inside assertions, "
0095 "wrap the expression inside parentheses, or decompose it");
0096 }
0097
0098 template<typename T>
0099 auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0100 static_assert(always_false<T>::value,
0101 "chained comparisons are not supported inside assertions, "
0102 "wrap the expression inside parentheses, or decompose it");
0103 }
0104
0105 template<typename T>
0106 auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0107 static_assert(always_false<T>::value,
0108 "chained comparisons are not supported inside assertions, "
0109 "wrap the expression inside parentheses, or decompose it");
0110 }
0111
0112 template<typename T>
0113 auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0114 static_assert(always_false<T>::value,
0115 "chained comparisons are not supported inside assertions, "
0116 "wrap the expression inside parentheses, or decompose it");
0117 }
0118
0119 template<typename T>
0120 auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0121 static_assert(always_false<T>::value,
0122 "chained comparisons are not supported inside assertions, "
0123 "wrap the expression inside parentheses, or decompose it");
0124 }
0125
0126 template<typename T>
0127 auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0128 static_assert(always_false<T>::value,
0129 "chained comparisons are not supported inside assertions, "
0130 "wrap the expression inside parentheses, or decompose it");
0131 }
0132
0133 template<typename T>
0134 auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0135 static_assert(always_false<T>::value,
0136 "chained comparisons are not supported inside assertions, "
0137 "wrap the expression inside parentheses, or decompose it");
0138 }
0139
0140 template<typename T>
0141 auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
0142 static_assert(always_false<T>::value,
0143 "chained comparisons are not supported inside assertions, "
0144 "wrap the expression inside parentheses, or decompose it");
0145 }
0146 };
0147
0148 template<typename LhsT>
0149 class UnaryExpr : public ITransientExpression {
0150 LhsT m_lhs;
0151
0152 void streamReconstructedExpression( std::ostream &os ) const override {
0153 os << Catch::Detail::stringify( m_lhs );
0154 }
0155
0156 public:
0157 explicit UnaryExpr( LhsT lhs )
0158 : ITransientExpression{ false, static_cast<bool>(lhs) },
0159 m_lhs( lhs )
0160 {}
0161 };
0162
0163
0164 template<typename LhsT>
0165 class ExprLhs {
0166 LhsT m_lhs;
0167 public:
0168 explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
0169
0170 #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \
0171 template <typename RhsT> \
0172 friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
0173 ->std::enable_if_t< \
0174 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0175 Detail::negation<std::is_arithmetic< \
0176 std::remove_reference_t<RhsT>>>>::value, \
0177 BinaryExpr<LhsT, RhsT const&>> { \
0178 return { \
0179 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0180 } \
0181 template <typename RhsT> \
0182 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0183 ->std::enable_if_t< \
0184 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0185 std::is_arithmetic<RhsT>>::value, \
0186 BinaryExpr<LhsT, RhsT>> { \
0187 return { \
0188 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0189 } \
0190 template <typename RhsT> \
0191 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0192 ->std::enable_if_t< \
0193 Detail::conjunction< \
0194 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0195 Detail::is_eq_0_comparable<LhsT>, \
0196 \
0197 Detail::disjunction<std::is_same<RhsT, int>, \
0198 std::is_same<RhsT, long>>>::value, \
0199 BinaryExpr<LhsT, RhsT>> { \
0200 if ( rhs != 0 ) { throw_test_failure_exception(); } \
0201 return { \
0202 static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
0203 } \
0204 template <typename RhsT> \
0205 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0206 ->std::enable_if_t< \
0207 Detail::conjunction< \
0208 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0209 Detail::is_eq_0_comparable<RhsT>, \
0210 \
0211 Detail::disjunction<std::is_same<LhsT, int>, \
0212 std::is_same<LhsT, long>>>::value, \
0213 BinaryExpr<LhsT, RhsT>> { \
0214 if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
0215 return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0216 }
0217
0218 CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == )
0219 CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != )
0220
0221 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR
0222
0223 #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
0224 template <typename RhsT> \
0225 friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
0226 ->std::enable_if_t< \
0227 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0228 Detail::negation<std::is_arithmetic< \
0229 std::remove_reference_t<RhsT>>>>::value, \
0230 BinaryExpr<LhsT, RhsT const&>> { \
0231 return { \
0232 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0233 } \
0234 template <typename RhsT> \
0235 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0236 ->std::enable_if_t< \
0237 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
0238 std::is_arithmetic<RhsT>>::value, \
0239 BinaryExpr<LhsT, RhsT>> { \
0240 return { \
0241 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0242 } \
0243 template <typename RhsT> \
0244 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0245 ->std::enable_if_t< \
0246 Detail::conjunction< \
0247 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0248 Detail::is_##id##_0_comparable<LhsT>, \
0249 std::is_same<RhsT, int>>::value, \
0250 BinaryExpr<LhsT, RhsT>> { \
0251 if ( rhs != 0 ) { throw_test_failure_exception(); } \
0252 return { \
0253 static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
0254 } \
0255 template <typename RhsT> \
0256 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0257 ->std::enable_if_t< \
0258 Detail::conjunction< \
0259 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
0260 Detail::is_##id##_0_comparable<RhsT>, \
0261 std::is_same<LhsT, int>>::value, \
0262 BinaryExpr<LhsT, RhsT>> { \
0263 if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
0264 return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0265 }
0266
0267 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < )
0268 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= )
0269 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > )
0270 CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= )
0271
0272 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR
0273
0274
0275 #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \
0276 template <typename RhsT> \
0277 friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
0278 ->std::enable_if_t< \
0279 !std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
0280 BinaryExpr<LhsT, RhsT const&>> { \
0281 return { \
0282 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0283 } \
0284 template <typename RhsT> \
0285 friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
0286 ->std::enable_if_t<std::is_arithmetic<RhsT>::value, \
0287 BinaryExpr<LhsT, RhsT>> { \
0288 return { \
0289 static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
0290 }
0291
0292 CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
0293 CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
0294 CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^)
0295
0296 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR
0297
0298 template<typename RhsT>
0299 friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
0300 static_assert(always_false<RhsT>::value,
0301 "operator&& is not supported inside assertions, "
0302 "wrap the expression inside parentheses, or decompose it");
0303 }
0304
0305 template<typename RhsT>
0306 friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
0307 static_assert(always_false<RhsT>::value,
0308 "operator|| is not supported inside assertions, "
0309 "wrap the expression inside parentheses, or decompose it");
0310 }
0311
0312 auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
0313 return UnaryExpr<LhsT>{ m_lhs };
0314 }
0315 };
0316
0317 struct Decomposer {
0318 template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>
0319 friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
0320 return ExprLhs<const T&>{ lhs };
0321 }
0322
0323 template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
0324 friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
0325 return ExprLhs<T>{ value };
0326 }
0327 };
0328
0329 }
0330
0331 #ifdef _MSC_VER
0332 #pragma warning(pop)
0333 #endif
0334 #ifdef __clang__
0335 # pragma clang diagnostic pop
0336 #elif defined __GNUC__
0337 # pragma GCC diagnostic pop
0338 #endif
0339
0340 #endif