added INFO() and CAPTURE() logging macros - they can log variables and construct a message which gets printed only when an assert in the same scope (or in a nested one) after them fails. They use lazy stringification and the stack to avoid heap allocations and unnecessary string construction for the common case where no asserts fail. fixes #48 fixes #23
diff --git a/README.md b/README.md
index af67b41..2f2e229 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,7 @@
 - [Assertion macros](doc/markdown/assertions.md)
 - [Test cases, subcases and test fixtures](doc/markdown/testcases.md)
 - [Command line](doc/markdown/commandline.md)
+- [Logging macros](doc/markdown/logging.md)
 - [```main()``` entry point](doc/markdown/main.md)
 - [Configuration](doc/markdown/configuration.md)
 - [String conversions](doc/markdown/stringification.md)
diff --git a/doc/markdown/configuration.md b/doc/markdown/configuration.md
index 8089eaa..6b979a1 100644
--- a/doc/markdown/configuration.md
+++ b/doc/markdown/configuration.md
@@ -11,6 +11,7 @@
 - [**```DOCTEST_CONFIG_DISABLE```**](#doctest_config_disable)
 - [**```DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL```**](#doctest_config_implementation_in_dll)
 - [**```DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES```**](#doctest_config_no_short_macro_names)
+- [**```DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK```**](#doctest_config_num_captures_on_stack)
 - [**```DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING```**](#doctest_config_treat_char_star_as_string)
 - [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](#doctest_config_super_fast_asserts)
 - [**```DOCTEST_CONFIG_USE_IOSFWD```**](#doctest_config_use_iosfwd)
@@ -80,6 +81,12 @@
 
 This can be defined both globally and in specific source files only.
 
+### **```DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK```**
+
+With this identifier the user may configure the number of captures on the stack by the ```INFO()``` [**logging macros**](logging.md) (read there for more info). The default is ```5``` - which means that for a call like this: ```INFO(var1 << "la la" << var2);``` all 3 logged variables will be captured on the stack (with the ability to hold 2 more - so no heap allocation unless an assert fails later in the same scope) - and a total of ```5 * (sizeof(void*) * 2))``` bytes are used on the stack for captures. A subsequent call to ```INFO()``` will have it's own stack space.
+
+This should be defined globally.
+
 ### **```DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING```**
 
 By default ```char*``` is being treated as a pointer. With this option comparing ```char*``` pointers will switch to using ```strcmp()``` for comparisons and when stringified the string will be printed instead of the pointer value. 
diff --git a/doc/markdown/features.md b/doc/markdown/features.md
index 1d674c2..7960854 100644
--- a/doc/markdown/features.md
+++ b/doc/markdown/features.md
@@ -54,6 +54,7 @@
 - offers a way to remove **everything** testing-related from the binary with the [**```DOCTEST_CONFIG_DISABLE```**](configuration.md#doctest_config_disable) macro
 - tests are registered automatically - no need to add them to a collection manually
 - supports [**subcases**](testcases.md) for easy setup/teardown of tests (also supports the retro [**test fixtures**](testcases.md#) with classes)
+- supports [**logging macros**](logging.md) for capturing local variables and strings - as a message for when an assert fails - with lazy stringification and no allocations when possible!
 - output from all compilers on all platforms is the same - byte by byte
 - binaries (exe/dll) can use the test runner of another binary - so tests end up in a single registry - [**example**](../../examples/dll_and_executable/)
 - supports [**BDD style**](testcases.md) tests
diff --git a/doc/markdown/logging.md b/doc/markdown/logging.md
new file mode 100644
index 0000000..71dc030
--- /dev/null
+++ b/doc/markdown/logging.md
@@ -0,0 +1,42 @@
+## Logging macros
+
+Additional messages can be logged during a test case.
+
+## INFO()
+
+The ```INFO()``` macro allows heterogenous sequences of values to be streamed using the insertion operator (```<<```) in the same way that ```std::ostream```, ```std::cout```, etc support it.
+
+```c++
+INFO("The number is " << i);
+```
+
+This message will be relevant to all asserts after it in the current scope or in scopes nested in the current one and will be printed later only if an assert fails.
+
+Note that there is no initial ```<<``` - instead the insertion sequence is placed in parentheses.
+
+The message is **NOT** constructed right away - instead it gets lazily stringified only when needed. This means that rvalues (temporaries) cannot be passed to the ```INFO()``` macro.
+
+Some notes:
+
+- the lazy stringification means the values will be stringified when an assert fails and not at the point of capture - so the value might have changed
+- if the library is built with C++11 rvalue reference support (see [**```DOCTEST_CONFIG_WITH_RVALUE_REFERENCES```**](configuration.md#doctest_config_with_rvalue_references)) then deleted overloads are provided to prohibit rvalues from being captured in an **```INFO()```** call - since the lazy stringification actually caches pointers to the objects
+- the [**```DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK```**](configuration.md#doctest_config_num_captures_on_stack) config identifier can be used to control how much stack space is used to avoid heap allocations for the streaming macros
+- stream manipulators (from ```<iomanip>```) can be used but need to be created as local variables and used as lvalues
+
+The lazy stringification and the stack usage means that in the common case when no asserts fail the code runs super fast. This makes it suitable even in loops - perhaps to log the iteration. 
+
+There is also the **```CAPTURE()```** macro which is a convenience wrapper of **```INFO()```**:
+
+```c++
+CAPTURE(some_variable)
+```
+
+This will handle the stringification of the variable name for you (actually it works with any expression, not just variables).
+
+This would log something like:
+
+some_variable := 42
+
+---
+
+[Home](readme.md#reference)
diff --git a/doc/markdown/readme.md b/doc/markdown/readme.md
index e7acd70..cbe3aba 100644
--- a/doc/markdown/readme.md
+++ b/doc/markdown/readme.md
@@ -13,6 +13,7 @@
 - [Tutorial](tutorial.md) - make sure you have read it before the other parts of the documentation
 - [Assertion macros](assertions.md)
 - [Test cases, subcases and test fixtures](testcases.md)
+- [Logging macros](logging.md)
 - [Command line](commandline.md)
 - [```main()``` entry point](main.md)
 - [Configuration](configuration.md)
diff --git a/doc/markdown/roadmap.md b/doc/markdown/roadmap.md
index f19679f..36c148d 100644
--- a/doc/markdown/roadmap.md
+++ b/doc/markdown/roadmap.md
@@ -6,18 +6,18 @@
 [![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/onqtam)
 [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3K423Q6TK48BN)
 
-Planned features for future releases - order may change.
+Planned features for future releases - order changes constantly...
 
 ### For 1.2:
 
-- adding contextual info to asserts (logging) - with an ```INFO```/```CONTEXT```/```CAPTURE```/```TRACEPOINT``` macro (also look at [this](https://github.com/philsquared/Catch/issues/601))
 - add ```ERROR```/```FAIL``` macros (also ```ADD_FAILURE_AT(file, line);``` and extend the asserts to have ```_AT``` variants)
 - Parametric test cases (Value/Type-parameterized tests) - https://github.com/onqtam/doctest/issues/38
-- crash handling: signals on UNIX platforms or structured exceptions on Windows (should also have DOCTEST_CONFIG_NO_SIGNAL_CATCHING)
+- crash handling: signals on UNIX platforms or structured exceptions on Windows (should also have DOCTEST_CONFIG_NO_SIGNAL_CATCHING) - look at [Using a Separate Signal Stack](https://www.gnu.org/software/libc/manual/html_node/Signal-Stack.html) - and what is a core dump?
 - runtime performance
     - lazily stringify expressions - only when needed
     - get rid of local statics on the hot path - like in getContextState()
     - make a pool allocator for the ```String``` class - currently very unoptimized
+    - add move semantics to the ```String``` class
 - benchmarking
     - make the bench.py script more usable - with command line arguments
     - redo the compile time ones - also look into CATCH_CONFIG_FAST_COMPILE
@@ -34,7 +34,7 @@
     - a system for writing custom reporters
     - ability to use multiple reporters at once (but only 1 to stdout)
     - a compact reporter
-    - a progress reporter
+    - a progress reporter - or maybe just an option for the console reporter
     - an xml reporter
     - jUnit/xUnit reporters
     - a listener interface - similar to a reporter - look at Catch
@@ -74,6 +74,8 @@
 - integrate static analysis on the CI: **msvc**, **clang**, **cppcheck**
 - extend Approx for types that have operator double - see [here](https://github.com/philsquared/Catch/issues/652) and [here](https://github.com/philsquared/Catch/pull/658)
 - option to list files in which there are test cases who match the current filters
+- option to list test suites and test cases in a tree view
+- decorators for test cases - like in boost test - like "description" - alternative (and broader) mechanism to tags
 - thread safety - asserts/subcases/captures should be safe to be used by multiple threads simultaneously
 - support for running tests in parallel in multiple threads
 - doctest in a GUI environment? with no console? APIs for attaching a console? querying if there is one? [investigate...](https://github.com/philsquared/Catch/blob/master/docs/configuration.md#stdout)
diff --git a/doctest/doctest.h b/doctest/doctest.h
index 27d90ff..04e3c4b 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -48,6 +48,8 @@
 #if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#pragma clang diagnostic ignored "-Wweak-vtables"
 #pragma clang diagnostic ignored "-Wpadded"
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
 #pragma clang diagnostic ignored "-Wshorten-64-to-32"
@@ -61,7 +63,9 @@
 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
 #pragma GCC diagnostic ignored "-Weffc++"
 #pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
 #pragma GCC diagnostic ignored "-Winline"
 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
@@ -293,6 +297,12 @@
 #define DOCTEST_INTERFACE
 #endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
 
+// other
+
+#ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+#define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5
+#endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+
 // =================================================================================================
 // == FEATURE DETECTION END ========================================================================
 // =================================================================================================
@@ -818,6 +828,7 @@
     DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags);
     DOCTEST_INTERFACE void throwException();
     DOCTEST_INTERFACE bool always_false();
+    DOCTEST_INTERFACE void my_memcpy(void* dest, void* src, int num);
 
     struct DOCTEST_INTERFACE SubcaseSignature
     {
@@ -1256,7 +1267,175 @@
     };
 
     DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction);
+    
+    template <bool C>
+    struct StringStreamBase
+    {
+        template <typename T>
+        static std::ostream& convert(std::ostream& stream, const T& in) {
+            stream << toString(in);
+            return stream;
+        }
+    };
 
+    template <>
+    struct StringStreamBase<true>
+    {
+        template <typename T>
+        static std::ostream& convert(std::ostream& stream, const T& in) {
+            stream << in;
+            return stream;
+        }
+    };
+
+    template <typename T>
+    struct StringStream : StringStreamBase<has_insertion_operator<T>::value>
+    {};
+
+    template <typename T>
+    std::ostream& toStream(std::ostream& stream, const DOCTEST_REF_WRAP(T) value) {
+        return StringStream<T>::convert(stream, value);
+    }
+
+    struct IContextScope {
+        virtual void build(std::ostream&) const = 0;
+    };
+
+    DOCTEST_INTERFACE void addToContexts(IContextScope* ptr);
+    DOCTEST_INTERFACE void popFromContexts();
+
+    class ContextBuilder {
+        friend class ContextScope;
+
+        struct ICapture {
+            virtual std::ostream& toStream(std::ostream&) const = 0;
+        };
+
+        template<typename T>
+        struct Capture : ICapture {
+            const T* capture;
+
+            Capture(const T* in)
+                : capture(in)
+            {}
+            virtual std::ostream& toStream(std::ostream& stream) const { // override
+                doctest::detail::toStream(stream, *capture);
+                return stream;
+            }
+        };
+
+        struct Chunk {
+            char buf[sizeof(Capture<char>)]; // place to construct a Capture<T>
+        };
+
+        struct Node {
+            Chunk chunk;
+            Node* next;
+        };
+
+        Chunk stackChunks[DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK];
+        int numCaptures;
+        Node* head;
+        Node* tail;
+
+        void build(std::ostream& stream) const {
+            int curr = 0;
+            // iterate over small buffer
+            while(curr < numCaptures && curr < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK)
+                reinterpret_cast<const ICapture*>(stackChunks[curr++].buf)->toStream(stream);
+            // iterate over list
+            Node* curr_elem = head;
+            while(curr < numCaptures) {
+                reinterpret_cast<const ICapture*>(curr_elem->chunk.buf)->toStream(stream);
+                curr_elem = curr_elem->next;
+                ++curr;
+            }
+        }
+
+        // steal the contents of the other - acting as a move constructor...
+        ContextBuilder(ContextBuilder& other)
+            : numCaptures(other.numCaptures)
+            , head(other.head)
+            , tail(other.tail)
+        {
+            other.numCaptures = 0;
+            other.head = 0;
+            other.tail = 0;
+            my_memcpy(stackChunks, other.stackChunks, sizeof(Chunk) * DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK);
+        }
+
+    public:
+
+        ContextBuilder()
+            : numCaptures(0)
+            , head(0)
+            , tail(0)
+        {}
+
+        template<typename T>
+        ContextBuilder& operator<<(const T& in) {
+            Capture<T> temp(&in);
+
+            // construct either on stack or on heap
+            // copy the bytes for the whole object - including the vtable because we cant construct
+            // the object directly in the buffer using placement new - need the <new> header...
+            if(numCaptures < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) {
+                my_memcpy(stackChunks[numCaptures].buf, &temp, sizeof(Chunk));
+            } else {
+                Node* curr = new Node;
+                curr->next = 0;
+                if(tail) {
+                    tail->next = curr;
+                    tail = curr;
+                } else {
+                    head = tail = curr;
+                }
+
+                my_memcpy(tail->chunk.buf, &temp, sizeof(Chunk));
+            }
+            ++numCaptures;
+            return *this;
+        }
+    
+        ~ContextBuilder() {
+            // free the linked list - the ones on the stack are left as-is
+            // no destructors are called at all - there is no need
+            while(head) {
+                Node* next = head->next;
+                delete head;
+                head = next;
+            }
+        }
+
+#ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#ifdef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+        template<typename T>
+        ContextBuilder& operator<<(const T&&) = delete;
+#else // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+    private:
+        template<typename T>
+        ContextBuilder& operator<<(const T&&);
+#endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+    };
+
+    class ContextScope : public IContextScope {
+        ContextBuilder contextBuilder;
+    public:
+        ContextScope(ContextBuilder& temp)
+            : contextBuilder(temp)
+        {
+            addToContexts(this);
+        }
+
+        ~ContextScope() {
+            popFromContexts();
+        }
+
+        void build(std::ostream& stream) const {
+            contextBuilder.build(stream);
+        }
+    };
 } // namespace detail
 
 #endif // DOCTEST_CONFIG_DISABLE
@@ -1395,6 +1574,10 @@
 #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
     DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), signature)
 
+// for logging
+#define DOCTEST_INFO(x) doctest::detail::ContextScope DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_)(doctest::detail::ContextBuilder() << x)
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x)
+
 // common code in asserts - for convenience
 #define DOCTEST_ASSERT_LOG_AND_REACT(rb)                                                           \
     if(rb.log())                                                                                   \
@@ -1806,6 +1989,9 @@
     template <typename T>                                                                          \
     static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
 
+#define DOCTEST_INFO(x) ((void)0)
+#define DOCTEST_CAPTURE(x) ((void)0)
+
 #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
 #define DOCTEST_WARN(...) ((void)0)
 #define DOCTEST_CHECK(...) ((void)0)
@@ -1970,6 +2156,8 @@
 #define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
 #define TEST_SUITE_END DOCTEST_TEST_SUITE_END
 #define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
+#define INFO DOCTEST_INFO
+#define CAPTURE DOCTEST_CAPTURE
 
 #define WARN DOCTEST_WARN
 #define WARN_FALSE DOCTEST_WARN_FALSE
@@ -2325,6 +2513,8 @@
         int             numFailedAssertions;
         int             numFailedAssertionsForCurrentTestcase;
 
+        std::vector<IContextScope*> contexts; // for logging with INFO() and friends
+
         // stuff for subcases
         std::set<SubcaseSignature> subcasesPassed;
         std::set<int>              subcasesEnteredLevels;
@@ -2693,6 +2883,13 @@
     }
     bool always_false() { return false; }
 
+    void my_memcpy(void* dest, void* src, int num) {
+        char* csrc = static_cast<char*>(src);
+        char* cdest = static_cast<char*>(dest);
+        for(int i = 0; i < num; ++i)
+            cdest[i] = csrc[i];
+    }
+
     // lowers ascii letters
     char tolower(const char c) { return ((c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c); }
 
@@ -3035,6 +3232,9 @@
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
     }
 
+    void addToContexts(IContextScope* ptr) { getContextState()->contexts.push_back(ptr); }
+    void popFromContexts() { getContextState()->contexts.pop_back(); }
+
     // this is needed because MSVC does not permit mixing 2 exception handling schemes in a function
     int callTestFunc(funcType f) {
         int res = EXIT_SUCCESS;
@@ -3185,6 +3385,19 @@
         printToDebugConsole(String(msg) + info1 + info2 + "\n");
     }
 
+    String logContext() {
+        std::ostringstream stream;
+        std::vector<IContextScope*>& contexts = getContextState()->contexts;
+        if(contexts.size() > 0)
+            stream << "with context:\n";
+        for(size_t i = 0; i < contexts.size(); ++i) {
+            stream << "  ";
+            contexts[i]->build(stream);
+            stream << "\n";
+        }
+        return stream.str().c_str();
+    }
+
     void logAssert(bool passed, const char* decomposition, bool threw, const String& exception, const char* expr,
                    assertType::Enum assert_type, const char* file, int line) {
         char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
@@ -3215,9 +3428,11 @@
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
         DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
     }
 
     void logAssertThrows(bool threw, const char* expr, assertType::Enum assert_type,
@@ -3242,9 +3457,11 @@
         DOCTEST_PRINTF_COLORED(msg, threw ? Color::BrightGreen : Color::Red);
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + context.c_str() + "\n");
     }
 
     void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const String& exception, const char* expr,
@@ -3276,9 +3493,11 @@
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
         DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
     }
 
     void logAssertNothrow(bool threw, const String& exception, const char* expr, assertType::Enum assert_type,
@@ -3307,9 +3526,11 @@
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
         DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
     }
 
     ResultBuilder::ResultBuilder(assertType::Enum assert_type, const char* file, int line,
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index b538e29..c57f40a 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -45,6 +45,8 @@
 #if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#pragma clang diagnostic ignored "-Wweak-vtables"
 #pragma clang diagnostic ignored "-Wpadded"
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
 #pragma clang diagnostic ignored "-Wshorten-64-to-32"
@@ -58,7 +60,9 @@
 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
 #pragma GCC diagnostic ignored "-Weffc++"
 #pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
 #pragma GCC diagnostic ignored "-Winline"
 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
@@ -290,6 +294,12 @@
 #define DOCTEST_INTERFACE
 #endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
 
+// other
+
+#ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+#define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5
+#endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+
 // =================================================================================================
 // == FEATURE DETECTION END ========================================================================
 // =================================================================================================
@@ -815,6 +825,7 @@
     DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags);
     DOCTEST_INTERFACE void throwException();
     DOCTEST_INTERFACE bool always_false();
+    DOCTEST_INTERFACE void my_memcpy(void* dest, void* src, int num);
 
     struct DOCTEST_INTERFACE SubcaseSignature
     {
@@ -1253,7 +1264,175 @@
     };
 
     DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction);
+    
+    template <bool C>
+    struct StringStreamBase
+    {
+        template <typename T>
+        static std::ostream& convert(std::ostream& stream, const T& in) {
+            stream << toString(in);
+            return stream;
+        }
+    };
 
+    template <>
+    struct StringStreamBase<true>
+    {
+        template <typename T>
+        static std::ostream& convert(std::ostream& stream, const T& in) {
+            stream << in;
+            return stream;
+        }
+    };
+
+    template <typename T>
+    struct StringStream : StringStreamBase<has_insertion_operator<T>::value>
+    {};
+
+    template <typename T>
+    std::ostream& toStream(std::ostream& stream, const DOCTEST_REF_WRAP(T) value) {
+        return StringStream<T>::convert(stream, value);
+    }
+
+    struct IContextScope {
+        virtual void build(std::ostream&) const = 0;
+    };
+
+    DOCTEST_INTERFACE void addToContexts(IContextScope* ptr);
+    DOCTEST_INTERFACE void popFromContexts();
+
+    class ContextBuilder {
+        friend class ContextScope;
+
+        struct ICapture {
+            virtual std::ostream& toStream(std::ostream&) const = 0;
+        };
+
+        template<typename T>
+        struct Capture : ICapture {
+            const T* capture;
+
+            Capture(const T* in)
+                : capture(in)
+            {}
+            virtual std::ostream& toStream(std::ostream& stream) const { // override
+                doctest::detail::toStream(stream, *capture);
+                return stream;
+            }
+        };
+
+        struct Chunk {
+            char buf[sizeof(Capture<char>)]; // place to construct a Capture<T>
+        };
+
+        struct Node {
+            Chunk chunk;
+            Node* next;
+        };
+
+        Chunk stackChunks[DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK];
+        int numCaptures;
+        Node* head;
+        Node* tail;
+
+        void build(std::ostream& stream) const {
+            int curr = 0;
+            // iterate over small buffer
+            while(curr < numCaptures && curr < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK)
+                reinterpret_cast<const ICapture*>(stackChunks[curr++].buf)->toStream(stream);
+            // iterate over list
+            Node* curr_elem = head;
+            while(curr < numCaptures) {
+                reinterpret_cast<const ICapture*>(curr_elem->chunk.buf)->toStream(stream);
+                curr_elem = curr_elem->next;
+                ++curr;
+            }
+        }
+
+        // steal the contents of the other - acting as a move constructor...
+        ContextBuilder(ContextBuilder& other)
+            : numCaptures(other.numCaptures)
+            , head(other.head)
+            , tail(other.tail)
+        {
+            other.numCaptures = 0;
+            other.head = 0;
+            other.tail = 0;
+            my_memcpy(stackChunks, other.stackChunks, sizeof(Chunk) * DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK);
+        }
+
+    public:
+
+        ContextBuilder()
+            : numCaptures(0)
+            , head(0)
+            , tail(0)
+        {}
+
+        template<typename T>
+        ContextBuilder& operator<<(const T& in) {
+            Capture<T> temp(&in);
+
+            // construct either on stack or on heap
+            // copy the bytes for the whole object - including the vtable because we cant construct
+            // the object directly in the buffer using placement new - need the <new> header...
+            if(numCaptures < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) {
+                my_memcpy(stackChunks[numCaptures].buf, &temp, sizeof(Chunk));
+            } else {
+                Node* curr = new Node;
+                curr->next = 0;
+                if(tail) {
+                    tail->next = curr;
+                    tail = curr;
+                } else {
+                    head = tail = curr;
+                }
+
+                my_memcpy(tail->chunk.buf, &temp, sizeof(Chunk));
+            }
+            ++numCaptures;
+            return *this;
+        }
+    
+        ~ContextBuilder() {
+            // free the linked list - the ones on the stack are left as-is
+            // no destructors are called at all - there is no need
+            while(head) {
+                Node* next = head->next;
+                delete head;
+                head = next;
+            }
+        }
+
+#ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#ifdef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+        template<typename T>
+        ContextBuilder& operator<<(const T&&) = delete;
+#else // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+    private:
+        template<typename T>
+        ContextBuilder& operator<<(const T&&);
+#endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+    };
+
+    class ContextScope : public IContextScope {
+        ContextBuilder contextBuilder;
+    public:
+        ContextScope(ContextBuilder& temp)
+            : contextBuilder(temp)
+        {
+            addToContexts(this);
+        }
+
+        ~ContextScope() {
+            popFromContexts();
+        }
+
+        void build(std::ostream& stream) const {
+            contextBuilder.build(stream);
+        }
+    };
 } // namespace detail
 
 #endif // DOCTEST_CONFIG_DISABLE
@@ -1392,6 +1571,10 @@
 #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
     DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), signature)
 
+// for logging
+#define DOCTEST_INFO(x) doctest::detail::ContextScope DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_)(doctest::detail::ContextBuilder() << x)
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x)
+
 // common code in asserts - for convenience
 #define DOCTEST_ASSERT_LOG_AND_REACT(rb)                                                           \
     if(rb.log())                                                                                   \
@@ -1803,6 +1986,9 @@
     template <typename T>                                                                          \
     static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
 
+#define DOCTEST_INFO(x) ((void)0)
+#define DOCTEST_CAPTURE(x) ((void)0)
+
 #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
 #define DOCTEST_WARN(...) ((void)0)
 #define DOCTEST_CHECK(...) ((void)0)
@@ -1967,6 +2153,8 @@
 #define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
 #define TEST_SUITE_END DOCTEST_TEST_SUITE_END
 #define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
+#define INFO DOCTEST_INFO
+#define CAPTURE DOCTEST_CAPTURE
 
 #define WARN DOCTEST_WARN
 #define WARN_FALSE DOCTEST_WARN_FALSE
diff --git a/doctest/parts/doctest_impl.h b/doctest/parts/doctest_impl.h
index 976d388..84163cc 100644
--- a/doctest/parts/doctest_impl.h
+++ b/doctest/parts/doctest_impl.h
@@ -254,6 +254,8 @@
         int             numFailedAssertions;
         int             numFailedAssertionsForCurrentTestcase;
 
+        std::vector<IContextScope*> contexts; // for logging with INFO() and friends
+
         // stuff for subcases
         std::set<SubcaseSignature> subcasesPassed;
         std::set<int>              subcasesEnteredLevels;
@@ -622,6 +624,13 @@
     }
     bool always_false() { return false; }
 
+    void my_memcpy(void* dest, void* src, int num) {
+        char* csrc = static_cast<char*>(src);
+        char* cdest = static_cast<char*>(dest);
+        for(int i = 0; i < num; ++i)
+            cdest[i] = csrc[i];
+    }
+
     // lowers ascii letters
     char tolower(const char c) { return ((c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c); }
 
@@ -964,6 +973,9 @@
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
     }
 
+    void addToContexts(IContextScope* ptr) { getContextState()->contexts.push_back(ptr); }
+    void popFromContexts() { getContextState()->contexts.pop_back(); }
+
     // this is needed because MSVC does not permit mixing 2 exception handling schemes in a function
     int callTestFunc(funcType f) {
         int res = EXIT_SUCCESS;
@@ -1114,6 +1126,19 @@
         printToDebugConsole(String(msg) + info1 + info2 + "\n");
     }
 
+    String logContext() {
+        std::ostringstream stream;
+        std::vector<IContextScope*>& contexts = getContextState()->contexts;
+        if(contexts.size() > 0)
+            stream << "with context:\n";
+        for(size_t i = 0; i < contexts.size(); ++i) {
+            stream << "  ";
+            contexts[i]->build(stream);
+            stream << "\n";
+        }
+        return stream.str().c_str();
+    }
+
     void logAssert(bool passed, const char* decomposition, bool threw, const String& exception, const char* expr,
                    assertType::Enum assert_type, const char* file, int line) {
         char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
@@ -1144,9 +1169,11 @@
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
         DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
     }
 
     void logAssertThrows(bool threw, const char* expr, assertType::Enum assert_type,
@@ -1171,9 +1198,11 @@
         DOCTEST_PRINTF_COLORED(msg, threw ? Color::BrightGreen : Color::Red);
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + context.c_str() + "\n");
     }
 
     void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const String& exception, const char* expr,
@@ -1205,9 +1234,11 @@
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
         DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
     }
 
     void logAssertNothrow(bool threw, const String& exception, const char* expr, assertType::Enum assert_type,
@@ -1236,9 +1267,11 @@
         DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
         DOCTEST_PRINTF_COLORED(info2, Color::None);
         DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
         DOCTEST_PRINTF_COLORED("\n", Color::None);
 
-        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n");
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
     }
 
     ResultBuilder::ResultBuilder(assertType::Enum assert_type, const char* file, int line,
diff --git a/scripts/random_dev_notes.md b/scripts/random_dev_notes.md
index fbc4873..d843d91 100644
--- a/scripts/random_dev_notes.md
+++ b/scripts/random_dev_notes.md
@@ -1,13 +1,40 @@
 
 
+todo:
+- MESSAGE (like WARN in catch and MESSAGE in boost test)
+- FAIL (like in catch)
+- FAIL_CHECK (like in catch)
 
-CAPTURE/INFO - relates #48 relates #23
-note that the lazy stringification means the values will be stringified when an assert fails and not at the point of capture - so the value might have changed
+- checkpoint
+- passpoint
+http://www.boost.org/doc/libs/1_63_0/libs/test/doc/html/boost_test/test_output/test_tools_support_for_logging/checkpoints.html
+
+- #define IREQUIRE(cond, msg) do { INFO(msg); REQUIRE(cond); } while(0)
+BOOST_WARN_MESSAGE(predicate, message);
+BOOST_CHECK_MESSAGE(predicate, message);
+BOOST_REQUIRE_MESSAGE(predicate, message);
+http://stackoverflow.com/questions/24009797/how-can-i-retrieve-the-last-argument-of-a-c99-variadic-macro
+
+
+look at boost test again:
+http://www.boost.org/doc/libs/1_63_0/libs/test/doc/html/index.html
+
+document that the exception in the _THROWS_AS macros is used as-is and users should use const ref
+
+
+note in all custom mains with a comment that the defaults/overrides are just an example and users shouldn't just copy/paste them!
+
 
 Rust #[test]
 
+dont rule out boost.test as not mainstream - its right there with catch and google test
 
-Nothing is better than documentation with examples. Nothing is worse than examples that don't actually work, because the code has changed since the documentation has been written.
+Nothing is better than documentation with examples. Nothing is worse than examples that don't actually work, because the code has changed since the documentation has been (was) written.
+
+
+
+try if there are warnings for unused variables when tests are disabled
+
 
 
 == when making a new release: