// 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 #ifndef CATCH_DECOMPOSER_HPP_INCLUDED #define CATCH_DECOMPOSER_HPP_INCLUDED #include #include #include #include #include #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #pragma warning(disable:4018) // more "signed/unsigned mismatch" #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #pragma warning(disable:4180) // qualifier applied to function type has no meaning #pragma warning(disable:4800) // Forcing result to true or false #endif #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #elif defined __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-compare" #endif namespace Catch { class ITransientExpression { bool m_isBinaryExpression; bool m_result; public: auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } auto getResult() const -> bool { return m_result; } virtual void streamReconstructedExpression( std::ostream &os ) const = 0; ITransientExpression( bool isBinaryExpression, bool result ) : m_isBinaryExpression( isBinaryExpression ), m_result( result ) {} ITransientExpression() = default; ITransientExpression(ITransientExpression const&) = default; ITransientExpression& operator=(ITransientExpression const&) = default; // We don't actually need a virtual destructor, but many static analysers // complain if it's not here :-( virtual ~ITransientExpression(); // = default; friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { expr.streamReconstructedExpression(out); return out; } }; void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template class BinaryExpr : public ITransientExpression { LhsT m_lhs; StringRef m_op; RhsT m_rhs; void streamReconstructedExpression( std::ostream &os ) const override { formatReconstructedExpression ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); } public: BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) : ITransientExpression{ true, comparisonResult }, m_lhs( lhs ), m_op( op ), m_rhs( rhs ) {} template auto operator && ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator || ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator == ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator != ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator > ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator < ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator >= ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator <= ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } }; template class UnaryExpr : public ITransientExpression { LhsT m_lhs; void streamReconstructedExpression( std::ostream &os ) const override { os << Catch::Detail::stringify( m_lhs ); } public: explicit UnaryExpr( LhsT lhs ) : ITransientExpression{ false, static_cast(lhs) }, m_lhs( lhs ) {} }; template class ExprLhs { LhsT m_lhs; public: explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ template < \ typename RhsT, \ std::enable_if_t< \ Detail::conjunction, \ Detail::negation>>>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ ->BinaryExpr { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template , \ std::is_arithmetic>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ->BinaryExpr { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template < \ typename RhsT, \ std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ Detail::disjunction, \ /* On some platforms `NULL` is a long */ \ std::is_same>>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ->BinaryExpr { \ if ( rhs != 0 ) { throw_test_failure_exception(); } \ return { \ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template < \ typename RhsT, \ std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ Detail::disjunction, \ /* On some platforms `NULL` is a long */ \ std::is_same>>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ->BinaryExpr { \ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ template < \ typename RhsT, \ std::enable_if_t< \ Detail::conjunction, \ Detail::negation>>>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ ->BinaryExpr { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template , \ std::is_arithmetic>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ->BinaryExpr { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template < \ typename RhsT, \ std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ std::is_same>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ->BinaryExpr { \ if ( rhs != 0 ) { throw_test_failure_exception(); } \ return { \ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template < \ typename RhsT, \ std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ std::is_same>::value, \ int> = 0> \ friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ->BinaryExpr { \ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \ template>::value, int> = 0> \ friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr { \ return { static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \ } \ template::value, int> = 0> \ friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr { \ return { static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \ } CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR template friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr { static_assert(always_false::value, "operator&& is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr { static_assert(always_false::value, "operator|| is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } auto makeUnaryExpr() const -> UnaryExpr { return UnaryExpr{ m_lhs }; } }; struct Decomposer { template>::value, int> = 0> friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { return ExprLhs{ lhs }; } template::value, int> = 0> friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { return ExprLhs{ value }; } }; } // end namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __clang__ # pragma clang diagnostic pop #elif defined __GNUC__ # pragma GCC diagnostic pop #endif #endif // CATCH_DECOMPOSER_HPP_INCLUDED