1 /*
2  *  Created by Phil on 28/04/2011.
3  *  Copyright 2010 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_APPROX_HPP_INCLUDED
9 #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
10 
11 #include "catch_tostring.h"
12 
13 #include <type_traits>
14 #include <stdexcept>
15 
16 namespace Catch {
17 namespace Detail {
18 
19     class Approx {
20     private:
21         bool equalityComparisonImpl(double other) const;
22 
23     public:
24         explicit Approx ( double value );
25 
26         static Approx custom();
27 
28         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator()29         Approx operator()( T const& value ) {
30             Approx approx( static_cast<double>(value) );
31             approx.epsilon( m_epsilon );
32             approx.margin( m_margin );
33             approx.scale( m_scale );
34             return approx;
35         }
36 
37         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx(T const & value)38         explicit Approx( T const& value ): Approx(static_cast<double>(value))
39         {}
40 
41 
42         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
43         friend bool operator == ( const T& lhs, Approx const& rhs ) {
44             auto lhs_v = static_cast<double>(lhs);
45             return rhs.equalityComparisonImpl(lhs_v);
46         }
47 
48         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
49         friend bool operator == ( Approx const& lhs, const T& rhs ) {
50             return operator==( rhs, lhs );
51         }
52 
53         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
54         friend bool operator != ( T const& lhs, Approx const& rhs ) {
55             return !operator==( lhs, rhs );
56         }
57 
58         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
59         friend bool operator != ( Approx const& lhs, T const& rhs ) {
60             return !operator==( rhs, lhs );
61         }
62 
63         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
64         friend bool operator <= ( T const& lhs, Approx const& rhs ) {
65             return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
66         }
67 
68         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
69         friend bool operator <= ( Approx const& lhs, T const& rhs ) {
70             return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
71         }
72 
73         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
74         friend bool operator >= ( T const& lhs, Approx const& rhs ) {
75             return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
76         }
77 
78         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
79         friend bool operator >= ( Approx const& lhs, T const& rhs ) {
80             return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
81         }
82 
83         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
epsilon(T const & newEpsilon)84         Approx& epsilon( T const& newEpsilon ) {
85             double epsilonAsDouble = static_cast<double>(newEpsilon);
86             if( epsilonAsDouble < 0 || epsilonAsDouble > 1.0 ) {
87                 throw std::domain_error
88                     (   "Invalid Approx::epsilon: " +
89                         Catch::Detail::stringify( epsilonAsDouble ) +
90                         ", Approx::epsilon has to be between 0 and 1" );
91             }
92             m_epsilon = epsilonAsDouble;
93             return *this;
94         }
95 
96         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
margin(T const & newMargin)97         Approx& margin( T const& newMargin ) {
98             double marginAsDouble = static_cast<double>(newMargin);
99             if( marginAsDouble < 0 ) {
100                 throw std::domain_error
101                     (   "Invalid Approx::margin: " +
102                          Catch::Detail::stringify( marginAsDouble ) +
103                          ", Approx::Margin has to be non-negative." );
104 
105             }
106             m_margin = marginAsDouble;
107             return *this;
108         }
109 
110         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
scale(T const & newScale)111         Approx& scale( T const& newScale ) {
112             m_scale = static_cast<double>(newScale);
113             return *this;
114         }
115 
116         std::string toString() const;
117 
118     private:
119         double m_epsilon;
120         double m_margin;
121         double m_scale;
122         double m_value;
123     };
124 }
125 
126 template<>
127 struct StringMaker<Catch::Detail::Approx> {
128     static std::string convert(Catch::Detail::Approx const& value);
129 };
130 
131 } // end namespace Catch
132 
133 #endif // TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
134