mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 04:07:10 +01:00 
			
		
		
		
	Allow for Catch::Approx to be used in a constexpr context
				
					
				
			This commit is contained in:
		| @@ -56,7 +56,6 @@ function(add_warnings_to_targets targets) | ||||
|           "-Wexit-time-destructors" | ||||
|           "-Wextra" | ||||
|           "-Wextra-semi" | ||||
|           "-Wfloat-equal" | ||||
|           "-Wglobal-constructors" | ||||
|           "-Winit-self" | ||||
|           "-Wmisleading-indentation" | ||||
|   | ||||
| @@ -10,74 +10,15 @@ | ||||
| #include <catch2/internal/catch_reusable_string_stream.hpp> | ||||
|  | ||||
| #include <cmath> | ||||
| #include <limits> | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| // Performs equivalent check of std::fabs(lhs - rhs) <= margin | ||||
| // But without the subtraction to allow for INFINITY in comparison | ||||
| bool marginComparison(double lhs, double rhs, double margin) { | ||||
|     return (lhs + margin >= rhs) && (rhs + margin >= lhs); | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     Approx::Approx ( double value ) | ||||
|     :   m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ), | ||||
|         m_margin( 0.0 ), | ||||
|         m_scale( 0.0 ), | ||||
|         m_value( value ) | ||||
|     {} | ||||
|  | ||||
|     Approx Approx::custom() { | ||||
|         return Approx( 0 ); | ||||
|     } | ||||
|  | ||||
|     Approx Approx::operator-() const { | ||||
|         auto temp(*this); | ||||
|         temp.m_value = -temp.m_value; | ||||
|         return temp; | ||||
|     } | ||||
|  | ||||
|  | ||||
| std::string Approx::toString() const { | ||||
|     ReusableStringStream rss; | ||||
|     rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; | ||||
|     return rss.str(); | ||||
| } | ||||
|  | ||||
|     bool Approx::equalityComparisonImpl(const double other) const { | ||||
|         // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value | ||||
|         // Thanks to Richard Harris for his help refining the scaled margin value | ||||
|         return marginComparison(m_value, other, m_margin) | ||||
|             || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); | ||||
|     } | ||||
|  | ||||
|     void Approx::setMargin(double newMargin) { | ||||
|         CATCH_ENFORCE(newMargin >= 0, | ||||
|             "Invalid Approx::margin: " << newMargin << '.' | ||||
|             << " Approx::Margin has to be non-negative."); | ||||
|         m_margin = newMargin; | ||||
|     } | ||||
|  | ||||
|     void Approx::setEpsilon(double newEpsilon) { | ||||
|         CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, | ||||
|             "Invalid Approx::epsilon: " << newEpsilon << '.' | ||||
|             << " Approx::epsilon has to be in [0, 1]"); | ||||
|         m_epsilon = newEpsilon; | ||||
|     } | ||||
|  | ||||
| namespace literals { | ||||
|     Approx operator ""_a(long double val) { | ||||
|         return Approx(val); | ||||
|     } | ||||
|     Approx operator ""_a(unsigned long long val) { | ||||
|         return Approx(val); | ||||
|     } | ||||
| } // end namespace literals | ||||
|  | ||||
| std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) { | ||||
|     return value.toString(); | ||||
| } | ||||
|   | ||||
| @@ -11,26 +11,67 @@ | ||||
| #include <catch2/catch_tostring.hpp> | ||||
|  | ||||
| #include <type_traits> | ||||
| #include <limits> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     class Approx { | ||||
|     private: | ||||
|         bool equalityComparisonImpl(double other) const; | ||||
|         // Sets and validates the new margin (margin >= 0) | ||||
|         void setMargin(double margin); | ||||
|         // Performs equivalent check of std::fabs(lhs - rhs) <= margin | ||||
|         // But without the subtraction to allow for INFINITY in comparison | ||||
|         constexpr bool marginComparison (double lhs, double rhs, double margin) const { | ||||
|             return (lhs + margin >= rhs) && (rhs + margin >= lhs); | ||||
|         } | ||||
|  | ||||
|         constexpr double fabs(double value) const { | ||||
|             return (value < 0.0) ? -value : value; | ||||
|         } | ||||
|  | ||||
|         constexpr bool isinf(double value) const { | ||||
|             return value == std::numeric_limits<double>::infinity() || value == -std::numeric_limits<double>::infinity(); | ||||
|         } | ||||
|  | ||||
|         constexpr bool equalityComparisonImpl(double other) const { | ||||
|             // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value | ||||
|             // Thanks to Richard Harris for his help refining the scaled margin value | ||||
|             return marginComparison(m_value, other, m_margin) | ||||
|                 || marginComparison(m_value, other, m_epsilon * (m_scale + fabs(isinf(m_value)? 0 : m_value))); | ||||
|         } | ||||
|  | ||||
|         // Sets and validates the new epsilon (0 < epsilon < 1) | ||||
|         void setEpsilon(double epsilon); | ||||
|         constexpr void setEpsilon(double epsilon) { | ||||
|             if(epsilon < 0) | ||||
|                 throw std::domain_error("Invalid Approx::epsilon. Approx::epsilon has to be in [0, 1]"); | ||||
|             m_epsilon = epsilon; | ||||
|         } | ||||
|  | ||||
|         // Sets and validates the new margin (margin >= 0) | ||||
|         constexpr void setMargin(double margin) { | ||||
|             if(margin < 0) | ||||
|                 throw std::domain_error("Invalid Approx::margin. Approx::Margin has to be non-negative."); | ||||
|             m_margin = margin; | ||||
|         } | ||||
|  | ||||
|     public: | ||||
|         explicit Approx ( double value ); | ||||
|         constexpr inline explicit Approx ( double value ) | ||||
|         :   m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ), | ||||
|             m_margin( 0.0 ), | ||||
|             m_scale( 0.0 ), | ||||
|             m_value( value ) | ||||
|         {} | ||||
|  | ||||
|         static Approx custom(); | ||||
|         static constexpr Approx custom() { | ||||
|             return Approx( 0.0 ); | ||||
|         } | ||||
|  | ||||
|         Approx operator-() const; | ||||
|         constexpr Approx operator-() const { | ||||
|             auto temp(*this); | ||||
|             temp.m_value = -temp.m_value; | ||||
|             return temp; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         Approx operator()( T const& value ) const { | ||||
|         constexpr Approx operator()( T const& value ) const { | ||||
|             Approx approx( static_cast<double>(value) ); | ||||
|             approx.m_epsilon = m_epsilon; | ||||
|             approx.m_margin = m_margin; | ||||
| @@ -39,67 +80,67 @@ namespace Catch { | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         explicit Approx( T const& value ): Approx(static_cast<double>(value)) | ||||
|         constexpr inline explicit Approx( T const& value ): Approx(static_cast<double>(value)) | ||||
|         {} | ||||
|  | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator == ( const T& lhs, Approx const& rhs ) { | ||||
|         friend constexpr bool operator == ( const T& lhs, Approx const& rhs ) { | ||||
|             auto lhs_v = static_cast<double>(lhs); | ||||
|             return rhs.equalityComparisonImpl(lhs_v); | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator == ( Approx const& lhs, const T& rhs ) { | ||||
|         friend constexpr bool operator == ( Approx const& lhs, const T& rhs ) { | ||||
|             return operator==( rhs, lhs ); | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator != ( T const& lhs, Approx const& rhs ) { | ||||
|         friend constexpr bool operator != ( T const& lhs, Approx const& rhs ) { | ||||
|             return !operator==( lhs, rhs ); | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator != ( Approx const& lhs, T const& rhs ) { | ||||
|         friend constexpr bool operator != ( Approx const& lhs, T const& rhs ) { | ||||
|             return !operator==( rhs, lhs ); | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator <= ( T const& lhs, Approx const& rhs ) { | ||||
|         friend constexpr bool operator <= ( T const& lhs, Approx const& rhs ) { | ||||
|             return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator <= ( Approx const& lhs, T const& rhs ) { | ||||
|         friend constexpr bool operator <= ( Approx const& lhs, T const& rhs ) { | ||||
|             return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator >= ( T const& lhs, Approx const& rhs ) { | ||||
|         friend constexpr bool operator >= ( T const& lhs, Approx const& rhs ) { | ||||
|             return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         friend bool operator >= ( Approx const& lhs, T const& rhs ) { | ||||
|         friend constexpr bool operator >= ( Approx const& lhs, T const& rhs ) { | ||||
|             return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         Approx& epsilon( T const& newEpsilon ) { | ||||
|         constexpr Approx& epsilon( T const& newEpsilon ) { | ||||
|             const auto epsilonAsDouble = static_cast<double>(newEpsilon); | ||||
|             setEpsilon(epsilonAsDouble); | ||||
|             return *this; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         Approx& margin( T const& newMargin ) { | ||||
|         constexpr Approx& margin( T const& newMargin ) { | ||||
|             const auto marginAsDouble = static_cast<double>(newMargin); | ||||
|             setMargin(marginAsDouble); | ||||
|             return *this; | ||||
|         } | ||||
|  | ||||
|         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>> | ||||
|         Approx& scale( T const& newScale ) { | ||||
|         constexpr Approx& scale( T const& newScale ) { | ||||
|             m_scale = static_cast<double>(newScale); | ||||
|             return *this; | ||||
|         } | ||||
| @@ -114,8 +155,12 @@ namespace Catch { | ||||
|     }; | ||||
|  | ||||
| namespace literals { | ||||
|     Approx operator ""_a(long double val); | ||||
|     Approx operator ""_a(unsigned long long val); | ||||
|     constexpr Approx operator ""_a(long double val) { | ||||
|         return Approx(val); | ||||
|     } | ||||
|     constexpr Approx operator ""_a(unsigned long long val) { | ||||
|         return Approx(val); | ||||
|     } | ||||
| } // end namespace literals | ||||
|  | ||||
| template<> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Chris Thrasher
					Chris Thrasher