Skip to content

Commit f783cd1

Browse files
committed
Allow for Catch::Approx to be used in a constexpr context
1 parent 4e8d92b commit f783cd1

File tree

2 files changed

+61
-81
lines changed

2 files changed

+61
-81
lines changed

src/catch2/catch_approx.cpp

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,74 +10,15 @@
1010
#include <catch2/internal/catch_reusable_string_stream.hpp>
1111

1212
#include <cmath>
13-
#include <limits>
14-
15-
namespace {
16-
17-
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
18-
// But without the subtraction to allow for INFINITY in comparison
19-
bool marginComparison(double lhs, double rhs, double margin) {
20-
return (lhs + margin >= rhs) && (rhs + margin >= lhs);
21-
}
22-
23-
}
2413

2514
namespace Catch {
2615

27-
Approx::Approx ( double value )
28-
: m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ),
29-
m_margin( 0.0 ),
30-
m_scale( 0.0 ),
31-
m_value( value )
32-
{}
33-
34-
Approx Approx::custom() {
35-
return Approx( 0 );
36-
}
37-
38-
Approx Approx::operator-() const {
39-
auto temp(*this);
40-
temp.m_value = -temp.m_value;
41-
return temp;
42-
}
43-
44-
4516
std::string Approx::toString() const {
4617
ReusableStringStream rss;
4718
rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
4819
return rss.str();
4920
}
5021

51-
bool Approx::equalityComparisonImpl(const double other) const {
52-
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
53-
// Thanks to Richard Harris for his help refining the scaled margin value
54-
return marginComparison(m_value, other, m_margin)
55-
|| marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
56-
}
57-
58-
void Approx::setMargin(double newMargin) {
59-
CATCH_ENFORCE(newMargin >= 0,
60-
"Invalid Approx::margin: " << newMargin << '.'
61-
<< " Approx::Margin has to be non-negative.");
62-
m_margin = newMargin;
63-
}
64-
65-
void Approx::setEpsilon(double newEpsilon) {
66-
CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
67-
"Invalid Approx::epsilon: " << newEpsilon << '.'
68-
<< " Approx::epsilon has to be in [0, 1]");
69-
m_epsilon = newEpsilon;
70-
}
71-
72-
namespace literals {
73-
Approx operator ""_a(long double val) {
74-
return Approx(val);
75-
}
76-
Approx operator ""_a(unsigned long long val) {
77-
return Approx(val);
78-
}
79-
} // end namespace literals
80-
8122
std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) {
8223
return value.toString();
8324
}

src/catch2/catch_approx.hpp

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,61 @@
1111
#include <catch2/catch_tostring.hpp>
1212

1313
#include <type_traits>
14+
#include <limits>
1415

1516
namespace Catch {
1617

1718
class Approx {
1819
private:
19-
bool equalityComparisonImpl(double other) const;
20-
// Sets and validates the new margin (margin >= 0)
21-
void setMargin(double margin);
20+
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
21+
// But without the subtraction to allow for INFINITY in comparison
22+
constexpr bool marginComparison (double lhs, double rhs, double margin) const {
23+
return (lhs + margin >= rhs) && (rhs + margin >= lhs);
24+
}
25+
26+
constexpr bool equalityComparisonImpl(double other) const {
27+
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
28+
// Thanks to Richard Harris for his help refining the scaled margin value
29+
return marginComparison(m_value, other, m_margin)
30+
|| marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
31+
}
32+
2233
// Sets and validates the new epsilon (0 < epsilon < 1)
23-
void setEpsilon(double epsilon);
34+
constexpr void setEpsilon(double epsilon) {
35+
// CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
36+
// "Invalid Approx::epsilon: " << epsilon << '.'
37+
// << " Approx::epsilon has to be in [0, 1]");
38+
m_epsilon = epsilon;
39+
}
40+
41+
// Sets and validates the new margin (margin >= 0)
42+
constexpr void setMargin(double margin) {
43+
// CATCH_ENFORCE(margin >= 0,
44+
// "Invalid Approx::margin: " << margin << '.'
45+
// << " Approx::Margin has to be non-negative.");
46+
m_margin = margin;
47+
}
2448

2549
public:
26-
explicit Approx ( double value );
50+
explicit constexpr Approx ( double value )
51+
: m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ),
52+
m_margin( 0.0 ),
53+
m_scale( 0.0 ),
54+
m_value( value )
55+
{}
2756

28-
static Approx custom();
57+
static constexpr Approx custom() {
58+
return Approx( 0 );
59+
}
2960

30-
Approx operator-() const;
61+
constexpr Approx operator-() const {
62+
auto temp(*this);
63+
temp.m_value = -temp.m_value;
64+
return temp;
65+
}
3166

3267
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
33-
Approx operator()( T const& value ) const {
68+
constexpr Approx operator()( T const& value ) const {
3469
Approx approx( static_cast<double>(value) );
3570
approx.m_epsilon = m_epsilon;
3671
approx.m_margin = m_margin;
@@ -39,67 +74,67 @@ namespace Catch {
3974
}
4075

4176
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
42-
explicit Approx( T const& value ): Approx(static_cast<double>(value))
77+
explicit constexpr Approx( T const& value ): Approx(static_cast<double>(value))
4378
{}
4479

4580

4681
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
47-
friend bool operator == ( const T& lhs, Approx const& rhs ) {
82+
friend constexpr bool operator == ( const T& lhs, Approx const& rhs ) {
4883
auto lhs_v = static_cast<double>(lhs);
4984
return rhs.equalityComparisonImpl(lhs_v);
5085
}
5186

5287
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
53-
friend bool operator == ( Approx const& lhs, const T& rhs ) {
88+
friend constexpr bool operator == ( Approx const& lhs, const T& rhs ) {
5489
return operator==( rhs, lhs );
5590
}
5691

5792
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
58-
friend bool operator != ( T const& lhs, Approx const& rhs ) {
93+
friend constexpr bool operator != ( T const& lhs, Approx const& rhs ) {
5994
return !operator==( lhs, rhs );
6095
}
6196

6297
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
63-
friend bool operator != ( Approx const& lhs, T const& rhs ) {
98+
friend constexpr bool operator != ( Approx const& lhs, T const& rhs ) {
6499
return !operator==( rhs, lhs );
65100
}
66101

67102
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
68-
friend bool operator <= ( T const& lhs, Approx const& rhs ) {
103+
friend constexpr bool operator <= ( T const& lhs, Approx const& rhs ) {
69104
return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
70105
}
71106

72107
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
73-
friend bool operator <= ( Approx const& lhs, T const& rhs ) {
108+
friend constexpr bool operator <= ( Approx const& lhs, T const& rhs ) {
74109
return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
75110
}
76111

77112
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
78-
friend bool operator >= ( T const& lhs, Approx const& rhs ) {
113+
friend constexpr bool operator >= ( T const& lhs, Approx const& rhs ) {
79114
return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
80115
}
81116

82117
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
83-
friend bool operator >= ( Approx const& lhs, T const& rhs ) {
118+
friend constexpr bool operator >= ( Approx const& lhs, T const& rhs ) {
84119
return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
85120
}
86121

87122
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
88-
Approx& epsilon( T const& newEpsilon ) {
123+
constexpr Approx& epsilon( T const& newEpsilon ) {
89124
const auto epsilonAsDouble = static_cast<double>(newEpsilon);
90125
setEpsilon(epsilonAsDouble);
91126
return *this;
92127
}
93128

94129
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
95-
Approx& margin( T const& newMargin ) {
130+
constexpr Approx& margin( T const& newMargin ) {
96131
const auto marginAsDouble = static_cast<double>(newMargin);
97132
setMargin(marginAsDouble);
98133
return *this;
99134
}
100135

101136
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
102-
Approx& scale( T const& newScale ) {
137+
constexpr Approx& scale( T const& newScale ) {
103138
m_scale = static_cast<double>(newScale);
104139
return *this;
105140
}
@@ -114,8 +149,12 @@ namespace Catch {
114149
};
115150

116151
namespace literals {
117-
Approx operator ""_a(long double val);
118-
Approx operator ""_a(unsigned long long val);
152+
constexpr Approx operator ""_a(long double val) {
153+
return Approx(val);
154+
}
155+
constexpr Approx operator ""_a(unsigned long long val) {
156+
return Approx(val);
157+
}
119158
} // end namespace literals
120159

121160
template<>

0 commit comments

Comments
 (0)