/* * Created by Phil Nash on 8/8/2017. * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED #define TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED #include "catch_tostring.h" #include "catch_stringref.h" #include "catch_meta.hpp" #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 #endif namespace Catch { struct ITransientExpression { 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 ) {} // We don't actually need a virtual destructor, but many static analysers // complain if it's not here :-( virtual ~ITransientExpression(); bool m_isBinaryExpression; bool m_result; }; 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 ) {} }; // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) template auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } template auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } template auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } template auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } template auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } template auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } template auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } template auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } template auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } template auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } template class ExprLhs { LhsT m_lhs; public: explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} template auto operator == ( RhsT const& rhs ) -> BinaryExpr const { return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; } auto operator == ( bool rhs ) -> BinaryExpr const { return { m_lhs == rhs, m_lhs, "==", rhs }; } template auto operator != ( RhsT const& rhs ) -> BinaryExpr const { return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; } auto operator != ( bool rhs ) -> BinaryExpr const { return { m_lhs != rhs, m_lhs, "!=", rhs }; } template auto operator > ( RhsT const& rhs ) -> BinaryExpr const { return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; } template auto operator < ( RhsT const& rhs ) -> BinaryExpr const { return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; } template auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; } template auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; } template auto operator && ( RhsT const& ) -> BinaryExpr const { static_assert(always_false::value, "operator&& is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator || ( RhsT const& ) -> BinaryExpr const { 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 }; } }; void handleExpression( ITransientExpression const& expr ); template void handleExpression( ExprLhs const& expr ) { handleExpression( expr.makeUnaryExpr() ); } struct Decomposer { template auto operator <= ( T const& lhs ) -> ExprLhs { return ExprLhs{ lhs }; } auto operator <=( bool value ) -> ExprLhs { return ExprLhs{ value }; } }; } // end namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif #endif // TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED