Option to remove usage of iostream, std::cout and std::cerr completely. (#741)

* Option to remove usage of std::cout and std::cerr completely.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>

* Correct copy/paste without updating tags.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>

* Fix spelling.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>

---------

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
diff --git a/doc/markdown/configuration.md b/doc/markdown/configuration.md
index 3bc1424..130c25d 100644
--- a/doc/markdown/configuration.md
+++ b/doc/markdown/configuration.md
@@ -37,6 +37,8 @@
 - [**```DOCTEST_CONFIG_ASSERTS_RETURN_VALUES```**](#doctest_config_asserts_return_values)
 - [**```DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED```**](#doctest_config_evaluate_asserts_even_when_disabled)
 - [**```DOCTEST_CONFIG_NO_CONTRADICTING_INLINE```**](#doctest_config_no_contradicting_inline)
+- [**```DOCTEST_CONFIG_NO_IOSTREAM```**](#doctest_config_no_iostream)
+- [**```DOCTEST_CONFIG_HANDLE_EXCEPTION```**](#doctest_config_handle_exception)
 
 For most people the only configuration needed is telling **doctest** which source file should host all the implementation code:
 
@@ -301,6 +303,17 @@
 
 However, this is known to cause some issues with a few compilers with hard to suppress warnings. This flag disables the use of the no-inline attribute in order to suppress the warning if your build requires that.
 
+### **```DOCTEST_CONFIG_NO_IOSTREAM```**
+
+This option disables any inclusion of `<iostream>`, `std::cout` and `std::cerr`. This implies that the `cout` context field must be supplied.  If
+```DOCTEST_CONFIG_NO_EXCEPTIONS``` is defined, then the unhandled exception is not printed to `std::cerr`.
+[```DOCTEST_CONFIG_HANDLE_EXCEPTION```](#doctest_config_handle_exception) can be defined to handle this case.
+
+### **```DOCTEST_CONFIG_HANDLE_EXCEPTION```**
+
+This macro function can be defined to handle exceptions instead of just printing them
+to `std::cerr`.
+
 ---------------
 
 [Home](readme.md#reference)
diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp
index 5ee79f7..1357200 100644
--- a/doctest/parts/doctest.cpp
+++ b/doctest/parts/doctest.cpp
@@ -71,7 +71,9 @@
 #include <utility>
 #include <fstream>
 #include <sstream>
+#ifdef DOCTEST_CONFIG_INCLUDE_IOSTREAM
 #include <iostream>
+#endif
 #include <algorithm>
 #include <iomanip>
 #include <vector>
@@ -193,8 +195,14 @@
 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
         throw e;
 #else  // DOCTEST_CONFIG_NO_EXCEPTIONS
+#ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION
+        DOCTEST_CONFIG_HANDLE_EXCEPTION(e);
+#else
+#ifdef DOCTEST_CONFIG_INCLUDE_IOSTREAM
         std::cerr << "doctest will terminate because it needed to throw an exception.\n"
                   << "The message was: " << e.what() << '\n';
+#endif // DOCTEST_CONFIG_INCLUDE_IOSTREAM
+#endif // DOCTEST_CONFIG_HANDLE_EXCEPTION
         std::terminate();
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
     }
@@ -269,7 +277,7 @@
 
 namespace timer_large_integer
 {
-    
+
 #if defined(DOCTEST_PLATFORM_WINDOWS)
     using type = ULONGLONG;
 #else // DOCTEST_PLATFORM_WINDOWS
@@ -1905,7 +1913,7 @@
             m_string = tlssPop();
             logged = true;
         }
-        
+
         DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
 
         const bool isWarn = m_severity & assertType::is_warn;
@@ -1974,7 +1982,11 @@
             mutable XmlWriter* m_writer = nullptr;
         };
 
+#ifdef DOCTEST_CONFIG_INCLUDE_IOSTREAM
         XmlWriter( std::ostream& os = std::cout );
+#else
+        XmlWriter( std::ostream& os );
+#endif
         ~XmlWriter();
 
         XmlWriter( XmlWriter const& ) = delete;
@@ -2456,7 +2468,7 @@
             test_case_start_impl(in);
             xml.ensureTagClosed();
         }
-        
+
         void test_case_reenter(const TestCaseData&) override {}
 
         void test_case_end(const CurrentTestCaseStats& st) override {
@@ -3178,7 +3190,7 @@
             subcasesStack.clear();
             currentSubcaseLevel = 0;
         }
-        
+
         void test_case_reenter(const TestCaseData&) override {
             subcasesStack.clear();
         }
@@ -3695,8 +3707,12 @@
             fstr.open(p->out.c_str(), std::fstream::out);
             p->cout = &fstr;
         } else {
+#ifdef DOCTEST_CONFIG_INCLUDE_IOSTREAM
             // stdout by default
             p->cout = &std::cout;
+#else
+            return EXIT_FAILURE;
+#endif
         }
     }
 
@@ -3861,7 +3877,7 @@
             DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc);
 
             p->timer.start();
-            
+
             bool run_test = true;
 
             do {
@@ -3902,7 +3918,7 @@
                     run_test = false;
                     p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
                 }
-                
+
                 if(!p->nextSubcaseStack.empty() && run_test)
                     DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
                 if(p->nextSubcaseStack.empty())
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index 8c4b044..098f9d7 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -396,6 +396,11 @@
 #endif
 #endif // DOCTEST_NO_SANITIZE_INTEGER
 
+// Should std::cout/std::cerr ever be used by doctest?
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+#define DOCTEST_CONFIG_INCLUDE_IOSTREAM
+#endif
+
 // =================================================================================================
 // == FEATURE DETECTION END ========================================================================
 // =================================================================================================