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 
14 #include <iosfwd>
15 
16 #ifdef _MSC_VER
17 #pragma warning(push)
18 #pragma warning(disable:4389) // '==' : signed/unsigned mismatch
19 #pragma warning(disable:4018) // more "signed/unsigned mismatch"
20 #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
21 #pragma warning(disable:4180) // qualifier applied to function type has no meaning
22 #endif
23 
24 namespace Catch {
25 
26     struct ITransientExpression {
27         auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
28         auto getResult() const -> bool { return m_result; }
29         virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
30 
ITransientExpressionITransientExpression31         ITransientExpression( bool isBinaryExpression, bool result )
32         :   m_isBinaryExpression( isBinaryExpression ),
33             m_result( result )
34         {}
35 
36         // We don't actually need a virtual destructor, but many static analysers
37         // complain if it's not here :-(
38         virtual ~ITransientExpression();
39 
40         bool m_isBinaryExpression;
41         bool m_result;
42 
43     };
44 
45     void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
46 
47     template<typename LhsT, typename RhsT>
48     class BinaryExpr  : public ITransientExpression {
49         LhsT m_lhs;
50         StringRef m_op;
51         RhsT m_rhs;
52 
streamReconstructedExpression(std::ostream & os)53         void streamReconstructedExpression( std::ostream &os ) const override {
54             formatReconstructedExpression
55                     ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
56         }
57 
58     public:
BinaryExpr(bool comparisonResult,LhsT lhs,StringRef op,RhsT rhs)59         BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
60         :   ITransientExpression{ true, comparisonResult },
61             m_lhs( lhs ),
62             m_op( op ),
63             m_rhs( rhs )
64         {}
65     };
66 
67     template<typename LhsT>
68     class UnaryExpr : public ITransientExpression {
69         LhsT m_lhs;
70 
streamReconstructedExpression(std::ostream & os)71         void streamReconstructedExpression( std::ostream &os ) const override {
72             os << Catch::Detail::stringify( m_lhs );
73         }
74 
75     public:
UnaryExpr(LhsT lhs)76         explicit UnaryExpr( LhsT lhs )
77         :   ITransientExpression{ false, lhs ? true : false },
78             m_lhs( lhs )
79         {}
80     };
81 
82 
83     // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
84     template<typename LhsT, typename RhsT>
85     auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
86     template<typename T>
87     auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
88     template<typename T>
89     auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
90     template<typename T>
91     auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
92     template<typename T>
93     auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
94 
95     template<typename LhsT, typename RhsT>
96     auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
97     template<typename T>
98     auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
99     template<typename T>
100     auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
101     template<typename T>
102     auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
103     template<typename T>
104     auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
105 
106 
107     template<typename LhsT>
108     class ExprLhs {
109         LhsT m_lhs;
110     public:
ExprLhs(LhsT lhs)111         explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
112 
113         template<typename RhsT>
114         auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
115             return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
116         }
117         auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
118             return { m_lhs == rhs, m_lhs, "==", rhs };
119         }
120 
121         template<typename RhsT>
122         auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
123             return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
124         }
125         auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
126             return { m_lhs != rhs, m_lhs, "!=", rhs };
127         }
128 
129         template<typename RhsT>
130         auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
131             return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs };
132         }
133         template<typename RhsT>
134         auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
135             return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs };
136         }
137         template<typename RhsT>
138         auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
139             return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs };
140         }
141         template<typename RhsT>
142         auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
143             return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
144         }
145 
146         auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
147             return UnaryExpr<LhsT>{ m_lhs };
148         }
149     };
150 
151     void handleExpression( ITransientExpression const& expr );
152 
153     template<typename T>
handleExpression(ExprLhs<T> const & expr)154     void handleExpression( ExprLhs<T> const& expr ) {
155         handleExpression( expr.makeUnaryExpr() );
156     }
157 
158     struct Decomposer {
159         template<typename T>
160         auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
161             return ExprLhs<T const&>{ lhs };
162         }
163 
164         auto operator <=( bool value ) -> ExprLhs<bool> {
165             return ExprLhs<bool>{ value };
166         }
167     };
168 
169 } // end namespace Catch
170 
171 #ifdef _MSC_VER
172 #pragma warning(pop)
173 #endif
174 
175 #endif // TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED
176