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