Add IsNaN operator! (#603)
* Add IsNaN operator!
* Docs
* More concise impl
diff --git a/doc/markdown/assertions.md b/doc/markdown/assertions.md
index 381dd52..5a6ad5c 100644
--- a/doc/markdown/assertions.md
+++ b/doc/markdown/assertions.md
@@ -163,6 +163,8 @@
CHECK(doctest::IsNaN(performComputation()); // captures the result!
```
+`IsNaN` is able to capture the value, even if negated via `!`.
+
--------
- Check out the [**example**](../../examples/all_features/assertion_macros.cpp) which shows many of these macros
diff --git a/doctest/doctest.h b/doctest/doctest.h
index 6c292a1..988a7e0 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -1104,8 +1104,9 @@
template <typename F>
struct DOCTEST_INTERFACE_DECL IsNaN
{
- F val;
- IsNaN(F f) : val(f) { }
+ F value; bool flipped;
+ IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+ IsNaN<F> operator!() const { return { value, !flipped }; }
operator bool() const;
};
#ifndef __MINGW32__
@@ -3645,6 +3646,12 @@
namespace detail {
template <typename T>
String fpToString(T value, int precision) {
+ if (std::isnan(value)) {
+ return "nan";
+ } else if (std::isinf(value)) {
+ return value > 0 ? "inf" : "-inf";
+ }
+
std::ostringstream oss;
oss << std::setprecision(precision) << std::fixed << value;
std::string d = oss.str();
@@ -3727,14 +3734,14 @@
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
template <typename F>
IsNaN<F>::operator bool() const {
- return std::isnan(val);
+ return std::isnan(value) ^ flipped;
}
DOCTEST_MSVC_SUPPRESS_WARNING_POP
template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
template <typename F>
-String toString(IsNaN<F> nanCheck) { return "IsNaN( " + doctest::toString(nanCheck.val) + " )"; }
+String toString(IsNaN<F> nanCheck) { return String(nanCheck.flipped ? "! " : "") + "IsNaN( " + doctest::toString(nanCheck.value) + " )"; }
String toString(IsNaN<float> nanCheck) { return toString<float>(nanCheck); }
String toString(IsNaN<double> nanCheck) { return toString<double>(nanCheck); }
String toString(IsNaN<double long> nanCheck) { return toString<double long>(nanCheck); }
diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp
index 81e90ca..ec9db19 100644
--- a/doctest/parts/doctest.cpp
+++ b/doctest/parts/doctest.cpp
@@ -759,6 +759,12 @@
namespace detail {
template <typename T>
String fpToString(T value, int precision) {
+ if (std::isnan(value)) {
+ return "nan";
+ } else if (std::isinf(value)) {
+ return value > 0 ? "inf" : "-inf";
+ }
+
std::ostringstream oss;
oss << std::setprecision(precision) << std::fixed << value;
std::string d = oss.str();
@@ -841,14 +847,14 @@
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
template <typename F>
IsNaN<F>::operator bool() const {
- return std::isnan(val);
+ return std::isnan(value) ^ flipped;
}
DOCTEST_MSVC_SUPPRESS_WARNING_POP
template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
template <typename F>
-String toString(IsNaN<F> nanCheck) { return "IsNaN( " + doctest::toString(nanCheck.val) + " )"; }
+String toString(IsNaN<F> nanCheck) { return String(nanCheck.flipped ? "! " : "") + "IsNaN( " + doctest::toString(nanCheck.value) + " )"; }
String toString(IsNaN<float> nanCheck) { return toString<float>(nanCheck); }
String toString(IsNaN<double> nanCheck) { return toString<double>(nanCheck); }
String toString(IsNaN<double long> nanCheck) { return toString<double long>(nanCheck); }
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index aa789af..ae18209 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -1101,8 +1101,9 @@
template <typename F>
struct DOCTEST_INTERFACE_DECL IsNaN
{
- F val;
- IsNaN(F f) : val(f) { }
+ F value; bool flipped;
+ IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+ IsNaN<F> operator!() const { return { value, !flipped }; }
operator bool() const;
};
#ifndef __MINGW32__
diff --git a/examples/all_features/stringification.cpp b/examples/all_features/stringification.cpp
index 69f89bb..626e486 100644
--- a/examples/all_features/stringification.cpp
+++ b/examples/all_features/stringification.cpp
@@ -179,7 +179,7 @@
CHECK(doctest::IsNaN<double>(0.5));
CHECK(doctest::IsNaN<float>(std::numeric_limits<float>::infinity()));
- // can't test actual nan because it's implementation defined
+ CHECK(!doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN()));
// lets see if this exception gets translated
throw_if(true, bla1);
diff --git a/examples/all_features/test_output/stringification.cpp.txt b/examples/all_features/test_output/stringification.cpp.txt
index a6c43dd..fbe1440 100644
--- a/examples/all_features/test_output/stringification.cpp.txt
+++ b/examples/all_features/test_output/stringification.cpp.txt
@@ -70,6 +70,9 @@
stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) ) is NOT correct!
values: CHECK( IsNaN( inff ) )
+stringification.cpp(0): ERROR: CHECK( !doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN()) ) is NOT correct!
+ values: CHECK( ! IsNaN( nanL ) )
+
stringification.cpp(0): ERROR: test case THREW exception: MyTypeInherited<int>(5, 4)
===============================================================================
@@ -80,6 +83,6 @@
===============================================================================
[doctest] test cases: 3 | 0 passed | 3 failed |
-[doctest] assertions: 16 | 2 passed | 14 failed |
+[doctest] assertions: 17 | 2 passed | 15 failed |
[doctest] Status: FAILURE!
Program code.
diff --git a/examples/all_features/test_output/stringification.cpp_junit.txt b/examples/all_features/test_output/stringification.cpp_junit.txt
index 6ed2e17..2ef89ce 100644
--- a/examples/all_features/test_output/stringification.cpp_junit.txt
+++ b/examples/all_features/test_output/stringification.cpp_junit.txt
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
- <testsuite name="all_features" errors="2" failures="14" tests="16">
+ <testsuite name="all_features" errors="2" failures="15" tests="17">
<testcase classname="stringification.cpp" name="no headers" status="run">
<failure message="1as == nullptr" type="CHECK">
stringification.cpp(0):
@@ -91,6 +91,12 @@
values: CHECK( IsNaN( inff ) )
</failure>
+ <failure message="! IsNaN( nanL )" type="CHECK">
+stringification.cpp(0):
+CHECK( !doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN()) ) is NOT correct!
+ values: CHECK( ! IsNaN( nanL ) )
+
+ </failure>
<error message="exception">
MyTypeInherited<int>(5, 4)
</error>
diff --git a/examples/all_features/test_output/stringification.cpp_xml.txt b/examples/all_features/test_output/stringification.cpp_xml.txt
index de58856..6313afe 100644
--- a/examples/all_features/test_output/stringification.cpp_xml.txt
+++ b/examples/all_features/test_output/stringification.cpp_xml.txt
@@ -172,10 +172,18 @@
IsNaN( inff )
</Expanded>
</Expression>
+ <Expression success="false" type="CHECK" filename="stringification.cpp" line="0">
+ <Original>
+ !doctest::IsNaN<double long>(std::numeric_limits<double long>::quiet_NaN())
+ </Original>
+ <Expanded>
+ ! IsNaN( nanL )
+ </Expanded>
+ </Expression>
<Exception crash="false">
MyTypeInherited<int>(5, 4)
</Exception>
- <OverallResultsAsserts successes="0" failures="9" test_case_success="false"/>
+ <OverallResultsAsserts successes="0" failures="10" test_case_success="false"/>
</TestCase>
<TestCase name="a test case that registers an exception translator for int and then throws one" filename="stringification.cpp" line="0">
<Exception crash="false">
@@ -184,7 +192,7 @@
<OverallResultsAsserts successes="0" failures="0" test_case_success="false"/>
</TestCase>
</TestSuite>
- <OverallResultsAsserts successes="2" failures="14"/>
+ <OverallResultsAsserts successes="2" failures="15"/>
<OverallResultsTestCases successes="0" failures="3"/>
</doctest>
Program code.