summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorciappi2022-03-17 20:40:49 +0100
committerciappi2022-03-17 20:40:49 +0100
commit73b0490d2f5dbf2d34a8678bf6d9c09b5f2d064a (patch)
tree02bbf1f83388f3f687d5f5cba024ff4304a5539c
parentf35f88c3c5c98344e09c3870b6a43653a8459fe6 (diff)
downloadaur-73b0490d2f5dbf2d34a8678bf6d9c09b5f2d064a.tar.gz
Apply patch to update doctest
-rw-r--r--.SRCINFO4
-rw-r--r--.gitignore1
-rw-r--r--PKGBUILD12
-rw-r--r--doctest.patch4833
4 files changed, 4846 insertions, 4 deletions
diff --git a/.SRCINFO b/.SRCINFO
index e0c5e2964bed..7b31c384ab9a 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = lfortran
pkgdesc = Modern interactive LLVM-based Fortran compiler
pkgver = 0.14.0
- pkgrel = 1
+ pkgrel = 2
url = https://lfortran.org
arch = x86_64
license = BSD
@@ -10,6 +10,8 @@ pkgbase = lfortran
depends = zlib
depends = ncurses
source = https://lfortran.github.io/tarballs/release/lfortran-0.14.0.tar.gz
+ source = doctest.patch
sha256sums = fc3c1d592c56ae2636065ec0228db747f154f65a0867f6311bc8091efd5c13a7
+ sha256sums = c0a8eabdefe530e65a22ac5b19948c7cbf77d8c46a7afeef8792e9e38006c5d5
pkgname = lfortran
diff --git a/.gitignore b/.gitignore
index 018a3de08144..498667d7dff1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
!PKGBUILD
!.SRCINFO
!.gitignore
+!doctest.patch
diff --git a/PKGBUILD b/PKGBUILD
index ea8b3364cc49..8b3676c880ff 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,7 +1,7 @@
# Maintainer: Ciappi <marco.scopesi@gmail.com>
pkgname=lfortran
pkgver=0.14.0
-pkgrel=1
+pkgrel=2
pkgdesc="Modern interactive LLVM-based Fortran compiler"
arch=('x86_64')
url="https://lfortran.org"
@@ -18,10 +18,16 @@ backup=()
options=()
install=
changelog=
-source=("https://lfortran.github.io/tarballs/release/"$pkgname-$pkgver.tar.gz)
-sha256sums=('fc3c1d592c56ae2636065ec0228db747f154f65a0867f6311bc8091efd5c13a7')
+source=("https://lfortran.github.io/tarballs/release/"$pkgname-$pkgver.tar.gz "doctest.patch")
+sha256sums=('fc3c1d592c56ae2636065ec0228db747f154f65a0867f6311bc8091efd5c13a7'
+ 'c0a8eabdefe530e65a22ac5b19948c7cbf77d8c46a7afeef8792e9e38006c5d5')
noextract=()
+prepare() {
+ cd "$pkgname-$pkgver"
+ patch --forward --strip=1 --input="${srcdir}/doctest.patch"
+}
+
build() {
cd "$srcdir/$pkgname-$pkgver"
cmake -DWITH_LLVM=yes -DCMAKE_INSTALL_PREFIX=/usr .
diff --git a/doctest.patch b/doctest.patch
new file mode 100644
index 000000000000..4b812d76e8eb
--- /dev/null
+++ b/doctest.patch
@@ -0,0 +1,4833 @@
+diff --unified --recursive --text package.orig/src/tests/doctest.h package.new/src/tests/doctest.h
+--- package.orig/src/tests/doctest.h 2021-09-23 08:22:52.000000000 +0200
++++ package.new/src/tests/doctest.h 2022-01-10 18:39:35.000000000 +0100
+@@ -4,14 +4,14 @@
+ //
+ // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
+ //
+-// Copyright (c) 2016-2019 Viktor Kirilov
++// Copyright (c) 2016-2021 Viktor Kirilov
+ //
+ // Distributed under the MIT Software License
+ // See accompanying file LICENSE.txt or copy at
+ // https://opensource.org/licenses/MIT
+ //
+ // The documentation can be found at the library's page:
+-// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md
++// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md
+ //
+ // =================================================================================================
+ // =================================================================================================
+@@ -47,9 +47,17 @@
+ // =================================================================================================
+
+ #define DOCTEST_VERSION_MAJOR 2
+-#define DOCTEST_VERSION_MINOR 3
+-#define DOCTEST_VERSION_PATCH 4
+-#define DOCTEST_VERSION_STR "2.3.4"
++#define DOCTEST_VERSION_MINOR 4
++#define DOCTEST_VERSION_PATCH 8
++
++// util we need here
++#define DOCTEST_TOSTR_IMPL(x) #x
++#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
++
++#define DOCTEST_VERSION_STR \
++ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \
++ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \
++ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH)
+
+ #define DOCTEST_VERSION \
+ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
+@@ -137,84 +145,93 @@
+ // == COMPILER WARNINGS ============================================================================
+ // =================================================================================================
+
++// both the header and the implementation suppress all of these,
++// so it only makes sense to aggregrate them like so
++#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
++ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
++ \
++ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \
++ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \
++ \
++ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
++ /* these 4 also disabled globally via cmake: */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
++ /* */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
++ /* static analysis */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */
++
++#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \
++ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
++ DOCTEST_GCC_SUPPRESS_WARNING_POP \
++ DOCTEST_MSVC_SUPPRESS_WARNING_POP
++
++DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
++
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
+
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Winline")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo")
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning
+-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning
+-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration
+-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression
+-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated
+-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant
+-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding
+-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted
+ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe
+-// static analysis
+-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept'
+-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable
+-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ...
+-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtr...
+-
+-// 4548 - expression before comma has no effect; expected expression with side - effect
+-// 4265 - class has virtual functions, but destructor is not virtual
+-// 4986 - exception specification does not match previous declaration
+-// 4350 - behavior change: 'member1' called instead of 'member2'
+-// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
+-// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch
+-// 4774 - format string expected in argument 'x' is not a string literal
+-// 4820 - padding in structs
+-
+-// only 4 should be disabled globally:
+-// - 4514 # unreferenced inline function has been removed
+-// - 4571 # SEH related
+-// - 4710 # function not inlined
+-// - 4711 # function 'x' selected for automatic inline expansion
+
+ #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4548) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4265) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4986) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4350) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4668) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4365) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4774) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4820) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4625) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4626) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(5027) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(5026) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(4623) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(5039) \
+- DOCTEST_MSVC_SUPPRESS_WARNING(5045)
++ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
++ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */
+
+ #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+@@ -227,6 +244,7 @@
+ // GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html
+ // MSVC version table:
+ // https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
++// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022)
+ // MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019)
+ // MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017)
+ // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+@@ -236,6 +254,10 @@
+ // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
+ // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+
++// Universal Windows Platform support
++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
++#define DOCTEST_CONFIG_NO_WINDOWS_SEH
++#endif // WINAPI_FAMILY
+ #if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+ #define DOCTEST_CONFIG_WINDOWS_SEH
+ #endif // MSVC
+@@ -300,15 +322,39 @@
+ #define DOCTEST_NOINLINE __declspec(noinline)
+ #define DOCTEST_UNUSED
+ #define DOCTEST_ALIGNMENT(x)
+-#else // MSVC
++#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0)
++#define DOCTEST_NOINLINE
++#define DOCTEST_UNUSED
++#define DOCTEST_ALIGNMENT(x)
++#else
+ #define DOCTEST_NOINLINE __attribute__((noinline))
+ #define DOCTEST_UNUSED __attribute__((unused))
+ #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
+-#endif // MSVC
++#endif
++
++#ifndef DOCTEST_NORETURN
++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
++#define DOCTEST_NORETURN
++#else // DOCTEST_MSVC
++#define DOCTEST_NORETURN [[noreturn]]
++#endif // DOCTEST_MSVC
++#endif // DOCTEST_NORETURN
+
+-#ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+-#define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5
+-#endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
++#ifndef DOCTEST_NOEXCEPT
++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
++#define DOCTEST_NOEXCEPT
++#else // DOCTEST_MSVC
++#define DOCTEST_NOEXCEPT noexcept
++#endif // DOCTEST_MSVC
++#endif // DOCTEST_NOEXCEPT
++
++#ifndef DOCTEST_CONSTEXPR
++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
++#define DOCTEST_CONSTEXPR const
++#else // DOCTEST_MSVC
++#define DOCTEST_CONSTEXPR constexpr
++#endif // DOCTEST_MSVC
++#endif // DOCTEST_CONSTEXPR
+
+ // =================================================================================================
+ // == FEATURE DETECTION END ========================================================================
+@@ -323,8 +369,6 @@
+ #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__)
+ #endif // __COUNTER__
+
+-#define DOCTEST_TOSTR(x) #x
+-
+ #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+ #define DOCTEST_REF_WRAP(x) x&
+ #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+@@ -342,23 +386,31 @@
+ #define DOCTEST_PLATFORM_LINUX
+ #endif // DOCTEST_PLATFORM
+
+-// clang-format off
+-#define DOCTEST_DELETE_COPIES(type) type(const type&) = delete; type& operator=(const type&) = delete
+-#define DOCTEST_DECLARE_COPIES(type) type(const type&); type& operator=(const type&)
+-#define DOCTEST_DEFINE_COPIES(type) type::type(const type&) = default; type& type::operator=(const type&) = default
+-#define DOCTEST_DECLARE_DEFAULTS(type) type(); ~type()
+-#define DOCTEST_DEFINE_DEFAULTS(type) type::type() = default; type::~type() = default
+-// clang-format on
++namespace doctest { namespace detail {
++ static DOCTEST_CONSTEXPR int consume(const int*, int) { return 0; }
++}}
+
+-#define DOCTEST_GLOBAL_NO_WARNINGS(var) \
++#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
+- DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \
+- static int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
+-#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP
++ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
++ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
++#ifndef DOCTEST_BREAK_INTO_DEBUGGER
+ // should probably take a look at https://github.com/scottt/debugbreak
+-#ifdef DOCTEST_PLATFORM_MAC
+-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :)
++#ifdef DOCTEST_PLATFORM_LINUX
++#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
++// Break at the location of the failing check if possible
++#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
++#else
++#include <signal.h>
++#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
++#endif
++#elif defined(DOCTEST_PLATFORM_MAC)
++#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
++#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
++#else
++#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler)
++#endif
+ #elif DOCTEST_MSVC
+ #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
+ #elif defined(__MINGW32__)
+@@ -367,40 +419,40 @@
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+ #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak()
+ #else // linux
+-#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0)
++#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast<void>(0))
+ #endif // linux
++#endif // DOCTEST_BREAK_INTO_DEBUGGER
+
+ // this is kept here for backwards compatibility since the config option was changed
+ #ifdef DOCTEST_CONFIG_USE_IOSFWD
+ #define DOCTEST_CONFIG_USE_STD_HEADERS
+ #endif // DOCTEST_CONFIG_USE_IOSFWD
+
+-#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
+-#include <iosfwd>
+-#include <cstddef>
+-#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
+-#include <ostream>
+-#endif // VS 2019
+-#else // DOCTEST_CONFIG_USE_STD_HEADERS
+-
++// for clang - always include ciso646 (which drags some std stuff) because
++// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in
++// which case we don't want to forward declare stuff from std - for reference:
++// https://github.com/doctest/doctest/issues/126
++// https://github.com/doctest/doctest/issues/356
+ #if DOCTEST_CLANG
+-// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier)
+ #include <ciso646>
+-#endif // clang
+-
+ #ifdef _LIBCPP_VERSION
+-#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD
+-#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD
+-#else // _LIBCPP_VERSION
+-#define DOCTEST_STD_NAMESPACE_BEGIN namespace std {
+-#define DOCTEST_STD_NAMESPACE_END }
++#define DOCTEST_CONFIG_USE_STD_HEADERS
+ #endif // _LIBCPP_VERSION
++#endif // clang
++
++#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
++#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
++#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
++#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
++#include <cstddef>
++#include <ostream>
++#include <istream>
++#else // DOCTEST_CONFIG_USE_STD_HEADERS
+
+ // Forward declaring 'X' in namespace std is not permitted by the C++ Standard.
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643)
+
+-DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp)
++namespace std { // NOLINT (cert-dcl58-cpp)
+ typedef decltype(nullptr) nullptr_t;
+ template <class charT>
+ struct char_traits;
+@@ -409,17 +461,20 @@
+ template <class charT, class traits>
+ class basic_ostream;
+ typedef basic_ostream<char, char_traits<char>> ostream;
++template <class charT, class traits>
++class basic_istream;
++typedef basic_istream<char, char_traits<char>> istream;
+ template <class... Types>
+ class tuple;
+ #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
+-template <class _Ty>
++// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
++template <class Ty>
+ class allocator;
+-template <class _Elem, class _Traits, class _Alloc>
++template <class Elem, class Traits, class Alloc>
+ class basic_string;
+ using string = basic_string<char, char_traits<char>, allocator<char>>;
+ #endif // VS 2019
+-DOCTEST_STD_NAMESPACE_END
++} // namespace std
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+@@ -471,6 +526,8 @@
+ view data;
+ };
+
++ char* allocate(unsigned sz);
++
+ bool isOnStack() const { return (buf[last] & 128) == 0; }
+ void setOnHeap();
+ void setLast(unsigned in = last);
+@@ -485,11 +542,12 @@
+ String(const char* in);
+ String(const char* in, unsigned in_size);
+
++ String(std::istream& in, unsigned in_size);
++
+ String(const String& other);
+ String& operator=(const String& other);
+
+ String& operator+=(const String& other);
+- String operator+(const String& other) const;
+
+ String(String&& other);
+ String& operator=(String&& other);
+@@ -512,6 +570,8 @@
+ int compare(const String& other, bool no_case = false) const;
+ };
+
++DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs);
++
+ DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs);
+@@ -592,6 +652,10 @@
+ DT_WARN_THROWS_WITH = is_throws_with | is_warn,
+ DT_CHECK_THROWS_WITH = is_throws_with | is_check,
+ DT_REQUIRE_THROWS_WITH = is_throws_with | is_require,
++
++ DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn,
++ DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check,
++ DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require,
+
+ DT_WARN_NOTHROW = is_nothrow | is_warn,
+ DT_CHECK_NOTHROW = is_nothrow | is_check,
+@@ -637,19 +701,18 @@
+
+ struct DOCTEST_INTERFACE TestCaseData
+ {
+- const char* m_file; // the file in which the test was registered
++ String m_file; // the file in which the test was registered (using String - see #350)
+ unsigned m_line; // the line where the test was registered
+ const char* m_name; // name of the test case
+ const char* m_test_suite; // the test suite in which the test was added
+ const char* m_description;
+ bool m_skip;
++ bool m_no_breaks;
++ bool m_no_output;
+ bool m_may_fail;
+ bool m_should_fail;
+ int m_expected_failures;
+ double m_timeout;
+-
+- DOCTEST_DECLARE_DEFAULTS(TestCaseData);
+- DOCTEST_DECLARE_COPIES(TestCaseData);
+ };
+
+ struct DOCTEST_INTERFACE AssertData
+@@ -672,9 +735,7 @@
+ // for specific exception-related asserts
+ bool m_threw_as;
+ const char* m_exception_type;
+-
+- DOCTEST_DECLARE_DEFAULTS(AssertData);
+- DOCTEST_DELETE_COPIES(AssertData);
++ const char* m_exception_string;
+ };
+
+ struct DOCTEST_INTERFACE MessageData
+@@ -683,39 +744,34 @@
+ const char* m_file;
+ int m_line;
+ assertType::Enum m_severity;
+-
+- DOCTEST_DECLARE_DEFAULTS(MessageData);
+- DOCTEST_DELETE_COPIES(MessageData);
+ };
+
+ struct DOCTEST_INTERFACE SubcaseSignature
+ {
+- const char* m_name;
++ String m_name;
+ const char* m_file;
+ int m_line;
+
+- SubcaseSignature(const char* name, const char* file, int line);
+-
+ bool operator<(const SubcaseSignature& other) const;
+-
+- DOCTEST_DECLARE_DEFAULTS(SubcaseSignature);
+- DOCTEST_DECLARE_COPIES(SubcaseSignature);
+ };
+
+ struct DOCTEST_INTERFACE IContextScope
+ {
+- DOCTEST_DELETE_COPIES(IContextScope);
+-
+ IContextScope();
+ virtual ~IContextScope();
+ virtual void stringify(std::ostream*) const = 0;
+ };
+
++namespace detail {
++ struct DOCTEST_INTERFACE TestCase;
++} // namespace detail
++
+ struct ContextOptions //!OCLINT too many fields
+ {
+- std::ostream* cout; // stdout stream - std::cout by default
+- std::ostream* cerr; // stderr stream - std::cerr by default
+- String binary_name; // the test binary name
++ std::ostream* cout = nullptr; // stdout stream
++ String binary_name; // the test binary name
++
++ const detail::TestCase* currentTest = nullptr;
+
+ // == parameters from the command line
+ String out; // output filename
+@@ -732,9 +788,12 @@
+ bool case_sensitive; // if filtering should be case sensitive
+ bool exit; // if the program should be exited after the tests are ran/whatever
+ bool duration; // print the time duration of each test case
++ bool minimal; // minimal console output (only test failures)
++ bool quiet; // no console output
+ bool no_throw; // to skip exceptions-related assertion macros
+ bool no_exitcode; // if the framework should return 0 as the exitcode
+ bool no_run; // to not run the tests at all (can be done with an "*" exclude)
++ bool no_intro; // to not print the intro of the framework
+ bool no_version; // to not print the version of the framework
+ bool no_colors; // if output to the console should be colorized
+ bool force_colors; // forces the use of colors even when a tty cannot be detected
+@@ -743,21 +802,19 @@
+ bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x):
+ bool no_path_in_filenames; // if the path to files should be removed from the output
+ bool no_line_numbers; // if source code line numbers should be omitted from the output
++ bool no_debug_output; // no output in the debug console when a debugger is attached
+ bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!!
++ bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!!
+
+ bool help; // to print the help
+ bool version; // to print the version
+- bool count; // if only the count of matching tests is to be retreived
++ bool count; // if only the count of matching tests is to be retrieved
+ bool list_test_cases; // to list all tests matching the filters
+ bool list_test_suites; // to list all suites matching the filters
+ bool list_reporters; // lists all registered reporters
+-
+- DOCTEST_DECLARE_DEFAULTS(ContextOptions);
+- DOCTEST_DELETE_COPIES(ContextOptions);
+ };
+
+ namespace detail {
+-#if defined(DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || defined(DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS)
+ template <bool CONDITION, typename TYPE = void>
+ struct enable_if
+ {};
+@@ -765,15 +822,48 @@
+ template <typename TYPE>
+ struct enable_if<true, TYPE>
+ { typedef TYPE type; };
+-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ // clang-format off
+ template<class T> struct remove_reference { typedef T type; };
+ template<class T> struct remove_reference<T&> { typedef T type; };
+ template<class T> struct remove_reference<T&&> { typedef T type; };
+
++ template<typename T, typename U = T&&> U declval(int);
++
++ template<typename T> T declval(long);
++
++ template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ;
++
++ template<class T> struct is_lvalue_reference { const static bool value=false; };
++ template<class T> struct is_lvalue_reference<T&> { const static bool value=true; };
++
++ template<class T> struct is_rvalue_reference { const static bool value=false; };
++ template<class T> struct is_rvalue_reference<T&&> { const static bool value=true; };
++
++ template <class T>
++ inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT
++ {
++ return static_cast<T&&>(t);
++ }
++
++ template <class T>
++ inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT
++ {
++ static_assert(!is_lvalue_reference<T>::value,
++ "Can not forward an rvalue as an lvalue.");
++ return static_cast<T&&>(t);
++ }
++
+ template<class T> struct remove_const { typedef T type; };
+ template<class T> struct remove_const<const T> { typedef T type; };
++#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
++ template<class T> struct is_enum : public std::is_enum<T> {};
++ template<class T> struct underlying_type : public std::underlying_type<T> {};
++#else
++ // Use compiler intrinsics
++ template<class T> struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); };
++ template<class T> struct underlying_type { typedef __underlying_type(T) type; };
++#endif
+ // clang-format on
+
+ template <typename T>
+@@ -782,38 +872,27 @@
+ { static const bool value = false; };
+
+ namespace has_insertion_operator_impl {
+- typedef char no;
+- typedef char yes[2];
+-
+- struct any_t
+- {
+- template <typename T>
+- // cppcheck-suppress noExplicitConstructor
+- any_t(const DOCTEST_REF_WRAP(T));
++ std::ostream &os();
++ template<class T>
++ DOCTEST_REF_WRAP(T) val();
++
++ template<class, class = void>
++ struct check {
++ static DOCTEST_CONSTEXPR bool value = false;
+ };
+
+- yes& testStreamable(std::ostream&);
+- no testStreamable(no);
+-
+- no operator<<(const std::ostream&, const any_t&);
+-
+- template <typename T>
+- struct has_insertion_operator
+- {
+- static std::ostream& s;
+- static const DOCTEST_REF_WRAP(T) t;
+- static const bool value = sizeof(decltype(testStreamable(s << t))) == sizeof(yes);
++ template<class T>
++ struct check<T, decltype(os() << val<T>(), void())> {
++ static DOCTEST_CONSTEXPR bool value = true;
+ };
+ } // namespace has_insertion_operator_impl
+
+- template <typename T>
+- struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
+- {};
++ template<class T>
++ using has_insertion_operator = has_insertion_operator_impl::check<const T>;
+
+- DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num);
++ DOCTEST_INTERFACE std::ostream* tlssPush();
++ DOCTEST_INTERFACE String tlssPop();
+
+- DOCTEST_INTERFACE std::ostream* getTlsOss(); // returns a thread-local ostringstream
+- DOCTEST_INTERFACE String getTlsOssResult();
+
+ template <bool C>
+ struct StringMakerBase
+@@ -824,13 +903,61 @@
+ }
+ };
+
++ // Vector<int> and various type other than pointer or array.
++ template<typename T>
++ struct filldata
++ {
++ static void fill(std::ostream* stream, const T &in) {
++ *stream << in;
++ }
++ };
++
++ template<typename T,unsigned long N>
++ struct filldata<T[N]>
++ {
++ static void fill(std::ostream* stream, const T (&in)[N]) {
++ for (unsigned long i = 0; i < N; i++) {
++ *stream << in[i];
++ }
++ }
++ };
++
++ // Specialized since we don't want the terminating null byte!
++ template<unsigned long N>
++ struct filldata<const char[N]>
++ {
++ static void fill(std::ostream* stream, const char(&in)[N]) {
++ *stream << in;
++ }
++ };
++
++ template<typename T>
++ void filloss(std::ostream* stream, const T& in) {
++ filldata<T>::fill(stream, in);
++ }
++
++ template<typename T,unsigned long N>
++ void filloss(std::ostream* stream, const T (&in)[N]) {
++ // T[N], T(&)[N], T(&&)[N] have same behaviour.
++ // Hence remove reference.
++ filldata<typename remove_reference<decltype(in)>::type>::fill(stream, in);
++ }
++
+ template <>
+ struct StringMakerBase<true>
+ {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T) in) {
+- *getTlsOss() << in;
+- return getTlsOssResult();
++ /* When parameter "in" is a null terminated const char* it works.
++ * When parameter "in" is a T arr[N] without '\0' we can fill the
++ * stringstream with N objects (T=char).If in is char pointer *
++ * without '\0' , it would cause segfault
++ * stepping over unaccessible memory.
++ */
++
++ std::ostream* stream = tlssPush();
++ filloss(stream, in);
++ return tlssPop();
+ }
+ };
+
+@@ -872,7 +999,7 @@
+ }
+ };
+
+-template <typename T>
++template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true>
+ String toString(const DOCTEST_REF_WRAP(T) value) {
+ return StringMaker<T>::convert(value);
+ }
+@@ -899,8 +1026,14 @@
+ DOCTEST_INTERFACE String toString(int long long unsigned in);
+ DOCTEST_INTERFACE String toString(std::nullptr_t in);
+
++template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true>
++String toString(const DOCTEST_REF_WRAP(T) value) {
++ typedef typename detail::underlying_type<T>::type UT;
++ return toString(static_cast<UT>(value));
++}
++
+ #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
++// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+ DOCTEST_INTERFACE String toString(const std::string& in);
+ #endif // VS 2019
+
+@@ -909,8 +1042,6 @@
+ public:
+ explicit Approx(double value);
+
+- DOCTEST_DECLARE_COPIES(Approx);
+-
+ Approx operator()(double value) const;
+
+ #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+@@ -1010,14 +1141,12 @@
+
+ struct DOCTEST_INTERFACE TestFailureException
+ {
+- DOCTEST_DECLARE_DEFAULTS(TestFailureException);
+- DOCTEST_DECLARE_COPIES(TestFailureException);
+ };
+
+ DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at);
+
+ #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+- [[noreturn]]
++ DOCTEST_NORETURN
+ #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ DOCTEST_INTERFACE void throwException();
+
+@@ -1026,24 +1155,42 @@
+ SubcaseSignature m_signature;
+ bool m_entered = false;
+
+- Subcase(const char* name, const char* file, int line);
++ Subcase(const String& name, const char* file, int line);
+ ~Subcase();
+
+- DOCTEST_DELETE_COPIES(Subcase);
+-
+ operator bool() const;
+ };
+
+ template <typename L, typename R>
+ String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
+ const DOCTEST_REF_WRAP(R) rhs) {
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ return toString(lhs) + op + toString(rhs);
+ }
+
++#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
++DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
++#endif
++
++// This will check if there is any way it could find a operator like member or friend and uses it.
++// If not it doesn't find the operator or if the operator at global scope is defined after
++// this template, the template won't be instantiated due to SFINAE. Once the template is not
++// instantiated it can look for global operator using normal conversions.
++#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval<L>() op doctest::detail::declval<R>()),ret{})
++
+ #define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
+ template <typename R> \
+- DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \
+- bool res = op_macro(lhs, rhs); \
++ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R&& rhs) { \
++ bool res = op_macro(doctest::detail::forward<const L>(lhs), doctest::detail::forward<const R>(rhs)); \
++ if(m_at & assertType::is_false) \
++ res = !res; \
++ if(!res || doctest::getContextOptions()->success) \
++ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \
++ return Result(res); \
++ } \
++ template <typename R ,typename enable_if<!doctest::detail::is_rvalue_reference<R>::value, void >::type* = nullptr> \
++ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R& rhs) { \
++ bool res = op_macro(doctest::detail::forward<const L>(lhs), rhs); \
+ if(m_at & assertType::is_false) \
+ res = !res; \
+ if(!res || doctest::getContextOptions()->success) \
+@@ -1067,12 +1214,10 @@
+ bool m_passed;
+ String m_decomp;
+
++ Result() = default;
+ Result(bool passed, const String& decomposition = String());
+
+- DOCTEST_DECLARE_DEFAULTS(Result);
+- DOCTEST_DECLARE_COPIES(Result);
+-
+- // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence
++ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
+ DOCTEST_FORBIT_EXPRESSION(Result, &)
+ DOCTEST_FORBIT_EXPRESSION(Result, ^)
+ DOCTEST_FORBIT_EXPRESSION(Result, |)
+@@ -1114,7 +1259,7 @@
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal")
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+- // http://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389
++ // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389
+ DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch
+ DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch
+ DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch
+@@ -1127,6 +1272,7 @@
+ #define DOCTEST_COMPARISON_RETURN_TYPE bool
+ #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ #define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
+ inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
+ inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
+@@ -1173,12 +1319,15 @@
+ L lhs;
+ assertType::Enum m_at;
+
+- explicit Expression_lhs(L in, assertType::Enum at)
+- : lhs(in)
++ explicit Expression_lhs(L&& in, assertType::Enum at)
++ : lhs(doctest::detail::forward<L>(in))
+ , m_at(at) {}
+
+ DOCTEST_NOINLINE operator Result() {
+- bool res = !!lhs;
++// this is needed only for MSVC 2015
++DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
++ bool res = static_cast<bool>(lhs);
++DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ res = !res;
+
+@@ -1187,6 +1336,9 @@
+ return Result(res);
+ }
+
++ /* This is required for user-defined conversions from Expression_lhs to L */
++ operator L() const { return lhs; }
++
+ // clang-format off
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional
+@@ -1196,7 +1348,7 @@
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional
+ // clang-format on
+
+- // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence
++ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |)
+@@ -1227,37 +1379,42 @@
+
+ #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
++#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
++DOCTEST_CLANG_SUPPRESS_WARNING_POP
++#endif
++
+ struct DOCTEST_INTERFACE ExpressionDecomposer
+ {
+ assertType::Enum m_at;
+
+ ExpressionDecomposer(assertType::Enum at);
+
+- DOCTEST_DECLARE_DEFAULTS(ExpressionDecomposer);
+- DOCTEST_DELETE_COPIES(ExpressionDecomposer);
+-
+ // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table)
+ // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now...
+ // https://github.com/catchorg/Catch2/issues/870
+ // https://github.com/catchorg/Catch2/issues/565
+ template <typename L>
+- Expression_lhs<const DOCTEST_REF_WRAP(L)> operator<<(const DOCTEST_REF_WRAP(L) operand) {
+- return Expression_lhs<const DOCTEST_REF_WRAP(L)>(operand, m_at);
++ Expression_lhs<const L> operator<<(const L &&operand) {
++ return Expression_lhs<const L>(doctest::detail::forward<const L>(operand), m_at);
++ }
++
++ template <typename L,typename enable_if<!doctest::detail::is_rvalue_reference<L>::value,void >::type* = nullptr>
++ Expression_lhs<const L&> operator<<(const L &operand) {
++ return Expression_lhs<const L&>(operand, m_at);
+ }
+ };
+
+ struct DOCTEST_INTERFACE TestSuite
+ {
+- const char* m_test_suite;
+- const char* m_description;
+- bool m_skip;
+- bool m_may_fail;
+- bool m_should_fail;
+- int m_expected_failures;
+- double m_timeout;
+-
+- DOCTEST_DECLARE_DEFAULTS(TestSuite);
+- DOCTEST_DECLARE_COPIES(TestSuite);
++ const char* m_test_suite = nullptr;
++ const char* m_description = nullptr;
++ bool m_skip = false;
++ bool m_no_breaks = false;
++ bool m_no_output = false;
++ bool m_may_fail = false;
++ bool m_should_fail = false;
++ int m_expected_failures = 0;
++ double m_timeout = 0;
+
+ TestSuite& operator*(const char* in);
+
+@@ -1281,8 +1438,6 @@
+ TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
+ const char* type = "", int template_id = -1);
+
+- DOCTEST_DECLARE_DEFAULTS(TestCase);
+-
+ TestCase(const TestCase& other);
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
+@@ -1327,33 +1482,31 @@
+ template <class L, class R> struct RelationalComparator<n, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } };
+ // clang-format on
+
+- DOCTEST_BINARY_RELATIONAL_OP(0, eq)
+- DOCTEST_BINARY_RELATIONAL_OP(1, ne)
+- DOCTEST_BINARY_RELATIONAL_OP(2, gt)
+- DOCTEST_BINARY_RELATIONAL_OP(3, lt)
+- DOCTEST_BINARY_RELATIONAL_OP(4, ge)
+- DOCTEST_BINARY_RELATIONAL_OP(5, le)
++ DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq)
++ DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne)
++ DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt)
++ DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt)
++ DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge)
++ DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le)
+
+ struct DOCTEST_INTERFACE ResultBuilder : public AssertData
+ {
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+- const char* exception_type = "");
+-
+- DOCTEST_DECLARE_DEFAULTS(ResultBuilder);
+- DOCTEST_DELETE_COPIES(ResultBuilder);
++ const char* exception_type = "", const char* exception_string = "");
+
+ void setResult(const Result& res);
+
+ template <int comparison, typename L, typename R>
+- DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs,
++ DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
+ if(m_failed || getContextOptions()->success)
+ m_decomp = stringifyBinaryExpr(lhs, ", ", rhs);
++ return !m_failed;
+ }
+
+ template <typename L>
+- DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) {
++ DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) {
+ m_failed = !val;
+
+ if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+@@ -1361,6 +1514,8 @@
+
+ if(m_failed || getContextOptions()->success)
+ m_decomp = toString(val);
++
++ return !m_failed;
+ }
+
+ void translateException();
+@@ -1380,7 +1535,7 @@
+
+ DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad);
+
+- DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line,
++ DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, Result result);
+
+ #define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \
+@@ -1396,7 +1551,7 @@
+ if(checkIfShouldThrow(at)) \
+ throwException(); \
+ } \
+- return; \
++ return !failed; \
+ } \
+ } while(false)
+
+@@ -1411,7 +1566,7 @@
+ throwException()
+
+ template <int comparison, typename L, typename R>
+- DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line,
++ DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const DOCTEST_REF_WRAP(L) lhs,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ bool failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
+@@ -1422,10 +1577,11 @@
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
++ return !failed;
+ }
+
+ template <typename L>
+- DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line,
++ DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const DOCTEST_REF_WRAP(L) val) {
+ bool failed = !val;
+
+@@ -1438,12 +1594,11 @@
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS(toString(val));
+ DOCTEST_ASSERT_IN_TESTS(toString(val));
++ return !failed;
+ }
+
+ struct DOCTEST_INTERFACE IExceptionTranslator
+ {
+- DOCTEST_DELETE_COPIES(IExceptionTranslator);
+-
+ IExceptionTranslator();
+ virtual ~IExceptionTranslator();
+ virtual bool translate(String&) const = 0;
+@@ -1464,9 +1619,9 @@
+ } catch(T ex) { // NOLINT
+ res = m_translateFunction(ex); //!OCLINT parameter reassignment
+ return true;
+- } catch(...) {} //!OCLINT - empty catch statement
+-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+- ((void)res); // to silence -Wunused-parameter
++ } catch(...) {} //!OCLINT - empty catch statement
++#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
++ static_cast<void>(res); // to silence -Wunused-parameter
+ return false;
+ }
+
+@@ -1533,40 +1688,57 @@
+ class DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
+ protected:
+ ContextScopeBase();
++ ContextScopeBase(ContextScopeBase&& other);
+
+ void destroy();
++ bool need_to_destroy{true};
+ };
+
+- template <typename L> class DOCTEST_INTERFACE ContextScope : public ContextScopeBase
++ template <typename L> class ContextScope : public ContextScopeBase
+ {
+- const L &lambda_;
++ const L lambda_;
+
+ public:
+ explicit ContextScope(const L &lambda) : lambda_(lambda) {}
+
+- ContextScope(ContextScope &&other) : lambda_(other.lambda_) {}
++ ContextScope(ContextScope &&other) : ContextScopeBase(static_cast<ContextScopeBase&&>(other)), lambda_(other.lambda_) {}
+
+ void stringify(std::ostream* s) const override { lambda_(s); }
+
+- ~ContextScope() override { destroy(); }
++ ~ContextScope() override {
++ if (need_to_destroy) {
++ destroy();
++ }
++ }
+ };
+
+ struct DOCTEST_INTERFACE MessageBuilder : public MessageData
+ {
+ std::ostream* m_stream;
++ bool logged = false;
+
+ MessageBuilder(const char* file, int line, assertType::Enum severity);
+ MessageBuilder() = delete;
+ ~MessageBuilder();
+
+- DOCTEST_DELETE_COPIES(MessageBuilder);
+-
++ // the preferred way of chaining parameters for stringification
+ template <typename T>
+- MessageBuilder& operator<<(const T& in) {
++ MessageBuilder& operator,(const T& in) {
+ toStream(m_stream, in);
+ return *this;
+ }
+
++ // kept here just for backwards-compatibility - the comma operator should be preferred now
++ template <typename T>
++ MessageBuilder& operator<<(const T& in) { return this->operator,(in); }
++
++ // the `,` operator has the lowest operator precedence - if `<<` is used by the user then
++ // the `,` operator will be called last which is not what we want and thus the `*` operator
++ // is used first (has higher operator precedence compared to `<<`) so that we guarantee that
++ // an operator of the MessageBuilder class is called first before the rest of the parameters
++ template <typename T>
++ MessageBuilder& operator*(const T& in) { return this->operator,(in); }
++
+ bool log();
+ void react();
+ };
+@@ -1590,6 +1762,8 @@
+ DOCTEST_DEFINE_DECORATOR(test_suite, const char*, "");
+ DOCTEST_DEFINE_DECORATOR(description, const char*, "");
+ DOCTEST_DEFINE_DECORATOR(skip, bool, true);
++DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true);
++DOCTEST_DEFINE_DECORATOR(no_output, bool, true);
+ DOCTEST_DEFINE_DECORATOR(timeout, double, 0);
+ DOCTEST_DEFINE_DECORATOR(may_fail, bool, true);
+ DOCTEST_DEFINE_DECORATOR(should_fail, bool, true);
+@@ -1634,14 +1808,13 @@
+ public:
+ explicit Context(int argc = 0, const char* const* argv = nullptr);
+
+- DOCTEST_DELETE_COPIES(Context);
+-
+ ~Context();
+
+ void applyCommandLine(int argc, const char* const* argv);
+
+ void addFilter(const char* filter, const char* value);
+ void clearFilters();
++ void setOption(const char* option, bool value);
+ void setOption(const char* option, int value);
+ void setOption(const char* option, const char* value);
+
+@@ -1651,6 +1824,8 @@
+
+ void setAssertHandler(detail::assert_handler ah);
+
++ void setCout(std::ostream* out);
++
+ int run();
+ };
+
+@@ -1677,9 +1852,7 @@
+ int numAssertsFailedCurrentTest;
+ double seconds;
+ int failure_flags; // use TestCaseFailureReason::Enum
+-
+- DOCTEST_DECLARE_DEFAULTS(CurrentTestCaseStats);
+- DOCTEST_DELETE_COPIES(CurrentTestCaseStats);
++ bool testCaseSuccess;
+ };
+
+ struct DOCTEST_INTERFACE TestCaseException
+@@ -1696,16 +1869,13 @@
+ unsigned numTestCasesFailed;
+ int numAsserts;
+ int numAssertsFailed;
+-
+- DOCTEST_DECLARE_DEFAULTS(TestRunStats);
+- DOCTEST_DELETE_COPIES(TestRunStats);
+ };
+
+ struct QueryData
+ {
+- const TestRunStats* run_stats = nullptr;
+- String* data = nullptr;
+- unsigned num_data = 0;
++ const TestRunStats* run_stats = nullptr;
++ const TestCaseData** data = nullptr;
++ unsigned num_data = 0;
+ };
+
+ struct DOCTEST_INTERFACE IReporter
+@@ -1724,6 +1894,8 @@
+
+ // called when a test case is started (safe to cache a pointer to the input)
+ virtual void test_case_start(const TestCaseData&) = 0;
++ // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input)
++ virtual void test_case_reenter(const TestCaseData&) = 0;
+ // called when a test case has ended
+ virtual void test_case_end(const CurrentTestCaseStats&) = 0;
+
+@@ -1778,10 +1950,11 @@
+ #if !defined(DOCTEST_CONFIG_DISABLE)
+
+ // common code in asserts - for convenience
+-#define DOCTEST_ASSERT_LOG_AND_REACT(b) \
++#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \
+ if(b.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+- b.react()
++ b.react(); \
++ return !b.m_failed
+
+ #ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+ #define DOCTEST_WRAP_IN_TRY(x) x;
+@@ -1789,27 +1962,26 @@
+ #define DOCTEST_WRAP_IN_TRY(x) \
+ try { \
+ x; \
+- } catch(...) { _DOCTEST_RB.translateException(); }
++ } catch(...) { DOCTEST_RB.translateException(); }
+ #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+ #ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+-#define DOCTEST_CAST_TO_VOID(x) \
++#define DOCTEST_CAST_TO_VOID(...) \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \
+- static_cast<void>(x); \
++ static_cast<void>(__VA_ARGS__); \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+ #else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+-#define DOCTEST_CAST_TO_VOID(x) x;
++#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__;
+ #endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+
+ // registers the test by initializing a dummy var with a function
+ #define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \
+- global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
++ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \
+ doctest::detail::regTest( \
+ doctest::detail::TestCase( \
+ f, __FILE__, __LINE__, \
+ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \
+- decorators); \
+- DOCTEST_GLOBAL_NO_WARNINGS_END()
++ decorators))
+
+ #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \
+ namespace { \
+@@ -1832,18 +2004,18 @@
+
+ #define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \
+ static doctest::detail::funcType proxy() { return f; } \
+- DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \
++ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \
+ static void f()
+
+ // for registering tests
+ #define DOCTEST_TEST_CASE(decorators) \
+- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
++ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
+
+ // for registering tests in classes - requires C++17 for inline variables!
+ #if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L)
+ #define DOCTEST_TEST_CASE_CLASS(decorators) \
+- DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), \
+- DOCTEST_ANONYMOUS(_DOCTEST_ANON_PROXY_), \
++ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \
++ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \
+ decorators)
+ #else // DOCTEST_TEST_CASE_CLASS
+ #define DOCTEST_TEST_CASE_CLASS(...) \
+@@ -1852,8 +2024,8 @@
+
+ // for registering tests with a fixture
+ #define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \
+- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \
+- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
++ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \
++ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
+
+ // for converting types to strings without the <typeinfo> header and demangling
+ #define DOCTEST_TYPE_TO_STRING_IMPL(...) \
+@@ -1866,7 +2038,7 @@
+ DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \
+ } \
+ } \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++ static_assert(true, "")
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \
+ template <typename T> \
+@@ -1897,20 +2069,20 @@
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \
+ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \
+- DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
++ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_))
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \
+- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \
+- doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\
+- DOCTEST_GLOBAL_NO_WARNINGS_END()
++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), \
++ doctest::detail::instantiationHelper( \
++ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0)))
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
+- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
++ static_assert(true, "")
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
+- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \
++ static_assert(true, "")
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \
+@@ -1919,11 +2091,11 @@
+ static void anon()
+
+ #define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \
+- DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__)
++ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__)
+
+ // for subcases
+ #define DOCTEST_SUBCASE(name) \
+- if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
++ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
+ doctest::detail::Subcase(name, __FILE__, __LINE__))
+
+ // for grouping tests in test suites by using code blocks
+@@ -1932,10 +2104,12 @@
+ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
+- static doctest::detail::TestSuite data; \
++ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
++ static doctest::detail::TestSuite data{}; \
+ static bool inited = false; \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
++ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ if(!inited) { \
+ data* decorators; \
+ inited = true; \
+@@ -1947,79 +2121,79 @@
+ namespace ns_name
+
+ #define DOCTEST_TEST_SUITE(decorators) \
+- DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_))
++ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_))
+
+ // for starting a testsuite block
+ #define DOCTEST_TEST_SUITE_BEGIN(decorators) \
+- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
+- doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \
+- DOCTEST_GLOBAL_NO_WARNINGS_END() \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \
++ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \
++ static_assert(true, "")
+
+ // for ending a testsuite block
+ #define DOCTEST_TEST_SUITE_END \
+- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
+- doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \
+- DOCTEST_GLOBAL_NO_WARNINGS_END() \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \
++ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \
++ typedef int DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_)
+
+ // for registering exception translators
+ #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \
+ inline doctest::String translatorName(signature); \
+- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \
+- doctest::registerExceptionTranslator(translatorName); \
+- DOCTEST_GLOBAL_NO_WARNINGS_END() \
++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \
++ doctest::registerExceptionTranslator(translatorName)) \
+ doctest::String translatorName(signature)
+
+ #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
+- DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \
++ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \
+ signature)
+
+ // for registering reporters
+ #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \
+- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \
+- doctest::registerReporter<reporter>(name, priority, true); \
+- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), \
++ doctest::registerReporter<reporter>(name, priority, true)) \
++ static_assert(true, "")
+
+ // for registering listeners
+ #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \
+- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \
+- doctest::registerReporter<reporter>(name, priority, false); \
+- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+-
+-// for logging
+-#define DOCTEST_INFO(expression) \
+- DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \
+- DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), expression)
+-
+-#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, expression) \
+- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \
+- auto lambda_name = [&](std::ostream* s_name) { \
++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), \
++ doctest::registerReporter<reporter>(name, priority, false)) \
++ static_assert(true, "")
++
++// clang-format off
++// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557
++#define DOCTEST_INFO(...) \
++ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \
++ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \
++ __VA_ARGS__)
++// clang-format on
++
++#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
++ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
++ [&](std::ostream* s_name) { \
+ doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
+ mb_name.m_stream = s_name; \
+- mb_name << expression; \
+- }; \
+- DOCTEST_MSVC_SUPPRESS_WARNING_POP \
+- auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name)
++ mb_name * __VA_ARGS__; \
++ })
+
+-#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x)
++#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
+
+-#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \
+- do { \
++#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \
++ [&] { \
+ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \
+- mb << x; \
+- DOCTEST_ASSERT_LOG_AND_REACT(mb); \
+- } while((void)0, 0)
++ mb * __VA_ARGS__; \
++ if(mb.log()) \
++ DOCTEST_BREAK_INTO_DEBUGGER(); \
++ mb.react(); \
++ }()
+
+ // clang-format off
+-#define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
+-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
+-#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
++#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
++#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
++#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+ // clang-format on
+
+-#define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x)
+-#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x)
+-#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x)
++#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__)
++#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__)
++#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__)
+
+ #define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility.
+
+@@ -2027,21 +2201,24 @@
+
+ #define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
+- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \
++ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \
+ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
+ << __VA_ARGS__)) \
+- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \
++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+ #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
+- do { \
++ [&] { \
+ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \
+- } while((void)0, 0)
++ }()
+
+ #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
++// necessary for <ASSERT>_MESSAGE
++#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1
++
+ #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
+ doctest::detail::decomp_assert( \
+@@ -2059,102 +2236,113 @@
+ #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__)
+
+ // clang-format off
+-#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while((void)0, 0)
+-#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while((void)0, 0)
+-#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while((void)0, 0)
+-#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while((void)0, 0)
+-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while((void)0, 0)
+-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while((void)0, 0)
++#define DOCTEST_WARN_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); }()
++#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); }()
++#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); }()
++#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); }()
++#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); }()
++#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); }()
+ // clang-format on
+
+-#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, ...) \
+- do { \
++#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \
++ [&] { \
+ if(!doctest::getContextOptions()->no_throw) { \
+- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+- __LINE__, #expr, #__VA_ARGS__); \
++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
++ __LINE__, #expr, #__VA_ARGS__, message); \
+ try { \
+ DOCTEST_CAST_TO_VOID(expr) \
+- } catch(const doctest::detail::remove_const< \
+- doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \
+- _DOCTEST_RB.translateException(); \
+- _DOCTEST_RB.m_threw_as = true; \
+- } catch(...) { _DOCTEST_RB.translateException(); } \
+- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
++ } catch(const typename doctest::detail::remove_const< \
++ typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \
++ DOCTEST_RB.translateException(); \
++ DOCTEST_RB.m_threw_as = true; \
++ } catch(...) { DOCTEST_RB.translateException(); } \
++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
++ } else { \
++ return false; \
+ } \
+- } while((void)0, 0)
++ }()
+
+-#define DOCTEST_ASSERT_THROWS_WITH(expr, assert_type, ...) \
+- do { \
++#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \
++ [&] { \
+ if(!doctest::getContextOptions()->no_throw) { \
+- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+- __LINE__, #expr, __VA_ARGS__); \
++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
++ __LINE__, expr_str, "", __VA_ARGS__); \
+ try { \
+ DOCTEST_CAST_TO_VOID(expr) \
+- } catch(...) { _DOCTEST_RB.translateException(); } \
+- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
++ } catch(...) { DOCTEST_RB.translateException(); } \
++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
++ } else { \
++ return false; \
+ } \
+- } while((void)0, 0)
++ }()
+
+-#define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \
+- do { \
+- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+- __LINE__, #expr); \
++#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \
++ [&] { \
++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
++ __LINE__, #__VA_ARGS__); \
+ try { \
+- DOCTEST_CAST_TO_VOID(expr) \
+- } catch(...) { _DOCTEST_RB.translateException(); } \
+- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+- } while((void)0, 0)
++ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \
++ } catch(...) { DOCTEST_RB.translateException(); } \
++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
++ }()
+
+ // clang-format off
+-#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS, "")
+-#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS, "")
+-#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS, "")
+-
+-#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, __VA_ARGS__)
+-#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, __VA_ARGS__)
+-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, __VA_ARGS__)
+-
+-#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS_WITH, __VA_ARGS__)
+-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS_WITH, __VA_ARGS__)
+-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__)
+-
+-#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW)
+-#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW)
+-#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW)
+-
+-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0)
+-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0)
+-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0)
+-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0)
+-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0)
+-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0)
+-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH(expr, ex); } while((void)0, 0)
+-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH(expr, ex); } while((void)0, 0)
+-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH(expr, ex); } while((void)0, 0)
+-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0)
+-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0)
+-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0)
++#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "")
++#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "")
++#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "")
++
++#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__)
++#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__)
++#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__)
++
++#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__)
++#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__)
++#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__)
++
++#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__)
++#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__)
++#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__)
++
++#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__)
++#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__)
++#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__)
++
++#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); }()
++#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); }()
++#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); }()
++#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); }()
++#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); }()
++#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); }()
++#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); }()
++#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); }()
++#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); }()
++#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); }()
++#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); }()
++#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); }()
++#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); }()
++#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); }()
++#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); }()
+ // clang-format on
+
+ #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+ #define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
+- do { \
+- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
++ [&] { \
++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY( \
+- _DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
++ DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
+ __VA_ARGS__)) \
+- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+- } while((void)0, 0)
++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
++ }()
+
+ #define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+- do { \
+- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
++ [&] { \
++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \
+- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+- } while((void)0, 0)
++ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \
++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
++ }()
+
+ #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+@@ -2205,6 +2393,9 @@
+ #undef DOCTEST_WARN_THROWS_WITH
+ #undef DOCTEST_CHECK_THROWS_WITH
+ #undef DOCTEST_REQUIRE_THROWS_WITH
++#undef DOCTEST_WARN_THROWS_WITH_AS
++#undef DOCTEST_CHECK_THROWS_WITH_AS
++#undef DOCTEST_REQUIRE_THROWS_WITH_AS
+ #undef DOCTEST_WARN_NOTHROW
+ #undef DOCTEST_CHECK_NOTHROW
+ #undef DOCTEST_REQUIRE_NOTHROW
+@@ -2218,37 +2409,46 @@
+ #undef DOCTEST_WARN_THROWS_WITH_MESSAGE
+ #undef DOCTEST_CHECK_THROWS_WITH_MESSAGE
+ #undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE
++#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE
++#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE
++#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE
+ #undef DOCTEST_WARN_NOTHROW_MESSAGE
+ #undef DOCTEST_CHECK_NOTHROW_MESSAGE
+ #undef DOCTEST_REQUIRE_NOTHROW_MESSAGE
+
+ #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+-#define DOCTEST_WARN_THROWS(expr) ((void)0)
+-#define DOCTEST_CHECK_THROWS(expr) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
+-#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0)
+-#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0)
+-#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0)
+-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0)
+-#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
+-#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
+-#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
+-
+-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0)
++#define DOCTEST_WARN_THROWS(...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS(...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS(...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_AS(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_AS(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ([] { return false; })
++#define DOCTEST_WARN_NOTHROW(...) ([] { return false; })
++#define DOCTEST_CHECK_NOTHROW(...) ([] { return false; })
++#define DOCTEST_REQUIRE_NOTHROW(...) ([] { return false; })
++
++#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; })
++#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) ([] { return false; })
+
+ #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+@@ -2290,35 +2490,32 @@
+
+ // for registering tests
+ #define DOCTEST_TEST_CASE(name) \
+- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
++ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+ // for registering tests in classes
+ #define DOCTEST_TEST_CASE_CLASS(name) \
+- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
++ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+ // for registering tests with a fixture
+ #define DOCTEST_TEST_CASE_FIXTURE(x, name) \
+- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \
+- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
++ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \
++ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+ // for converting types to strings without the <typeinfo> header and demangling
+-#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "")
+ #define DOCTEST_TYPE_TO_STRING_IMPL(...)
+
+ // for typed tests
+ #define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \
+ template <typename type> \
+- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
++ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
+
+ #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \
+ template <typename type> \
+- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
++ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
+
+-#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+-
+-#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
+- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "")
++#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "")
+
+ // for subcases
+ #define DOCTEST_SUBCASE(name)
+@@ -2327,92 +2524,159 @@
+ #define DOCTEST_TEST_SUITE(name) namespace
+
+ // for starting a testsuite block
+-#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "")
+
+ // for ending a testsuite block
+-#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
++#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_)
+
+ #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+- static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
++ static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature)
+
+ #define DOCTEST_REGISTER_REPORTER(name, priority, reporter)
+ #define DOCTEST_REGISTER_LISTENER(name, priority, reporter)
+
+-#define DOCTEST_INFO(x) ((void)0)
+-#define DOCTEST_CAPTURE(x) ((void)0)
+-#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0)
+-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0)
+-#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0)
+-#define DOCTEST_MESSAGE(x) ((void)0)
+-#define DOCTEST_FAIL_CHECK(x) ((void)0)
+-#define DOCTEST_FAIL(x) ((void)0)
+-
+-#define DOCTEST_WARN(...) ((void)0)
+-#define DOCTEST_CHECK(...) ((void)0)
+-#define DOCTEST_REQUIRE(...) ((void)0)
+-#define DOCTEST_WARN_FALSE(...) ((void)0)
+-#define DOCTEST_CHECK_FALSE(...) ((void)0)
+-#define DOCTEST_REQUIRE_FALSE(...) ((void)0)
+-
+-#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0)
+-#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0)
+-#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0)
+-#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0)
+-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0)
+-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0)
+-
+-#define DOCTEST_WARN_THROWS(expr) ((void)0)
+-#define DOCTEST_CHECK_THROWS(expr) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
+-#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0)
+-#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0)
+-#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0)
+-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0)
+-#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
+-#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
+-#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
+-
+-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0)
+-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0)
+-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0)
+-
+-#define DOCTEST_WARN_EQ(...) ((void)0)
+-#define DOCTEST_CHECK_EQ(...) ((void)0)
+-#define DOCTEST_REQUIRE_EQ(...) ((void)0)
+-#define DOCTEST_WARN_NE(...) ((void)0)
+-#define DOCTEST_CHECK_NE(...) ((void)0)
+-#define DOCTEST_REQUIRE_NE(...) ((void)0)
+-#define DOCTEST_WARN_GT(...) ((void)0)
+-#define DOCTEST_CHECK_GT(...) ((void)0)
+-#define DOCTEST_REQUIRE_GT(...) ((void)0)
+-#define DOCTEST_WARN_LT(...) ((void)0)
+-#define DOCTEST_CHECK_LT(...) ((void)0)
+-#define DOCTEST_REQUIRE_LT(...) ((void)0)
+-#define DOCTEST_WARN_GE(...) ((void)0)
+-#define DOCTEST_CHECK_GE(...) ((void)0)
+-#define DOCTEST_REQUIRE_GE(...) ((void)0)
+-#define DOCTEST_WARN_LE(...) ((void)0)
+-#define DOCTEST_CHECK_LE(...) ((void)0)
+-#define DOCTEST_REQUIRE_LE(...) ((void)0)
+-
+-#define DOCTEST_WARN_UNARY(...) ((void)0)
+-#define DOCTEST_CHECK_UNARY(...) ((void)0)
+-#define DOCTEST_REQUIRE_UNARY(...) ((void)0)
+-#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0)
+-#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0)
+-#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0)
++#define DOCTEST_INFO(...) (static_cast<void>(0))
++#define DOCTEST_CAPTURE(x) (static_cast<void>(0))
++#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast<void>(0))
++#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast<void>(0))
++#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast<void>(0))
++#define DOCTEST_MESSAGE(...) (static_cast<void>(0))
++#define DOCTEST_FAIL_CHECK(...) (static_cast<void>(0))
++#define DOCTEST_FAIL(...) (static_cast<void>(0))
++
++#ifdef DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
++
++#define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }()
++#define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }()
++#define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }()
++#define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }()
++#define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }()
++
++#define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }()
++#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }()
++#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }()
++#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
++#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
++#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
++
++namespace doctest {
++namespace detail {
++#define DOCTEST_RELATIONAL_OP(name, op) \
++ template <typename L, typename R> \
++ bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; }
++
++ DOCTEST_RELATIONAL_OP(eq, ==)
++ DOCTEST_RELATIONAL_OP(ne, !=)
++ DOCTEST_RELATIONAL_OP(lt, <)
++ DOCTEST_RELATIONAL_OP(gt, >)
++ DOCTEST_RELATIONAL_OP(le, <=)
++ DOCTEST_RELATIONAL_OP(ge, >=)
++} // namespace detail
++} // namespace doctest
++
++#define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
++#define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
++#define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
++#define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
++#define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
++#define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
++#define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
++#define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
++#define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
++#define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
++#define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
++#define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
++#define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }()
++#define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }()
++#define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }()
++#define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
++#define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
++#define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
++
++#else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
++
++#define DOCTEST_WARN(...) ([] { return false; })
++#define DOCTEST_CHECK(...) ([] { return false; })
++#define DOCTEST_REQUIRE(...) ([] { return false; })
++#define DOCTEST_WARN_FALSE(...) ([] { return false; })
++#define DOCTEST_CHECK_FALSE(...) ([] { return false; })
++#define DOCTEST_REQUIRE_FALSE(...) ([] { return false; })
++
++#define DOCTEST_WARN_MESSAGE(cond, ...) ([] { return false; })
++#define DOCTEST_CHECK_MESSAGE(cond, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_MESSAGE(cond, ...) ([] { return false; })
++#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) ([] { return false; })
++#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) ([] { return false; })
++
++#define DOCTEST_WARN_EQ(...) ([] { return false; })
++#define DOCTEST_CHECK_EQ(...) ([] { return false; })
++#define DOCTEST_REQUIRE_EQ(...) ([] { return false; })
++#define DOCTEST_WARN_NE(...) ([] { return false; })
++#define DOCTEST_CHECK_NE(...) ([] { return false; })
++#define DOCTEST_REQUIRE_NE(...) ([] { return false; })
++#define DOCTEST_WARN_GT(...) ([] { return false; })
++#define DOCTEST_CHECK_GT(...) ([] { return false; })
++#define DOCTEST_REQUIRE_GT(...) ([] { return false; })
++#define DOCTEST_WARN_LT(...) ([] { return false; })
++#define DOCTEST_CHECK_LT(...) ([] { return false; })
++#define DOCTEST_REQUIRE_LT(...) ([] { return false; })
++#define DOCTEST_WARN_GE(...) ([] { return false; })
++#define DOCTEST_CHECK_GE(...) ([] { return false; })
++#define DOCTEST_REQUIRE_GE(...) ([] { return false; })
++#define DOCTEST_WARN_LE(...) ([] { return false; })
++#define DOCTEST_CHECK_LE(...) ([] { return false; })
++#define DOCTEST_REQUIRE_LE(...) ([] { return false; })
++
++#define DOCTEST_WARN_UNARY(...) ([] { return false; })
++#define DOCTEST_CHECK_UNARY(...) ([] { return false; })
++#define DOCTEST_REQUIRE_UNARY(...) ([] { return false; })
++#define DOCTEST_WARN_UNARY_FALSE(...) ([] { return false; })
++#define DOCTEST_CHECK_UNARY_FALSE(...) ([] { return false; })
++#define DOCTEST_REQUIRE_UNARY_FALSE(...) ([] { return false; })
++
++#endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
++
++// TODO: think about if these also need to work properly even when doctest is disabled
++#define DOCTEST_WARN_THROWS(...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS(...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS(...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_AS(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_AS(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ([] { return false; })
++#define DOCTEST_WARN_NOTHROW(...) ([] { return false; })
++#define DOCTEST_CHECK_NOTHROW(...) ([] { return false; })
++#define DOCTEST_REQUIRE_NOTHROW(...) ([] { return false; })
++
++#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; })
++#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; })
++#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; })
++#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) ([] { return false; })
++#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) ([] { return false; })
+
+ #endif // DOCTEST_CONFIG_DISABLE
+
+@@ -2444,7 +2708,7 @@
+ #define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
+ #define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
+
+-#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INVOKE
++#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__)
+ // clang-format on
+
+ // BDD style macros
+@@ -2464,132 +2728,138 @@
+ // == SHORT VERSIONS OF THE MACROS
+ #if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
+
+-#define TEST_CASE DOCTEST_TEST_CASE
+-#define TEST_CASE_CLASS DOCTEST_TEST_CASE_CLASS
+-#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE
+-#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING
+-#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE
+-#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE
+-#define TEST_CASE_TEMPLATE_INVOKE DOCTEST_TEST_CASE_TEMPLATE_INVOKE
+-#define TEST_CASE_TEMPLATE_APPLY DOCTEST_TEST_CASE_TEMPLATE_APPLY
+-#define SUBCASE DOCTEST_SUBCASE
+-#define TEST_SUITE DOCTEST_TEST_SUITE
+-#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
++#define TEST_CASE(name) DOCTEST_TEST_CASE(name)
++#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name)
++#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name)
++#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__)
++#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__)
++#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id)
++#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__)
++#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__)
++#define SUBCASE(name) DOCTEST_SUBCASE(name)
++#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators)
++#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name)
+ #define TEST_SUITE_END DOCTEST_TEST_SUITE_END
+-#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
+-#define REGISTER_REPORTER DOCTEST_REGISTER_REPORTER
+-#define REGISTER_LISTENER DOCTEST_REGISTER_LISTENER
+-#define INFO DOCTEST_INFO
+-#define CAPTURE DOCTEST_CAPTURE
+-#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT
+-#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT
+-#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT
+-#define MESSAGE DOCTEST_MESSAGE
+-#define FAIL_CHECK DOCTEST_FAIL_CHECK
+-#define FAIL DOCTEST_FAIL
+-#define TO_LVALUE DOCTEST_TO_LVALUE
+-
+-#define WARN DOCTEST_WARN
+-#define WARN_FALSE DOCTEST_WARN_FALSE
+-#define WARN_THROWS DOCTEST_WARN_THROWS
+-#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS
+-#define WARN_THROWS_WITH DOCTEST_WARN_THROWS_WITH
+-#define WARN_NOTHROW DOCTEST_WARN_NOTHROW
+-#define CHECK DOCTEST_CHECK
+-#define CHECK_FALSE DOCTEST_CHECK_FALSE
+-#define CHECK_THROWS DOCTEST_CHECK_THROWS
+-#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS
+-#define CHECK_THROWS_WITH DOCTEST_CHECK_THROWS_WITH
+-#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW
+-#define REQUIRE DOCTEST_REQUIRE
+-#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE
+-#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS
+-#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS
+-#define REQUIRE_THROWS_WITH DOCTEST_REQUIRE_THROWS_WITH
+-#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW
+-
+-#define WARN_MESSAGE DOCTEST_WARN_MESSAGE
+-#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE
+-#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE
+-#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE
+-#define WARN_THROWS_WITH_MESSAGE DOCTEST_WARN_THROWS_WITH_MESSAGE
+-#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE
+-#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE
+-#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE
+-#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE
+-#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE
+-#define CHECK_THROWS_WITH_MESSAGE DOCTEST_CHECK_THROWS_WITH_MESSAGE
+-#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE
+-#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE
+-#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE
+-#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE
+-#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE
+-#define REQUIRE_THROWS_WITH_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_MESSAGE
+-#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE
+-
+-#define SCENARIO DOCTEST_SCENARIO
+-#define SCENARIO_CLASS DOCTEST_SCENARIO_CLASS
+-#define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE
+-#define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE
+-#define GIVEN DOCTEST_GIVEN
+-#define WHEN DOCTEST_WHEN
+-#define AND_WHEN DOCTEST_AND_WHEN
+-#define THEN DOCTEST_THEN
+-#define AND_THEN DOCTEST_AND_THEN
+-
+-#define WARN_EQ DOCTEST_WARN_EQ
+-#define CHECK_EQ DOCTEST_CHECK_EQ
+-#define REQUIRE_EQ DOCTEST_REQUIRE_EQ
+-#define WARN_NE DOCTEST_WARN_NE
+-#define CHECK_NE DOCTEST_CHECK_NE
+-#define REQUIRE_NE DOCTEST_REQUIRE_NE
+-#define WARN_GT DOCTEST_WARN_GT
+-#define CHECK_GT DOCTEST_CHECK_GT
+-#define REQUIRE_GT DOCTEST_REQUIRE_GT
+-#define WARN_LT DOCTEST_WARN_LT
+-#define CHECK_LT DOCTEST_CHECK_LT
+-#define REQUIRE_LT DOCTEST_REQUIRE_LT
+-#define WARN_GE DOCTEST_WARN_GE
+-#define CHECK_GE DOCTEST_CHECK_GE
+-#define REQUIRE_GE DOCTEST_REQUIRE_GE
+-#define WARN_LE DOCTEST_WARN_LE
+-#define CHECK_LE DOCTEST_CHECK_LE
+-#define REQUIRE_LE DOCTEST_REQUIRE_LE
+-#define WARN_UNARY DOCTEST_WARN_UNARY
+-#define CHECK_UNARY DOCTEST_CHECK_UNARY
+-#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY
+-#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE
+-#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
+-#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
++#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature)
++#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter)
++#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter)
++#define INFO(...) DOCTEST_INFO(__VA_ARGS__)
++#define CAPTURE(x) DOCTEST_CAPTURE(x)
++#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__)
++#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__)
++#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__)
++#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__)
++#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__)
++#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__)
++#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__)
++
++#define WARN(...) DOCTEST_WARN(__VA_ARGS__)
++#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__)
++#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__)
++#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__)
++#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__)
++#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__)
++#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__)
++#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__)
++#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__)
++#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__)
++#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__)
++#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__)
++#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__)
++#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__)
++#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__)
++#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__)
++#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__)
++#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__)
++#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__)
++#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__)
++#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__)
++
++#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__)
++#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__)
++#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__)
++#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
++#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
++#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
++#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__)
++#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__)
++#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__)
++#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__)
++#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
++#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
++#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
++#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__)
++#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__)
++#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__)
++#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__)
++#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
++#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
++#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
++#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__)
++
++#define SCENARIO(name) DOCTEST_SCENARIO(name)
++#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name)
++#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__)
++#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id)
++#define GIVEN(name) DOCTEST_GIVEN(name)
++#define WHEN(name) DOCTEST_WHEN(name)
++#define AND_WHEN(name) DOCTEST_AND_WHEN(name)
++#define THEN(name) DOCTEST_THEN(name)
++#define AND_THEN(name) DOCTEST_AND_THEN(name)
++
++#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__)
++#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__)
++#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__)
++#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__)
++#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__)
++#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__)
++#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__)
++#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__)
++#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__)
++#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__)
++#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__)
++#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__)
++#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__)
++#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__)
++#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__)
++#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__)
++#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__)
++#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__)
++#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__)
++#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__)
++#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__)
++#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__)
++#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__)
++#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
+
+ // KEPT FOR BACKWARDS COMPATIBILITY
+-#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ
+-#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ
+-#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ
+-#define FAST_WARN_NE DOCTEST_FAST_WARN_NE
+-#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE
+-#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE
+-#define FAST_WARN_GT DOCTEST_FAST_WARN_GT
+-#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT
+-#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT
+-#define FAST_WARN_LT DOCTEST_FAST_WARN_LT
+-#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT
+-#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT
+-#define FAST_WARN_GE DOCTEST_FAST_WARN_GE
+-#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE
+-#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE
+-#define FAST_WARN_LE DOCTEST_FAST_WARN_LE
+-#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE
+-#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE
+-
+-#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY
+-#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY
+-#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY
+-#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE
+-#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE
+-#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE
++#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__)
++#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__)
++#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__)
++#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__)
++#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__)
++#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__)
++#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__)
++#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__)
++#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__)
++#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__)
++#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__)
++#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__)
++#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__)
++#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__)
++#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__)
++#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__)
++#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__)
++#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__)
++
++#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__)
++#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__)
++#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__)
++#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__)
++#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__)
++#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
+
+-#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE
++#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__)
+
+ #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
+
+@@ -2626,6 +2896,8 @@
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+
++DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
++
+ #endif // DOCTEST_LIBRARY_INCLUDED
+
+ #ifndef DOCTEST_SINGLE_HEADER
+@@ -2645,13 +2917,11 @@
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
++DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
++
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations")
+@@ -2659,64 +2929,35 @@
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
+-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
++DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
+
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Winline")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance")
+-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute")
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning
+-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning
+-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration
+ DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data
+-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression
+-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated
+-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant
+ DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled
+ DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified
+ DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal
+ DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch
+-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs
+-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe
+ DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C
+-DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff
+-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted
+-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted
+ DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning)
+-// static analysis
+-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept'
+-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable
+-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ...
+-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtor...
++DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed
+
+ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
+
+@@ -2724,7 +2965,7 @@
+ #include <ctime>
+ #include <cmath>
+ #include <climits>
+-// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37
++// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37
+ #ifdef __BORLANDC__
+ #include <math.h>
+ #endif // __BORLANDC__
+@@ -2746,9 +2987,7 @@
+ #include <map>
+ #include <exception>
+ #include <stdexcept>
+-#ifdef DOCTEST_CONFIG_POSIX_SIGNALS
+ #include <csignal>
+-#endif // DOCTEST_CONFIG_POSIX_SIGNALS
+ #include <cfloat>
+ #include <cctype>
+ #include <cstdint>
+@@ -2773,7 +3012,7 @@
+ #ifdef __AFXDLL
+ #include <AfxWin.h>
+ #else
+-#include <Windows.h>
++#include <windows.h>
+ #endif
+ #include <io.h>
+
+@@ -2784,6 +3023,12 @@
+
+ #endif // DOCTEST_PLATFORM_WINDOWS
+
++// this is a fix for https://github.com/doctest/doctest/issues/348
++// https://mail.gnome.org/archives/xml/2012-January/msg00000.html
++#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO)
++#define STDOUT_FILENO fileno(stdout)
++#endif // HAVE_UNISTD_H
++
+ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
+
+ // counts the number of elements in a C array
+@@ -2800,7 +3045,19 @@
+ #endif
+
+ #ifndef DOCTEST_THREAD_LOCAL
++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
++#define DOCTEST_THREAD_LOCAL
++#else // DOCTEST_MSVC
+ #define DOCTEST_THREAD_LOCAL thread_local
++#endif // DOCTEST_MSVC
++#endif // DOCTEST_THREAD_LOCAL
++
++#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES
++#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32
++#endif
++
++#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE
++#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64
+ #endif
+
+ #ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+@@ -2809,12 +3066,38 @@
+ #define DOCTEST_OPTIONS_PREFIX_DISPLAY ""
+ #endif
+
++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
++#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
++#endif
++
++#ifndef DOCTEST_CDECL
++#define DOCTEST_CDECL __cdecl
++#endif
++
+ namespace doctest {
+
+ bool is_running_in_test = false;
+
+ namespace {
+ using namespace detail;
++
++ template <typename Ex>
++ DOCTEST_NORETURN void throw_exception(Ex const& e) {
++#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
++ throw e;
++#else // DOCTEST_CONFIG_NO_EXCEPTIONS
++ std::cerr << "doctest will terminate because it needed to throw an exception.\n"
++ << "The message was: " << e.what() << '\n';
++ std::terminate();
++#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
++ }
++
++#ifndef DOCTEST_INTERNAL_ERROR
++#define DOCTEST_INTERNAL_ERROR(msg) \
++ throw_exception(std::logic_error( \
++ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
++#endif // DOCTEST_INTERNAL_ERROR
++
+ // case insensitive strcmp
+ int stricmp(const char* a, const char* b) {
+ for(;; a++, b++) {
+@@ -2858,8 +3141,6 @@
+ } // namespace
+
+ namespace detail {
+- void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); }
+-
+ String rawMemoryToString(const void* object, unsigned size) {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>(size), inc = 1;
+@@ -2869,49 +3150,76 @@
+ }
+
+ unsigned const char* bytes = static_cast<unsigned const char*>(object);
+- std::ostringstream oss;
+- oss << "0x" << std::setfill('0') << std::hex;
++ std::ostream* oss = tlssPush();
++ *oss << "0x" << std::setfill('0') << std::hex;
+ for(; i != end; i += inc)
+- oss << std::setw(2) << static_cast<unsigned>(bytes[i]);
+- return oss.str().c_str();
++ *oss << std::setw(2) << static_cast<unsigned>(bytes[i]);
++ return tlssPop();
+ }
+
+- DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp)
++ DOCTEST_THREAD_LOCAL class
++ {
++ std::vector<std::streampos> stack;
++ std::stringstream ss;
++
++ public:
++ std::ostream* push() {
++ stack.push_back(ss.tellp());
++ return &ss;
++ }
++
++ String pop() {
++ if (stack.empty())
++ DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!");
++
++ std::streampos pos = stack.back();
++ stack.pop_back();
++ unsigned sz = static_cast<unsigned>(ss.tellp() - pos);
++ ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out);
++ return String(ss, sz);
++ }
++ } g_oss;
+
+- std::ostream* getTlsOss() {
+- g_oss.clear(); // there shouldn't be anything worth clearing in the flags
+- g_oss.str(""); // the slow way of resetting a string stream
+- //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383
+- return &g_oss;
++ std::ostream* tlssPush() {
++ return g_oss.push();
+ }
+
+- String getTlsOssResult() {
+- //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383
+- return g_oss.str().c_str();
++ String tlssPop() {
++ return g_oss.pop();
+ }
+
+ #ifndef DOCTEST_CONFIG_DISABLE
+
+- typedef uint64_t UInt64;
++namespace timer_large_integer
++{
++
++#if defined(DOCTEST_PLATFORM_WINDOWS)
++ typedef ULONGLONG type;
++#else // DOCTEST_PLATFORM_WINDOWS
++ typedef std::uint64_t type;
++#endif // DOCTEST_PLATFORM_WINDOWS
++}
++
++typedef timer_large_integer::type ticks_t;
+
+ #ifdef DOCTEST_CONFIG_GETCURRENTTICKS
+- UInt64 getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); }
++ ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); }
+ #elif defined(DOCTEST_PLATFORM_WINDOWS)
+- UInt64 getCurrentTicks() {
+- static UInt64 hz = 0, hzo = 0;
+- if(!hz) {
+- QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&hz));
+- QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&hzo));
+- }
+- UInt64 t;
+- QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t));
+- return ((t - hzo) * 1000000) / hz;
++ ticks_t getCurrentTicks() {
++ static LARGE_INTEGER hz = {0}, hzo = {0};
++ if(!hz.QuadPart) {
++ QueryPerformanceFrequency(&hz);
++ QueryPerformanceCounter(&hzo);
++ }
++ LARGE_INTEGER t;
++ QueryPerformanceCounter(&t);
++ return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart;
+ }
+ #else // DOCTEST_PLATFORM_WINDOWS
+- UInt64 getCurrentTicks() {
++ ticks_t getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t, nullptr);
+- return static_cast<UInt64>(t.tv_sec) * 1000000 + static_cast<UInt64>(t.tv_usec);
++ return static_cast<ticks_t>(t.tv_sec) * 1000000 + static_cast<ticks_t>(t.tv_usec);
+ }
+ #endif // DOCTEST_PLATFORM_WINDOWS
+
+@@ -2924,24 +3232,111 @@
+ //unsigned int getElapsedMilliseconds() const {
+ // return static_cast<unsigned int>(getElapsedMicroseconds() / 1000);
+ //}
+- double getElapsedSeconds() const { return getElapsedMicroseconds() / 1000000.0; }
++ double getElapsedSeconds() const { return static_cast<double>(getCurrentTicks() - m_ticks) / 1000000.0; }
++
++ private:
++ ticks_t m_ticks = 0;
++ };
++
++#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
++ template <typename T>
++ using AtomicOrMultiLaneAtomic = std::atomic<T>;
++#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
++ // Provides a multilane implementation of an atomic variable that supports add, sub, load,
++ // store. Instead of using a single atomic variable, this splits up into multiple ones,
++ // each sitting on a separate cache line. The goal is to provide a speedup when most
++ // operations are modifying. It achieves this with two properties:
++ //
++ // * Multiple atomics are used, so chance of congestion from the same atomic is reduced.
++ // * Each atomic sits on a separate cache line, so false sharing is reduced.
++ //
++ // The disadvantage is that there is a small overhead due to the use of TLS, and load/store
++ // is slower because all atomics have to be accessed.
++ template <typename T>
++ class MultiLaneAtomic
++ {
++ struct CacheLineAlignedAtomic
++ {
++ std::atomic<T> atomic{};
++ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
++ };
++ CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
++
++ static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE,
++ "guarantee one atomic takes exactly one cache line");
++
++ public:
++ T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; }
++
++ T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); }
++
++ T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
++ return myAtomic().fetch_add(arg, order);
++ }
++
++ T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
++ return myAtomic().fetch_sub(arg, order);
++ }
++
++ operator T() const DOCTEST_NOEXCEPT { return load(); }
++
++ T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT {
++ auto result = T();
++ for(auto const& c : m_atomics) {
++ result += c.atomic.load(order);
++ }
++ return result;
++ }
++
++ T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this]
++ store(desired);
++ return desired;
++ }
++
++ void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
++ // first value becomes desired", all others become 0.
++ for(auto& c : m_atomics) {
++ c.atomic.store(desired, order);
++ desired = {};
++ }
++ }
+
+ private:
+- UInt64 m_ticks = 0;
++ // Each thread has a different atomic that it operates on. If more than NumLanes threads
++ // use this, some will use the same atomic. So performance will degrade a bit, but still
++ // everything will work.
++ //
++ // The logic here is a bit tricky. The call should be as fast as possible, so that there
++ // is minimal to no overhead in determining the correct atomic for the current thread.
++ //
++ // 1. A global static counter laneCounter counts continuously up.
++ // 2. Each successive thread will use modulo operation of that counter so it gets an atomic
++ // assigned in a round-robin fashion.
++ // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
++ // little overhead.
++ std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
++ static std::atomic<size_t> laneCounter;
++ DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
++ laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
++
++ return m_atomics[tlsLaneIdx].atomic;
++ }
+ };
+
++ template <typename T>
++ using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
++#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
++
+ // this holds both parameters from the command line and runtime data for tests
+ struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
+ {
+- std::atomic<int> numAssertsCurrentTest_atomic;
+- std::atomic<int> numAssertsFailedCurrentTest_atomic;
++ AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
++ AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
+
+ std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
+
+ std::vector<IReporter*> reporters_currently_used;
+
+- const TestCase* currentTest = nullptr;
+-
+ assert_handler ah = nullptr;
+
+ Timer timer;
+@@ -2949,10 +3344,11 @@
+ std::vector<String> stringifiedContexts; // logging from INFO() due to an exception
+
+ // stuff for subcases
+- std::set<SubcaseSignature> subcasesPassed;
+- std::set<int> subcasesEnteredLevels;
+- int subcasesCurrentLevel;
+- bool should_reenter;
++ std::vector<SubcaseSignature> subcasesStack;
++ std::set<decltype(subcasesStack)> subcasesPassed;
++ int subcasesCurrentMaxLevel;
++ bool should_reenter;
++ std::atomic<bool> shouldLogCurrentException;
+
+ void resetRunData() {
+ numTestCases = 0;
+@@ -3002,7 +3398,8 @@
+ (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags);
+
+ // if any subcase has failed - the whole test case has failed
+- if(failure_flags && !ok_to_fail)
++ testCaseSuccess = !(failure_flags && !ok_to_fail);
++ if(!testCaseSuccess)
+ numTestCasesFailed++;
+ }
+ };
+@@ -3017,6 +3414,21 @@
+ #endif // DOCTEST_CONFIG_DISABLE
+ } // namespace detail
+
++char* String::allocate(unsigned sz) {
++ if (sz <= last) {
++ buf[sz] = '\0';
++ setLast(last - sz);
++ return buf;
++ } else {
++ setOnHeap();
++ data.size = sz;
++ data.capacity = data.size + 1;
++ data.ptr = new char[data.capacity];
++ data.ptr[sz] = '\0';
++ return data.ptr;
++ }
++}
++
+ void String::setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
+ void String::setLast(unsigned in) { buf[last] = char(in); }
+
+@@ -3024,11 +3436,7 @@
+ if(other.isOnStack()) {
+ memcpy(buf, other.buf, len);
+ } else {
+- setOnHeap();
+- data.size = other.data.size;
+- data.capacity = data.size + 1;
+- data.ptr = new char[data.capacity];
+- memcpy(data.ptr, other.data.ptr, data.size + 1);
++ memcpy(allocate(other.data.size), other.data.ptr, other.data.size);
+ }
+ }
+
+@@ -3040,22 +3448,18 @@
+ String::~String() {
+ if(!isOnStack())
+ delete[] data.ptr;
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ }
+
+ String::String(const char* in)
+ : String(in, strlen(in)) {}
+
+ String::String(const char* in, unsigned in_size) {
+- if(in_size <= last) {
+- memcpy(buf, in, in_size + 1);
+- setLast(last - in_size);
+- } else {
+- setOnHeap();
+- data.size = in_size;
+- data.capacity = data.size + 1;
+- data.ptr = new char[data.capacity];
+- memcpy(data.ptr, in, in_size + 1);
+- }
++ memcpy(allocate(in_size), in, in_size);
++}
++
++String::String(std::istream& in, unsigned in_size) {
++ in.read(allocate(in_size), in_size);
+ }
+
+ String::String(const String& other) { copy(other); }
+@@ -3079,6 +3483,7 @@
+ if(total_size < len) {
+ // append to the current stack space
+ memcpy(buf + my_old_size, other.c_str(), other_size + 1);
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ setLast(last - total_size);
+ } else {
+ // alloc new chunk
+@@ -3120,8 +3525,6 @@
+ return *this;
+ }
+
+-String String::operator+(const String& other) const { return String(*this) += other; }
+-
+ String::String(String&& other) {
+ memcpy(buf, other.buf, len);
+ other.buf[0] = '\0';
+@@ -3165,7 +3568,7 @@
+
+ int String::compare(const char* other, bool no_case) const {
+ if(no_case)
+- return stricmp(c_str(), other);
++ return doctest::stricmp(c_str(), other);
+ return std::strcmp(c_str(), other);
+ }
+
+@@ -3173,6 +3576,9 @@
+ return compare(other.c_str(), no_case);
+ }
+
++// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
++String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; }
++
+ // clang-format off
+ bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
+ bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
+@@ -3219,6 +3625,10 @@
+ case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH";
+ case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH";
+
++ case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS";
++ case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS";
++ case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS";
++
+ case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW";
+ case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW";
+ case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW";
+@@ -3268,6 +3678,7 @@
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference")
+ // depending on the current options this will remove the path of filenames
+ const char* skipPathFromFilename(const char* file) {
++#ifndef DOCTEST_CONFIG_DISABLE
+ if(getContextOptions()->no_path_in_filenames) {
+ auto back = std::strrchr(file, '\\');
+ auto forward = std::strrchr(file, '/');
+@@ -3277,41 +3688,26 @@
+ return forward + 1;
+ }
+ }
++#endif // DOCTEST_CONFIG_DISABLE
+ return file;
+ }
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+
+-DOCTEST_DEFINE_DEFAULTS(TestCaseData);
+-DOCTEST_DEFINE_COPIES(TestCaseData);
+-
+-DOCTEST_DEFINE_DEFAULTS(AssertData);
+-
+-DOCTEST_DEFINE_DEFAULTS(MessageData);
+-
+-SubcaseSignature::SubcaseSignature(const char* name, const char* file, int line)
+- : m_name(name)
+- , m_file(file)
+- , m_line(line) {}
+-
+-DOCTEST_DEFINE_DEFAULTS(SubcaseSignature);
+-DOCTEST_DEFINE_COPIES(SubcaseSignature);
+-
+ bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
+ if(m_line != other.m_line)
+ return m_line < other.m_line;
+ if(std::strcmp(m_file, other.m_file) != 0)
+ return std::strcmp(m_file, other.m_file) < 0;
+- return std::strcmp(m_name, other.m_name) < 0;
++ return m_name.compare(other.m_name) < 0;
+ }
+
+ IContextScope::IContextScope() = default;
+ IContextScope::~IContextScope() = default;
+
+-DOCTEST_DEFINE_DEFAULTS(ContextOptions);
+-
+ #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ String toString(char* in) { return toString(static_cast<const char*>(in)); }
++// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
+ #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ String toString(bool in) { return in ? "true" : "false"; }
+@@ -3341,7 +3737,7 @@
+ String toString(std::nullptr_t) { return "NULL"; }
+
+ #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
++// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+ String toString(const std::string& in) { return in.c_str(); }
+ #endif // VS 2019
+
+@@ -3350,8 +3746,6 @@
+ , m_scale(1.0)
+ , m_value(value) {}
+
+-DOCTEST_DEFINE_COPIES(Approx);
+-
+ Approx Approx::operator()(double value) const {
+ Approx approx(value);
+ approx.epsilon(m_epsilon);
+@@ -3386,7 +3780,8 @@
+ bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
+
+ String toString(const Approx& in) {
+- return String("Approx( ") + doctest::toString(in.m_value) + " )";
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
++ return "Approx( " + doctest::toString(in.m_value) + " )";
+ }
+ const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); }
+
+@@ -3399,17 +3794,15 @@
+ void Context::applyCommandLine(int, const char* const*) {}
+ void Context::addFilter(const char*, const char*) {}
+ void Context::clearFilters() {}
++void Context::setOption(const char*, bool) {}
+ void Context::setOption(const char*, int) {}
+ void Context::setOption(const char*, const char*) {}
+ bool Context::shouldExit() { return false; }
+ void Context::setAsDefaultForAssertsOutOfTestCases() {}
+ void Context::setAssertHandler(detail::assert_handler) {}
++void Context::setCout(std::ostream* out) {}
+ int Context::run() { return 0; }
+
+-DOCTEST_DEFINE_DEFAULTS(CurrentTestCaseStats);
+-
+-DOCTEST_DEFINE_DEFAULTS(TestRunStats);
+-
+ IReporter::~IReporter() = default;
+
+ int IReporter::get_num_active_contexts() { return 0; }
+@@ -3435,7 +3828,7 @@
+ namespace doctest_detail_test_suite_ns {
+ // holds the current test suite
+ doctest::detail::TestSuite& getCurrentTestSuite() {
+- static doctest::detail::TestSuite data;
++ static doctest::detail::TestSuite data{};
+ return data;
+ }
+ } // namespace doctest_detail_test_suite_ns
+@@ -3460,8 +3853,6 @@
+ for(auto& curr_rep : g_cs->reporters_currently_used) \
+ curr_rep->function(__VA_ARGS__)
+
+- DOCTEST_DEFINE_DEFAULTS(TestFailureException);
+- DOCTEST_DEFINE_COPIES(TestFailureException);
+ bool checkIfShouldThrow(assertType::Enum at) {
+ if(at & assertType::is_require) //!OCLINT bitwise operator in conditional
+ return true;
+@@ -3476,7 +3867,10 @@
+ }
+
+ #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+- [[noreturn]] void throwException() { throw TestFailureException(); } // NOLINT(cert-err60-cpp)
++ DOCTEST_NORETURN void throwException() {
++ g_cs->shouldLogCurrentException = false;
++ throw TestFailureException();
++ } // NOLINT(cert-err60-cpp)
+ #else // DOCTEST_CONFIG_NO_EXCEPTIONS
+ void throwException() {}
+ #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+@@ -3487,8 +3881,8 @@
+ // matching of a string against a wildcard mask (case sensitivity configurable) taken from
+ // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing
+ int wildcmp(const char* str, const char* wild, bool caseSensitive) {
+- const char* cp = nullptr;
+- const char* mp = nullptr;
++ const char* cp = str;
++ const char* mp = wild;
+
+ while((*str) && (*wild != '*')) {
+ if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) &&
+@@ -3544,73 +3938,82 @@
+ } // namespace
+ namespace detail {
+
+- Subcase::Subcase(const char* name, const char* file, int line)
+- : m_signature(name, file, line) {
+- ContextState* s = g_cs;
+-
+- // if we have already completed it
+- if(s->subcasesPassed.count(m_signature) != 0)
+- return;
++ Subcase::Subcase(const String& name, const char* file, int line)
++ : m_signature({name, file, line}) {
++ auto* s = g_cs;
+
+ // check subcase filters
+- if(s->subcasesCurrentLevel < s->subcase_filter_levels) {
+- if(!matchesAny(m_signature.m_name, s->filters[6], true, s->case_sensitive))
++ if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) {
++ if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive))
+ return;
+- if(matchesAny(m_signature.m_name, s->filters[7], false, s->case_sensitive))
++ if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive))
+ return;
+ }
+-
++
+ // if a Subcase on the same level has already been entered
+- if(s->subcasesEnteredLevels.count(s->subcasesCurrentLevel) != 0) {
++ if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) {
+ s->should_reenter = true;
+ return;
+ }
+
+- s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++);
++ // push the current signature to the stack so we can check if the
++ // current stack + the current new subcase have been traversed
++ s->subcasesStack.push_back(m_signature);
++ if(s->subcasesPassed.count(s->subcasesStack) != 0) {
++ // pop - revert to previous stack since we've already passed this
++ s->subcasesStack.pop_back();
++ return;
++ }
++
++ s->subcasesCurrentMaxLevel = s->subcasesStack.size();
+ m_entered = true;
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ }
+
++ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
++ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
++ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
++
+ Subcase::~Subcase() {
+ if(m_entered) {
+- ContextState* s = g_cs;
+-
+- s->subcasesCurrentLevel--;
+- // only mark the subcase as passed if no subcases have been skipped
+- if(s->should_reenter == false)
+- s->subcasesPassed.insert(m_signature);
++ // only mark the subcase stack as passed if no subcases have been skipped
++ if(g_cs->should_reenter == false)
++ g_cs->subcasesPassed.insert(g_cs->subcasesStack);
++ g_cs->subcasesStack.pop_back();
+
++#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
++ if(std::uncaught_exceptions() > 0
++#else
++ if(std::uncaught_exception()
++#endif
++ && g_cs->shouldLogCurrentException) {
++ DOCTEST_ITERATE_THROUGH_REPORTERS(
++ test_case_exception, {"exception thrown in subcase - will translate later "
++ "when the whole test case has been exited (cannot "
++ "translate while there is an active exception)",
++ false});
++ g_cs->shouldLogCurrentException = false;
++ }
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
+ }
+ }
+
++ DOCTEST_CLANG_SUPPRESS_WARNING_POP
++ DOCTEST_GCC_SUPPRESS_WARNING_POP
++ DOCTEST_MSVC_SUPPRESS_WARNING_POP
++
+ Subcase::operator bool() const { return m_entered; }
+
+ Result::Result(bool passed, const String& decomposition)
+ : m_passed(passed)
+ , m_decomp(decomposition) {}
+
+- DOCTEST_DEFINE_DEFAULTS(Result);
+- DOCTEST_DEFINE_COPIES(Result);
+-
+ ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at)
+ : m_at(at) {}
+
+- DOCTEST_DEFINE_DEFAULTS(ExpressionDecomposer);
+-
+- DOCTEST_DEFINE_DEFAULTS(TestSuite);
+- DOCTEST_DEFINE_COPIES(TestSuite);
+-
+ TestSuite& TestSuite::operator*(const char* in) {
+ m_test_suite = in;
+- // clear state
+- m_description = nullptr;
+- m_skip = false;
+- m_may_fail = false;
+- m_should_fail = false;
+- m_expected_failures = 0;
+- m_timeout = 0;
+ return *this;
+ }
+
+@@ -3622,6 +4025,8 @@
+ m_test_suite = test_suite.m_test_suite;
+ m_description = test_suite.m_description;
+ m_skip = test_suite.m_skip;
++ m_no_breaks = test_suite.m_no_breaks;
++ m_no_output = test_suite.m_no_output;
+ m_may_fail = test_suite.m_may_fail;
+ m_should_fail = test_suite.m_should_fail;
+ m_expected_failures = test_suite.m_expected_failures;
+@@ -3632,8 +4037,6 @@
+ m_template_id = template_id;
+ }
+
+- DOCTEST_DEFINE_DEFAULTS(TestCase);
+-
+ TestCase::TestCase(const TestCase& other)
+ : TestCaseData() {
+ *this = other;
+@@ -3667,25 +4070,31 @@
+ }
+
+ bool TestCase::operator<(const TestCase& other) const {
++ // this will be used only to differentiate between test cases - not relevant for sorting
+ if(m_line != other.m_line)
+ return m_line < other.m_line;
+- const int file_cmp = std::strcmp(m_file, other.m_file);
++ const int name_cmp = strcmp(m_name, other.m_name);
++ if(name_cmp != 0)
++ return name_cmp < 0;
++ const int file_cmp = m_file.compare(other.m_file);
+ if(file_cmp != 0)
+ return file_cmp < 0;
+ return m_template_id < other.m_template_id;
+ }
++
++ // all the registered tests
++ std::set<TestCase>& getRegisteredTests() {
++ static std::set<TestCase> data;
++ return data;
++ }
+ } // namespace detail
+ namespace {
+ using namespace detail;
+ // for sorting tests by file/line
+ bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) {
+-#if DOCTEST_MSVC
+ // this is needed because MSVC gives different case for drive letters
+ // for __FILE__ when evaluated in a header and a source file
+- const int res = stricmp(lhs->m_file, rhs->m_file);
+-#else // MSVC
+- const int res = std::strcmp(lhs->m_file, rhs->m_file);
+-#endif // MSVC
++ const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC));
+ if(res != 0)
+ return res < 0;
+ if(lhs->m_line != rhs->m_line)
+@@ -3709,39 +4118,10 @@
+ return suiteOrderComparator(lhs, rhs);
+ }
+
+- // all the registered tests
+- std::set<TestCase>& getRegisteredTests() {
+- static std::set<TestCase> data;
+- return data;
+- }
+-
+-#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+- HANDLE g_stdoutHandle;
+- WORD g_origFgAttrs;
+- WORD g_origBgAttrs;
+- bool g_attrsInitted = false;
+-
+- int colors_init() {
+- if(!g_attrsInitted) {
+- g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+- g_attrsInitted = true;
+- CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+- GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo);
+- g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
+- BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+- g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
+- FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+- }
+- return 0;
+- }
+-
+- int dumy_init_console_colors = colors_init();
+-#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+-
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ void color_to_stream(std::ostream& s, Color::Enum code) {
+- ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
+- ((void)code); // for DOCTEST_CONFIG_COLORS_NONE
++ static_cast<void>(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
++ static_cast<void>(code); // for DOCTEST_CONFIG_COLORS_NONE
+ #ifdef DOCTEST_CONFIG_COLORS_ANSI
+ if(g_no_colors ||
+ (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false))
+@@ -3771,10 +4151,26 @@
+
+ #ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+ if(g_no_colors ||
+- (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false))
++ (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false))
+ return;
+
+-#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs)
++ static struct ConsoleHelper {
++ HANDLE stdoutHandle;
++ WORD origFgAttrs;
++ WORD origBgAttrs;
++
++ ConsoleHelper() {
++ stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
++ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
++ GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo);
++ origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
++ BACKGROUND_BLUE | BACKGROUND_INTENSITY);
++ origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
++ FOREGROUND_BLUE | FOREGROUND_INTENSITY);
++ }
++ } ch;
++
++#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs)
+
+ // clang-format off
+ switch (code) {
+@@ -3791,7 +4187,7 @@
+ case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
+ case Color::None:
+ case Color::Bright: // invalid
+- default: DOCTEST_SET_ATTR(g_origFgAttrs);
++ default: DOCTEST_SET_ATTR(ch.origFgAttrs);
+ }
+ // clang-format on
+ #endif // DOCTEST_CONFIG_COLORS_WINDOWS
+@@ -3844,9 +4240,33 @@
+ return 0;
+ }
+
+-#ifdef DOCTEST_PLATFORM_MAC
++#ifdef DOCTEST_IS_DEBUGGER_ACTIVE
++ bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); }
++#else // DOCTEST_IS_DEBUGGER_ACTIVE
++#ifdef DOCTEST_PLATFORM_LINUX
++ class ErrnoGuard {
++ public:
++ ErrnoGuard() : m_oldErrno(errno) {}
++ ~ErrnoGuard() { errno = m_oldErrno; }
++ private:
++ int m_oldErrno;
++ };
++ // See the comments in Catch2 for the reasoning behind this implementation:
++ // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102
++ bool isDebuggerActive() {
++ ErrnoGuard guard;
++ std::ifstream in("/proc/self/status");
++ for(std::string line; std::getline(in, line);) {
++ static const int PREFIX_LEN = 11;
++ if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) {
++ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
++ }
++ }
++ return false;
++ }
++#elif defined(DOCTEST_PLATFORM_MAC)
+ // The following function is taken directly from the following technical note:
+- // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
++ // https://developer.apple.com/library/archive/qa/qa1361/_index.html
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive() {
+@@ -3871,11 +4291,12 @@
+ // We're being debugged if the P_TRACED flag is set.
+ return ((info.kp_proc.p_flag & P_TRACED) != 0);
+ }
+-#elif DOCTEST_MSVC || defined(__MINGW32__)
++#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__)
+ bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; }
+ #else
+ bool isDebuggerActive() { return false; }
+ #endif // Platform
++#endif // DOCTEST_IS_DEBUGGER_ACTIVE
+
+ void registerExceptionTranslatorImpl(const IExceptionTranslator* et) {
+ if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) ==
+@@ -3910,41 +4331,47 @@
+ g_infoContexts.push_back(this);
+ }
+
++ ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) {
++ if (other.need_to_destroy) {
++ other.destroy();
++ }
++ other.need_to_destroy = false;
++ g_infoContexts.push_back(this);
++ }
++
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
++
+ // destroy cannot be inlined into the destructor because that would mean calling stringify after
+ // ContextScope has been destroyed (base class destructors run after derived class destructors).
+ // Instead, ContextScope calls this method directly from its destructor.
+ void ContextScopeBase::destroy() {
++#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
++ if(std::uncaught_exceptions() > 0) {
++#else
+ if(std::uncaught_exception()) {
++#endif
+ std::ostringstream s;
+ this->stringify(&s);
+ g_cs->stringifiedContexts.push_back(s.str().c_str());
+ }
+ g_infoContexts.pop_back();
+ }
++
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+-
+ } // namespace detail
+ namespace {
+ using namespace detail;
+
+- std::ostream& file_line_to_stream(std::ostream& s, const char* file, int line,
+- const char* tail = "") {
+- const auto opt = getContextOptions();
+- s << Color::LightGrey << skipPathFromFilename(file) << (opt->gnu_file_line ? ":" : "(")
+- << (opt->no_line_numbers ? 0 : line) // 0 or the real num depending on the option
+- << (opt->gnu_file_line ? ":" : "):") << tail;
+- return s;
+- }
+-
+ #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+ struct FatalConditionHandler
+ {
+- void reset() {}
++ static void reset() {}
++ static void allocateAltStackMem() {}
++ static void freeAltStackMem() {}
+ };
+ #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
+
+@@ -3961,43 +4388,113 @@
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ SignalDefs signalDefs[] = {
+- {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"},
+- {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"},
+- {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"},
+- {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"},
++ {static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),
++ "SIGILL - Illegal instruction signal"},
++ {static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"},
++ {static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION),
++ "SIGSEGV - Segmentation violation signal"},
++ {static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"},
+ };
+
+ struct FatalConditionHandler
+ {
+- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+- for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
+- if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+- reportFatal(signalDefs[i].name);
++ static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
++ // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
++ // console just once no matter how many threads have crashed.
++ static std::mutex mutex;
++ static bool execute = true;
++ {
++ std::lock_guard<std::mutex> lock(mutex);
++ if(execute) {
++ bool reported = false;
++ for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
++ if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
++ reportFatal(signalDefs[i].name);
++ reported = true;
++ break;
++ }
++ }
++ if(reported == false)
++ reportFatal("Unhandled SEH exception caught");
++ if(isDebuggerActive() && !g_cs->no_breaks)
++ DOCTEST_BREAK_INTO_DEBUGGER();
+ }
++ execute = false;
+ }
+- // If its not an exception we care about, pass it along.
+- // This stops us from eating debugger breaks etc.
+- return EXCEPTION_CONTINUE_SEARCH;
++ std::exit(EXIT_FAILURE);
+ }
+
++ static void allocateAltStackMem() {}
++ static void freeAltStackMem() {}
++
+ FatalConditionHandler() {
+ isSet = true;
+ // 32k seems enough for doctest to handle stack overflow,
+ // but the value was found experimentally, so there is no strong guarantee
+ guaranteeSize = 32 * 1024;
+- exceptionHandlerHandle = nullptr;
+- // Register as first handler in current chain
+- exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
++ // Register an unhandled exception filter
++ previousTop = SetUnhandledExceptionFilter(handleException);
+ // Pass in guarantee size to be filled
+ SetThreadStackGuarantee(&guaranteeSize);
++
++ // On Windows uncaught exceptions from another thread, exceptions from
++ // destructors, or calls to std::terminate are not a SEH exception
++
++ // The terminal handler gets called when:
++ // - std::terminate is called FROM THE TEST RUNNER THREAD
++ // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD
++ original_terminate_handler = std::get_terminate();
++ std::set_terminate([]() DOCTEST_NOEXCEPT {
++ reportFatal("Terminate handler called");
++ if(isDebuggerActive() && !g_cs->no_breaks)
++ DOCTEST_BREAK_INTO_DEBUGGER();
++ std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well
++ });
++
++ // SIGABRT is raised when:
++ // - std::terminate is called FROM A DIFFERENT THREAD
++ // - an exception is thrown from a destructor FROM A DIFFERENT THREAD
++ // - an uncaught exception is thrown FROM A DIFFERENT THREAD
++ prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT {
++ if(signal == SIGABRT) {
++ reportFatal("SIGABRT - Abort (abnormal termination) signal");
++ if(isDebuggerActive() && !g_cs->no_breaks)
++ DOCTEST_BREAK_INTO_DEBUGGER();
++ std::exit(EXIT_FAILURE);
++ }
++ });
++
++ // The following settings are taken from google test, and more
++ // specifically from UnitTest::Run() inside of gtest.cc
++
++ // the user does not want to see pop-up dialogs about crashes
++ prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
++ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
++ // This forces the abort message to go to stderr in all circumstances.
++ prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR);
++ // In the debug version, Visual Studio pops up a separate dialog
++ // offering a choice to debug the aborted program - we want to disable that.
++ prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
++ // In debug mode, the Windows CRT can crash with an assertion over invalid
++ // input (e.g. passing an invalid file descriptor). The default handling
++ // for these assertions is to pop up a dialog and wait for user input.
++ // Instead ask the CRT to dump such assertions to stderr non-interactively.
++ prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
++ prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ }
+
+ static void reset() {
+ if(isSet) {
+ // Unregister handler and restore the old guarantee
+- RemoveVectoredExceptionHandler(exceptionHandlerHandle);
++ SetUnhandledExceptionFilter(previousTop);
+ SetThreadStackGuarantee(&guaranteeSize);
+- exceptionHandlerHandle = nullptr;
++ std::set_terminate(original_terminate_handler);
++ std::signal(SIGABRT, prev_sigabrt_handler);
++ SetErrorMode(prev_error_mode_1);
++ _set_error_mode(prev_error_mode_2);
++ _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
++ static_cast<void>(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode));
++ static_cast<void>(_CrtSetReportFile(_CRT_ASSERT, prev_report_file));
+ isSet = false;
+ }
+ }
+@@ -4005,14 +4502,28 @@
+ ~FatalConditionHandler() { reset(); }
+
+ private:
++ static UINT prev_error_mode_1;
++ static int prev_error_mode_2;
++ static unsigned int prev_abort_behavior;
++ static int prev_report_mode;
++ static _HFILE prev_report_file;
++ static void (DOCTEST_CDECL *prev_sigabrt_handler)(int);
++ static std::terminate_handler original_terminate_handler;
+ static bool isSet;
+ static ULONG guaranteeSize;
+- static PVOID exceptionHandlerHandle;
++ static LPTOP_LEVEL_EXCEPTION_FILTER previousTop;
+ };
+
++ UINT FatalConditionHandler::prev_error_mode_1;
++ int FatalConditionHandler::prev_error_mode_2;
++ unsigned int FatalConditionHandler::prev_abort_behavior;
++ int FatalConditionHandler::prev_report_mode;
++ _HFILE FatalConditionHandler::prev_report_file;
++ void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int);
++ std::terminate_handler FatalConditionHandler::original_terminate_handler;
+ bool FatalConditionHandler::isSet = false;
+ ULONG FatalConditionHandler::guaranteeSize = 0;
+- PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
++ LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr;
+
+ #else // DOCTEST_PLATFORM_WINDOWS
+
+@@ -4033,7 +4544,8 @@
+ static bool isSet;
+ static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)];
+ static stack_t oldSigStack;
+- static char altStackMem[4 * SIGSTKSZ];
++ static size_t altStackSize;
++ static char* altStackMem;
+
+ static void handleSignal(int sig) {
+ const char* name = "<unknown signal>";
+@@ -4049,11 +4561,19 @@
+ raise(sig);
+ }
+
++ static void allocateAltStackMem() {
++ altStackMem = new char[altStackSize];
++ }
++
++ static void freeAltStackMem() {
++ delete[] altStackMem;
++ }
++
+ FatalConditionHandler() {
+ isSet = true;
+ stack_t sigStack;
+ sigStack.ss_sp = altStackMem;
+- sigStack.ss_size = sizeof(altStackMem);
++ sigStack.ss_size = altStackSize;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = {};
+@@ -4078,10 +4598,11 @@
+ }
+ };
+
+- bool FatalConditionHandler::isSet = false;
++ bool FatalConditionHandler::isSet = false;
+ struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {};
+- stack_t FatalConditionHandler::oldSigStack = {};
+- char FatalConditionHandler::altStackMem[] = {};
++ stack_t FatalConditionHandler::oldSigStack = {};
++ size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ;
++ char* FatalConditionHandler::altStackMem = nullptr;
+
+ #endif // DOCTEST_PLATFORM_WINDOWS
+ #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
+@@ -4114,8 +4635,10 @@
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true});
+
+- while(g_cs->subcasesCurrentLevel--)
++ while(g_cs->subcasesStack.size()) {
++ g_cs->subcasesStack.pop_back();
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
++ }
+
+ g_cs->finalizeTestCaseData();
+
+@@ -4128,24 +4651,23 @@
+ namespace detail {
+
+ ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+- const char* exception_type) {
+- m_test_case = g_cs->currentTest;
+- m_at = at;
+- m_file = file;
+- m_line = line;
+- m_expr = expr;
+- m_failed = true;
+- m_threw = false;
+- m_threw_as = false;
+- m_exception_type = exception_type;
++ const char* exception_type, const char* exception_string) {
++ m_test_case = g_cs->currentTest;
++ m_at = at;
++ m_file = file;
++ m_line = line;
++ m_expr = expr;
++ m_failed = true;
++ m_threw = false;
++ m_threw_as = false;
++ m_exception_type = exception_type;
++ m_exception_string = exception_string;
+ #if DOCTEST_MSVC
+ if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
+ ++m_expr;
+ #endif // MSVC
+ }
+
+- DOCTEST_DEFINE_DEFAULTS(ResultBuilder);
+-
+ void ResultBuilder::setResult(const Result& res) {
+ m_decomp = res.m_decomp;
+ m_failed = !res.m_passed;
+@@ -4159,16 +4681,18 @@
+ bool ResultBuilder::log() {
+ if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
+ m_failed = !m_threw;
++ } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT
++ m_failed = !m_threw_as || (m_exception != m_exception_string);
+ } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
+ m_failed = !m_threw_as;
+ } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
+- m_failed = m_exception != m_exception_type;
++ m_failed = m_exception != m_exception_string;
+ } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
+ m_failed = m_threw;
+ }
+
+ if(m_exception.size())
+- m_exception = String("\"") + m_exception + "\"";
++ m_exception = "\"" + m_exception + "\"";
+
+ if(is_running_in_test) {
+ addAssert(m_at);
+@@ -4180,8 +4704,8 @@
+ failed_out_of_a_testing_context(*this);
+ }
+
+- return m_failed && isDebuggerActive() &&
+- !getContextOptions()->no_breaks; // break into debugger
++ return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks &&
++ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
+ }
+
+ void ResultBuilder::react() const {
+@@ -4196,7 +4720,7 @@
+ std::abort();
+ }
+
+- void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
++ bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
+ Result result) {
+ bool failed = !result.m_passed;
+
+@@ -4206,58 +4730,53 @@
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp);
+ DOCTEST_ASSERT_IN_TESTS(result.m_decomp);
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
++ return !failed;
+ }
+
+ MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) {
+- m_stream = getTlsOss();
++ m_stream = tlssPush();
+ m_file = file;
+ m_line = line;
+ m_severity = severity;
+ }
+
++ MessageBuilder::~MessageBuilder() {
++ if (!logged)
++ tlssPop();
++ }
++
+ IExceptionTranslator::IExceptionTranslator() = default;
+ IExceptionTranslator::~IExceptionTranslator() = default;
+
+ bool MessageBuilder::log() {
+- m_string = getTlsOssResult();
++ if (!logged) {
++ m_string = tlssPop();
++ logged = true;
++ }
++
+ DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
+
+ const bool isWarn = m_severity & assertType::is_warn;
+
+- // warn is just a message in this context so we dont treat it as an assert
++ // warn is just a message in this context so we don't treat it as an assert
+ if(!isWarn) {
+ addAssert(m_severity);
+ addFailedAssert(m_severity);
+ }
+
+- return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break
++ return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn &&
++ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
+ }
+
+ void MessageBuilder::react() {
+ if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional
+ throwException();
+ }
+-
+- MessageBuilder::~MessageBuilder() = default;
+ } // namespace detail
+ namespace {
+ using namespace detail;
+
+- template <typename Ex>
+- [[noreturn]] void throw_exception(Ex const& e) {
+-#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+- throw e;
+-#else // DOCTEST_CONFIG_NO_EXCEPTIONS
+- std::cerr << "doctest will terminate because it needed to throw an exception.\n"
+- << "The message was: " << e.what() << '\n';
+- std::terminate();
+-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+- }
+-
+-#define DOCTEST_INTERNAL_ERROR(msg) \
+- throw_exception(std::logic_error( \
+- __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
+-
+ // clang-format off
+
+ // =================================================================================================
+@@ -4287,8 +4806,8 @@
+ public:
+ ScopedElement( XmlWriter* writer );
+
+- ScopedElement( ScopedElement&& other ) noexcept;
+- ScopedElement& operator=( ScopedElement&& other ) noexcept;
++ ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT;
++ ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT;
+
+ ~ScopedElement();
+
+@@ -4404,7 +4923,7 @@
+
+ void XmlEncode::encodeTo( std::ostream& os ) const {
+ // Apostrophe escaping not necessary if we always use " to write attributes
+- // (see: http://www.w3.org/TR/xml/#syntax)
++ // (see: https://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+ uchar c = m_str[idx];
+@@ -4413,7 +4932,7 @@
+ case '&': os << "&amp;"; break;
+
+ case '>':
+- // See: http://www.w3.org/TR/xml/#syntax
++ // See: https://www.w3.org/TR/xml/#syntax
+ if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
+ os << "&gt;";
+ else
+@@ -4431,7 +4950,7 @@
+ // Check for control characters and invalid utf-8
+
+ // Escape control characters in standard ascii
+- // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
++ // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
+ hexEscapeChar(os, c);
+ break;
+@@ -4505,11 +5024,11 @@
+ : m_writer( writer )
+ {}
+
+- XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
++ XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT
+ : m_writer( other.m_writer ){
+ other.m_writer = nullptr;
+ }
+- XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
++ XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT {
+ if ( m_writer ) {
+ m_writer->endElement();
+ }
+@@ -4671,7 +5190,7 @@
+ void test_case_start_impl(const TestCaseData& in) {
+ bool open_ts_tag = false;
+ if(tc != nullptr) { // we have already opened a test suite
+- if(strcmp(tc->m_test_suite, in.m_test_suite) != 0) {
++ if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) {
+ xml.endElement();
+ open_ts_tag = true;
+ }
+@@ -4688,7 +5207,7 @@
+ tc = &in;
+ xml.startElement("TestCase")
+ .writeAttribute("name", in.m_name)
+- .writeAttribute("filename", skipPathFromFilename(in.m_file))
++ .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str()))
+ .writeAttribute("line", line(in.m_line))
+ .writeAttribute("description", in.m_description);
+
+@@ -4716,13 +5235,18 @@
+ .writeAttribute("priority", curr.first.first)
+ .writeAttribute("name", curr.first.second);
+ } else if(opt.count || opt.list_test_cases) {
+- for(unsigned i = 0; i < in.num_data; ++i)
+- xml.scopedElement("TestCase").writeAttribute("name", in.data[i]);
++ for(unsigned i = 0; i < in.num_data; ++i) {
++ xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name)
++ .writeAttribute("testsuite", in.data[i]->m_test_suite)
++ .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str()))
++ .writeAttribute("line", line(in.data[i]->m_line))
++ .writeAttribute("skipped", in.data[i]->m_skip);
++ }
+ xml.scopedElement("OverallResultsTestCases")
+ .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
+ } else if(opt.list_test_suites) {
+ for(unsigned i = 0; i < in.num_data; ++i)
+- xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]);
++ xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite);
+ xml.scopedElement("OverallResultsTestCases")
+ .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
+ xml.scopedElement("OverallResultsTestSuites")
+@@ -4779,12 +5303,15 @@
+ test_case_start_impl(in);
+ xml.ensureTagClosed();
+ }
++
++ void test_case_reenter(const TestCaseData&) override {}
+
+ void test_case_end(const CurrentTestCaseStats& st) override {
+ xml.startElement("OverallResultsAsserts")
+ .writeAttribute("successes",
+ st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest)
+- .writeAttribute("failures", st.numAssertsFailedCurrentTest);
++ .writeAttribute("failures", st.numAssertsFailedCurrentTest)
++ .writeAttribute("test_case_success", st.testCaseSuccess);
+ if(opt.duration)
+ xml.writeAttribute("duration", st.seconds);
+ if(tc->m_expected_failures)
+@@ -4803,8 +5330,6 @@
+ }
+
+ void subcase_start(const SubcaseSignature& in) override {
+- std::lock_guard<std::mutex> lock(mutex);
+-
+ xml.startElement("SubCase")
+ .writeAttribute("name", in.m_name)
+ .writeAttribute("filename", skipPathFromFilename(in.m_file))
+@@ -4831,11 +5356,12 @@
+ if(rb.m_threw)
+ xml.scopedElement("Exception").writeText(rb.m_exception.c_str());
+
+- if(rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) {
++ if(rb.m_at & assertType::is_throws_as)
+ xml.scopedElement("ExpectedException").writeText(rb.m_exception_type);
+- } else if((rb.m_at & assertType::is_normal) && !rb.m_threw) {
++ if(rb.m_at & assertType::is_throws_with)
++ xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string);
++ if((rb.m_at & assertType::is_normal) && !rb.m_threw)
+ xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str());
+- }
+
+ log_contexts();
+
+@@ -4868,6 +5394,278 @@
+
+ DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter);
+
++ void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) {
++ if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==
++ 0) //!OCLINT bitwise operator in conditional
++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) "
++ << Color::None;
++
++ if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
++ s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n";
++ } else if((rb.m_at & assertType::is_throws_as) &&
++ (rb.m_at & assertType::is_throws_with)) { //!OCLINT
++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
++ << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None;
++ if(rb.m_threw) {
++ if(!rb.m_failed) {
++ s << "threw as expected!\n";
++ } else {
++ s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n";
++ }
++ } else {
++ s << "did NOT throw at all!\n";
++ }
++ } else if(rb.m_at &
++ assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", "
++ << rb.m_exception_type << " ) " << Color::None
++ << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" :
++ "threw a DIFFERENT exception: ") :
++ "did NOT throw at all!")
++ << Color::Cyan << rb.m_exception << "\n";
++ } else if(rb.m_at &
++ assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
++ << rb.m_exception_string << "\" ) " << Color::None
++ << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
++ "threw a DIFFERENT exception: ") :
++ "did NOT throw at all!")
++ << Color::Cyan << rb.m_exception << "\n";
++ } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
++ s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan
++ << rb.m_exception << "\n";
++ } else {
++ s << (rb.m_threw ? "THREW exception: " :
++ (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n"));
++ if(rb.m_threw)
++ s << rb.m_exception << "\n";
++ else
++ s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n";
++ }
++ }
++
++ // TODO:
++ // - log_message()
++ // - respond to queries
++ // - honor remaining options
++ // - more attributes in tags
++ struct JUnitReporter : public IReporter
++ {
++ XmlWriter xml;
++ std::mutex mutex;
++ Timer timer;
++ std::vector<String> deepestSubcaseStackNames;
++
++ struct JUnitTestCaseData
++ {
++ static std::string getCurrentTimestamp() {
++ // Beware, this is not reentrant because of backward compatibility issues
++ // Also, UTC only, again because of backward compatibility (%z is C++11)
++ time_t rawtime;
++ std::time(&rawtime);
++ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
++
++ std::tm timeInfo;
++#ifdef DOCTEST_PLATFORM_WINDOWS
++ gmtime_s(&timeInfo, &rawtime);
++#else // DOCTEST_PLATFORM_WINDOWS
++ gmtime_r(&rawtime, &timeInfo);
++#endif // DOCTEST_PLATFORM_WINDOWS
++
++ char timeStamp[timeStampSize];
++ const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
++
++ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
++ return std::string(timeStamp);
++ }
++
++ struct JUnitTestMessage
++ {
++ JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details)
++ : message(_message), type(_type), details(_details) {}
++
++ JUnitTestMessage(const std::string& _message, const std::string& _details)
++ : message(_message), type(), details(_details) {}
++
++ std::string message, type, details;
++ };
++
++ struct JUnitTestCase
++ {
++ JUnitTestCase(const std::string& _classname, const std::string& _name)
++ : classname(_classname), name(_name), time(0), failures() {}
++
++ std::string classname, name;
++ double time;
++ std::vector<JUnitTestMessage> failures, errors;
++ };
++
++ void add(const std::string& classname, const std::string& name) {
++ testcases.emplace_back(classname, name);
++ }
++
++ void appendSubcaseNamesToLastTestcase(std::vector<String> nameStack) {
++ for(auto& curr: nameStack)
++ if(curr.size())
++ testcases.back().name += std::string("/") + curr.c_str();
++ }
++
++ void addTime(double time) {
++ if(time < 1e-4)
++ time = 0;
++ testcases.back().time = time;
++ totalSeconds += time;
++ }
++
++ void addFailure(const std::string& message, const std::string& type, const std::string& details) {
++ testcases.back().failures.emplace_back(message, type, details);
++ ++totalFailures;
++ }
++
++ void addError(const std::string& message, const std::string& details) {
++ testcases.back().errors.emplace_back(message, details);
++ ++totalErrors;
++ }
++
++ std::vector<JUnitTestCase> testcases;
++ double totalSeconds = 0;
++ int totalErrors = 0, totalFailures = 0;
++ };
++
++ JUnitTestCaseData testCaseData;
++
++ // caching pointers/references to objects of these types - safe to do
++ const ContextOptions& opt;
++ const TestCaseData* tc = nullptr;
++
++ JUnitReporter(const ContextOptions& co)
++ : xml(*co.cout)
++ , opt(co) {}
++
++ unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; }
++
++ // =========================================================================================
++ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
++ // =========================================================================================
++
++ void report_query(const QueryData&) override {}
++
++ void test_run_start() override {}
++
++ void test_run_end(const TestRunStats& p) override {
++ // remove .exe extension - mainly to have the same output on UNIX and Windows
++ std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
++#ifdef DOCTEST_PLATFORM_WINDOWS
++ if(binary_name.rfind(".exe") != std::string::npos)
++ binary_name = binary_name.substr(0, binary_name.length() - 4);
++#endif // DOCTEST_PLATFORM_WINDOWS
++ xml.startElement("testsuites");
++ xml.startElement("testsuite").writeAttribute("name", binary_name)
++ .writeAttribute("errors", testCaseData.totalErrors)
++ .writeAttribute("failures", testCaseData.totalFailures)
++ .writeAttribute("tests", p.numAsserts);
++ if(opt.no_time_in_output == false) {
++ xml.writeAttribute("time", testCaseData.totalSeconds);
++ xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp());
++ }
++ if(opt.no_version == false)
++ xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR);
++
++ for(const auto& testCase : testCaseData.testcases) {
++ xml.startElement("testcase")
++ .writeAttribute("classname", testCase.classname)
++ .writeAttribute("name", testCase.name);
++ if(opt.no_time_in_output == false)
++ xml.writeAttribute("time", testCase.time);
++ // This is not ideal, but it should be enough to mimic gtest's junit output.
++ xml.writeAttribute("status", "run");
++
++ for(const auto& failure : testCase.failures) {
++ xml.scopedElement("failure")
++ .writeAttribute("message", failure.message)
++ .writeAttribute("type", failure.type)
++ .writeText(failure.details, false);
++ }
++
++ for(const auto& error : testCase.errors) {
++ xml.scopedElement("error")
++ .writeAttribute("message", error.message)
++ .writeText(error.details);
++ }
++
++ xml.endElement();
++ }
++ xml.endElement();
++ xml.endElement();
++ }
++
++ void test_case_start(const TestCaseData& in) override {
++ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
++ timer.start();
++ }
++
++ void test_case_reenter(const TestCaseData& in) override {
++ testCaseData.addTime(timer.getElapsedSeconds());
++ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
++ deepestSubcaseStackNames.clear();
++
++ timer.start();
++ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
++ }
++
++ void test_case_end(const CurrentTestCaseStats&) override {
++ testCaseData.addTime(timer.getElapsedSeconds());
++ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
++ deepestSubcaseStackNames.clear();
++ }
++
++ void test_case_exception(const TestCaseException& e) override {
++ std::lock_guard<std::mutex> lock(mutex);
++ testCaseData.addError("exception", e.error_string.c_str());
++ }
++
++ void subcase_start(const SubcaseSignature& in) override {
++ deepestSubcaseStackNames.push_back(in.m_name);
++ }
++
++ void subcase_end() override {}
++
++ void log_assert(const AssertData& rb) override {
++ if(!rb.m_failed) // report only failures & ignore the `success` option
++ return;
++
++ std::lock_guard<std::mutex> lock(mutex);
++
++ std::ostringstream os;
++ os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
++ << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
++
++ fulltext_log_assert_to_stream(os, rb);
++ log_contexts(os);
++ testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str());
++ }
++
++ void log_message(const MessageData&) override {}
++
++ void test_case_skipped(const TestCaseData&) override {}
++
++ void log_contexts(std::ostringstream& s) {
++ int num_contexts = get_num_active_contexts();
++ if(num_contexts) {
++ auto contexts = get_active_contexts();
++
++ s << " logged: ";
++ for(int i = 0; i < num_contexts; ++i) {
++ s << (i == 0 ? "" : " ");
++ contexts[i]->stringify(&s);
++ s << std::endl;
++ }
++ }
++ }
++ };
++
++ DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter);
++
+ struct Whitespace
+ {
+ int nrSpaces;
+@@ -4886,6 +5684,7 @@
+ std::ostream& s;
+ bool hasLoggedCurrentTestStart;
+ std::vector<SubcaseSignature> subcasesStack;
++ size_t currentSubcaseLevel;
+ std::mutex mutex;
+
+ // caching pointers/references to objects of these types - safe to do
+@@ -4944,23 +5743,40 @@
+ s << "\n";
+ }
+
++ // this was requested to be made virtual so users could override it
++ virtual void file_line_to_stream(const char* file, int line,
++ const char* tail = "") {
++ s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(")
++ << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option
++ << (opt.gnu_file_line ? ":" : "):") << tail;
++ }
++
+ void logTestStart() {
+ if(hasLoggedCurrentTestStart)
+ return;
+
+ separator_to_stream();
+- file_line_to_stream(s, tc->m_file, tc->m_line, "\n");
++ file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n");
+ if(tc->m_description)
+ s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n";
+ if(tc->m_test_suite && tc->m_test_suite[0] != '\0')
+ s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n";
+ if(strncmp(tc->m_name, " Scenario:", 11) != 0)
+- s << Color::None << "TEST CASE: ";
++ s << Color::Yellow << "TEST CASE: ";
+ s << Color::None << tc->m_name << "\n";
+
+- for(auto& curr : subcasesStack)
+- if(curr.m_name[0] != '\0')
+- s << " " << curr.m_name << "\n";
++ for(size_t i = 0; i < currentSubcaseLevel; ++i) {
++ if(subcasesStack[i].m_name[0] != '\0')
++ s << " " << subcasesStack[i].m_name << "\n";
++ }
++
++ if(currentSubcaseLevel != subcasesStack.size()) {
++ s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None;
++ for(size_t i = 0; i < subcasesStack.size(); ++i) {
++ if(subcasesStack[i].m_name[0] != '\0')
++ s << " " << subcasesStack[i].m_name << "\n";
++ }
++ }
+
+ s << "\n";
+
+@@ -4974,9 +5790,11 @@
+ }
+
+ void printIntro() {
+- printVersion();
+- s << Color::Cyan << "[doctest] " << Color::None
+- << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
++ if(opt.no_intro == false) {
++ printVersion();
++ s << Color::Cyan << "[doctest] " << Color::None
++ << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
++ }
+ }
+
+ void printHelp() {
+@@ -5038,7 +5856,7 @@
+ << Whitespace(sizePrefixDisplay*1) << "output filename\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string> "
+ << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n";
+- s << Whitespace(sizePrefixDisplay*3) << " <string> - by [file/suite/name/rand]\n";
++ s << Whitespace(sizePrefixDisplay*3) << " <string> - [file/suite/name/rand/none]\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int> "
+@@ -5061,12 +5879,18 @@
+ << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n";
++ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal=<bool> "
++ << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n";
++ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet=<bool> "
++ << Whitespace(sizePrefixDisplay*1) << "no console output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n";
++ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro=<bool> "
++ << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool> "
+@@ -5104,22 +5928,6 @@
+ printReporters(getReporters(), "reporters");
+ }
+
+- void list_query_results() {
+- separator_to_stream();
+- if(opt.count || opt.list_test_cases) {
+- s << Color::Cyan << "[doctest] " << Color::None
+- << "unskipped test cases passing the current filters: "
+- << g_cs->numTestCasesPassingFilters << "\n";
+- } else if(opt.list_test_suites) {
+- s << Color::Cyan << "[doctest] " << Color::None
+- << "unskipped test cases passing the current filters: "
+- << g_cs->numTestCasesPassingFilters << "\n";
+- s << Color::Cyan << "[doctest] " << Color::None
+- << "test suites with unskipped test cases passing the current filters: "
+- << g_cs->numTestSuitesPassingFilters << "\n";
+- }
+- }
+-
+ // =========================================================================================
+ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
+ // =========================================================================================
+@@ -5139,7 +5947,7 @@
+ }
+
+ for(unsigned i = 0; i < in.num_data; ++i)
+- s << Color::None << in.data[i] << "\n";
++ s << Color::None << in.data[i]->m_name << "\n";
+
+ separator_to_stream();
+
+@@ -5152,7 +5960,7 @@
+ separator_to_stream();
+
+ for(unsigned i = 0; i < in.num_data; ++i)
+- s << Color::None << in.data[i] << "\n";
++ s << Color::None << in.data[i]->m_test_suite << "\n";
+
+ separator_to_stream();
+
+@@ -5165,30 +5973,40 @@
+ }
+ }
+
+- void test_run_start() override { printIntro(); }
++ void test_run_start() override {
++ if(!opt.minimal)
++ printIntro();
++ }
+
+ void test_run_end(const TestRunStats& p) override {
++ if(opt.minimal && p.numTestCasesFailed == 0)
++ return;
++
+ separator_to_stream();
++ s << std::dec;
+
++ auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
++ auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
++ auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
+ const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
+- s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6)
++ s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
+ << p.numTestCasesPassingFilters << " | "
+ << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None :
+ Color::Green)
+- << std::setw(6) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed"
++ << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed"
+ << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None)
+- << std::setw(6) << p.numTestCasesFailed << " failed" << Color::None << " | ";
++ << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |";
+ if(opt.no_skipped_summary == false) {
+ const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters;
+- s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped
++ s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped
+ << " skipped" << Color::None;
+ }
+ s << "\n";
+- s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6)
++ s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth)
+ << p.numAsserts << " | "
+ << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green)
+- << std::setw(6) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None
+- << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6)
++ << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None
++ << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth)
+ << p.numAssertsFailed << " failed" << Color::None << " |\n";
+ s << Color::Cyan << "[doctest] " << Color::None
+ << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green)
+@@ -5198,9 +6016,18 @@
+ void test_case_start(const TestCaseData& in) override {
+ hasLoggedCurrentTestStart = false;
+ tc = &in;
++ subcasesStack.clear();
++ currentSubcaseLevel = 0;
++ }
++
++ void test_case_reenter(const TestCaseData&) override {
++ subcasesStack.clear();
+ }
+
+ void test_case_end(const CurrentTestCaseStats& st) override {
++ if(tc->m_no_output)
++ return;
++
+ // log the preamble of the test case only if there is something
+ // else to print - something other than that an assert has failed
+ if(opt.duration ||
+@@ -5235,9 +6062,13 @@
+ }
+
+ void test_case_exception(const TestCaseException& e) override {
++ std::lock_guard<std::mutex> lock(mutex);
++ if(tc->m_no_output)
++ return;
++
+ logTestStart();
+
+- file_line_to_stream(s, tc->m_file, tc->m_line, " ");
++ file_line_to_stream(tc->m_file.c_str(), tc->m_line, " ");
+ successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require :
+ assertType::is_check);
+ s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ")
+@@ -5256,71 +6087,41 @@
+ }
+
+ void subcase_start(const SubcaseSignature& subc) override {
+- std::lock_guard<std::mutex> lock(mutex);
+ subcasesStack.push_back(subc);
++ ++currentSubcaseLevel;
+ hasLoggedCurrentTestStart = false;
+ }
+
+ void subcase_end() override {
+- std::lock_guard<std::mutex> lock(mutex);
+- subcasesStack.pop_back();
++ --currentSubcaseLevel;
+ hasLoggedCurrentTestStart = false;
+ }
+
+ void log_assert(const AssertData& rb) override {
+- if(!rb.m_failed && !opt.success)
++ if((!rb.m_failed && !opt.success) || tc->m_no_output)
+ return;
+
+ std::lock_guard<std::mutex> lock(mutex);
+
+ logTestStart();
+
+- file_line_to_stream(s, rb.m_file, rb.m_line, " ");
++ file_line_to_stream(rb.m_file, rb.m_line, " ");
+ successOrFailColoredStringToStream(!rb.m_failed, rb.m_at);
+- if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==
+- 0) //!OCLINT bitwise operator in conditional
+- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) "
+- << Color::None;
+-
+- if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
+- s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n";
+- } else if(rb.m_at &
+- assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
+- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", "
+- << rb.m_exception_type << " ) " << Color::None
+- << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" :
+- "threw a DIFFERENT exception: ") :
+- "did NOT throw at all!")
+- << Color::Cyan << rb.m_exception << "\n";
+- } else if(rb.m_at &
+- assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
+- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
+- << rb.m_exception_type << "\" ) " << Color::None
+- << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
+- "threw a DIFFERENT exception: ") :
+- "did NOT throw at all!")
+- << Color::Cyan << rb.m_exception << "\n";
+- } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
+- s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan
+- << rb.m_exception << "\n";
+- } else {
+- s << (rb.m_threw ? "THREW exception: " :
+- (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n"));
+- if(rb.m_threw)
+- s << rb.m_exception << "\n";
+- else
+- s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n";
+- }
++
++ fulltext_log_assert_to_stream(s, rb);
+
+ log_contexts();
+ }
+
+ void log_message(const MessageData& mb) override {
++ if(tc->m_no_output)
++ return;
++
+ std::lock_guard<std::mutex> lock(mutex);
+
+ logTestStart();
+
+- file_line_to_stream(s, mb.m_file, mb.m_line, " ");
++ file_line_to_stream(mb.m_file, mb.m_line, " ");
+ s << getSuccessOrFailColor(false, mb.m_severity)
+ << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity,
+ "MESSAGE") << ": ";
+@@ -5346,14 +6147,17 @@
+ bool with_col = g_no_colors; \
+ g_no_colors = false; \
+ ConsoleReporter::func(arg); \
+- DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
+- oss.str(""); \
++ if(oss.tellp() != std::streampos{}) { \
++ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
++ oss.str(""); \
++ } \
+ g_no_colors = with_col; \
+ }
+
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in)
++ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in)
+@@ -5368,7 +6172,7 @@
+
+ // the implementation of parseOption()
+ bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) {
+- // going from the end to the begining and stopping on the first occurance from the end
++ // going from the end to the beginning and stopping on the first occurrence from the end
+ for(int i = argc; i > 0; --i) {
+ auto index = i - 1;
+ auto temp = std::strstr(argv[index], pattern);
+@@ -5424,18 +6228,42 @@
+ std::vector<String>& res) {
+ String filtersString;
+ if(parseOption(argc, argv, pattern, &filtersString)) {
+- // tokenize with "," as a separator
+- // cppcheck-suppress strtokCalled
+- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+- auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string
+- while(pch != nullptr) {
+- if(strlen(pch))
+- res.push_back(pch);
+- // uses the strtok() internal state to go to the next token
+- // cppcheck-suppress strtokCalled
+- pch = std::strtok(nullptr, ",");
++ // tokenize with "," as a separator, unless escaped with backslash
++ std::ostringstream s;
++ auto flush = [&s, &res]() {
++ auto string = s.str();
++ if(string.size() > 0) {
++ res.push_back(string.c_str());
++ }
++ s.str("");
++ };
++
++ bool seenBackslash = false;
++ const char* current = filtersString.c_str();
++ const char* end = current + strlen(current);
++ while(current != end) {
++ char character = *current++;
++ if(seenBackslash) {
++ seenBackslash = false;
++ if(character == ',') {
++ s.put(',');
++ continue;
++ }
++ s.put('\\');
++ }
++ if(character == '\\') {
++ seenBackslash = true;
++ } else if(character == ',') {
++ flush();
++ } else {
++ s.put(character);
++ }
++ }
++
++ if(seenBackslash) {
++ s.put('\\');
+ }
+- DOCTEST_CLANG_SUPPRESS_WARNING_POP
++ flush();
+ return true;
+ }
+ return false;
+@@ -5533,7 +6361,7 @@
+ #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \
+ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \
+ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \
+- p->var = !!intRes; \
++ p->var = static_cast<bool>(intRes); \
+ else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \
+ p->var = true; \
+@@ -5568,9 +6396,12 @@
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false);
++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false);
++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false);
++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false);
+@@ -5579,7 +6410,9 @@
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC));
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false);
++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false);
++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false);
+ // clang-format on
+
+ if(withDefaults) {
+@@ -5632,9 +6465,15 @@
+ curr.clear();
+ }
+
+-// allows the user to override procedurally the int/bool options from the command line
++// allows the user to override procedurally the bool options from the command line
++void Context::setOption(const char* option, bool value) {
++ setOption(option, value ? "true" : "false");
++}
++
++// allows the user to override procedurally the int options from the command line
+ void Context::setOption(const char* option, int value) {
+ setOption(option, toString(value).c_str());
++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ }
+
+ // allows the user to override procedurally the string options from the command line
+@@ -5651,6 +6490,31 @@
+
+ void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; }
+
++void Context::setCout(std::ostream* out) { p->cout = out; }
++
++static class DiscardOStream : public std::ostream
++{
++private:
++ class : public std::streambuf
++ {
++ private:
++ // allowing some buffering decreases the amount of calls to overflow
++ char buf[1024];
++
++ protected:
++ std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; }
++
++ int_type overflow(int_type ch) override {
++ setp(std::begin(buf), std::end(buf));
++ return traits_type::not_eof(ch);
++ }
++ } discardBuf;
++
++public:
++ DiscardOStream()
++ : std::ostream(&discardBuf) {}
++} discardOut;
++
+ // the main function that does all the filtering and test running
+ int Context::run() {
+ using namespace detail;
+@@ -5664,18 +6528,25 @@
+ g_no_colors = p->no_colors;
+ p->resetRunData();
+
+- // stdout by default
+- p->cout = &std::cout;
+- p->cerr = &std::cerr;
+-
+- // or to a file if specified
+ std::fstream fstr;
+- if(p->out.size()) {
+- fstr.open(p->out.c_str(), std::fstream::out);
+- p->cout = &fstr;
++ if(p->cout == nullptr) {
++ if(p->quiet) {
++ p->cout = &discardOut;
++ } else if(p->out.size()) {
++ // to a file if specified
++ fstr.open(p->out.c_str(), std::fstream::out);
++ p->cout = &fstr;
++ } else {
++ // stdout by default
++ p->cout = &std::cout;
++ }
+ }
+
++ FatalConditionHandler::allocateAltStackMem();
++
+ auto cleanup_and_return = [&]() {
++ FatalConditionHandler::freeAltStackMem();
++
+ if(fstr.is_open())
+ fstr.close();
+
+@@ -5710,7 +6581,7 @@
+ p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs));
+
+ #ifdef DOCTEST_PLATFORM_WINDOWS
+- if(isDebuggerActive())
++ if(isDebuggerActive() && p->no_debug_output == false)
+ p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs));
+ #endif // DOCTEST_PLATFORM_WINDOWS
+
+@@ -5747,13 +6618,16 @@
+ first[i] = first[idxToSwap];
+ first[idxToSwap] = temp;
+ }
++ } else if(p->order_by.compare("none", true) == 0) {
++ // means no sorting - beneficial for death tests which call into the executable
++ // with a specific test case in mind - we don't want to slow down the startup times
+ }
+ }
+
+ std::set<String> testSuitesPassingFilt;
+
+- bool query_mode = p->count || p->list_test_cases || p->list_test_suites;
+- std::vector<String> queryResults;
++ bool query_mode = p->count || p->list_test_cases || p->list_test_suites;
++ std::vector<const TestCaseData*> queryResults;
+
+ if(!query_mode)
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY);
+@@ -5766,9 +6640,9 @@
+ if(tc.m_skip && !p->no_skip)
+ skip_me = true;
+
+- if(!matchesAny(tc.m_file, p->filters[0], true, p->case_sensitive))
++ if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive))
+ skip_me = true;
+- if(matchesAny(tc.m_file, p->filters[1], false, p->case_sensitive))
++ if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive))
+ skip_me = true;
+ if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive))
+ skip_me = true;
+@@ -5799,14 +6673,14 @@
+
+ // print the name of the test and don't execute it
+ if(p->list_test_cases) {
+- queryResults.push_back(tc.m_name);
++ queryResults.push_back(&tc);
+ continue;
+ }
+
+ // print the name of the test suite if not done already and don't execute it
+ if(p->list_test_suites) {
+ if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') {
+- queryResults.push_back(tc.m_test_suite);
++ queryResults.push_back(&tc);
+ testSuitesPassingFilt.insert(tc.m_test_suite);
+ p->numTestSuitesPassingFilters++;
+ }
+@@ -5829,12 +6703,16 @@
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc);
+
+ p->timer.start();
++
++ bool run_test = true;
+
+ do {
+ // reset some of the fields for subcases (except for the set of fully passed ones)
+- p->should_reenter = false;
+- p->subcasesCurrentLevel = 0;
+- p->subcasesEnteredLevels.clear();
++ p->should_reenter = false;
++ p->subcasesCurrentMaxLevel = 0;
++ p->subcasesStack.clear();
++
++ p->shouldLogCurrentException = true;
+
+ // reset stuff for logging with INFO()
+ p->stringifiedContexts.clear();
+@@ -5842,10 +6720,13 @@
+ #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ try {
+ #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
++// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method)
++DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable
+ FatalConditionHandler fatalConditionHandler; // Handle signals
+ // execute the test
+ tc.m_test();
+ fatalConditionHandler.reset();
++DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ } catch(const TestFailureException&) {
+ p->failure_flags |= TestCaseFailureReason::AssertFailure;
+@@ -5859,10 +6740,15 @@
+ // exit this loop if enough assertions have failed - even if there are more subcases
+ if(p->abort_after > 0 &&
+ p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) {
+- p->should_reenter = false;
++ run_test = false;
+ p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
+ }
+- } while(p->should_reenter == true);
++
++ if(p->should_reenter && run_test)
++ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
++ if(!p->should_reenter)
++ run_test = false;
++ } while(run_test);
+
+ p->finalizeTestCaseData();
+
+@@ -5886,20 +6772,9 @@
+ DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata);
+ }
+
+- // see these issues on the reasoning for this:
+- // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903
+- // - https://github.com/onqtam/doctest/issues/126
+- auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE
+- { std::cout << std::string(); };
+- DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS();
+-
+ return cleanup_and_return();
+ }
+
+-DOCTEST_DEFINE_DEFAULTS(CurrentTestCaseStats);
+-
+-DOCTEST_DEFINE_DEFAULTS(TestRunStats);
+-
+ IReporter::~IReporter() = default;
+
+ int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); }
+@@ -5935,5 +6810,7 @@
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+
++DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
++
+ #endif // DOCTEST_LIBRARY_IMPLEMENTATION
+ #endif // DOCTEST_CONFIG_IMPLEMENT