/* * 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 <iosfwd> #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 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<typename LhsT, typename RhsT> 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<typename T> auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename T> auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<T>::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } }; template<typename LhsT> 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<bool>(lhs) }, m_lhs( lhs ) {} }; // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) template<typename LhsT, typename RhsT> auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); } template<typename T> auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } template<typename T> auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } template<typename T> auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } template<typename T> auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } template<typename LhsT, typename RhsT> auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); } template<typename T> auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } template<typename T> auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } template<typename T> auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } template<typename T> auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } template<typename LhsT> class ExprLhs { LhsT m_lhs; public: explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} template<typename RhsT> auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; } auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { return { m_lhs == rhs, m_lhs, "==", rhs }; } template<typename RhsT> auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; } auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { return { m_lhs != rhs, m_lhs, "!=", rhs }; } template<typename RhsT> auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs }; } template<typename RhsT> auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs }; } template<typename RhsT> auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs }; } template<typename RhsT> auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs }; } template<typename RhsT> auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<RhsT>::value, "operator&& is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template<typename RhsT> auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { static_assert(always_false<RhsT>::value, "operator|| is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } auto makeUnaryExpr() const -> UnaryExpr<LhsT> { return UnaryExpr<LhsT>{ m_lhs }; } }; void handleExpression( ITransientExpression const& expr ); template<typename T> void handleExpression( ExprLhs<T> const& expr ) { handleExpression( expr.makeUnaryExpr() ); } struct Decomposer { template<typename T> auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { return ExprLhs<T const&>{ lhs }; } auto operator <=( bool value ) -> ExprLhs<bool> { return ExprLhs<bool>{ value }; } }; } // end namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif #endif // TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED