1 /* 2 * Created by Phil Nash on 8/8/2017. 3 * Copyright 2017 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 #ifndef TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED 9 #define TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED 10 11 #include "catch_tostring.h" 12 #include "catch_stringref.h" 13 #include "catch_meta.hpp" 14 15 #include <iosfwd> 16 17 #ifdef _MSC_VER 18 #pragma warning(push) 19 #pragma warning(disable:4389) // '==' : signed/unsigned mismatch 20 #pragma warning(disable:4018) // more "signed/unsigned mismatch" 21 #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) 22 #pragma warning(disable:4180) // qualifier applied to function type has no meaning 23 #pragma warning(disable:4800) // Forcing result to true or false 24 #endif 25 26 namespace Catch { 27 28 struct ITransientExpression { 29 auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } 30 auto getResult() const -> bool { return m_result; } 31 virtual void streamReconstructedExpression( std::ostream &os ) const = 0; 32 ITransientExpressionITransientExpression33 ITransientExpression( bool isBinaryExpression, bool result ) 34 : m_isBinaryExpression( isBinaryExpression ), 35 m_result( result ) 36 {} 37 38 // We don't actually need a virtual destructor, but many static analysers 39 // complain if it's not here :-( 40 virtual ~ITransientExpression(); 41 42 bool m_isBinaryExpression; 43 bool m_result; 44 45 }; 46 47 void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); 48 49 template<typename LhsT, typename RhsT> 50 class BinaryExpr : public ITransientExpression { 51 LhsT m_lhs; 52 StringRef m_op; 53 RhsT m_rhs; 54 streamReconstructedExpression(std::ostream & os)55 void streamReconstructedExpression( std::ostream &os ) const override { 56 formatReconstructedExpression 57 ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); 58 } 59 60 public: BinaryExpr(bool comparisonResult,LhsT lhs,StringRef op,RhsT rhs)61 BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) 62 : ITransientExpression{ true, comparisonResult }, 63 m_lhs( lhs ), 64 m_op( op ), 65 m_rhs( rhs ) 66 {} 67 68 template<typename T> 69 auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 70 static_assert(always_false<T>::value, 71 "chained comparisons are not supported inside assertions, " 72 "wrap the expression inside parentheses, or decompose it"); 73 } 74 75 template<typename T> 76 auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 77 static_assert(always_false<T>::value, 78 "chained comparisons are not supported inside assertions, " 79 "wrap the expression inside parentheses, or decompose it"); 80 } 81 82 template<typename T> 83 auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 84 static_assert(always_false<T>::value, 85 "chained comparisons are not supported inside assertions, " 86 "wrap the expression inside parentheses, or decompose it"); 87 } 88 89 template<typename T> 90 auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 91 static_assert(always_false<T>::value, 92 "chained comparisons are not supported inside assertions, " 93 "wrap the expression inside parentheses, or decompose it"); 94 } 95 96 template<typename T> 97 auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 98 static_assert(always_false<T>::value, 99 "chained comparisons are not supported inside assertions, " 100 "wrap the expression inside parentheses, or decompose it"); 101 } 102 103 template<typename T> 104 auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 105 static_assert(always_false<T>::value, 106 "chained comparisons are not supported inside assertions, " 107 "wrap the expression inside parentheses, or decompose it"); 108 } 109 110 template<typename T> 111 auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 112 static_assert(always_false<T>::value, 113 "chained comparisons are not supported inside assertions, " 114 "wrap the expression inside parentheses, or decompose it"); 115 } 116 117 template<typename T> 118 auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { 119 static_assert(always_false<T>::value, 120 "chained comparisons are not supported inside assertions, " 121 "wrap the expression inside parentheses, or decompose it"); 122 } 123 }; 124 125 template<typename LhsT> 126 class UnaryExpr : public ITransientExpression { 127 LhsT m_lhs; 128 streamReconstructedExpression(std::ostream & os)129 void streamReconstructedExpression( std::ostream &os ) const override { 130 os << Catch::Detail::stringify( m_lhs ); 131 } 132 133 public: UnaryExpr(LhsT lhs)134 explicit UnaryExpr( LhsT lhs ) 135 : ITransientExpression{ false, static_cast<bool>(lhs) }, 136 m_lhs( lhs ) 137 {} 138 }; 139 140 141 // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) 142 template<typename LhsT, typename RhsT> 143 auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); } 144 template<typename T> 145 auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } 146 template<typename T> 147 auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } 148 template<typename T> 149 auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } 150 template<typename T> 151 auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } 152 153 template<typename LhsT, typename RhsT> 154 auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); } 155 template<typename T> 156 auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } 157 template<typename T> 158 auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } 159 template<typename T> 160 auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } 161 template<typename T> 162 auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } 163 164 165 template<typename LhsT> 166 class ExprLhs { 167 LhsT m_lhs; 168 public: ExprLhs(LhsT lhs)169 explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} 170 171 template<typename RhsT> 172 auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 173 return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; 174 } 175 auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { 176 return { m_lhs == rhs, m_lhs, "==", rhs }; 177 } 178 179 template<typename RhsT> 180 auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 181 return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; 182 } 183 auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { 184 return { m_lhs != rhs, m_lhs, "!=", rhs }; 185 } 186 187 template<typename RhsT> 188 auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 189 return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs }; 190 } 191 template<typename RhsT> 192 auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 193 return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs }; 194 } 195 template<typename RhsT> 196 auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 197 return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs }; 198 } 199 template<typename RhsT> 200 auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { 201 return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs }; 202 } 203 204 template<typename RhsT> 205 auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { 206 static_assert(always_false<RhsT>::value, 207 "operator&& is not supported inside assertions, " 208 "wrap the expression inside parentheses, or decompose it"); 209 } 210 211 template<typename RhsT> 212 auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { 213 static_assert(always_false<RhsT>::value, 214 "operator|| is not supported inside assertions, " 215 "wrap the expression inside parentheses, or decompose it"); 216 } 217 218 auto makeUnaryExpr() const -> UnaryExpr<LhsT> { 219 return UnaryExpr<LhsT>{ m_lhs }; 220 } 221 }; 222 223 void handleExpression( ITransientExpression const& expr ); 224 225 template<typename T> handleExpression(ExprLhs<T> const & expr)226 void handleExpression( ExprLhs<T> const& expr ) { 227 handleExpression( expr.makeUnaryExpr() ); 228 } 229 230 struct Decomposer { 231 template<typename T> 232 auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { 233 return ExprLhs<T const&>{ lhs }; 234 } 235 236 auto operator <=( bool value ) -> ExprLhs<bool> { 237 return ExprLhs<bool>{ value }; 238 } 239 }; 240 241 } // end namespace Catch 242 243 #ifdef _MSC_VER 244 #pragma warning(pop) 245 #endif 246 247 #endif // TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED 248