summaryrefslogtreecommitdiff
path: root/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch
diff options
context:
space:
mode:
Diffstat (limited to '0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch')
-rw-r--r--0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch23784
1 files changed, 23784 insertions, 0 deletions
diff --git a/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch b/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch
new file mode 100644
index 0000000..77dc7cf
--- /dev/null
+++ b/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch
@@ -0,0 +1,23784 @@
+From e8ce42bd1756361349b41b648e6f744934f8e44e Mon Sep 17 00:00:00 2001
+From: Jonathan Dieter <jdieter@gmail.com>
+Date: Tue, 26 Dec 2017 17:27:46 +0200
+Subject: [PATCH] Put customized spdlog in source so we don't download during
+ build
+
+Signed-off-by: Jonathan Dieter <jdieter@gmail.com>
+---
+ external/spdlog-0.14.0/.gitignore | 64 +
+ external/spdlog-0.14.0/.travis.yml | 90 +
+ external/spdlog-0.14.0/CMakeLists.txt | 87 +
+ external/spdlog-0.14.0/INSTALL | 13 +
+ external/spdlog-0.14.0/LICENSE | 22 +
+ external/spdlog-0.14.0/README.md | 224 +
+ external/spdlog-0.14.0/astyle.sh | 5 +
+ external/spdlog-0.14.0/bench/Makefile.mingw | 57 +
+ external/spdlog-0.14.0/bench/boost-bench-mt.cpp | 84 +
+ external/spdlog-0.14.0/bench/boost-bench.cpp | 47 +
+ external/spdlog-0.14.0/bench/easyl.conf | 10 +
+ .../spdlog-0.14.0/bench/easylogging-bench-mt.cpp | 52 +
+ external/spdlog-0.14.0/bench/easylogging-bench.cpp | 22 +
+ external/spdlog-0.14.0/bench/g2log-async.cpp | 62 +
+ external/spdlog-0.14.0/bench/glog-bench-mt.cpp | 50 +
+ external/spdlog-0.14.0/bench/glog-bench.cpp | 21 +
+ external/spdlog-0.14.0/bench/latency/compare.sh | 13 +
+ .../spdlog-0.14.0/bench/latency/g3log-crush.cpp | 37 +
+ .../spdlog-0.14.0/bench/latency/g3log-latency.cpp | 129 +
+ .../spdlog-0.14.0/bench/latency/spdlog-latency.cpp | 128 +
+ external/spdlog-0.14.0/bench/latency/utils.h | 35 +
+ external/spdlog-0.14.0/bench/logs/.gitignore | 4 +
+ external/spdlog-0.14.0/bench/spdlog-async.cpp | 62 +
+ external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp | 55 +
+ external/spdlog-0.14.0/bench/spdlog-bench.cpp | 20 +
+ external/spdlog-0.14.0/bench/spdlog-null-async.cpp | 112 +
+ external/spdlog-0.14.0/bench/utils.h | 35 +
+ external/spdlog-0.14.0/cmake/Config.cmake.in | 24 +
+ external/spdlog-0.14.0/cmake/spdlog.pc.in | 6 +
+ external/spdlog-0.14.0/example/CMakeLists.txt | 49 +
+ external/spdlog-0.14.0/example/Makefile.clang | 32 +
+ external/spdlog-0.14.0/example/Makefile.mingw | 32 +
+ external/spdlog-0.14.0/example/bench.cpp | 144 +
+ external/spdlog-0.14.0/example/example.cpp | 174 +
+ external/spdlog-0.14.0/example/example.sln | 26 +
+ external/spdlog-0.14.0/example/example.vcxproj | 126 +
+ external/spdlog-0.14.0/example/jni/Android.mk | 15 +
+ external/spdlog-0.14.0/example/jni/Application.mk | 2 +
+ external/spdlog-0.14.0/example/jni/example.cpp | 1 +
+ external/spdlog-0.14.0/example/multisink.cpp | 47 +
+ external/spdlog-0.14.0/example/utils.h | 35 +
+ .../spdlog-0.14.0/include/spdlog/async_logger.h | 82 +
+ external/spdlog-0.14.0/include/spdlog/common.h | 160 +
+ .../include/spdlog/details/async_log_helper.h | 399 +
+ .../include/spdlog/details/async_logger_impl.h | 105 +
+ .../include/spdlog/details/file_helper.h | 117 +
+ .../spdlog-0.14.0/include/spdlog/details/log_msg.h | 50 +
+ .../include/spdlog/details/logger_impl.h | 564 ++
+ .../include/spdlog/details/mpmc_bounded_q.h | 172 +
+ .../include/spdlog/details/null_mutex.h | 45 +
+ external/spdlog-0.14.0/include/spdlog/details/os.h | 469 +
+ .../spdlog/details/pattern_formatter_impl.h | 665 ++
+ .../include/spdlog/details/registry.h | 214 +
+ .../include/spdlog/details/spdlog_impl.h | 263 +
+ .../include/spdlog/fmt/bundled/format.cc | 535 ++
+ .../include/spdlog/fmt/bundled/format.h | 4012 +++++++++
+ .../include/spdlog/fmt/bundled/ostream.cc | 35 +
+ .../include/spdlog/fmt/bundled/ostream.h | 105 +
+ .../include/spdlog/fmt/bundled/posix.cc | 241 +
+ .../include/spdlog/fmt/bundled/posix.h | 367 +
+ .../include/spdlog/fmt/bundled/time.h | 143 +
+ external/spdlog-0.14.0/include/spdlog/fmt/fmt.h | 28 +
+ external/spdlog-0.14.0/include/spdlog/fmt/ostr.h | 17 +
+ external/spdlog-0.14.0/include/spdlog/formatter.h | 47 +
+ external/spdlog-0.14.0/include/spdlog/logger.h | 132 +
+ .../include/spdlog/sinks/android_sink.h | 90 +
+ .../include/spdlog/sinks/ansicolor_sink.h | 133 +
+ .../spdlog-0.14.0/include/spdlog/sinks/base_sink.h | 50 +
+ .../spdlog-0.14.0/include/spdlog/sinks/dist_sink.h | 73 +
+ .../include/spdlog/sinks/file_sinks.h | 242 +
+ .../spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h | 51 +
+ .../spdlog-0.14.0/include/spdlog/sinks/null_sink.h | 34 +
+ .../include/spdlog/sinks/ostream_sink.h | 47 +
+ external/spdlog-0.14.0/include/spdlog/sinks/sink.h | 53 +
+ .../include/spdlog/sinks/stdout_sinks.h | 77 +
+ .../include/spdlog/sinks/syslog_sink.h | 81 +
+ .../include/spdlog/sinks/wincolor_sink.h | 121 +
+ external/spdlog-0.14.0/include/spdlog/spdlog.h | 189 +
+ external/spdlog-0.14.0/include/spdlog/tweakme.h | 141 +
+ external/spdlog-0.14.0/tests/CMakeLists.txt | 19 +
+ external/spdlog-0.14.0/tests/catch.hpp | 9427 ++++++++++++++++++++
+ external/spdlog-0.14.0/tests/cond_logging.cpp | 154 +
+ external/spdlog-0.14.0/tests/errors.cpp | 113 +
+ external/spdlog-0.14.0/tests/file_helper.cpp | 78 +
+ external/spdlog-0.14.0/tests/file_log.cpp | 151 +
+ external/spdlog-0.14.0/tests/format.cpp | 56 +
+ external/spdlog-0.14.0/tests/includes.h | 16 +
+ external/spdlog-0.14.0/tests/install_libcxx.sh | 12 +
+ external/spdlog-0.14.0/tests/main.cpp | 2 +
+ external/spdlog-0.14.0/tests/registry.cpp | 84 +
+ external/spdlog-0.14.0/tests/tests.sln | 28 +
+ external/spdlog-0.14.0/tests/tests.vcxproj | 145 +
+ external/spdlog-0.14.0/tests/tests.vcxproj.filters | 54 +
+ external/spdlog-0.14.0/tests/utils.cpp | 48 +
+ external/spdlog-0.14.0/tests/utils.h | 15 +
+ 95 files changed, 23004 insertions(+)
+ create mode 100644 external/spdlog-0.14.0/.gitignore
+ create mode 100644 external/spdlog-0.14.0/.travis.yml
+ create mode 100644 external/spdlog-0.14.0/CMakeLists.txt
+ create mode 100644 external/spdlog-0.14.0/INSTALL
+ create mode 100644 external/spdlog-0.14.0/LICENSE
+ create mode 100644 external/spdlog-0.14.0/README.md
+ create mode 100755 external/spdlog-0.14.0/astyle.sh
+ create mode 100644 external/spdlog-0.14.0/bench/Makefile.mingw
+ create mode 100644 external/spdlog-0.14.0/bench/boost-bench-mt.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/boost-bench.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/easyl.conf
+ create mode 100644 external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/easylogging-bench.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/g2log-async.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/glog-bench-mt.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/glog-bench.cpp
+ create mode 100755 external/spdlog-0.14.0/bench/latency/compare.sh
+ create mode 100644 external/spdlog-0.14.0/bench/latency/g3log-crush.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/latency/g3log-latency.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/latency/utils.h
+ create mode 100644 external/spdlog-0.14.0/bench/logs/.gitignore
+ create mode 100644 external/spdlog-0.14.0/bench/spdlog-async.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/spdlog-bench.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/spdlog-null-async.cpp
+ create mode 100644 external/spdlog-0.14.0/bench/utils.h
+ create mode 100644 external/spdlog-0.14.0/cmake/Config.cmake.in
+ create mode 100644 external/spdlog-0.14.0/cmake/spdlog.pc.in
+ create mode 100644 external/spdlog-0.14.0/example/CMakeLists.txt
+ create mode 100644 external/spdlog-0.14.0/example/Makefile.clang
+ create mode 100644 external/spdlog-0.14.0/example/Makefile.mingw
+ create mode 100644 external/spdlog-0.14.0/example/bench.cpp
+ create mode 100644 external/spdlog-0.14.0/example/example.cpp
+ create mode 100644 external/spdlog-0.14.0/example/example.sln
+ create mode 100644 external/spdlog-0.14.0/example/example.vcxproj
+ create mode 100644 external/spdlog-0.14.0/example/jni/Android.mk
+ create mode 100644 external/spdlog-0.14.0/example/jni/Application.mk
+ create mode 120000 external/spdlog-0.14.0/example/jni/example.cpp
+ create mode 100644 external/spdlog-0.14.0/example/multisink.cpp
+ create mode 100644 external/spdlog-0.14.0/example/utils.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/async_logger.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/common.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/file_helper.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/log_msg.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/logger_impl.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/null_mutex.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/os.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/registry.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/fmt.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/ostr.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/formatter.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/logger.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/spdlog.h
+ create mode 100644 external/spdlog-0.14.0/include/spdlog/tweakme.h
+ create mode 100644 external/spdlog-0.14.0/tests/CMakeLists.txt
+ create mode 100644 external/spdlog-0.14.0/tests/catch.hpp
+ create mode 100644 external/spdlog-0.14.0/tests/cond_logging.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/errors.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/file_helper.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/file_log.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/format.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/includes.h
+ create mode 100755 external/spdlog-0.14.0/tests/install_libcxx.sh
+ create mode 100644 external/spdlog-0.14.0/tests/main.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/registry.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/tests.sln
+ create mode 100644 external/spdlog-0.14.0/tests/tests.vcxproj
+ create mode 100644 external/spdlog-0.14.0/tests/tests.vcxproj.filters
+ create mode 100644 external/spdlog-0.14.0/tests/utils.cpp
+ create mode 100644 external/spdlog-0.14.0/tests/utils.h
+
+diff --git a/external/spdlog-0.14.0/.gitignore b/external/spdlog-0.14.0/.gitignore
+new file mode 100644
+index 00000000..b51a05b7
+--- /dev/null
++++ b/external/spdlog-0.14.0/.gitignore
+@@ -0,0 +1,64 @@
++# Auto generated files
++*.slo
++*.lo
++*.o
++*.obj
++*.suo
++*.tlog
++*.ilk
++*.log
++*.pdb
++*.idb
++*.iobj
++*.ipdb
++*.opensdf
++*.sdf
++
++# Compiled Dynamic libraries
++*.so
++*.dylib
++*.dll
++
++# Compiled Static libraries
++*.lai
++*.la
++*.a
++*.lib
++
++# Executables
++*.exe
++*.out
++*.app
++
++# Codelite
++.codelite
++
++# .orig files
++*.orig
++
++# example files
++example/*
++!example/example.cpp
++!example/bench.cpp
++!example/utils.h
++!example/Makefile*
++!example/example.sln
++!example/example.vcxproj
++!example/CMakeLists.txt
++!example/multisink.cpp
++!example/jni
++
++# generated files
++generated
++
++# Cmake
++CMakeCache.txt
++CMakeFiles
++CMakeScripts
++Makefile
++cmake_install.cmake
++install_manifest.txt
++/tests/tests.VC.VC.opendb
++/tests/tests.VC.db
++/tests/tests
++/tests/logs/file_helper_test.txt
+diff --git a/external/spdlog-0.14.0/.travis.yml b/external/spdlog-0.14.0/.travis.yml
+new file mode 100644
+index 00000000..c65e84d4
+--- /dev/null
++++ b/external/spdlog-0.14.0/.travis.yml
+@@ -0,0 +1,90 @@
++# Adapted from various sources, including:
++# - Louis Dionne's Hana: https://github.com/ldionne/hana
++# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
++# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
++language: cpp
++
++# Test matrix:
++# - Build matrix per compiler: C++11/C++14 + Debug/Release
++# - Optionally: AddressSanitizer (ASAN)
++# - Valgrind: all release builds are also tested with valgrind
++# - clang 3.4, 3.5, 3.6, trunk
++# - Note: 3.4 and trunk are tested with/without ASAN,
++# the rest is only tested with ASAN=On.
++# - gcc 4.9, 5.0
++#
++matrix:
++ include:
++
++# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
++ - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
++ os: linux
++ addons: &gcc48
++ apt:
++ packages:
++ - g++-4.8
++ - valgrind
++ sources:
++ - ubuntu-toolchain-r-test
++
++ - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
++ os: linux
++ addons: *gcc48
++
++ # Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
++ - env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
++ os: linux
++ addons: &gcc49
++ apt:
++ packages:
++ - g++-4.9
++ - valgrind
++ sources:
++ - ubuntu-toolchain-r-test
++
++ - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
++ os: linux
++ addons: *gcc49
++
++# Install dependencies
++before_install:
++ - export CHECKOUT_PATH=`pwd`;
++ - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
++ - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
++ - if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
++ - which $CXX
++ - which $CC
++ - which valgrind
++ - if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
++
++install:
++ - cd $CHECKOUT_PATH
++
++ # Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
++ # It is fixed in valgrind 3.10 so this won't be necessary if someone
++ # replaces the current valgrind (3.7) with valgrind-3.10
++ - sed -i 's/march=native/msse4.2/' example/Makefile
++
++ - if [ ! -d build ]; then mkdir build; fi
++ - export CXX_FLAGS="-I${CHECKOUT_PATH}/include"
++ - export CXX_LINKER_FLAGS=""
++ - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
++ - if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
++ - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
++ - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
++ - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
++ - CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
++
++ # Build examples
++ - cd example
++ - if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
++ - if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
++
++
++script:
++ - ./"${BIN}"
++ - valgrind --trace-children=yes --leak-check=full ./"${BIN}"
++ - cd $CHECKOUT_PATH/tests; make rebuild; ./tests
++
++notifications:
++ email: false
+diff --git a/external/spdlog-0.14.0/CMakeLists.txt b/external/spdlog-0.14.0/CMakeLists.txt
+new file mode 100644
+index 00000000..61c45b5e
+--- /dev/null
++++ b/external/spdlog-0.14.0/CMakeLists.txt
+@@ -0,0 +1,87 @@
++#
++# Copyright(c) 2015 Ruslan Baratov.
++# Distributed under the MIT License (http://opensource.org/licenses/MIT)
++#
++
++cmake_minimum_required(VERSION 3.1)
++project(spdlog VERSION 1.0.0)
++include(CTest)
++
++set(CMAKE_CXX_STANDARD 11)
++set(CMAKE_CXX_STANDARD_REQUIRED ON)
++
++if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
++ set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
++endif()
++
++add_library(spdlog INTERFACE)
++
++option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
++option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON)
++
++target_include_directories(
++ spdlog
++ INTERFACE
++ "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
++ "$<INSTALL_INTERFACE:include>"
++)
++
++set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
++
++if(SPDLOG_BUILD_EXAMPLES)
++ add_subdirectory(example)
++endif()
++
++if(SPDLOG_BUILD_TESTING)
++ add_subdirectory(tests)
++endif()
++
++### Install ###
++# * https://github.com/forexample/package-example
++set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
++
++set(config_install_dir "lib/cmake/${PROJECT_NAME}")
++set(include_install_dir "include")
++set(pkgconfig_install_dir "lib/pkgconfig")
++
++set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
++set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
++set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
++set(targets_export_name "${PROJECT_NAME}Targets")
++set(namespace "${PROJECT_NAME}::")
++
++include(CMakePackageConfigHelpers)
++write_basic_package_version_file(
++ "${version_config}" COMPATIBILITY SameMajorVersion
++)
++
++# Note: use 'targets_export_name'
++configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
++configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
++
++install(
++ TARGETS spdlog
++ EXPORT "${targets_export_name}"
++ INCLUDES DESTINATION "${include_install_dir}"
++)
++
++install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
++
++install(
++ FILES "${project_config}" "${version_config}"
++ DESTINATION "${config_install_dir}"
++)
++
++install(
++ FILES "${pkg_config}"
++ DESTINATION "${pkgconfig_install_dir}"
++)
++
++install(
++ EXPORT "${targets_export_name}"
++ NAMESPACE "${namespace}"
++ DESTINATION "${config_install_dir}"
++)
++
++file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
++add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
+diff --git a/external/spdlog-0.14.0/INSTALL b/external/spdlog-0.14.0/INSTALL
+new file mode 100644
+index 00000000..664509d2
+--- /dev/null
++++ b/external/spdlog-0.14.0/INSTALL
+@@ -0,0 +1,13 @@
++spdlog is header only library.
++Just copy the files to your build tree and use a C++11 compiler
++
++Tested on:
++gcc 4.8.1 and above
++clang 3.5
++Visual Studio 2013
++
++gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
++gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
++
++
++see the makefile in the example folder
+diff --git a/external/spdlog-0.14.0/LICENSE b/external/spdlog-0.14.0/LICENSE
+new file mode 100644
+index 00000000..4b43e064
+--- /dev/null
++++ b/external/spdlog-0.14.0/LICENSE
+@@ -0,0 +1,22 @@
++The MIT License (MIT)
++
++Copyright (c) 2016 Gabi Melman.
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
++
+diff --git a/external/spdlog-0.14.0/README.md b/external/spdlog-0.14.0/README.md
+new file mode 100644
+index 00000000..e2fd18ae
+--- /dev/null
++++ b/external/spdlog-0.14.0/README.md
+@@ -0,0 +1,224 @@
++# spdlog
++
++Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog)
++
++
++## Install
++#### Just copy the headers:
++
++* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
++
++#### Or use your favourite package manager:
++
++* Ubuntu: `apt-get install libspdlog-dev`
++* Homebrew: `brew install spdlog`
++* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
++* Fedora: `yum install spdlog`
++* Gentoo: `emerge dev-libs/spdlog`
++* Arch Linux: `pacman -S spdlog-git`
++* vcpkg: `vcpkg install spdlog`
++
++
++## Platforms
++ * Linux, FreeBSD, Solaris
++ * Windows (vc 2013+, cygwin/mingw)
++ * Mac OSX (clang 3.5+)
++ * Android
++
++## Features
++* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
++* Headers only, just copy and use.
++* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
++* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
++* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
++* Conditional Logging
++* Multi/Single threaded loggers.
++* Various log targets:
++ * Rotating log files.
++ * Daily log files.
++ * Console logging (colors supported).
++ * syslog.
++ * Windows debugger (```OutputDebugString(..)```)
++ * Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
++* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
++
++
++
++## Benchmarks
++
++Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
++
++#### Synchronous mode
++Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
++
++|threads|boost log 1.54|glog |easylogging |spdlog|
++|-------|:-------:|:-----:|----------:|------:|
++|1| 4.169s |1.066s |0.975s |0.302s|
++|10| 6.180s |3.032s |2.857s |0.968s|
++|100| 5.981s |1.139s |4.512s |0.497s|
++
++
++#### Asynchronous mode
++Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
++
++|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
++|:-------|:-----:|-------------------------:|
++|1| 1.850s |0.216s |
++|10| 0.943s |0.173s|
++|100| 0.959s |0.202s|
++
++
++
++
++## Usage Example
++```c++
++
++#include "spdlog/spdlog.h"
++
++#include <iostream>
++#include <memory>
++
++void async_example();
++void syslog_example();
++void user_defined_example();
++void err_handler_example();
++
++namespace spd = spdlog;
++int main(int, char*[])
++{
++ try
++ {
++ // Console logger with color
++ auto console = spd::stdout_color_mt("console");
++ console->info("Welcome to spdlog!");
++ console->error("Some error message with arg{}..", 1);
++
++ // Conditional logging example
++ auto i = 2;
++ console->warn_if(i != 0, "an important message");
++
++ // Formatting examples
++ console->warn("Easy padding in numbers like {:08d}", 12);
++ console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
++ console->info("Support for floats {:03.2f}", 1.23456);
++ console->info("Positional args are {1} {0}..", "too", "supported");
++ console->info("{:<30}", "left aligned");
++
++
++ spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
++
++ // Create basic file logger (not rotated)
++ auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
++ my_logger->info("Some log message");
++
++ // Create a file rotating logger with 5mb size max and 3 rotated files
++ auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
++ for (int i = 0; i < 10; ++i)
++ rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
++
++ // Create a daily logger - a new file is created every day on 2:30am
++ auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
++ // trigger flush if the log severity is error or higher
++ daily_logger->flush_on(spd::level::err);
++ daily_logger->info(123.44);
++
++ // Customize msg format for all messages
++ spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
++ rotating_logger->info("This is another message with custom format");
++
++
++ // Runtime log levels
++ spd::set_level(spd::level::info); //Set global log level to info
++ console->debug("This message shold not be displayed!");
++ console->set_level(spd::level::debug); // Set specific logger's log level
++ console->debug("This message shold be displayed..");
++
++ // Compile time log levels
++ // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
++ SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
++ SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
++
++ // Asynchronous logging is very fast..
++ // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
++ async_example();
++
++ // syslog example. linux/osx only
++ syslog_example();
++
++ // android example. compile with NDK
++ android_example();
++
++ // Log user-defined types example
++ user_defined_example();
++
++ // Change default log error handler
++ err_handler_example();
++
++ // Apply a function on all registered loggers
++ spd::apply_all([&](std::shared_ptr<spd::logger> l)
++ {
++ l->info("End of example.");
++ });
++
++ // Release and close all loggers
++ spd::drop_all();
++ }
++ // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
++ catch (const spd::spdlog_ex& ex)
++ {
++ std::cout << "Log init failed: " << ex.what() << std::endl;
++ return 1;
++ }
++}
++
++void async_example()
++{
++ size_t q_size = 4096; //queue size must be power of 2
++ spd::set_async_mode(q_size);
++ auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
++ for (int i = 0; i < 100; ++i)
++ async_file->info("Async message #{}", i);
++}
++
++//syslog example
++void syslog_example()
++{
++#ifdef SPDLOG_ENABLE_SYSLOG
++ std::string ident = "spdlog-example";
++ auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
++ syslog_logger->warn("This is warning that will end up in syslog..");
++#endif
++}
++
++// user defined types logging by implementing operator<<
++struct my_type
++{
++ int i;
++ template<typename OStream>
++ friend OStream& operator<<(OStream& os, const my_type &c)
++ {
++ return os << "[my_type i="<<c.i << "]";
++ }
++};
++
++#include <spdlog/fmt/ostr.h> // must be included
++void user_defined_example()
++{
++ spd::get("console")->info("user defined type: {}", my_type { 14 });
++}
++
++//
++//custom error handler
++//
++void err_handler_example()
++{
++ spd::set_error_handler([](const std::string& msg) {
++ std::cerr << "my err handler: " << msg << std::endl;
++ });
++ // (or logger->set_error_handler(..) to set for specific logger)
++}
++
++```
++
++## Documentation
++Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
+diff --git a/external/spdlog-0.14.0/astyle.sh b/external/spdlog-0.14.0/astyle.sh
+new file mode 100755
+index 00000000..a7a90510
+--- /dev/null
++++ b/external/spdlog-0.14.0/astyle.sh
+@@ -0,0 +1,5 @@
++#!/bin/bash
++find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix
++find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -c -A1
++
++
+diff --git a/external/spdlog-0.14.0/bench/Makefile.mingw b/external/spdlog-0.14.0/bench/Makefile.mingw
+new file mode 100644
+index 00000000..b4357be4
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/Makefile.mingw
+@@ -0,0 +1,57 @@
++CXX ?= g++
++CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
++CXX_RELEASE_FLAGS = -O3 -flto
++
++
++binaries=spdlog-bench spdlog-bench-mt spdlog-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
++
++all: $(binaries)
++
++spdlog-bench: spdlog-bench.cpp
++ $(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++spdlog-bench-mt: spdlog-bench-mt.cpp
++ $(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++spdlog-async: spdlog-async.cpp
++ $(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++
++BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/home/gabi/devel/boost_1_56_0/ -L/home/gabi/devel/boost_1_56_0/stage/lib -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
++
++boost-bench: boost-bench.cpp
++ $(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
++
++boost-bench-mt: boost-bench-mt.cpp
++ $(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
++
++
++GLOG_FLAGS = -lglog
++glog-bench: glog-bench.cpp
++ $(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
++
++glog-bench-mt: glog-bench-mt.cpp
++ $(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
++
++
++G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
++g2log-async: g2log-async.cpp
++ $(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
++
++
++EASYL_FLAGS = -I../../easylogging/src/
++easylogging-bench: easylogging-bench.cpp
++ $(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
++easylogging-bench-mt: easylogging-bench-mt.cpp
++ $(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
++
++.PHONY: clean
++
++clean:
++ rm -f *.o logs/* $(binaries)
++
++
++rebuild: clean all
++
++
++
+diff --git a/external/spdlog-0.14.0/bench/boost-bench-mt.cpp b/external/spdlog-0.14.0/bench/boost-bench-mt.cpp
+new file mode 100644
+index 00000000..d845fcec
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/boost-bench-mt.cpp
+@@ -0,0 +1,84 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include <thread>
++#include <vector>
++#include <atomic>
++
++#include <boost/log/core.hpp>
++#include <boost/log/trivial.hpp>
++#include <boost/log/expressions.hpp>
++#include <boost/log/sinks/text_file_backend.hpp>
++#include <boost/log/utility/setup/file.hpp>
++#include <boost/log/utility/setup/common_attributes.hpp>
++#include <boost/log/sources/severity_logger.hpp>
++#include <boost/log/sources/record_ostream.hpp>
++
++namespace logging = boost::log;
++namespace src = boost::log::sources;
++namespace sinks = boost::log::sinks;
++namespace keywords = boost::log::keywords;
++
++void init()
++{
++ logging::add_file_log
++ (
++ keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
++ keywords::auto_flush = false,
++ keywords::format = "[%TimeStamp%]: %Message%"
++ );
++
++ logging::core::get()->set_filter
++ (
++ logging::trivial::severity >= logging::trivial::info
++ );
++}
++
++
++
++using namespace std;
++
++int main(int argc, char* argv[])
++{
++ int thread_count = 10;
++ if(argc > 1)
++ thread_count = atoi(argv[1]);
++
++ int howmany = 1000000;
++
++
++ init();
++ logging::add_common_attributes();
++
++
++ using namespace logging::trivial;
++
++ src::severity_logger_mt< severity_level > lg;
++
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ while (true)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure";
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/boost-bench.cpp b/external/spdlog-0.14.0/bench/boost-bench.cpp
+new file mode 100644
+index 00000000..32c5b692
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/boost-bench.cpp
+@@ -0,0 +1,47 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++#include <boost/log/core.hpp>
++#include <boost/log/trivial.hpp>
++#include <boost/log/expressions.hpp>
++#include <boost/log/sinks/text_file_backend.hpp>
++#include <boost/log/utility/setup/file.hpp>
++#include <boost/log/utility/setup/common_attributes.hpp>
++#include <boost/log/sources/severity_logger.hpp>
++#include <boost/log/sources/record_ostream.hpp>
++
++namespace logging = boost::log;
++namespace src = boost::log::sources;
++namespace sinks = boost::log::sinks;
++namespace keywords = boost::log::keywords;
++
++void init()
++{
++ logging::add_file_log
++ (
++ keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
++ keywords::auto_flush = false,
++ keywords::format = "[%TimeStamp%]: %Message%"
++ );
++
++ logging::core::get()->set_filter
++ (
++ logging::trivial::severity >= logging::trivial::info
++ );
++}
++
++
++int main(int argc, char* [])
++{
++ int howmany = 1000000;
++ init();
++ logging::add_common_attributes();
++
++ using namespace logging::trivial;
++ src::severity_logger_mt< severity_level > lg;
++ for(int i = 0 ; i < howmany; ++i)
++ BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure";
++
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/easyl.conf b/external/spdlog-0.14.0/bench/easyl.conf
+new file mode 100644
+index 00000000..3bfb5440
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/easyl.conf
+@@ -0,0 +1,10 @@
++* GLOBAL:
++ FORMAT = "[%datetime]: %msg"
++ FILENAME = ./logs/easylogging.log
++ ENABLED = true
++ TO_FILE = true
++ TO_STANDARD_OUTPUT = false
++ MILLISECONDS_WIDTH = 3
++ PERFORMANCE_TRACKING = false
++ MAX_LOG_FILE_SIZE = 10485760
++ Log_Flush_Threshold = 10485760
+diff --git a/external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp b/external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp
+new file mode 100644
+index 00000000..98d1ae35
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp
+@@ -0,0 +1,52 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include <thread>
++#include <vector>
++#include <atomic>
++
++#define _ELPP_THREAD_SAFE
++#include "easylogging++.h"
++_INITIALIZE_EASYLOGGINGPP
++
++using namespace std;
++
++int main(int argc, char* argv[])
++{
++
++ int thread_count = 10;
++ if(argc > 1)
++ thread_count = atoi(argv[1]);
++
++ int howmany = 1000000;
++
++ // Load configuration from file
++ el::Configurations conf("easyl.conf");
++ el::Loggers::reconfigureLogger("default", conf);
++
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ while (true)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/easylogging-bench.cpp b/external/spdlog-0.14.0/bench/easylogging-bench.cpp
+new file mode 100644
+index 00000000..a952cbd5
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/easylogging-bench.cpp
+@@ -0,0 +1,22 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++
++#include "easylogging++.h"
++
++_INITIALIZE_EASYLOGGINGPP
++
++int main(int, char* [])
++{
++ int howmany = 1000000;
++
++ // Load configuration from file
++ el::Configurations conf("easyl.conf");
++ el::Loggers::reconfigureLogger("default", conf);
++
++ for(int i = 0 ; i < howmany; ++i)
++ LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure";
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/g2log-async.cpp b/external/spdlog-0.14.0/bench/g2log-async.cpp
+new file mode 100644
+index 00000000..9f9eb71e
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/g2log-async.cpp
+@@ -0,0 +1,62 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include <thread>
++#include <vector>
++#include <atomic>
++#include <iostream>
++#include <chrono>
++
++#include "g2logworker.h"
++#include "g2log.h"
++
++using namespace std;
++template<typename T> std::string format(const T& value);
++
++int main(int argc, char* argv[])
++{
++ using namespace std::chrono;
++ using clock=steady_clock;
++ int thread_count = 10;
++
++ if(argc > 1)
++ thread_count = atoi(argv[1]);
++ int howmany = 1000000;
++
++ g2LogWorker g2log(argv[0], "logs");
++ g2::initializeLogging(&g2log);
++
++
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++ auto start = clock::now();
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ while (true)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ LOG(INFO) << "g2log message #" << counter << ": This is some text for your pleasure";
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++ duration<float> delta = clock::now() - start;
++ float deltaf = delta.count();
++ auto rate = howmany/deltaf;
++
++ cout << "Total: " << howmany << std::endl;
++ cout << "Threads: " << thread_count << std::endl;
++ std::cout << "Delta = " << deltaf << " seconds" << std::endl;
++ std::cout << "Rate = " << rate << "/sec" << std::endl;
++}
+diff --git a/external/spdlog-0.14.0/bench/glog-bench-mt.cpp b/external/spdlog-0.14.0/bench/glog-bench-mt.cpp
+new file mode 100644
+index 00000000..db193aeb
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/glog-bench-mt.cpp
+@@ -0,0 +1,50 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include <thread>
++#include <vector>
++#include <atomic>
++
++#include "glog/logging.h"
++
++using namespace std;
++
++int main(int argc, char* argv[])
++{
++
++ int thread_count = 10;
++ if(argc > 1)
++ thread_count = atoi(argv[1]);
++
++ int howmany = 1000000;
++
++ FLAGS_logtostderr = 0;
++ FLAGS_log_dir = "logs";
++ google::InitGoogleLogging(argv[0]);
++
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ while (true)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure";
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/glog-bench.cpp b/external/spdlog-0.14.0/bench/glog-bench.cpp
+new file mode 100644
+index 00000000..cf7e70a2
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/glog-bench.cpp
+@@ -0,0 +1,21 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include "glog/logging.h"
++
++
++int main(int, char* argv[])
++{
++ int howmany = 1000000;
++
++
++ FLAGS_logtostderr = 0;
++ FLAGS_log_dir = "logs";
++ google::InitGoogleLogging(argv[0]);
++ for(int i = 0 ; i < howmany; ++i)
++ LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure";
++
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/latency/compare.sh b/external/spdlog-0.14.0/bench/latency/compare.sh
+new file mode 100755
+index 00000000..0f0e4c97
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/latency/compare.sh
+@@ -0,0 +1,13 @@
++#!/bin/bash
++echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).."
++rm -f *.log
++for i in {1..10}
++
++do
++ echo
++ sleep 0.5
++ ./spdlog-latency ${1:-10} 2>/dev/null || exit
++ sleep 0.5
++ ./g3log-latency ${1:-10} 2>/dev/null || exit
++
++done
+diff --git a/external/spdlog-0.14.0/bench/latency/g3log-crush.cpp b/external/spdlog-0.14.0/bench/latency/g3log-crush.cpp
+new file mode 100644
+index 00000000..417b014c
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/latency/g3log-crush.cpp
+@@ -0,0 +1,37 @@
++#include <iostream>
++
++#include <g3log/g3log.hpp>
++#include <g3log/logworker.hpp>
++
++void CrusherLoop()
++{
++ size_t counter = 0;
++ while (true)
++ {
++ LOGF(INFO, "Some text to crush you machine. thread:");
++ if(++counter % 1000000 == 0)
++ {
++ std::cout << "Wrote " << counter << " entries" << std::endl;
++ }
++ }
++}
++
++
++int main(int argc, char** argv)
++{
++ std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl;
++ std::cout << "Are you sure you want to continue ? " << std::endl;
++ char c;
++ std::cin >> c;
++ if (toupper( c ) != 'Y')
++ return 0;
++
++ auto worker = g3::LogWorker::createLogWorker();
++ auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
++ g3::initializeLogging(worker.get());
++ CrusherLoop();
++
++ return 0;
++}
++
++
+diff --git a/external/spdlog-0.14.0/bench/latency/g3log-latency.cpp b/external/spdlog-0.14.0/bench/latency/g3log-latency.cpp
+new file mode 100644
+index 00000000..e96e421b
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/latency/g3log-latency.cpp
+@@ -0,0 +1,129 @@
++#include <thread>
++#include <vector>
++#include <atomic>
++#include <iostream>
++#include <chrono>
++#include <algorithm>
++#include <iomanip>
++#include <iostream>
++#include <sstream>
++#include <fstream>
++#include <cstdio>
++#include <map>
++#include <numeric>
++#include <functional>
++#include <thread>
++#include "utils.h"
++#include <g3log/g3log.hpp>
++#include <g3log/logworker.hpp>
++
++
++namespace
++{
++const uint64_t g_iterations = 1000000;
++
++
++std::atomic<size_t> g_counter = {0};
++
++
++void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
++{
++
++ while (true)
++ {
++ const size_t value_now = ++g_counter;
++ if (value_now > g_iterations)
++ {
++ return;
++ }
++
++ auto start_time = std::chrono::high_resolution_clock::now();
++ LOGF(INFO, "Some text to log for thread: %ld", id);
++ auto stop_time = std::chrono::high_resolution_clock::now();
++ uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
++ result.push_back(time_us);
++ }
++}
++
++
++
++void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
++{
++
++ std::vector<uint64_t> all_measurements;
++ all_measurements.reserve(g_iterations);
++ for (auto& t_result : threads_result)
++ {
++ all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
++ }
++
++ // calc worst latenct
++ auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
++
++ // calc avg
++ auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
++ auto avg = double(total)/all_measurements.size();
++
++ std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
++
++}
++}// anonymous
++
++
++// The purpose of this test is NOT to see how fast
++// each thread can possibly write. It is to see what
++// the worst latency is for writing a log entry
++//
++// In the test 1 million log entries will be written
++// an atomic counter is used to give each thread what
++// it is to write next. The overhead of atomic
++// synchronization between the threads are not counted in the worst case latency
++int main(int argc, char** argv)
++{
++ size_t number_of_threads {0};
++ if (argc == 2)
++ {
++ number_of_threads = atoi(argv[1]);
++ }
++ if (argc != 2 || number_of_threads == 0)
++ {
++ std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
++ return 1;
++ }
++
++
++ std::vector<std::thread> threads(number_of_threads);
++ std::map<size_t, std::vector<uint64_t>> threads_result;
++
++ for (size_t idx = 0; idx < number_of_threads; ++idx)
++ {
++ // reserve to 1 million for all the result
++ // it's a test so let's not care about the wasted space
++ threads_result[idx].reserve(g_iterations);
++ }
++
++ const std::string g_path = "./" ;
++ const std::string g_prefix_log_name = "g3log-performance-";
++ const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
++
++ auto worker = g3::LogWorker::createLogWorker();
++ auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
++ g3::initializeLogging(worker.get());
++
++ auto start_time_application_total = std::chrono::high_resolution_clock::now();
++ for (uint64_t idx = 0; idx < number_of_threads; ++idx)
++ {
++ threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
++ }
++ for (size_t idx = 0; idx < number_of_threads; ++idx)
++ {
++ threads[idx].join();
++ }
++ auto stop_time_application_total = std::chrono::high_resolution_clock::now();
++
++ uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
++ PrintResults(threads_result, total_time_in_us);
++ return 0;
++}
++
++
+diff --git a/external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp b/external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp
+new file mode 100644
+index 00000000..ed4966cc
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp
+@@ -0,0 +1,128 @@
++
++#include <thread>
++#include <vector>
++#include <atomic>
++#include <iostream>
++#include <chrono>
++#include <algorithm>
++#include <iostream>
++#include <cstdio>
++#include <map>
++#include <numeric>
++#include <functional>
++#include "utils.h"
++#include <thread>
++
++#include "spdlog/spdlog.h"
++
++namespace spd = spdlog;
++
++namespace
++{
++const uint64_t g_iterations = 1000000;
++
++
++std::atomic<size_t> g_counter = {0};
++
++
++void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
++{
++ auto logger = spd::get("file_logger");
++ while (true)
++ {
++ const size_t value_now = ++g_counter;
++ if (value_now > g_iterations)
++ {
++ return;
++ }
++
++ auto start_time = std::chrono::high_resolution_clock::now();
++ logger->info("Some text to log for thread: [somemore text...............................] {}", id);
++ auto stop_time = std::chrono::high_resolution_clock::now();
++ uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
++ result.push_back(time_us);
++ }
++}
++
++
++void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
++{
++
++ std::vector<uint64_t> all_measurements;
++ all_measurements.reserve(g_iterations);
++ for (auto& t_result : threads_result)
++ {
++ all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
++ }
++
++ // calc worst latenct
++ auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
++
++ // calc avg
++ auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
++ auto avg = double(total)/all_measurements.size();
++
++ std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
++
++}
++}// anonymous
++
++
++// The purpose of this test is NOT to see how fast
++// each thread can possibly write. It is to see what
++// the worst latency is for writing a log entry
++//
++// In the test 1 million log entries will be written
++// an atomic counter is used to give each thread what
++// it is to write next. The overhead of atomic
++// synchronization between the threads are not counted in the worst case latency
++int main(int argc, char** argv)
++{
++ size_t number_of_threads {0};
++ if (argc == 2)
++ {
++ number_of_threads = atoi(argv[1]);
++ }
++ if (argc != 2 || number_of_threads == 0)
++ {
++ std::cerr << "usage: " << argv[0] << " number_threads" << std::endl;
++ return 1;
++ }
++
++
++ std::vector<std::thread> threads(number_of_threads);
++ std::map<size_t, std::vector<uint64_t>> threads_result;
++
++ for (size_t idx = 0; idx < number_of_threads; ++idx)
++ {
++ // reserve to 1 million for all the result
++ // it's a test so let's not care about the wasted space
++ threads_result[idx].reserve(g_iterations);
++ }
++
++ int queue_size = 1048576; // 2 ^ 20
++ spdlog::set_async_mode(queue_size);
++ auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
++
++ //force flush on every call to compare with g3log
++ auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get();
++ s->set_force_flush(true);
++
++ auto start_time_application_total = std::chrono::high_resolution_clock::now();
++ for (uint64_t idx = 0; idx < number_of_threads; ++idx)
++ {
++ threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
++ }
++ for (size_t idx = 0; idx < number_of_threads; ++idx)
++ {
++ threads[idx].join();
++ }
++ auto stop_time_application_total = std::chrono::high_resolution_clock::now();
++
++ uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
++
++ PrintResults(threads_result, total_time_in_us);
++ return 0;
++}
++
++
+diff --git a/external/spdlog-0.14.0/bench/latency/utils.h b/external/spdlog-0.14.0/bench/latency/utils.h
+new file mode 100644
+index 00000000..b260f724
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/latency/utils.h
+@@ -0,0 +1,35 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include <sstream>
++#include <iomanip>
++#include <locale>
++
++namespace utils
++{
++
++template<typename T>
++inline std::string format(const T& value)
++{
++ static std::locale loc("");
++ std::stringstream ss;
++ ss.imbue(loc);
++ ss << value;
++ return ss.str();
++}
++
++template<>
++inline std::string format(const double & value)
++{
++ static std::locale loc("");
++ std::stringstream ss;
++ ss.imbue(loc);
++ ss << std::fixed << std::setprecision(1) << value;
++ return ss.str();
++}
++
++}
+diff --git a/external/spdlog-0.14.0/bench/logs/.gitignore b/external/spdlog-0.14.0/bench/logs/.gitignore
+new file mode 100644
+index 00000000..40637012
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/logs/.gitignore
+@@ -0,0 +1,4 @@
++# Ignore everything in this directory
++*
++# Except this file
++!.gitignore
+diff --git a/external/spdlog-0.14.0/bench/spdlog-async.cpp b/external/spdlog-0.14.0/bench/spdlog-async.cpp
+new file mode 100644
+index 00000000..f788e4df
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/spdlog-async.cpp
+@@ -0,0 +1,62 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include <thread>
++#include <vector>
++#include <atomic>
++#include <iostream>
++#include <chrono>
++#include <cstdlib>
++#include "spdlog/spdlog.h"
++
++using namespace std;
++
++int main(int argc, char* argv[])
++{
++
++ using namespace std::chrono;
++ using clock=steady_clock;
++ namespace spd = spdlog;
++
++ int thread_count = 10;
++ if(argc > 1)
++ thread_count = ::atoi(argv[1]);
++ int howmany = 1000000;
++
++ spd::set_async_mode(1048576);
++ auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-async.txt", false);
++ logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
++
++
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++ auto start = clock::now();
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ while (true)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ logger->info("spdlog message #{}: This is some text for your pleasure", counter);
++ }
++ }));
++ }
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++ duration<float> delta = clock::now() - start;
++ float deltaf = delta.count();
++ auto rate = howmany/deltaf;
++
++ cout << "Total: " << howmany << std::endl;
++ cout << "Threads: " << thread_count << std::endl;
++ std::cout << "Delta = " << deltaf << " seconds" << std::endl;
++ std::cout << "Rate = " << rate << "/sec" << std::endl;
++}
+diff --git a/external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp b/external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp
+new file mode 100644
+index 00000000..e28e7bb8
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp
+@@ -0,0 +1,55 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include <thread>
++#include <vector>
++#include <atomic>
++#include <cstdlib>
++#include "spdlog/spdlog.h"
++
++
++using namespace std;
++
++int main(int argc, char* argv[])
++{
++
++ int thread_count = 10;
++ if(argc > 1)
++ thread_count = std::atoi(argv[1]);
++
++ int howmany = 1000000;
++
++ namespace spd = spdlog;
++
++ auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-mt.txt", false);
++
++ logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
++
++ std::atomic<int > msg_counter {0};
++ std::vector<thread> threads;
++
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ while (true)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ logger->info("spdlog message #{}: This is some text for your pleasure", counter);
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++
++
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/spdlog-bench.cpp b/external/spdlog-0.14.0/bench/spdlog-bench.cpp
+new file mode 100644
+index 00000000..4ac95f6a
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/spdlog-bench.cpp
+@@ -0,0 +1,20 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#include "spdlog/spdlog.h"
++
++
++int main(int, char* [])
++{
++ int howmany = 1000000;
++ namespace spd = spdlog;
++ ///Create a file rotating logger with 5mb size max and 3 rotated files
++ auto logger = spdlog::create<spd::sinks::simple_file_sink_st>("file_logger", "logs/spd-bench-st.txt", false);
++
++ logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
++ for(int i = 0 ; i < howmany; ++i)
++ logger->info("spdlog message #{} : This is some text for your pleasure", i);
++ return 0;
++}
+diff --git a/external/spdlog-0.14.0/bench/spdlog-null-async.cpp b/external/spdlog-0.14.0/bench/spdlog-null-async.cpp
+new file mode 100644
+index 00000000..3874371a
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/spdlog-null-async.cpp
+@@ -0,0 +1,112 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++//
++// bench.cpp : spdlog benchmarks
++//
++#include <atomic>
++#include <cstdlib> // EXIT_FAILURE
++#include <iostream>
++#include <memory>
++#include <string>
++#include <thread>
++#include "spdlog/spdlog.h"
++#include "spdlog/async_logger.h"
++#include "spdlog/sinks/null_sink.h"
++#include "utils.h"
++
++
++using namespace std;
++using namespace std::chrono;
++using namespace spdlog;
++using namespace spdlog::sinks;
++using namespace utils;
++
++
++
++size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
++
++int main(int argc, char* argv[])
++{
++
++ int queue_size = 1048576;
++ int howmany = 1000000;
++ int threads = 10;
++ int iters = 10;
++
++ try
++ {
++
++ if(argc > 1)
++ howmany = atoi(argv[1]);
++ if (argc > 2)
++ threads = atoi(argv[2]);
++ if (argc > 3)
++ queue_size = atoi(argv[3]);
++
++
++ cout << "\n*******************************************************************************\n";
++ cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
++ cout << "*******************************************************************************\n";
++
++ spdlog::set_async_mode(queue_size);
++
++ size_t total_rate = 0;
++
++ for(int i = 0; i < iters; ++i)
++ {
++ //auto as = spdlog::daily_logger_st("as", "logs/daily_async");
++ auto as = spdlog::create<null_sink_st>("async(null-sink)");
++ total_rate+= bench_as(howmany, as, threads);
++ spdlog::drop("async(null-sink)");
++ }
++ std::cout << endl;
++ std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
++
++ }
++ catch (std::exception &ex)
++ {
++ std::cerr << "Error: " << ex.what() << std::endl;
++ perror("Last error");
++ return EXIT_FAILURE;
++ }
++ return EXIT_SUCCESS;
++}
++
++
++
++//return rate/sec
++size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
++{
++ cout << log->name() << "...\t\t" << flush;
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++ auto start = system_clock::now();
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ for(;;)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ log->info("Hello logger: msg number {}", counter);
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++
++ auto delta = system_clock::now() - start;
++ auto delta_d = duration_cast<duration<double>> (delta).count();
++ auto per_sec = size_t(howmany / delta_d);
++ cout << format(per_sec) << "/sec" << endl;
++ return per_sec;
++}
+diff --git a/external/spdlog-0.14.0/bench/utils.h b/external/spdlog-0.14.0/bench/utils.h
+new file mode 100644
+index 00000000..b260f724
+--- /dev/null
++++ b/external/spdlog-0.14.0/bench/utils.h
+@@ -0,0 +1,35 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include <sstream>
++#include <iomanip>
++#include <locale>
++
++namespace utils
++{
++
++template<typename T>
++inline std::string format(const T& value)
++{
++ static std::locale loc("");
++ std::stringstream ss;
++ ss.imbue(loc);
++ ss << value;
++ return ss.str();
++}
++
++template<>
++inline std::string format(const double & value)
++{
++ static std::locale loc("");
++ std::stringstream ss;
++ ss.imbue(loc);
++ ss << std::fixed << std::setprecision(1) << value;
++ return ss.str();
++}
++
++}
+diff --git a/external/spdlog-0.14.0/cmake/Config.cmake.in b/external/spdlog-0.14.0/cmake/Config.cmake.in
+new file mode 100644
+index 00000000..ba0b36f2
+--- /dev/null
++++ b/external/spdlog-0.14.0/cmake/Config.cmake.in
+@@ -0,0 +1,24 @@
++# *************************************************************************/
++# * Copyright (c) 2015 Ruslan Baratov. */
++# * */
++# * Permission is hereby granted, free of charge, to any person obtaining */
++# * a copy of this software and associated documentation files (the */
++# * "Software"), to deal in the Software without restriction, including */
++# * without limitation the rights to use, copy, modify, merge, publish, */
++# * distribute, sublicense, and/or sell copies of the Software, and to */
++# * permit persons to whom the Software is furnished to do so, subject to */
++# * the following conditions: */
++# * */
++# * The above copyright notice and this permission notice shall be */
++# * included in all copies or substantial portions of the Software. */
++# * */
++# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
++# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
++# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
++# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
++# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
++# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
++# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
++# *************************************************************************/
++
++include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
+diff --git a/external/spdlog-0.14.0/cmake/spdlog.pc.in b/external/spdlog-0.14.0/cmake/spdlog.pc.in
+new file mode 100644
+index 00000000..262248a7
+--- /dev/null
++++ b/external/spdlog-0.14.0/cmake/spdlog.pc.in
+@@ -0,0 +1,6 @@
++prefix=@CMAKE_INSTALL_PREFIX@
++includedir=${prefix}/include
++
++Name: @PROJECT_NAME@
++Description: Super fast C++ logging library.
++Version: @PROJECT_VERSION@
+diff --git a/external/spdlog-0.14.0/example/CMakeLists.txt b/external/spdlog-0.14.0/example/CMakeLists.txt
+new file mode 100644
+index 00000000..7859e4d5
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/CMakeLists.txt
+@@ -0,0 +1,49 @@
++# *************************************************************************/
++# * Copyright (c) 2015 Ruslan Baratov. */
++# * */
++# * Permission is hereby granted, free of charge, to any person obtaining */
++# * a copy of this software and associated documentation files (the */
++# * "Software"), to deal in the Software without restriction, including */
++# * without limitation the rights to use, copy, modify, merge, publish, */
++# * distribute, sublicense, and/or sell copies of the Software, and to */
++# * permit persons to whom the Software is furnished to do so, subject to */
++# * the following conditions: */
++# * */
++# * The above copyright notice and this permission notice shall be */
++# * included in all copies or substantial portions of the Software. */
++# * */
++# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
++# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
++# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
++# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
++# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
++# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
++# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
++# *************************************************************************/
++
++cmake_minimum_required(VERSION 3.0)
++project(SpdlogExamples)
++
++if(TARGET spdlog)
++ # Part of the main project
++ add_library(spdlog::spdlog ALIAS spdlog)
++else()
++ # Stand-alone build
++ find_package(spdlog CONFIG REQUIRED)
++endif()
++
++find_package(Threads)
++
++add_executable(example example.cpp)
++target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
++
++add_executable(benchmark bench.cpp)
++target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
++
++add_executable(multisink multisink.cpp)
++target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
++
++enable_testing()
++file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
++add_test(NAME RunExample COMMAND example)
++add_test(NAME RunBenchmark COMMAND benchmark)
+diff --git a/external/spdlog-0.14.0/example/Makefile.clang b/external/spdlog-0.14.0/example/Makefile.clang
+new file mode 100644
+index 00000000..0ed004d0
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/Makefile.clang
+@@ -0,0 +1,32 @@
++CXX ?= clang++
++CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include
++CXX_RELEASE_FLAGS = -O2
++CXX_DEBUG_FLAGS= -g
++
++
++all: example bench
++debug: example-debug bench-debug
++
++example: example.cpp
++ $(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++bench: bench.cpp
++ $(CXX) bench.cpp -o bench-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++
++example-debug: example.cpp
++ $(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
++
++bench-debug: bench.cpp
++ $(CXX) bench.cpp -o bench-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
++
++
++
++clean:
++ rm -f *.o logs/*.txt example-clang example-clang-debug bench-clang bench-clang-debug
++
++
++rebuild: clean all
++rebuild-debug: clean debug
++
++
+diff --git a/external/spdlog-0.14.0/example/Makefile.mingw b/external/spdlog-0.14.0/example/Makefile.mingw
+new file mode 100644
+index 00000000..b9ffd711
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/Makefile.mingw
+@@ -0,0 +1,32 @@
++CXX ?= g++
++CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
++CXX_RELEASE_FLAGS = -O3
++CXX_DEBUG_FLAGS= -g
++
++
++all: example bench
++debug: example-debug bench-debug
++
++example: example.cpp
++ $(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++bench: bench.cpp
++ $(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
++
++
++example-debug: example.cpp
++ $(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
++
++bench-debug: bench.cpp
++ $(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
++
++
++
++clean:
++ rm -f *.o logs/*.txt example example-debug bench bench-debug
++
++
++rebuild: clean all
++rebuild-debug: clean debug
++
++
+diff --git a/external/spdlog-0.14.0/example/bench.cpp b/external/spdlog-0.14.0/example/bench.cpp
+new file mode 100644
+index 00000000..b21c4435
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/bench.cpp
+@@ -0,0 +1,144 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++//
++// bench.cpp : spdlog benchmarks
++//
++#include <atomic>
++#include <cstdlib> // EXIT_FAILURE
++#include <iostream>
++#include <memory>
++#include <string>
++#include <thread>
++#include "spdlog/spdlog.h"
++#include "spdlog/async_logger.h"
++#include "spdlog/sinks/file_sinks.h"
++#include "spdlog/sinks/null_sink.h"
++#include "utils.h"
++
++
++using namespace std;
++using namespace std::chrono;
++using namespace spdlog;
++using namespace spdlog::sinks;
++using namespace utils;
++
++
++void bench(int howmany, std::shared_ptr<spdlog::logger> log);
++void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
++
++int main(int argc, char* argv[])
++{
++
++ int queue_size = 1048576;
++ int howmany = 1000000;
++ int threads = 10;
++ int file_size = 30 * 1024 * 1024;
++ int rotating_files = 5;
++
++ try
++ {
++
++ if(argc > 1)
++ howmany = atoi(argv[1]);
++ if (argc > 2)
++ threads = atoi(argv[2]);
++ if (argc > 3)
++ queue_size = atoi(argv[3]);
++
++
++ cout << "*******************************************************************************\n";
++ cout << "Single thread, " << format(howmany) << " iterations" << endl;
++ cout << "*******************************************************************************\n";
++
++ auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files);
++ bench(howmany, rotating_st);
++ auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
++ bench(howmany, daily_st);
++ bench(howmany, spdlog::create<null_sink_st>("null_st"));
++
++ cout << "\n*******************************************************************************\n";
++ cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
++ cout << "*******************************************************************************\n";
++
++ auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files);
++ bench_mt(howmany, rotating_mt, threads);
++
++
++ auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
++ bench_mt(howmany, daily_mt, threads);
++ bench(howmany, spdlog::create<null_sink_st>("null_mt"));
++
++ cout << "\n*******************************************************************************\n";
++ cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
++ cout << "*******************************************************************************\n";
++
++
++ spdlog::set_async_mode(queue_size);
++
++ for(int i = 0; i < 3; ++i)
++ {
++ auto as = spdlog::daily_logger_st("as", "logs/daily_async");
++ bench_mt(howmany, as, threads);
++ spdlog::drop("as");
++ }
++ }
++ catch (std::exception &ex)
++ {
++ std::cerr << "Error: " << ex.what() << std::endl;
++ perror("Last error");
++ return EXIT_FAILURE;
++ }
++ return EXIT_SUCCESS;
++}
++
++
++void bench(int howmany, std::shared_ptr<spdlog::logger> log)
++{
++ cout << log->name() << "...\t\t" << flush;
++ auto start = system_clock::now();
++ for (auto i = 0; i < howmany; ++i)
++ {
++ log->info("Hello logger: msg number {}", i);
++ }
++
++
++ auto delta = system_clock::now() - start;
++ auto delta_d = duration_cast<duration<double>> (delta).count();
++ cout << format(int(howmany / delta_d)) << "/sec" << endl;
++}
++
++
++void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
++{
++
++ cout << log->name() << "...\t\t" << flush;
++ std::atomic<int > msg_counter {0};
++ vector<thread> threads;
++ auto start = system_clock::now();
++ for (int t = 0; t < thread_count; ++t)
++ {
++ threads.push_back(std::thread([&]()
++ {
++ for(;;)
++ {
++ int counter = ++msg_counter;
++ if (counter > howmany) break;
++ log->info("Hello logger: msg number {}", counter);
++ }
++ }));
++ }
++
++
++ for(auto &t:threads)
++ {
++ t.join();
++ };
++
++
++ auto delta = system_clock::now() - start;
++ auto delta_d = duration_cast<duration<double>> (delta).count();
++ cout << format(int(howmany / delta_d)) << "/sec" << endl;
++}
+diff --git a/external/spdlog-0.14.0/example/example.cpp b/external/spdlog-0.14.0/example/example.cpp
+new file mode 100644
+index 00000000..98231ff5
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/example.cpp
+@@ -0,0 +1,174 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++//
++// spdlog usage example
++//
++//
++
++#define SPDLOG_TRACE_ON
++#define SPDLOG_DEBUG_ON
++
++#include "spdlog/spdlog.h"
++
++#include <iostream>
++#include <memory>
++
++void async_example();
++void syslog_example();
++void android_example();
++void user_defined_example();
++void err_handler_example();
++
++namespace spd = spdlog;
++int main(int, char*[])
++{
++ try
++ {
++ // Console logger with color
++ auto console = spd::stdout_color_mt("console");
++ console->info("Welcome to spdlog!");
++ console->error("Some error message with arg{}..", 1);
++
++ // Conditional logging example
++ console->info_if(true, "Welcome to spdlog conditional logging!");
++
++ // Formatting examples
++ console->warn("Easy padding in numbers like {:08d}", 12);
++ console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
++ console->info("Support for floats {:03.2f}", 1.23456);
++ console->info("Positional args are {1} {0}..", "too", "supported");
++ console->info("{:<30}", "left aligned");
++
++ SPDLOG_DEBUG_IF(console, true, "This is a debug log");
++
++
++ spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
++
++
++ // Create basic file logger (not rotated)
++ auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic");
++ my_logger->info("Some log message");
++
++ // Create a file rotating logger with 5mb size max and 3 rotated files
++ auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
++ for (int i = 0; i < 10; ++i)
++ rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
++
++ // Create a daily logger - a new file is created every day on 2:30am
++ auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
++ // trigger flush if the log severity is error or higher
++ daily_logger->flush_on(spd::level::err);
++ daily_logger->info(123.44);
++
++ // Customize msg format for all messages
++ spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
++ rotating_logger->info("This is another message with custom format");
++
++
++ // Runtime log levels
++ spd::set_level(spd::level::info); //Set global log level to info
++ console->debug("This message shold not be displayed!");
++ console->set_level(spd::level::debug); // Set specific logger's log level
++ console->debug("This message shold be displayed..");
++
++ // Compile time log levels
++ // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
++ SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
++ SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
++ SPDLOG_DEBUG_IF(console, true, "This is a debug log");
++
++
++ // Asynchronous logging is very fast..
++ // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
++ async_example();
++
++ // syslog example. linux/osx only
++ syslog_example();
++
++ // android example. compile with NDK
++ android_example();
++
++ // Log user-defined types example
++ user_defined_example();
++
++ // Change default log error handler
++ err_handler_example();
++
++ // Apply a function on all registered loggers
++ spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
++ {
++ l->info("End of example.");
++ });
++
++ // Release and close all loggers
++ spdlog::drop_all();
++ }
++ // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
++ catch (const spd::spdlog_ex& ex)
++ {
++ std::cout << "Log init failed: " << ex.what() << std::endl;
++ return 1;
++ }
++}
++
++void async_example()
++{
++ size_t q_size = 4096; //queue size must be power of 2
++ spdlog::set_async_mode(q_size);
++ auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log");
++
++ for (int i = 0; i < 100; ++i)
++ async_file->info("Async message #{}", i);
++}
++
++//syslog example (linux/osx/freebsd)
++void syslog_example()
++{
++#ifdef SPDLOG_ENABLE_SYSLOG
++ std::string ident = "spdlog-example";
++ auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
++ syslog_logger->warn("This is warning that will end up in syslog.");
++#endif
++}
++
++// Android example
++void android_example()
++{
++#if defined(__ANDROID__)
++ std::string tag = "spdlog-android";
++ auto android_logger = spd::android_logger("android", tag);
++ android_logger->critical("Use \"adb shell logcat\" to view this message.");
++#endif
++}
++
++// user defined types logging by implementing operator<<
++struct my_type
++{
++ int i;
++ template<typename OStream>
++ friend OStream& operator<<(OStream& os, const my_type &c)
++ {
++ return os << "[my_type i="<<c.i << "]";
++ }
++};
++
++#include "spdlog/fmt/ostr.h" // must be included
++void user_defined_example()
++{
++ spd::get("console")->info("user defined type: {}", my_type { 14 });
++}
++
++//
++//custom error handler
++//
++void err_handler_example()
++{
++ //can be set globaly or per logger(logger->set_error_handler(..))
++ spdlog::set_error_handler([](const std::string& msg)
++ {
++ std::cerr << "my err handler: " << msg << std::endl;
++ });
++ spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
++}
+diff --git a/external/spdlog-0.14.0/example/example.sln b/external/spdlog-0.14.0/example/example.sln
+new file mode 100644
+index 00000000..81f45629
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/example.sln
+@@ -0,0 +1,26 @@
++
++Microsoft Visual Studio Solution File, Format Version 12.00
++# Visual Studio 14
++VisualStudioVersion = 14.0.25420.1
++MinimumVisualStudioVersion = 10.0.40219.1
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
++EndProject
++Global
++ GlobalSection(SolutionConfigurationPlatforms) = preSolution
++ Debug|Win32 = Debug|Win32
++ Debug|x64 = Debug|x64
++ Release|Win32 = Release|Win32
++ Release|x64 = Release|x64
++ EndGlobalSection
++ GlobalSection(ProjectConfigurationPlatforms) = postSolution
++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32
++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32
++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|Win32
++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32
++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|Win32
++ EndGlobalSection
++ GlobalSection(SolutionProperties) = preSolution
++ HideSolutionNode = FALSE
++ EndGlobalSection
++EndGlobal
+diff --git a/external/spdlog-0.14.0/example/example.vcxproj b/external/spdlog-0.14.0/example/example.vcxproj
+new file mode 100644
+index 00000000..63db2b5d
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/example.vcxproj
+@@ -0,0 +1,126 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="example.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\include\spdlog\async_logger.h" />
++ <ClInclude Include="..\include\spdlog\common.h" />
++ <ClInclude Include="..\include\spdlog\details\async_logger_impl.h" />
++ <ClInclude Include="..\include\spdlog\details\async_log_helper.h" />
++ <ClInclude Include="..\include\spdlog\details\file_helper.h" />
++ <ClInclude Include="..\include\spdlog\details\logger_impl.h" />
++ <ClInclude Include="..\include\spdlog\details\log_msg.h" />
++ <ClInclude Include="..\include\spdlog\details\mpmc_bounded_q.h" />
++ <ClInclude Include="..\include\spdlog\details\null_mutex.h" />
++ <ClInclude Include="..\include\spdlog\details\os.h" />
++ <ClInclude Include="..\include\spdlog\details\pattern_formatter_impl.h" />
++ <ClInclude Include="..\include\spdlog\details\registry.h" />
++ <ClInclude Include="..\include\spdlog\details\spdlog_impl.h" />
++ <ClInclude Include="..\include\spdlog\fmt\fmt.h" />
++ <ClInclude Include="..\include\spdlog\fmt\ostr.h" />
++ <ClInclude Include="..\include\spdlog\formatter.h" />
++ <ClInclude Include="..\include\spdlog\logger.h" />
++ <ClInclude Include="..\include\spdlog\sinks\android_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\ansicolor_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\base_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\dist_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\file_sinks.h" />
++ <ClInclude Include="..\include\spdlog\sinks\msvc_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\null_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\ostream_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
++ <ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
++ <ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
++ <ClInclude Include="..\include\spdlog\spdlog.h" />
++ <ClInclude Include="..\include\spdlog\tweakme.h" />
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
++ <Keyword>Win32Proj</Keyword>
++ <RootNamespace>.</RootNamespace>
++ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>Application</ConfigurationType>
++ <UseDebugLibraries>true</UseDebugLibraries>
++ <PlatformToolset>v120</PlatformToolset>
++ <CharacterSet>Unicode</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>Application</ConfigurationType>
++ <UseDebugLibraries>false</UseDebugLibraries>
++ <PlatformToolset>v120</PlatformToolset>
++ <WholeProgramOptimization>true</WholeProgramOptimization>
++ <CharacterSet>Unicode</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <LinkIncremental>true</LinkIncremental>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <LinkIncremental>false</LinkIncremental>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>Disabled</Optimization>
++ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PrecompiledHeaderFile />
++ <PrecompiledHeaderOutputFile />
++ </ClCompile>
++ <Link>
++ <SubSystem>Console</SubSystem>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <Optimization>MaxSpeed</Optimization>
++ <FunctionLevelLinking>true</FunctionLevelLinking>
++ <IntrinsicFunctions>true</IntrinsicFunctions>
++ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PrecompiledHeaderFile />
++ <PrecompiledHeaderOutputFile />
++ </ClCompile>
++ <Link>
++ <SubSystem>Console</SubSystem>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <EnableCOMDATFolding>true</EnableCOMDATFolding>
++ <OptimizeReferences>true</OptimizeReferences>
++ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
++ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
++ </Link>
++ </ItemDefinitionGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/example/jni/Android.mk b/external/spdlog-0.14.0/example/jni/Android.mk
+new file mode 100644
+index 00000000..7accbad3
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/jni/Android.mk
+@@ -0,0 +1,15 @@
++# Setup a project
++LOCAL_PATH := $(call my-dir)
++include $(CLEAR_VARS)
++
++LOCAL_MODULE := example
++LOCAL_SRC_FILES := example.cpp
++LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
++LOCAL_LDFLAGS += -fPIE -pie
++
++# Add exception support and set path for spdlog's headers
++LOCAL_CPPFLAGS += -fexceptions -I../include
++# Use android's log library
++LOCAL_LDFLAGS += -llog
++
++include $(BUILD_EXECUTABLE)
+diff --git a/external/spdlog-0.14.0/example/jni/Application.mk b/external/spdlog-0.14.0/example/jni/Application.mk
+new file mode 100644
+index 00000000..dccd2a5a
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/jni/Application.mk
+@@ -0,0 +1,2 @@
++# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
++APP_STL = gnustl_static
+diff --git a/external/spdlog-0.14.0/example/jni/example.cpp b/external/spdlog-0.14.0/example/jni/example.cpp
+new file mode 120000
+index 00000000..6170abce
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/jni/example.cpp
+@@ -0,0 +1 @@
++../example.cpp
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/example/multisink.cpp b/external/spdlog-0.14.0/example/multisink.cpp
+new file mode 100644
+index 00000000..fe6539b5
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/multisink.cpp
+@@ -0,0 +1,47 @@
++#include "spdlog/spdlog.h"
++
++#include <iostream>
++#include <memory>
++
++namespace spd = spdlog;
++int main(int, char*[])
++{
++ bool enable_debug = true;
++ try
++ {
++ // This other example use a single logger with multiple sinks.
++ // This means that the same log_msg is forwarded to multiple sinks;
++ // Each sink can have it's own log level and a message will be logged.
++ std::vector<spdlog::sink_ptr> sinks;
++ sinks.push_back( std::make_shared<spdlog::sinks::stdout_sink_mt>() );
++ sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") );
++ sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt") );
++
++ spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() );
++ console_multisink.set_level( spdlog::level::warn);
++
++ sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value
++ sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value
++ sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything.
++
++ console_multisink.warn("warn: will print only on console and regular file");
++
++ if( enable_debug )
++ {
++ console_multisink.set_level( spdlog::level::debug); // level of the logger
++ sinks[1]->set_level( spdlog::level::debug); // regular file
++ sinks[2]->set_level( spdlog::level::debug); // debug file
++ }
++ console_multisink.debug("Debug: you should see this on console and both files");
++
++ // Release and close all loggers
++ spdlog::drop_all();
++ }
++ // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
++ catch (const spd::spdlog_ex& ex)
++ {
++ std::cout << "Log init failed: " << ex.what() << std::endl;
++ return 1;
++ }
++}
++
+diff --git a/external/spdlog-0.14.0/example/utils.h b/external/spdlog-0.14.0/example/utils.h
+new file mode 100644
+index 00000000..b260f724
+--- /dev/null
++++ b/external/spdlog-0.14.0/example/utils.h
+@@ -0,0 +1,35 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include <sstream>
++#include <iomanip>
++#include <locale>
++
++namespace utils
++{
++
++template<typename T>
++inline std::string format(const T& value)
++{
++ static std::locale loc("");
++ std::stringstream ss;
++ ss.imbue(loc);
++ ss << value;
++ return ss.str();
++}
++
++template<>
++inline std::string format(const double & value)
++{
++ static std::locale loc("");
++ std::stringstream ss;
++ ss.imbue(loc);
++ ss << std::fixed << std::setprecision(1) << value;
++ return ss.str();
++}
++
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/async_logger.h b/external/spdlog-0.14.0/include/spdlog/async_logger.h
+new file mode 100644
+index 00000000..9d7e08fa
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/async_logger.h
+@@ -0,0 +1,82 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++// Very fast asynchronous logger (millions of logs per second on an average desktop)
++// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
++// Creates a single back thread to pop messages from the queue and log them.
++//
++// Upon each log write the logger:
++// 1. Checks if its log level is enough to log the message
++// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
++// 3. will throw spdlog_ex upon log exceptions
++// Upon destruction, logs all remaining messages in the queue before destructing..
++
++#include "spdlog/common.h"
++#include "spdlog/logger.h"
++
++#include <chrono>
++#include <functional>
++#include <string>
++#include <memory>
++
++namespace spdlog
++{
++
++namespace details
++{
++class async_log_helper;
++}
++
++class async_logger SPDLOG_FINAL :public logger
++{
++public:
++ template<class It>
++ async_logger(const std::string& name,
++ const It& begin,
++ const It& end,
++ size_t queue_size,
++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
++ const std::function<void()>& worker_warmup_cb = nullptr,
++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
++ const std::function<void()>& worker_teardown_cb = nullptr);
++
++ async_logger(const std::string& logger_name,
++ sinks_init_list sinks,
++ size_t queue_size,
++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
++ const std::function<void()>& worker_warmup_cb = nullptr,
++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
++ const std::function<void()>& worker_teardown_cb = nullptr);
++
++ async_logger(const std::string& logger_name,
++ sink_ptr single_sink,
++ size_t queue_size,
++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
++ const std::function<void()>& worker_warmup_cb = nullptr,
++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
++ const std::function<void()>& worker_teardown_cb = nullptr);
++
++ //Wait for the queue to be empty, and flush synchronously
++ //Warning: this can potentially last forever as we wait it to complete
++ void flush() override;
++
++ // Error handler
++ virtual void set_error_handler(log_err_handler) override;
++ virtual log_err_handler error_handler() override;
++
++protected:
++ void _sink_it(details::log_msg& msg) override;
++ void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
++ void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
++
++private:
++ std::unique_ptr<details::async_log_helper> _async_log_helper;
++};
++}
++
++
++#include "spdlog/details/async_logger_impl.h"
+diff --git a/external/spdlog-0.14.0/include/spdlog/common.h b/external/spdlog-0.14.0/include/spdlog/common.h
+new file mode 100644
+index 00000000..7e352fa0
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/common.h
+@@ -0,0 +1,160 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include <string>
++#include <initializer_list>
++#include <chrono>
++#include <memory>
++#include <atomic>
++#include <exception>
++#include<functional>
++
++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
++#include <codecvt>
++#include <locale>
++#endif
++
++#include "spdlog/details/null_mutex.h"
++
++//visual studio upto 2013 does not support noexcept nor constexpr
++#if defined(_MSC_VER) && (_MSC_VER < 1900)
++#define SPDLOG_NOEXCEPT throw()
++#define SPDLOG_CONSTEXPR
++#else
++#define SPDLOG_NOEXCEPT noexcept
++#define SPDLOG_CONSTEXPR constexpr
++#endif
++
++// See tweakme.h
++#if !defined(SPDLOG_FINAL)
++#define SPDLOG_FINAL
++#endif
++
++#if defined(__GNUC__) || defined(__clang__)
++#define SPDLOG_DEPRECATED __attribute__((deprecated))
++#elif defined(_MSC_VER)
++#define SPDLOG_DEPRECATED __declspec(deprecated)
++#else
++#define SPDLOG_DEPRECATED
++#endif
++
++
++#include "spdlog/fmt/fmt.h"
++
++namespace spdlog
++{
++
++class formatter;
++
++namespace sinks
++{
++class sink;
++}
++
++using log_clock = std::chrono::system_clock;
++using sink_ptr = std::shared_ptr < sinks::sink >;
++using sinks_init_list = std::initializer_list < sink_ptr >;
++using formatter_ptr = std::shared_ptr<spdlog::formatter>;
++#if defined(SPDLOG_NO_ATOMIC_LEVELS)
++using level_t = details::null_atomic_int;
++#else
++using level_t = std::atomic<int>;
++#endif
++
++using log_err_handler = std::function<void(const std::string &err_msg)>;
++
++//Log level enum
++namespace level
++{
++typedef enum
++{
++ trace = 0,
++ debug = 1,
++ info = 2,
++ warn = 3,
++ err = 4,
++ critical = 5,
++ off = 6
++} level_enum;
++
++#if !defined(SPDLOG_LEVEL_NAMES)
++#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" };
++#endif
++static const char* level_names[] SPDLOG_LEVEL_NAMES
++
++static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
++
++inline const char* to_str(spdlog::level::level_enum l)
++{
++ return level_names[l];
++}
++
++inline const char* to_short_str(spdlog::level::level_enum l)
++{
++ return short_level_names[l];
++}
++} //level
++
++
++//
++// Async overflow policy - block by default.
++//
++enum class async_overflow_policy
++{
++ block_retry, // Block / yield / sleep until message can be enqueued
++ discard_log_msg // Discard the message it enqueue fails
++};
++
++//
++// Pattern time - specific time getting to use for pattern_formatter.
++// local time by default
++//
++enum class pattern_time_type
++{
++ local, // log localtime
++ utc // log utc
++};
++
++//
++// Log exception
++//
++namespace details
++{
++namespace os
++{
++std::string errno_str(int err_num);
++}
++}
++class spdlog_ex: public std::exception
++{
++public:
++ spdlog_ex(const std::string& msg):_msg(msg)
++ {}
++ spdlog_ex(const std::string& msg, int last_errno)
++ {
++ _msg = msg + ": " + details::os::errno_str(last_errno);
++ }
++ const char* what() const SPDLOG_NOEXCEPT override
++ {
++ return _msg.c_str();
++ }
++private:
++ std::string _msg;
++
++};
++
++//
++// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
++//
++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
++using filename_t = std::wstring;
++#else
++using filename_t = std::string;
++#endif
++
++
++} //spdlog
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h b/external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h
+new file mode 100644
+index 00000000..6145dfa6
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h
+@@ -0,0 +1,399 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++// async log helper :
++// Process logs asynchronously using a back thread.
++//
++// If the internal queue of log messages reaches its max size,
++// then the client call will block until there is more room.
++//
++
++#pragma once
++
++#include "spdlog/common.h"
++#include "spdlog/sinks/sink.h"
++#include "spdlog/details/mpmc_bounded_q.h"
++#include "spdlog/details/log_msg.h"
++#include "spdlog/details/os.h"
++#include "spdlog/formatter.h"
++
++#include <chrono>
++#include <exception>
++#include <functional>
++#include <memory>
++#include <string>
++#include <thread>
++#include <utility>
++#include <vector>
++
++namespace spdlog
++{
++namespace details
++{
++
++class async_log_helper
++{
++ // Async msg to move to/from the queue
++ // Movable only. should never be copied
++ enum class async_msg_type
++ {
++ log,
++ flush,
++ terminate
++ };
++ struct async_msg
++ {
++ std::string logger_name;
++ level::level_enum level;
++ log_clock::time_point time;
++ size_t thread_id;
++ std::string txt;
++ async_msg_type msg_type;
++ size_t msg_id;
++
++ async_msg() = default;
++ ~async_msg() = default;
++
++
++async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
++ logger_name(std::move(other.logger_name)),
++ level(std::move(other.level)),
++ time(std::move(other.time)),
++ thread_id(other.thread_id),
++ txt(std::move(other.txt)),
++ msg_type(std::move(other.msg_type)),
++ msg_id(other.msg_id)
++ {}
++
++ async_msg(async_msg_type m_type):
++ level(level::info),
++ thread_id(0),
++ msg_type(m_type),
++ msg_id(0)
++ {}
++
++ async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
++ {
++ logger_name = std::move(other.logger_name);
++ level = other.level;
++ time = std::move(other.time);
++ thread_id = other.thread_id;
++ txt = std::move(other.txt);
++ msg_type = other.msg_type;
++ msg_id = other.msg_id;
++ return *this;
++ }
++
++ // never copy or assign. should only be moved..
++ async_msg(const async_msg&) = delete;
++ async_msg& operator=(const async_msg& other) = delete;
++
++ // construct from log_msg
++ async_msg(const details::log_msg& m):
++ level(m.level),
++ time(m.time),
++ thread_id(m.thread_id),
++ txt(m.raw.data(), m.raw.size()),
++ msg_type(async_msg_type::log),
++ msg_id(m.msg_id)
++ {
++#ifndef SPDLOG_NO_NAME
++ logger_name = *m.logger_name;
++#endif
++ }
++
++
++ // copy into log_msg
++ void fill_log_msg(log_msg &msg)
++ {
++ msg.logger_name = &logger_name;
++ msg.level = level;
++ msg.time = time;
++ msg.thread_id = thread_id;
++ msg.raw << txt;
++ msg.msg_id = msg_id;
++ }
++ };
++
++public:
++
++ using item_type = async_msg;
++ using q_type = details::mpmc_bounded_queue<item_type>;
++
++ using clock = std::chrono::steady_clock;
++
++
++ async_log_helper(formatter_ptr formatter,
++ const std::vector<sink_ptr>& sinks,
++ size_t queue_size,
++ const log_err_handler err_handler,
++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
++ const std::function<void()>& worker_warmup_cb = nullptr,
++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
++ const std::function<void()>& worker_teardown_cb = nullptr);
++
++ void log(const details::log_msg& msg);
++
++ // stop logging and join the back thread
++ ~async_log_helper();
++
++ void set_formatter(formatter_ptr);
++
++ void flush(bool wait_for_q);
++
++ void set_error_handler(spdlog::log_err_handler err_handler);
++
++private:
++ formatter_ptr _formatter;
++ std::vector<std::shared_ptr<sinks::sink>> _sinks;
++
++ // queue of messages to log
++ q_type _q;
++
++ log_err_handler _err_handler;
++
++ bool _flush_requested;
++
++ bool _terminate_requested;
++
++
++ // overflow policy
++ const async_overflow_policy _overflow_policy;
++
++ // worker thread warmup callback - one can set thread priority, affinity, etc
++ const std::function<void()> _worker_warmup_cb;
++
++ // auto periodic sink flush parameter
++ const std::chrono::milliseconds _flush_interval_ms;
++
++ // worker thread teardown callback
++ const std::function<void()> _worker_teardown_cb;
++
++ // worker thread
++ std::thread _worker_thread;
++
++ void push_msg(async_msg&& new_msg);
++
++ // worker thread main loop
++ void worker_loop();
++
++ // pop next message from the queue and process it. will set the last_pop to the pop time
++ // return false if termination of the queue is required
++ bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
++
++ void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
++
++ // sleep,yield or return immediately using the time passed since last message as a hint
++ static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
++
++ // wait until the queue is empty
++ void wait_empty_q();
++
++};
++}
++}
++
++///////////////////////////////////////////////////////////////////////////////
++// async_sink class implementation
++///////////////////////////////////////////////////////////////////////////////
++inline spdlog::details::async_log_helper::async_log_helper(
++ formatter_ptr formatter,
++ const std::vector<sink_ptr>& sinks,
++ size_t queue_size,
++ log_err_handler err_handler,
++ const async_overflow_policy overflow_policy,
++ const std::function<void()>& worker_warmup_cb,
++ const std::chrono::milliseconds& flush_interval_ms,
++ const std::function<void()>& worker_teardown_cb):
++ _formatter(formatter),
++ _sinks(sinks),
++ _q(queue_size),
++ _err_handler(err_handler),
++ _flush_requested(false),
++ _terminate_requested(false),
++ _overflow_policy(overflow_policy),
++ _worker_warmup_cb(worker_warmup_cb),
++ _flush_interval_ms(flush_interval_ms),
++ _worker_teardown_cb(worker_teardown_cb),
++ _worker_thread(&async_log_helper::worker_loop, this)
++{}
++
++// Send to the worker thread termination message(level=off)
++// and wait for it to finish gracefully
++inline spdlog::details::async_log_helper::~async_log_helper()
++{
++ try
++ {
++ push_msg(async_msg(async_msg_type::terminate));
++ _worker_thread.join();
++ }
++ catch (...) // don't crash in destructor
++ {
++ }
++}
++
++
++//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
++inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
++{
++ push_msg(async_msg(msg));
++}
++
++inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
++{
++ if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
++ {
++ auto last_op_time = details::os::now();
++ auto now = last_op_time;
++ do
++ {
++ now = details::os::now();
++ sleep_or_yield(now, last_op_time);
++ }
++ while (!_q.enqueue(std::move(new_msg)));
++ }
++}
++
++// optionally wait for the queue be empty and request flush from the sinks
++inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
++{
++ push_msg(async_msg(async_msg_type::flush));
++ if (wait_for_q)
++ wait_empty_q(); //return only make after the above flush message was processed
++}
++
++inline void spdlog::details::async_log_helper::worker_loop()
++{
++ if (_worker_warmup_cb) _worker_warmup_cb();
++ auto last_pop = details::os::now();
++ auto last_flush = last_pop;
++ auto active = true;
++ while (active)
++ {
++ try
++ {
++ active = process_next_msg(last_pop, last_flush);
++ }
++ catch (const std::exception &ex)
++ {
++ _err_handler(ex.what());
++ }
++ catch (...)
++ {
++ _err_handler("Unknown exception");
++ }
++ }
++ if (_worker_teardown_cb) _worker_teardown_cb();
++
++
++}
++
++// process next message in the queue
++// return true if this thread should still be active (while no terminate msg was received)
++inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
++{
++ async_msg incoming_async_msg;
++
++ if (_q.dequeue(incoming_async_msg))
++ {
++ last_pop = details::os::now();
++ switch (incoming_async_msg.msg_type)
++ {
++ case async_msg_type::flush:
++ _flush_requested = true;
++ break;
++
++ case async_msg_type::terminate:
++ _flush_requested = true;
++ _terminate_requested = true;
++ break;
++
++ default:
++ log_msg incoming_log_msg;
++ incoming_async_msg.fill_log_msg(incoming_log_msg);
++ _formatter->format(incoming_log_msg);
++ for (auto &s : _sinks)
++ {
++ if (s->should_log(incoming_log_msg.level))
++ {
++ s->log(incoming_log_msg);
++ }
++ }
++ }
++ return true;
++ }
++
++ // Handle empty queue..
++ // This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
++ else
++ {
++ auto now = details::os::now();
++ handle_flush_interval(now, last_flush);
++ sleep_or_yield(now, last_pop);
++ return !_terminate_requested;
++ }
++}
++
++// flush all sinks if _flush_interval_ms has expired
++inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
++{
++ auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
++ if (should_flush)
++ {
++ for (auto &s : _sinks)
++ s->flush();
++ now = last_flush = details::os::now();
++ _flush_requested = false;
++ }
++}
++
++inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
++{
++ _formatter = msg_formatter;
++}
++
++
++// spin, yield or sleep. use the time passed since last message as a hint
++inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
++{
++ using namespace std::this_thread;
++ using std::chrono::milliseconds;
++ using std::chrono::microseconds;
++
++ auto time_since_op = now - last_op_time;
++
++ // spin upto 50 micros
++ if (time_since_op <= microseconds(50))
++ return;
++
++ // yield upto 150 micros
++ if (time_since_op <= microseconds(100))
++ return std::this_thread::yield();
++
++ // sleep for 20 ms upto 200 ms
++ if (time_since_op <= milliseconds(200))
++ return sleep_for(milliseconds(20));
++
++ // sleep for 200 ms
++ return sleep_for(milliseconds(200));
++}
++
++// wait for the queue to be empty
++inline void spdlog::details::async_log_helper::wait_empty_q()
++{
++ auto last_op = details::os::now();
++ while (_q.approx_size() > 0)
++ {
++ sleep_or_yield(details::os::now(), last_op);
++ }
++}
++
++inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
++{
++ _err_handler = err_handler;
++}
++
++
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h b/external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h
+new file mode 100644
+index 00000000..33486c28
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h
+@@ -0,0 +1,105 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++// Async Logger implementation
++// Use an async_sink (queue per logger) to perform the logging in a worker thread
++
++#include "spdlog/details/async_log_helper.h"
++#include "spdlog/async_logger.h"
++
++#include <string>
++#include <functional>
++#include <chrono>
++#include <memory>
++
++template<class It>
++inline spdlog::async_logger::async_logger(const std::string& logger_name,
++ const It& begin,
++ const It& end,
++ size_t queue_size,
++ const async_overflow_policy overflow_policy,
++ const std::function<void()>& worker_warmup_cb,
++ const std::chrono::milliseconds& flush_interval_ms,
++ const std::function<void()>& worker_teardown_cb) :
++ logger(logger_name, begin, end),
++ _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
++{
++}
++
++inline spdlog::async_logger::async_logger(const std::string& logger_name,
++ sinks_init_list sinks_list,
++ size_t queue_size,
++ const async_overflow_policy overflow_policy,
++ const std::function<void()>& worker_warmup_cb,
++ const std::chrono::milliseconds& flush_interval_ms,
++ const std::function<void()>& worker_teardown_cb) :
++ async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
++
++inline spdlog::async_logger::async_logger(const std::string& logger_name,
++ sink_ptr single_sink,
++ size_t queue_size,
++ const async_overflow_policy overflow_policy,
++ const std::function<void()>& worker_warmup_cb,
++ const std::chrono::milliseconds& flush_interval_ms,
++ const std::function<void()>& worker_teardown_cb) :
++ async_logger(logger_name,
++{
++ single_sink
++}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
++
++
++inline void spdlog::async_logger::flush()
++{
++ _async_log_helper->flush(true);
++}
++
++// Error handler
++inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
++{
++ _err_handler = err_handler;
++ _async_log_helper->set_error_handler(err_handler);
++
++}
++inline spdlog::log_err_handler spdlog::async_logger::error_handler()
++{
++ return _err_handler;
++}
++
++
++inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
++{
++ _formatter = msg_formatter;
++ _async_log_helper->set_formatter(_formatter);
++}
++
++inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
++{
++ _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
++ _async_log_helper->set_formatter(_formatter);
++}
++
++
++inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
++{
++ try
++ {
++#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
++ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
++#endif
++ _async_log_helper->log(msg);
++ if (_should_flush_on(msg))
++ _async_log_helper->flush(false); // do async flush
++ }
++ catch (const std::exception &ex)
++ {
++ _err_handler(ex.what());
++ }
++ catch (...)
++ {
++ _err_handler("Unknown exception");
++ }
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/file_helper.h b/external/spdlog-0.14.0/include/spdlog/details/file_helper.h
+new file mode 100644
+index 00000000..d0d730e2
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/file_helper.h
+@@ -0,0 +1,117 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++// Helper class for file sink
++// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
++// Throw spdlog_ex exception on errors
++
++#include "spdlog/details/os.h"
++#include "spdlog/details/log_msg.h"
++
++#include <chrono>
++#include <cstdio>
++#include <string>
++#include <thread>
++#include <cerrno>
++
++namespace spdlog
++{
++namespace details
++{
++
++class file_helper
++{
++
++public:
++ const int open_tries = 5;
++ const int open_interval = 10;
++
++ explicit file_helper() :
++ _fd(nullptr)
++ {}
++
++ file_helper(const file_helper&) = delete;
++ file_helper& operator=(const file_helper&) = delete;
++
++ ~file_helper()
++ {
++ close();
++ }
++
++
++ void open(const filename_t& fname, bool truncate = false)
++ {
++
++ close();
++ auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
++ _filename = fname;
++ for (int tries = 0; tries < open_tries; ++tries)
++ {
++ if (!os::fopen_s(&_fd, fname, mode))
++ return;
++
++ std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
++ }
++
++ throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
++ }
++
++ void reopen(bool truncate)
++ {
++ if (_filename.empty())
++ throw spdlog_ex("Failed re opening file - was not opened before");
++ open(_filename, truncate);
++
++ }
++
++ void flush()
++ {
++ std::fflush(_fd);
++ }
++
++ void close()
++ {
++ if (_fd)
++ {
++ std::fclose(_fd);
++ _fd = nullptr;
++ }
++ }
++
++ void write(const log_msg& msg)
++ {
++
++ size_t msg_size = msg.formatted.size();
++ auto data = msg.formatted.data();
++ if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
++ throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
++ }
++
++ size_t size()
++ {
++ if (!_fd)
++ throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
++ return os::filesize(_fd);
++ }
++
++ const filename_t& filename() const
++ {
++ return _filename;
++ }
++
++ static bool file_exists(const filename_t& name)
++ {
++
++ return os::file_exists(name);
++ }
++
++private:
++ FILE* _fd;
++ filename_t _filename;
++};
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/log_msg.h b/external/spdlog-0.14.0/include/spdlog/details/log_msg.h
+new file mode 100644
+index 00000000..0d7ce4ba
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/log_msg.h
+@@ -0,0 +1,50 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/common.h"
++#include "spdlog/details/os.h"
++
++
++#include <string>
++#include <utility>
++
++namespace spdlog
++{
++namespace details
++{
++struct log_msg
++{
++ log_msg() = default;
++ log_msg(const std::string *loggers_name, level::level_enum lvl) :
++ logger_name(loggers_name),
++ level(lvl),
++ msg_id(0)
++ {
++#ifndef SPDLOG_NO_DATETIME
++ time = os::now();
++#endif
++
++#ifndef SPDLOG_NO_THREAD_ID
++ thread_id = os::thread_id();
++#endif
++ }
++
++ log_msg(const log_msg& other) = delete;
++ log_msg& operator=(log_msg&& other) = delete;
++ log_msg(log_msg&& other) = delete;
++
++
++ const std::string *logger_name;
++ level::level_enum level;
++ log_clock::time_point time;
++ size_t thread_id;
++ fmt::MemoryWriter raw;
++ fmt::MemoryWriter formatted;
++ size_t msg_id;
++};
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/logger_impl.h b/external/spdlog-0.14.0/include/spdlog/details/logger_impl.h
+new file mode 100644
+index 00000000..16222909
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/logger_impl.h
+@@ -0,0 +1,564 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/logger.h"
++#include "spdlog/sinks/stdout_sinks.h"
++
++#include <memory>
++#include <string>
++
++
++// create logger with given name, sinks and the default pattern formatter
++// all other ctors will call this one
++template<class It>
++inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
++ _name(logger_name),
++ _sinks(begin, end),
++ _formatter(std::make_shared<pattern_formatter>("%+")),
++ _level(level::info),
++ _flush_level(level::off),
++ _last_err_time(0),
++ _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
++{
++ _err_handler = [this](const std::string &msg)
++ {
++ this->_default_err_handler(msg);
++ };
++}
++
++// ctor with sinks as init list
++inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
++ logger(logger_name, sinks_list.begin(), sinks_list.end())
++{}
++
++
++// ctor with single sink
++inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
++ logger(logger_name,
++{
++ single_sink
++})
++{}
++
++
++inline spdlog::logger::~logger() = default;
++
++
++inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
++{
++ _set_formatter(msg_formatter);
++}
++
++inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time)
++{
++ _set_pattern(pattern, pattern_time);
++}
++
++
++template <typename... Args>
++inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
++{
++ if (!should_log(lvl)) return;
++
++ try
++ {
++ details::log_msg log_msg(&_name, lvl);
++ log_msg.raw.write(fmt, args...);
++ _sink_it(log_msg);
++ }
++ catch (const std::exception &ex)
++ {
++ _err_handler(ex.what());
++ }
++ catch (...)
++ {
++ _err_handler("Unknown exception");
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
++{
++ if (!should_log(lvl)) return;
++ try
++ {
++ details::log_msg log_msg(&_name, lvl);
++ log_msg.raw << msg;
++ _sink_it(log_msg);
++ }
++ catch (const std::exception &ex)
++ {
++ _err_handler(ex.what());
++ }
++ catch (...)
++ {
++ _err_handler("Unknown exception");
++ }
++
++}
++
++template<typename T>
++inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
++{
++ if (!should_log(lvl)) return;
++ try
++ {
++ details::log_msg log_msg(&_name, lvl);
++ log_msg.raw << msg;
++ _sink_it(log_msg);
++ }
++ catch (const std::exception &ex)
++ {
++ _err_handler(ex.what());
++ }
++ catch (...)
++ {
++ _err_handler("Unknown exception");
++ }
++}
++
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ log(level::trace, fmt, arg1, args...);
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ log(level::debug, fmt, arg1, args...);
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ log(level::info, fmt, arg1, args...);
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ log(level::warn, fmt, arg1, args...);
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ log(level::err, fmt, arg1, args...);
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ log(level::critical, fmt, arg1, args...);
++}
++
++template <typename... Args>
++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg)
++{
++ if (flag)
++ {
++ log(lvl, msg);
++ }
++}
++
++template<typename T>
++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg)
++{
++ if (flag)
++ {
++ log(lvl, msg);
++ }
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::trace, fmt, arg1, args...);
++ }
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::debug, fmt, arg1, args...);
++ }
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::info, fmt, arg1, args...);
++ }
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::warn, fmt, arg1, args...);
++ }
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::err, fmt, arg1, args...);
++ }
++}
++
++template <typename Arg1, typename... Args>
++inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::critical, fmt, arg1, args...);
++ }
++}
++
++
++template<typename T>
++inline void spdlog::logger::trace(const T& msg)
++{
++ log(level::trace, msg);
++}
++
++template<typename T>
++inline void spdlog::logger::debug(const T& msg)
++{
++ log(level::debug, msg);
++}
++
++
++template<typename T>
++inline void spdlog::logger::info(const T& msg)
++{
++ log(level::info, msg);
++}
++
++
++template<typename T>
++inline void spdlog::logger::warn(const T& msg)
++{
++ log(level::warn, msg);
++}
++
++template<typename T>
++inline void spdlog::logger::error(const T& msg)
++{
++ log(level::err, msg);
++}
++
++template<typename T>
++inline void spdlog::logger::critical(const T& msg)
++{
++ log(level::critical, msg);
++}
++
++template<typename T>
++inline void spdlog::logger::trace_if(const bool flag, const T& msg)
++{
++ if (flag)
++ {
++ log(level::trace, msg);
++ }
++}
++
++template<typename T>
++inline void spdlog::logger::debug_if(const bool flag, const T& msg)
++{
++ if (flag)
++ {
++ log(level::debug, msg);
++ }
++}
++
++template<typename T>
++inline void spdlog::logger::info_if(const bool flag, const T& msg)
++{
++ if (flag)
++ {
++ log(level::info, msg);
++ }
++}
++
++template<typename T>
++inline void spdlog::logger::warn_if(const bool flag, const T& msg)
++{
++ if (flag)
++ {
++ log(level::warn, msg);
++ }
++}
++
++template<typename T>
++inline void spdlog::logger::error_if(const bool flag, const T& msg)
++{
++ if (flag)
++ {
++ log(level::err, msg);
++ }
++}
++
++template<typename T>
++inline void spdlog::logger::critical_if(const bool flag, const T& msg)
++{
++ if (flag)
++ {
++ log(level::critical, msg);
++ }
++}
++
++
++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
++#include <codecvt>
++#include <locale>
++
++template <typename... Args>
++inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
++{
++ std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
++
++ log(lvl, conv.to_bytes(msg));
++}
++
++template <typename... Args>
++inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args)
++{
++ fmt::WMemoryWriter wWriter;
++
++ wWriter.write(fmt, args...);
++ log(lvl, wWriter.c_str());
++}
++
++template <typename... Args>
++inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args)
++{
++ log(level::trace, fmt, args...);
++}
++
++template <typename... Args>
++inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args)
++{
++ log(level::debug, fmt, args...);
++}
++
++template <typename... Args>
++inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args)
++{
++ log(level::info, fmt, args...);
++}
++
++
++template <typename... Args>
++inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args)
++{
++ log(level::warn, fmt, args...);
++}
++
++template <typename... Args>
++inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args)
++{
++ log(level::err, fmt, args...);
++}
++
++template <typename... Args>
++inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
++{
++ log(level::critical, fmt, args...);
++}
++
++//
++// conditional logging
++//
++
++template <typename... Args>
++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg)
++{
++ if (flag)
++ {
++ log(lvl, msg);
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(lvl, fmt, args...);
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::trace, fmt, args...);
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::debug, fmt, args...);
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::info, fmt, args...);
++ }
++}
++
++
++template <typename... Args>
++inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::warn, fmt, args...);
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::err, fmt, args...);
++ }
++}
++
++template <typename... Args>
++inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args)
++{
++ if (flag)
++ {
++ log(level::critical, fmt, args...);
++ }
++}
++
++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
++
++
++
++//
++// name and level
++//
++inline const std::string& spdlog::logger::name() const
++{
++ return _name;
++}
++
++inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
++{
++ _level.store(log_level);
++}
++
++inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
++{
++ _err_handler = err_handler;
++}
++
++inline spdlog::log_err_handler spdlog::logger::error_handler()
++{
++ return _err_handler;
++}
++
++
++inline void spdlog::logger::flush_on(level::level_enum log_level)
++{
++ _flush_level.store(log_level);
++}
++
++inline spdlog::level::level_enum spdlog::logger::level() const
++{
++ return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
++}
++
++inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
++{
++ return msg_level >= _level.load(std::memory_order_relaxed);
++}
++
++//
++// protected virtual called at end of each user log call (if enabled) by the line_logger
++//
++inline void spdlog::logger::_sink_it(details::log_msg& msg)
++{
++#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
++ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
++#endif
++ _formatter->format(msg);
++ for (auto &sink : _sinks)
++ {
++ if( sink->should_log( msg.level))
++ {
++ sink->log(msg);
++ }
++ }
++
++ if(_should_flush_on(msg))
++ flush();
++}
++
++inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
++{
++ _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
++}
++inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
++{
++ _formatter = msg_formatter;
++}
++
++inline void spdlog::logger::flush()
++{
++ for (auto& sink : _sinks)
++ sink->flush();
++}
++
++inline void spdlog::logger::_default_err_handler(const std::string &msg)
++{
++ auto now = time(nullptr);
++ if (now - _last_err_time < 60)
++ return;
++ auto tm_time = details::os::localtime(now);
++ char date_buf[100];
++ std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
++ details::log_msg err_msg;
++ err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
++ sinks::stderr_sink_mt::instance()->log(err_msg);
++ _last_err_time = now;
++}
++
++inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
++{
++ const auto flush_level = _flush_level.load(std::memory_order_relaxed);
++ return (msg.level >= flush_level) && (msg.level != level::off);
++}
++
++inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
++{
++ return _sinks;
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h b/external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h
+new file mode 100644
+index 00000000..afd4c881
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h
+@@ -0,0 +1,172 @@
++/*
++A modified version of Bounded MPMC queue by Dmitry Vyukov.
++
++Original code from:
++http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
++
++licensed by Dmitry Vyukov under the terms below:
++
++Simplified BSD license
++
++Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
++Redistribution and use in source and binary forms, with or without modification,
++are permitted provided that the following conditions are met:
++1. Redistributions of source code must retain the above copyright notice, this list of
++conditions and the following disclaimer.
++
++2. Redistributions in binary form must reproduce the above copyright notice, this list
++of conditions and the following disclaimer in the documentation and/or other materials
++provided with the distribution.
++
++THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
++WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
++SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
++OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
++OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
++ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++The views and conclusions contained in the software and documentation are those of the authors and
++should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
++*/
++
++/*
++The code in its current form adds the license below:
++
++Copyright(c) 2015 Gabi Melman.
++Distributed under the MIT License (http://opensource.org/licenses/MIT)
++
++*/
++
++#pragma once
++
++#include "spdlog/common.h"
++
++#include <atomic>
++#include <utility>
++
++namespace spdlog
++{
++namespace details
++{
++
++template<typename T>
++class mpmc_bounded_queue
++{
++public:
++
++ using item_type = T;
++ mpmc_bounded_queue(size_t buffer_size)
++ :max_size_(buffer_size),
++ buffer_(new cell_t [buffer_size]),
++ buffer_mask_(buffer_size - 1)
++ {
++ //queue size must be power of two
++ if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
++ throw spdlog_ex("async logger queue size must be power of two");
++
++ for (size_t i = 0; i != buffer_size; i += 1)
++ buffer_[i].sequence_.store(i, std::memory_order_relaxed);
++ enqueue_pos_.store(0, std::memory_order_relaxed);
++ dequeue_pos_.store(0, std::memory_order_relaxed);
++ }
++
++ ~mpmc_bounded_queue()
++ {
++ delete [] buffer_;
++ }
++
++
++ bool enqueue(T&& data)
++ {
++ cell_t* cell;
++ size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
++ for (;;)
++ {
++ cell = &buffer_[pos & buffer_mask_];
++ size_t seq = cell->sequence_.load(std::memory_order_acquire);
++ intptr_t dif = (intptr_t)seq - (intptr_t)pos;
++ if (dif == 0)
++ {
++ if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
++ break;
++ }
++ else if (dif < 0)
++ {
++ return false;
++ }
++ else
++ {
++ pos = enqueue_pos_.load(std::memory_order_relaxed);
++ }
++ }
++ cell->data_ = std::move(data);
++ cell->sequence_.store(pos + 1, std::memory_order_release);
++ return true;
++ }
++
++ bool dequeue(T& data)
++ {
++ cell_t* cell;
++ size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
++ for (;;)
++ {
++ cell = &buffer_[pos & buffer_mask_];
++ size_t seq =
++ cell->sequence_.load(std::memory_order_acquire);
++ intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
++ if (dif == 0)
++ {
++ if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
++ break;
++ }
++ else if (dif < 0)
++ return false;
++ else
++ pos = dequeue_pos_.load(std::memory_order_relaxed);
++ }
++ data = std::move(cell->data_);
++ cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
++ return true;
++ }
++
++ size_t approx_size()
++ {
++ size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
++ size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
++ if (last_pos <= first_pos)
++ return 0;
++ auto size = last_pos - first_pos;
++ return size < max_size_ ? size : max_size_;
++ }
++
++private:
++ struct cell_t
++ {
++ std::atomic<size_t> sequence_;
++ T data_;
++ };
++
++ size_t const max_size_;
++
++ static size_t const cacheline_size = 64;
++ typedef char cacheline_pad_t [cacheline_size];
++
++ cacheline_pad_t pad0_;
++ cell_t* const buffer_;
++ size_t const buffer_mask_;
++ cacheline_pad_t pad1_;
++ std::atomic<size_t> enqueue_pos_;
++ cacheline_pad_t pad2_;
++ std::atomic<size_t> dequeue_pos_;
++ cacheline_pad_t pad3_;
++
++ mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
++ void operator= (mpmc_bounded_queue const&) = delete;
++};
++
++} // ns details
++} // ns spdlog
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/null_mutex.h b/external/spdlog-0.14.0/include/spdlog/details/null_mutex.h
+new file mode 100644
+index 00000000..67b0aeee
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/null_mutex.h
+@@ -0,0 +1,45 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include <atomic>
++// null, no cost dummy "mutex" and dummy "atomic" int
++
++namespace spdlog
++{
++namespace details
++{
++struct null_mutex
++{
++ void lock() {}
++ void unlock() {}
++ bool try_lock()
++ {
++ return true;
++ }
++};
++
++struct null_atomic_int
++{
++ int value;
++ null_atomic_int() = default;
++
++ null_atomic_int(int val):value(val)
++ {}
++
++ int load(std::memory_order) const
++ {
++ return value;
++ }
++
++ void store(int val)
++ {
++ value = val;
++ }
++};
++
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/os.h b/external/spdlog-0.14.0/include/spdlog/details/os.h
+new file mode 100644
+index 00000000..6241aa5f
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/os.h
+@@ -0,0 +1,469 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++#pragma once
++
++#include "spdlog/common.h"
++
++#include <cstdio>
++#include <ctime>
++#include <functional>
++#include <string>
++#include <chrono>
++#include <thread>
++#include <algorithm>
++#include <cstring>
++#include <cstdlib>
++#include <sys/stat.h>
++#include <sys/types.h>
++
++#ifdef _WIN32
++
++#ifndef NOMINMAX
++#define NOMINMAX //prevent windows redefining min/max
++#endif
++
++#ifndef WIN32_LEAN_AND_MEAN
++#define WIN32_LEAN_AND_MEAN
++#endif
++#include <windows.h>
++#include <process.h> // _get_pid support
++#include <io.h> // _get_osfhandle and _isatty support
++
++#ifdef __MINGW32__
++#include <share.h>
++#endif
++
++#else // unix
++
++#include <unistd.h>
++#include <fcntl.h>
++
++#ifdef __linux__
++#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
++
++#elif __FreeBSD__
++#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
++#endif
++
++#endif //unix
++
++#ifndef __has_feature // Clang - feature checking macros.
++#define __has_feature(x) 0 // Compatibility with non-clang compilers.
++#endif
++
++
++namespace spdlog
++{
++namespace details
++{
++namespace os
++{
++
++inline spdlog::log_clock::time_point now()
++{
++
++#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
++ timespec ts;
++ ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
++ return std::chrono::time_point<log_clock, typename log_clock::duration>(
++ std::chrono::duration_cast<typename log_clock::duration>(
++ std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
++
++
++#else
++ return log_clock::now();
++#endif
++
++}
++inline std::tm localtime(const std::time_t &time_tt)
++{
++
++#ifdef _WIN32
++ std::tm tm;
++ localtime_s(&tm, &time_tt);
++#else
++ std::tm tm;
++ localtime_r(&time_tt, &tm);
++#endif
++ return tm;
++}
++
++inline std::tm localtime()
++{
++ std::time_t now_t = time(nullptr);
++ return localtime(now_t);
++}
++
++
++inline std::tm gmtime(const std::time_t &time_tt)
++{
++
++#ifdef _WIN32
++ std::tm tm;
++ gmtime_s(&tm, &time_tt);
++#else
++ std::tm tm;
++ gmtime_r(&time_tt, &tm);
++#endif
++ return tm;
++}
++
++inline std::tm gmtime()
++{
++ std::time_t now_t = time(nullptr);
++ return gmtime(now_t);
++}
++inline bool operator==(const std::tm& tm1, const std::tm& tm2)
++{
++ return (tm1.tm_sec == tm2.tm_sec &&
++ tm1.tm_min == tm2.tm_min &&
++ tm1.tm_hour == tm2.tm_hour &&
++ tm1.tm_mday == tm2.tm_mday &&
++ tm1.tm_mon == tm2.tm_mon &&
++ tm1.tm_year == tm2.tm_year &&
++ tm1.tm_isdst == tm2.tm_isdst);
++}
++
++inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
++{
++ return !(tm1 == tm2);
++}
++
++// eol definition
++#if !defined (SPDLOG_EOL)
++#ifdef _WIN32
++#define SPDLOG_EOL "\r\n"
++#else
++#define SPDLOG_EOL "\n"
++#endif
++#endif
++
++SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
++SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
++
++inline void prevent_child_fd(FILE *f)
++{
++#ifdef _WIN32
++ auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
++ if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
++ throw spdlog_ex("SetHandleInformation failed", errno);
++#else
++ auto fd = fileno(f);
++ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
++ throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
++#endif
++}
++
++
++//fopen_s on non windows for writing
++inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
++{
++#ifdef _WIN32
++#ifdef SPDLOG_WCHAR_FILENAMES
++ *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
++#else
++ *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
++#endif
++#else //unix
++ *fp = fopen((filename.c_str()), mode.c_str());
++#endif
++
++#ifdef SPDLOG_PREVENT_CHILD_FD
++ if (*fp != nullptr)
++ prevent_child_fd(*fp);
++#endif
++ return *fp == nullptr;
++}
++
++
++inline int remove(const filename_t &filename)
++{
++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
++ return _wremove(filename.c_str());
++#else
++ return std::remove(filename.c_str());
++#endif
++}
++
++inline int rename(const filename_t& filename1, const filename_t& filename2)
++{
++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
++ return _wrename(filename1.c_str(), filename2.c_str());
++#else
++ return std::rename(filename1.c_str(), filename2.c_str());
++#endif
++}
++
++
++//Return if file exists
++inline bool file_exists(const filename_t& filename)
++{
++#ifdef _WIN32
++#ifdef SPDLOG_WCHAR_FILENAMES
++ auto attribs = GetFileAttributesW(filename.c_str());
++#else
++ auto attribs = GetFileAttributesA(filename.c_str());
++#endif
++ return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
++#else //common linux/unix all have the stat system call
++ struct stat buffer;
++ return (stat(filename.c_str(), &buffer) == 0);
++#endif
++}
++
++
++
++
++//Return file size according to open FILE* object
++inline size_t filesize(FILE *f)
++{
++ if (f == nullptr)
++ throw spdlog_ex("Failed getting file size. fd is null");
++#ifdef _WIN32
++ int fd = _fileno(f);
++#if _WIN64 //64 bits
++ struct _stat64 st;
++ if (_fstat64(fd, &st) == 0)
++ return st.st_size;
++
++#else //windows 32 bits
++ long ret = _filelength(fd);
++ if (ret >= 0)
++ return static_cast<size_t>(ret);
++#endif
++
++#else // unix
++ int fd = fileno(f);
++ //64 bits(but not in osx, where fstat64 is deprecated)
++#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
++ struct stat64 st;
++ if (fstat64(fd, &st) == 0)
++ return static_cast<size_t>(st.st_size);
++#else // unix 32 bits or osx
++ struct stat st;
++ if (fstat(fd, &st) == 0)
++ return static_cast<size_t>(st.st_size);
++#endif
++#endif
++ throw spdlog_ex("Failed getting file size from fd", errno);
++}
++
++
++
++
++//Return utc offset in minutes or throw spdlog_ex on failure
++inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
++{
++
++#ifdef _WIN32
++#if _WIN32_WINNT < _WIN32_WINNT_WS08
++ TIME_ZONE_INFORMATION tzinfo;
++ auto rv = GetTimeZoneInformation(&tzinfo);
++#else
++ DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
++ auto rv = GetDynamicTimeZoneInformation(&tzinfo);
++#endif
++ if (rv == TIME_ZONE_ID_INVALID)
++ throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
++
++ int offset = -tzinfo.Bias;
++ if (tm.tm_isdst)
++ offset -= tzinfo.DaylightBias;
++ else
++ offset -= tzinfo.StandardBias;
++ return offset;
++#else
++
++#if defined(sun) || defined(__sun)
++ // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
++ struct helper
++ {
++ static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
++ {
++ int local_year = localtm.tm_year + (1900 - 1);
++ int gmt_year = gmtm.tm_year + (1900 - 1);
++
++ long int days = (
++ // difference in day of year
++ localtm.tm_yday - gmtm.tm_yday
++
++ // + intervening leap days
++ + ((local_year >> 2) - (gmt_year >> 2))
++ - (local_year / 100 - gmt_year / 100)
++ + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
++
++ // + difference in years * 365 */
++ + (long int)(local_year - gmt_year) * 365
++ );
++
++ long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
++ long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
++ long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
++
++ return secs;
++ }
++ };
++
++ long int offset_seconds = helper::calculate_gmt_offset(tm);
++#else
++ long int offset_seconds = tm.tm_gmtoff;
++#endif
++
++ return static_cast<int>(offset_seconds / 60);
++#endif
++}
++
++//Return current thread id as size_t
++//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
++inline size_t _thread_id()
++{
++#ifdef _WIN32
++ return static_cast<size_t>(::GetCurrentThreadId());
++#elif __linux__
++# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
++# define SYS_gettid __NR_gettid
++# endif
++ return static_cast<size_t>(syscall(SYS_gettid));
++#elif __FreeBSD__
++ long tid;
++ thr_self(&tid);
++ return static_cast<size_t>(tid);
++#elif __APPLE__
++ uint64_t tid;
++ pthread_threadid_np(nullptr, &tid);
++ return static_cast<size_t>(tid);
++#else //Default to standard C++11 (other Unix)
++ return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
++#endif
++}
++
++//Return current thread id as size_t (from thread local storage)
++inline size_t thread_id()
++{
++#if !defined(LIZARDFS_HAVE_THREAD_LOCAL) || defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
++ return _thread_id();
++#else
++ static thread_local const size_t tid = _thread_id();
++ return tid;
++#endif
++}
++
++
++
++
++// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
++#define SPDLOG_FILENAME_T(s) L ## s
++inline std::string filename_to_str(const filename_t& filename)
++{
++ std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
++ return c.to_bytes(filename);
++}
++#else
++#define SPDLOG_FILENAME_T(s) s
++inline std::string filename_to_str(const filename_t& filename)
++{
++ return filename;
++}
++#endif
++
++inline std::string errno_to_string(char[256], char* res)
++{
++ return std::string(res);
++}
++
++inline std::string errno_to_string(char buf[256], int res)
++{
++ if (res == 0)
++ {
++ return std::string(buf);
++ }
++ else
++ {
++ return "Unknown error";
++ }
++}
++
++// Return errno string (thread safe)
++inline std::string errno_str(int err_num)
++{
++ char buf[256];
++ SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
++
++#ifdef _WIN32
++ if (strerror_s(buf, buf_size, err_num) == 0)
++ return std::string(buf);
++ else
++ return "Unknown error";
++
++#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
++ ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
++
++ if (strerror_r(err_num, buf, buf_size) == 0)
++ return std::string(buf);
++ else
++ return "Unknown error";
++
++#else // gnu version (might not use the given buf, so its retval pointer must be used)
++ auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
++ return errno_to_string(buf, err); // use overloading to select correct stringify function
++#endif
++}
++
++inline int pid()
++{
++
++#ifdef _WIN32
++ return ::_getpid();
++#else
++ return static_cast<int>(::getpid());
++#endif
++
++}
++
++
++// Detrmine if the terminal supports colors
++// Source: https://github.com/agauniyal/rang/
++inline bool is_color_terminal()
++{
++#ifdef _WIN32
++ return true;
++#else
++ static constexpr const char* Terms[] =
++ {
++ "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
++ "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
++ };
++
++ const char *env_p = std::getenv("TERM");
++ if (env_p == nullptr)
++ {
++ return false;
++ }
++
++ static const bool result = std::any_of(
++ std::begin(Terms), std::end(Terms), [&](const char* term)
++ {
++ return std::strstr(env_p, term) != nullptr;
++ });
++ return result;
++#endif
++}
++
++
++// Detrmine if the terminal attached
++// Source: https://github.com/agauniyal/rang/
++inline bool in_terminal(FILE* file)
++{
++
++#ifdef _WIN32
++ return _isatty(_fileno(file)) ? true : false;
++#else
++ return isatty(fileno(file)) ? true : false;
++#endif
++}
++} //os
++} //details
++} //spdlog
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h b/external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h
+new file mode 100644
+index 00000000..bd2ebc71
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h
+@@ -0,0 +1,665 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/formatter.h"
++#include "spdlog/details/log_msg.h"
++#include "spdlog/details/os.h"
++#include "spdlog/fmt/fmt.h"
++
++#include <chrono>
++#include <ctime>
++#include <memory>
++#include <mutex>
++#include <string>
++#include <thread>
++#include <utility>
++#include <vector>
++#include <array>
++
++namespace spdlog
++{
++namespace details
++{
++class flag_formatter
++{
++public:
++ virtual ~flag_formatter()
++ {}
++ virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
++};
++
++///////////////////////////////////////////////////////////////////////
++// name & level pattern appenders
++///////////////////////////////////////////////////////////////////////
++namespace
++{
++class name_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << *msg.logger_name;
++ }
++};
++}
++
++// log level appender
++class level_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << level::to_str(msg.level);
++ }
++};
++
++// short log level appender
++class short_level_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << level::to_short_str(msg.level);
++ }
++};
++
++///////////////////////////////////////////////////////////////////////
++// Date time pattern appenders
++///////////////////////////////////////////////////////////////////////
++
++static const char* ampm(const tm& t)
++{
++ return t.tm_hour >= 12 ? "PM" : "AM";
++}
++
++static int to12h(const tm& t)
++{
++ return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
++}
++
++//Abbreviated weekday name
++static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
++class a_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << days[tm_time.tm_wday];
++ }
++};
++
++//Full weekday name
++static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
++class A_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << full_days[tm_time.tm_wday];
++ }
++};
++
++//Abbreviated month
++static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
++class b_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << months[tm_time.tm_mon];
++ }
++};
++
++//Full month name
++static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
++class B_formatter:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << full_months[tm_time.tm_mon];
++ }
++};
++
++
++//write 2 ints seperated by sep with padding of 2
++static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
++{
++ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
++ return w;
++}
++
++//write 3 ints seperated by sep with padding of 2
++static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
++{
++ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
++ return w;
++}
++
++
++//Date and time representation (Thu Aug 23 15:35:46 2014)
++class c_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
++ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
++ }
++};
++
++
++// year - 2 digit
++class C_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
++ }
++};
++
++
++
++// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
++class D_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
++ }
++};
++
++
++// year - 4 digit
++class Y_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << tm_time.tm_year + 1900;
++ }
++};
++
++// month 1-12
++class m_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
++ }
++};
++
++// day of month 1-31
++class d_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
++ }
++};
++
++// hours in 24 format 0-23
++class H_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
++ }
++};
++
++// hours in 12 format 1-12
++class I_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
++ }
++};
++
++// minutes 0-59
++class M_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
++ }
++};
++
++// seconds 0-59
++class S_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
++ }
++};
++
++// milliseconds
++class e_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ auto duration = msg.time.time_since_epoch();
++ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
++ msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
++ }
++};
++
++// microseconds
++class f_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ auto duration = msg.time.time_since_epoch();
++ auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
++ msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
++ }
++};
++
++// nanoseconds
++class F_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ auto duration = msg.time.time_since_epoch();
++ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
++ msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
++ }
++};
++
++// AM/PM
++class p_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ msg.formatted << ampm(tm_time);
++ }
++};
++
++
++// 12 hour clock 02:55:02 pm
++class r_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
++ }
++};
++
++// 24-hour HH:MM time, equivalent to %H:%M
++class R_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
++ }
++};
++
++// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
++class T_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
++ }
++};
++
++// ISO 8601 offset from UTC in timezone (+-HH:MM)
++class z_formatter SPDLOG_FINAL:public flag_formatter
++{
++public:
++ const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
++
++ z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
++ {}
++ z_formatter(const z_formatter&) = delete;
++ z_formatter& operator=(const z_formatter&) = delete;
++
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++#ifdef _WIN32
++ int total_minutes = get_cached_offset(msg, tm_time);
++#else
++ // No need to chache under gcc,
++ // it is very fast (already stored in tm.tm_gmtoff)
++ int total_minutes = os::utc_minutes_offset(tm_time);
++#endif
++ bool is_negative = total_minutes < 0;
++ char sign;
++ if (is_negative)
++ {
++ total_minutes = -total_minutes;
++ sign = '-';
++ }
++ else
++ {
++ sign = '+';
++ }
++
++ int h = total_minutes / 60;
++ int m = total_minutes % 60;
++ msg.formatted << sign;
++ pad_n_join(msg.formatted, h, m, ':');
++ }
++private:
++ log_clock::time_point _last_update;
++ int _offset_minutes;
++ std::mutex _mutex;
++
++ int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
++ {
++ using namespace std::chrono;
++ std::lock_guard<std::mutex> l(_mutex);
++ if (msg.time - _last_update >= cache_refresh)
++ {
++ _offset_minutes = os::utc_minutes_offset(tm_time);
++ _last_update = msg.time;
++ }
++ return _offset_minutes;
++ }
++};
++
++
++
++// Thread id
++class t_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << msg.thread_id;
++ }
++};
++
++// Current pid
++class pid_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << details::os::pid();
++ }
++};
++
++
++class v_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
++ }
++};
++
++class ch_formatter SPDLOG_FINAL:public flag_formatter
++{
++public:
++ explicit ch_formatter(char ch): _ch(ch)
++ {}
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << _ch;
++ }
++private:
++ char _ch;
++};
++
++
++//aggregate user chars to display as is
++class aggregate_formatter SPDLOG_FINAL:public flag_formatter
++{
++public:
++ aggregate_formatter()
++ {}
++ void add_ch(char ch)
++ {
++ _str += ch;
++ }
++ void format(details::log_msg& msg, const std::tm&) override
++ {
++ msg.formatted << _str;
++ }
++private:
++ std::string _str;
++};
++
++// Full info formatter
++// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
++class full_formatter SPDLOG_FINAL:public flag_formatter
++{
++ void format(details::log_msg& msg, const std::tm& tm_time) override
++ {
++#ifndef SPDLOG_NO_DATETIME
++ auto duration = msg.time.time_since_epoch();
++ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
++
++ /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
++ msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
++ tm_time.tm_year + 1900,
++ tm_time.tm_mon + 1,
++ tm_time.tm_mday,
++ tm_time.tm_hour,
++ tm_time.tm_min,
++ tm_time.tm_sec,
++ static_cast<int>(millis),
++ msg.logger_name,
++ level::to_str(msg.level),
++ msg.raw.str());*/
++
++
++ // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
++ msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
++ << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
++ << fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
++ << fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
++ << fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
++ << fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
++ << fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
++
++ //no datetime needed
++#else
++ (void)tm_time;
++#endif
++
++#ifndef SPDLOG_NO_NAME
++ msg.formatted << '[' << *msg.logger_name << "] ";
++#endif
++
++ msg.formatted << '[' << level::to_str(msg.level) << "] ";
++ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
++ }
++};
++
++
++
++}
++}
++///////////////////////////////////////////////////////////////////////////////
++// pattern_formatter inline impl
++///////////////////////////////////////////////////////////////////////////////
++inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
++ : _pattern_time(pattern_time)
++{
++ compile_pattern(pattern);
++}
++
++inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
++{
++ auto end = pattern.end();
++ std::unique_ptr<details::aggregate_formatter> user_chars;
++ for (auto it = pattern.begin(); it != end; ++it)
++ {
++ if (*it == '%')
++ {
++ if (user_chars) //append user chars found so far
++ _formatters.push_back(std::move(user_chars));
++
++ if (++it != end)
++ handle_flag(*it);
++ else
++ break;
++ }
++ else // chars not following the % sign should be displayed as is
++ {
++ if (!user_chars)
++ user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
++ user_chars->add_ch(*it);
++ }
++ }
++ if (user_chars) //append raw chars found so far
++ {
++ _formatters.push_back(std::move(user_chars));
++ }
++
++}
++inline void spdlog::pattern_formatter::handle_flag(char flag)
++{
++ switch (flag)
++ {
++ // logger name
++ case 'n':
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
++ break;
++
++ case 'l':
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
++ break;
++
++ case 'L':
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
++ break;
++
++ case('t'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
++ break;
++
++ case('v'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
++ break;
++
++ case('a'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
++ break;
++
++ case('A'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
++ break;
++
++ case('b'):
++ case('h'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
++ break;
++
++ case('B'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
++ break;
++ case('c'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
++ break;
++
++ case('C'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
++ break;
++
++ case('Y'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
++ break;
++
++ case('D'):
++ case('x'):
++
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
++ break;
++
++ case('m'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
++ break;
++
++ case('d'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
++ break;
++
++ case('H'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
++ break;
++
++ case('I'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
++ break;
++
++ case('M'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
++ break;
++
++ case('S'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
++ break;
++
++ case('e'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
++ break;
++
++ case('f'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
++ break;
++ case('F'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
++ break;
++
++ case('p'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
++ break;
++
++ case('r'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
++ break;
++
++ case('R'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
++ break;
++
++ case('T'):
++ case('X'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
++ break;
++
++ case('z'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
++ break;
++
++ case ('+'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
++ break;
++
++ case ('P'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
++ break;
++
++#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
++ case ('i'):
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
++ break;
++#endif
++
++ default: //Unknown flag appears as is
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
++ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
++ break;
++ }
++}
++
++inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
++{
++ if (_pattern_time == pattern_time_type::local)
++ return details::os::localtime(log_clock::to_time_t(msg.time));
++ else
++ return details::os::gmtime(log_clock::to_time_t(msg.time));
++}
++
++inline void spdlog::pattern_formatter::format(details::log_msg& msg)
++{
++
++#ifndef SPDLOG_NO_DATETIME
++ auto tm_time = get_time(msg);
++#else
++ std::tm tm_time;
++#endif
++ for (auto &f : _formatters)
++ {
++ f->format(msg, tm_time);
++ }
++ //write eol
++ msg.formatted.write(details::os::eol, details::os::eol_size);
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/registry.h b/external/spdlog-0.14.0/include/spdlog/details/registry.h
+new file mode 100644
+index 00000000..b518990a
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/registry.h
+@@ -0,0 +1,214 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++// Loggers registy of unique name->logger pointer
++// An attempt to create a logger with an already existing name will be ignored
++// If user requests a non existing logger, nullptr will be returned
++// This class is thread safe
++
++#include "spdlog/details/null_mutex.h"
++#include "spdlog/logger.h"
++#include "spdlog/async_logger.h"
++#include "spdlog/common.h"
++
++#include <chrono>
++#include <functional>
++#include <memory>
++#include <mutex>
++#include <string>
++#include <unordered_map>
++
++namespace spdlog
++{
++namespace details
++{
++template <class Mutex> class registry_t
++{
++public:
++
++ void register_logger(std::shared_ptr<logger> logger)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ auto logger_name = logger->name();
++ throw_if_exists(logger_name);
++ _loggers[logger_name] = logger;
++ }
++
++
++ std::shared_ptr<logger> get(const std::string& logger_name)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ auto found = _loggers.find(logger_name);
++ return found == _loggers.end() ? nullptr : found->second;
++ }
++
++ template<class It>
++ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ throw_if_exists(logger_name);
++ std::shared_ptr<logger> new_logger;
++ if (_async_mode)
++ new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
++ else
++ new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
++
++ if (_formatter)
++ new_logger->set_formatter(_formatter);
++
++ if (_err_handler)
++ new_logger->set_error_handler(_err_handler);
++
++ new_logger->set_level(_level);
++
++
++ //Add to registry
++ _loggers[logger_name] = new_logger;
++ return new_logger;
++ }
++
++ template<class It>
++ std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, const It& sinks_begin, const It& sinks_end)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ throw_if_exists(logger_name);
++ auto new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
++
++ if (_formatter)
++ new_logger->set_formatter(_formatter);
++
++ if (_err_handler)
++ new_logger->set_error_handler(_err_handler);
++
++ new_logger->set_level(_level);
++
++ //Add to registry
++ _loggers[logger_name] = new_logger;
++ return new_logger;
++ }
++
++ void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ for (auto &l : _loggers)
++ fun(l.second);
++ }
++
++ void drop(const std::string& logger_name)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _loggers.erase(logger_name);
++ }
++
++ void drop_all()
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _loggers.clear();
++ }
++ std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
++ {
++ return create(logger_name, sinks.begin(), sinks.end());
++ }
++
++ std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
++ {
++ return create(logger_name, { sink });
++ }
++
++ std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
++ {
++ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
++ }
++
++ std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
++ {
++ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
++ }
++
++ void formatter(formatter_ptr f)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _formatter = f;
++ for (auto& l : _loggers)
++ l.second->set_formatter(_formatter);
++ }
++
++ void set_pattern(const std::string& pattern)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _formatter = std::make_shared<pattern_formatter>(pattern);
++ for (auto& l : _loggers)
++ l.second->set_formatter(_formatter);
++ }
++
++ void set_level(level::level_enum log_level)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ for (auto& l : _loggers)
++ l.second->set_level(log_level);
++ _level = log_level;
++ }
++
++ void set_error_handler(log_err_handler handler)
++ {
++ for (auto& l : _loggers)
++ l.second->set_error_handler(handler);
++ _err_handler = handler;
++ }
++
++ void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _async_mode = true;
++ _async_q_size = q_size;
++ _overflow_policy = overflow_policy;
++ _worker_warmup_cb = worker_warmup_cb;
++ _flush_interval_ms = flush_interval_ms;
++ _worker_teardown_cb = worker_teardown_cb;
++ }
++
++ void set_sync_mode()
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _async_mode = false;
++ }
++
++ static registry_t<Mutex>& instance()
++ {
++ static registry_t<Mutex> s_instance;
++ return s_instance;
++ }
++
++private:
++ registry_t<Mutex>() {}
++ registry_t<Mutex>(const registry_t<Mutex>&) = delete;
++ registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
++
++ void throw_if_exists(const std::string &logger_name)
++ {
++ if (_loggers.find(logger_name) != _loggers.end())
++ throw spdlog_ex("logger with name '" + logger_name + "' already exists");
++ }
++ Mutex _mutex;
++ std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
++ formatter_ptr _formatter;
++ level::level_enum _level = level::info;
++ log_err_handler _err_handler;
++ bool _async_mode = false;
++ size_t _async_q_size = 0;
++ async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
++ std::function<void()> _worker_warmup_cb = nullptr;
++ std::chrono::milliseconds _flush_interval_ms;
++ std::function<void()> _worker_teardown_cb = nullptr;
++};
++#ifdef SPDLOG_NO_REGISTRY_MUTEX
++typedef registry_t<spdlog::details::null_mutex> registry;
++#else
++typedef registry_t<std::mutex> registry;
++#endif
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h b/external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h
+new file mode 100644
+index 00000000..7fe9ab40
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h
+@@ -0,0 +1,263 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++//
++// Global registry functions
++//
++#include "spdlog/spdlog.h"
++#include "spdlog/details/registry.h"
++#include "spdlog/sinks/file_sinks.h"
++#include "spdlog/sinks/stdout_sinks.h"
++#ifdef SPDLOG_ENABLE_SYSLOG
++#include "spdlog/sinks/syslog_sink.h"
++#endif
++
++#ifdef _WIN32
++#include "spdlog/sinks/wincolor_sink.h"
++#else
++#include "spdlog/sinks/ansicolor_sink.h"
++#endif
++
++
++#ifdef __ANDROID__
++#include "spdlog/sinks/android_sink.h"
++#endif
++
++#include <chrono>
++#include <functional>
++#include <memory>
++#include <string>
++
++inline void spdlog::register_logger(std::shared_ptr<logger> logger)
++{
++ return details::registry::instance().register_logger(logger);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
++{
++ return details::registry::instance().get(name);
++}
++
++inline void spdlog::drop(const std::string &name)
++{
++ details::registry::instance().drop(name);
++}
++
++// Create multi/single threaded simple file logger
++inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
++{
++ return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
++{
++ return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
++}
++
++// Create multi/single threaded rotating file logger
++inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
++{
++ return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
++{
++ return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
++}
++
++// Create file logger which creates new file at midnight):
++inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
++{
++ return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
++{
++ return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
++}
++
++
++//
++// stdout/stderr loggers
++//
++inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
++{
++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
++{
++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
++{
++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
++{
++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
++}
++
++//
++// stdout/stderr color loggers
++//
++#ifdef _WIN32
++inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++
++inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++#else //ansi terminal colors
++
++inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++
++inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
++{
++ auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
++ return spdlog::details::registry::instance().create(logger_name, sink);
++}
++#endif
++
++#ifdef SPDLOG_ENABLE_SYSLOG
++// Create syslog logger
++inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
++{
++ return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
++}
++#endif
++
++#ifdef __ANDROID__
++inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
++{
++ return create<spdlog::sinks::android_sink>(logger_name, tag);
++}
++#endif
++
++// Create and register a logger a single sink
++inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
++{
++ return details::registry::instance().create(logger_name, sink);
++}
++
++//Create logger with multiple sinks
++
++inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
++{
++ return details::registry::instance().create(logger_name, sinks);
++}
++
++
++template <typename Sink, typename... Args>
++inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
++{
++ sink_ptr sink = std::make_shared<Sink>(args...);
++ return details::registry::instance().create(logger_name, { sink });
++}
++
++
++template<class It>
++inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
++{
++ return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
++}
++
++// Create and register an async logger with a single sink
++inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
++{
++ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
++}
++
++// Create and register an async logger with multiple sinks
++inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb )
++{
++ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
++}
++
++template<class It>
++inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
++{
++ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
++}
++
++inline void spdlog::set_formatter(spdlog::formatter_ptr f)
++{
++ details::registry::instance().formatter(f);
++}
++
++inline void spdlog::set_pattern(const std::string& format_string)
++{
++ return details::registry::instance().set_pattern(format_string);
++}
++
++inline void spdlog::set_level(level::level_enum log_level)
++{
++ return details::registry::instance().set_level(log_level);
++}
++
++inline void spdlog::set_error_handler(log_err_handler handler)
++{
++ return details::registry::instance().set_error_handler(handler);
++}
++
++
++inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
++{
++ details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
++}
++
++inline void spdlog::set_sync_mode()
++{
++ details::registry::instance().set_sync_mode();
++}
++
++inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
++{
++ details::registry::instance().apply_all(fun);
++}
++
++inline void spdlog::drop_all()
++{
++ details::registry::instance().drop_all();
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc
+new file mode 100644
+index 00000000..09d2ea9f
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc
+@@ -0,0 +1,535 @@
++/*
++ Formatting library for C++
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions are met:
++
++ 1. Redistributions of source code must retain the above copyright notice, this
++ list of conditions and the following disclaimer.
++ 2. Redistributions in binary form must reproduce the above copyright notice,
++ this list of conditions and the following disclaimer in the documentation
++ and/or other materials provided with the distribution.
++
++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
++ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "format.h"
++
++#include <string.h>
++
++#include <cctype>
++#include <cerrno>
++#include <climits>
++#include <cmath>
++#include <cstdarg>
++#include <cstddef> // for std::ptrdiff_t
++
++#if defined(_WIN32) && defined(__MINGW32__)
++# include <cstring>
++#endif
++
++#if FMT_USE_WINDOWS_H
++# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
++# define WIN32_LEAN_AND_MEAN
++# endif
++# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
++# include <windows.h>
++# else
++# define NOMINMAX
++# include <windows.h>
++# undef NOMINMAX
++# endif
++#endif
++
++#if FMT_EXCEPTIONS
++# define FMT_TRY try
++# define FMT_CATCH(x) catch (x)
++#else
++# define FMT_TRY if (true)
++# define FMT_CATCH(x) if (false)
++#endif
++
++#ifdef _MSC_VER
++# pragma warning(push)
++# pragma warning(disable: 4127) // conditional expression is constant
++# pragma warning(disable: 4702) // unreachable code
++// Disable deprecation warning for strerror. The latter is not called but
++// MSVC fails to detect it.
++# pragma warning(disable: 4996)
++#endif
++
++// Dummy implementations of strerror_r and strerror_s called if corresponding
++// system functions are not available.
++static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
++ return fmt::internal::Null<>();
++}
++static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
++ return fmt::internal::Null<>();
++}
++
++namespace fmt {
++
++FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
++FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
++FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
++
++namespace {
++
++#ifndef _MSC_VER
++# define FMT_SNPRINTF snprintf
++#else // _MSC_VER
++inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
++ va_list args;
++ va_start(args, format);
++ int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
++ va_end(args);
++ return result;
++}
++# define FMT_SNPRINTF fmt_snprintf
++#endif // _MSC_VER
++
++#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
++# define FMT_SWPRINTF snwprintf
++#else
++# define FMT_SWPRINTF swprintf
++#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
++
++const char RESET_COLOR[] = "\x1b[0m";
++
++typedef void (*FormatFunc)(Writer &, int, StringRef);
++
++// Portable thread-safe version of strerror.
++// Sets buffer to point to a string describing the error code.
++// This can be either a pointer to a string stored in buffer,
++// or a pointer to some static immutable string.
++// Returns one of the following values:
++// 0 - success
++// ERANGE - buffer is not large enough to store the error message
++// other - failure
++// Buffer should be at least of size 1.
++int safe_strerror(
++ int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
++ FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
++
++ class StrError {
++ private:
++ int error_code_;
++ char *&buffer_;
++ std::size_t buffer_size_;
++
++ // A noop assignment operator to avoid bogus warnings.
++ void operator=(const StrError &) {}
++
++ // Handle the result of XSI-compliant version of strerror_r.
++ int handle(int result) {
++ // glibc versions before 2.13 return result in errno.
++ return result == -1 ? errno : result;
++ }
++
++ // Handle the result of GNU-specific version of strerror_r.
++ int handle(char *message) {
++ // If the buffer is full then the message is probably truncated.
++ if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
++ return ERANGE;
++ buffer_ = message;
++ return 0;
++ }
++
++ // Handle the case when strerror_r is not available.
++ int handle(internal::Null<>) {
++ return fallback(strerror_s(buffer_, buffer_size_, error_code_));
++ }
++
++ // Fallback to strerror_s when strerror_r is not available.
++ int fallback(int result) {
++ // If the buffer is full then the message is probably truncated.
++ return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
++ ERANGE : result;
++ }
++
++ // Fallback to strerror if strerror_r and strerror_s are not available.
++ int fallback(internal::Null<>) {
++ errno = 0;
++ buffer_ = strerror(error_code_);
++ return errno;
++ }
++
++ public:
++ StrError(int err_code, char *&buf, std::size_t buf_size)
++ : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
++
++ int run() {
++ // Suppress a warning about unused strerror_r.
++ strerror_r(0, FMT_NULL, "");
++ return handle(strerror_r(error_code_, buffer_, buffer_size_));
++ }
++ };
++ return StrError(error_code, buffer, buffer_size).run();
++}
++
++void format_error_code(Writer &out, int error_code,
++ StringRef message) FMT_NOEXCEPT {
++ // Report error code making sure that the output fits into
++ // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
++ // bad_alloc.
++ out.clear();
++ static const char SEP[] = ": ";
++ static const char ERROR_STR[] = "error ";
++ // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
++ std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
++ typedef internal::IntTraits<int>::MainType MainType;
++ MainType abs_value = static_cast<MainType>(error_code);
++ if (internal::is_negative(error_code)) {
++ abs_value = 0 - abs_value;
++ ++error_code_size;
++ }
++ error_code_size += internal::count_digits(abs_value);
++ if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
++ out << message << SEP;
++ out << ERROR_STR << error_code;
++ assert(out.size() <= internal::INLINE_BUFFER_SIZE);
++}
++
++void report_error(FormatFunc func, int error_code,
++ StringRef message) FMT_NOEXCEPT {
++ MemoryWriter full_message;
++ func(full_message, error_code, message);
++ // Use Writer::data instead of Writer::c_str to avoid potential memory
++ // allocation.
++ std::fwrite(full_message.data(), full_message.size(), 1, stderr);
++ std::fputc('\n', stderr);
++}
++} // namespace
++
++FMT_FUNC void SystemError::init(
++ int err_code, CStringRef format_str, ArgList args) {
++ error_code_ = err_code;
++ MemoryWriter w;
++ format_system_error(w, err_code, format(format_str, args));
++ std::runtime_error &base = *this;
++ base = std::runtime_error(w.str());
++}
++
++template <typename T>
++int internal::CharTraits<char>::format_float(
++ char *buffer, std::size_t size, const char *format,
++ unsigned width, int precision, T value) {
++ if (width == 0) {
++ return precision < 0 ?
++ FMT_SNPRINTF(buffer, size, format, value) :
++ FMT_SNPRINTF(buffer, size, format, precision, value);
++ }
++ return precision < 0 ?
++ FMT_SNPRINTF(buffer, size, format, width, value) :
++ FMT_SNPRINTF(buffer, size, format, width, precision, value);
++}
++
++template <typename T>
++int internal::CharTraits<wchar_t>::format_float(
++ wchar_t *buffer, std::size_t size, const wchar_t *format,
++ unsigned width, int precision, T value) {
++ if (width == 0) {
++ return precision < 0 ?
++ FMT_SWPRINTF(buffer, size, format, value) :
++ FMT_SWPRINTF(buffer, size, format, precision, value);
++ }
++ return precision < 0 ?
++ FMT_SWPRINTF(buffer, size, format, width, value) :
++ FMT_SWPRINTF(buffer, size, format, width, precision, value);
++}
++
++template <typename T>
++const char internal::BasicData<T>::DIGITS[] =
++ "0001020304050607080910111213141516171819"
++ "2021222324252627282930313233343536373839"
++ "4041424344454647484950515253545556575859"
++ "6061626364656667686970717273747576777879"
++ "8081828384858687888990919293949596979899";
++
++#define FMT_POWERS_OF_10(factor) \
++ factor * 10, \
++ factor * 100, \
++ factor * 1000, \
++ factor * 10000, \
++ factor * 100000, \
++ factor * 1000000, \
++ factor * 10000000, \
++ factor * 100000000, \
++ factor * 1000000000
++
++template <typename T>
++const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
++ 0, FMT_POWERS_OF_10(1)
++};
++
++template <typename T>
++const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
++ 0,
++ FMT_POWERS_OF_10(1),
++ FMT_POWERS_OF_10(ULongLong(1000000000)),
++ // Multiply several constants instead of using a single long long constant
++ // to avoid warnings about C++98 not supporting long long.
++ ULongLong(1000000000) * ULongLong(1000000000) * 10
++};
++
++FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
++ (void)type;
++ if (std::isprint(static_cast<unsigned char>(code))) {
++ FMT_THROW(FormatError(
++ format("unknown format code '{}' for {}", code, type)));
++ }
++ FMT_THROW(FormatError(
++ format("unknown format code '\\x{:02x}' for {}",
++ static_cast<unsigned>(code), type)));
++}
++
++#if FMT_USE_WINDOWS_H
++
++FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
++ static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
++ if (s.size() > INT_MAX)
++ FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
++ int s_size = static_cast<int>(s.size());
++ int length = MultiByteToWideChar(
++ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
++ if (length == 0)
++ FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
++ buffer_.resize(length + 1);
++ length = MultiByteToWideChar(
++ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
++ if (length == 0)
++ FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
++ buffer_[length] = 0;
++}
++
++FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
++ if (int error_code = convert(s)) {
++ FMT_THROW(WindowsError(error_code,
++ "cannot convert string from UTF-16 to UTF-8"));
++ }
++}
++
++FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
++ if (s.size() > INT_MAX)
++ return ERROR_INVALID_PARAMETER;
++ int s_size = static_cast<int>(s.size());
++ int length = WideCharToMultiByte(
++ CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
++ if (length == 0)
++ return GetLastError();
++ buffer_.resize(length + 1);
++ length = WideCharToMultiByte(
++ CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
++ if (length == 0)
++ return GetLastError();
++ buffer_[length] = 0;
++ return 0;
++}
++
++FMT_FUNC void WindowsError::init(
++ int err_code, CStringRef format_str, ArgList args) {
++ error_code_ = err_code;
++ MemoryWriter w;
++ internal::format_windows_error(w, err_code, format(format_str, args));
++ std::runtime_error &base = *this;
++ base = std::runtime_error(w.str());
++}
++
++FMT_FUNC void internal::format_windows_error(
++ Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
++ FMT_TRY {
++ MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
++ buffer.resize(INLINE_BUFFER_SIZE);
++ for (;;) {
++ wchar_t *system_message = &buffer[0];
++ int result = FormatMessageW(
++ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
++ FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
++ system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
++ if (result != 0) {
++ UTF16ToUTF8 utf8_message;
++ if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
++ out << message << ": " << utf8_message;
++ return;
++ }
++ break;
++ }
++ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
++ break; // Can't get error message, report error code instead.
++ buffer.resize(buffer.size() * 2);
++ }
++ } FMT_CATCH(...) {}
++ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
++}
++
++#endif // FMT_USE_WINDOWS_H
++
++FMT_FUNC void format_system_error(
++ Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
++ FMT_TRY {
++ internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
++ buffer.resize(internal::INLINE_BUFFER_SIZE);
++ for (;;) {
++ char *system_message = &buffer[0];
++ int result = safe_strerror(error_code, system_message, buffer.size());
++ if (result == 0) {
++ out << message << ": " << system_message;
++ return;
++ }
++ if (result != ERANGE)
++ break; // Can't get error message, report error code instead.
++ buffer.resize(buffer.size() * 2);
++ }
++ } FMT_CATCH(...) {}
++ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
++}
++
++template <typename Char>
++void internal::ArgMap<Char>::init(const ArgList &args) {
++ if (!map_.empty())
++ return;
++ typedef internal::NamedArg<Char> NamedArg;
++ const NamedArg *named_arg = FMT_NULL;
++ bool use_values =
++ args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
++ if (use_values) {
++ for (unsigned i = 0;/*nothing*/; ++i) {
++ internal::Arg::Type arg_type = args.type(i);
++ switch (arg_type) {
++ case internal::Arg::NONE:
++ return;
++ case internal::Arg::NAMED_ARG:
++ named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
++ map_.push_back(Pair(named_arg->name, *named_arg));
++ break;
++ default:
++ /*nothing*/;
++ }
++ }
++ return;
++ }
++ for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
++ internal::Arg::Type arg_type = args.type(i);
++ if (arg_type == internal::Arg::NAMED_ARG) {
++ named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
++ map_.push_back(Pair(named_arg->name, *named_arg));
++ }
++ }
++ for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
++ switch (args.args_[i].type) {
++ case internal::Arg::NONE:
++ return;
++ case internal::Arg::NAMED_ARG:
++ named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
++ map_.push_back(Pair(named_arg->name, *named_arg));
++ break;
++ default:
++ /*nothing*/;
++ }
++ }
++}
++
++template <typename Char>
++void internal::FixedBuffer<Char>::grow(std::size_t) {
++ FMT_THROW(std::runtime_error("buffer overflow"));
++}
++
++FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
++ unsigned arg_index, const char *&error) {
++ internal::Arg arg = args_[arg_index];
++ switch (arg.type) {
++ case internal::Arg::NONE:
++ error = "argument index out of range";
++ break;
++ case internal::Arg::NAMED_ARG:
++ arg = *static_cast<const internal::Arg*>(arg.pointer);
++ break;
++ default:
++ /*nothing*/;
++ }
++ return arg;
++}
++
++FMT_FUNC void report_system_error(
++ int error_code, fmt::StringRef message) FMT_NOEXCEPT {
++ // 'fmt::' is for bcc32.
++ report_error(format_system_error, error_code, message);
++}
++
++#if FMT_USE_WINDOWS_H
++FMT_FUNC void report_windows_error(
++ int error_code, fmt::StringRef message) FMT_NOEXCEPT {
++ // 'fmt::' is for bcc32.
++ report_error(internal::format_windows_error, error_code, message);
++}
++#endif
++
++FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
++ MemoryWriter w;
++ w.write(format_str, args);
++ std::fwrite(w.data(), 1, w.size(), f);
++}
++
++FMT_FUNC void print(CStringRef format_str, ArgList args) {
++ print(stdout, format_str, args);
++}
++
++FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
++ char escape[] = "\x1b[30m";
++ escape[3] = static_cast<char>('0' + c);
++ std::fputs(escape, stdout);
++ print(format, args);
++ std::fputs(RESET_COLOR, stdout);
++}
++
++#ifndef FMT_HEADER_ONLY
++
++template struct internal::BasicData<void>;
++
++// Explicit instantiations for char.
++
++template void internal::FixedBuffer<char>::grow(std::size_t);
++
++template void internal::ArgMap<char>::init(const ArgList &args);
++
++template FMT_API int internal::CharTraits<char>::format_float(
++ char *buffer, std::size_t size, const char *format,
++ unsigned width, int precision, double value);
++
++template FMT_API int internal::CharTraits<char>::format_float(
++ char *buffer, std::size_t size, const char *format,
++ unsigned width, int precision, long double value);
++
++// Explicit instantiations for wchar_t.
++
++template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
++
++template void internal::ArgMap<wchar_t>::init(const ArgList &args);
++
++template FMT_API int internal::CharTraits<wchar_t>::format_float(
++ wchar_t *buffer, std::size_t size, const wchar_t *format,
++ unsigned width, int precision, double value);
++
++template FMT_API int internal::CharTraits<wchar_t>::format_float(
++ wchar_t *buffer, std::size_t size, const wchar_t *format,
++ unsigned width, int precision, long double value);
++
++#endif // FMT_HEADER_ONLY
++
++} // namespace fmt
++
++#ifdef _MSC_VER
++# pragma warning(pop)
++#endif
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h
+new file mode 100644
+index 00000000..6ee9d2a2
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h
+@@ -0,0 +1,4012 @@
++/*
++ Formatting library for C++
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions are met:
++
++ 1. Redistributions of source code must retain the above copyright notice, this
++ list of conditions and the following disclaimer.
++ 2. Redistributions in binary form must reproduce the above copyright notice,
++ this list of conditions and the following disclaimer in the documentation
++ and/or other materials provided with the distribution.
++
++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
++ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifndef FMT_FORMAT_H_
++#define FMT_FORMAT_H_
++
++#include <cassert>
++#include <clocale>
++#include <cmath>
++#include <cstdio>
++#include <cstring>
++#include <limits>
++#include <memory>
++#include <stdexcept>
++#include <string>
++#include <vector>
++#include <utility> // for std::pair
++
++// The fmt library version in the form major * 10000 + minor * 100 + patch.
++#define FMT_VERSION 40000
++
++#ifdef _SECURE_SCL
++# define FMT_SECURE_SCL _SECURE_SCL
++#else
++# define FMT_SECURE_SCL 0
++#endif
++
++#if FMT_SECURE_SCL
++# include <iterator>
++#endif
++
++#ifdef _MSC_VER
++# define FMT_MSC_VER _MSC_VER
++#else
++# define FMT_MSC_VER 0
++#endif
++
++#if FMT_MSC_VER && FMT_MSC_VER <= 1500
++typedef unsigned __int32 uint32_t;
++typedef unsigned __int64 uint64_t;
++typedef __int64 intmax_t;
++#else
++#include <stdint.h>
++#endif
++
++#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
++# ifdef FMT_EXPORT
++# define FMT_API __declspec(dllexport)
++# elif defined(FMT_SHARED)
++# define FMT_API __declspec(dllimport)
++# endif
++#endif
++#ifndef FMT_API
++# define FMT_API
++#endif
++
++#ifdef __GNUC__
++# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
++# define FMT_GCC_EXTENSION __extension__
++# if FMT_GCC_VERSION >= 406
++# pragma GCC diagnostic push
++// Disable the warning about "long long" which is sometimes reported even
++// when using __extension__.
++# pragma GCC diagnostic ignored "-Wlong-long"
++// Disable the warning about declaration shadowing because it affects too
++// many valid cases.
++# pragma GCC diagnostic ignored "-Wshadow"
++// Disable the warning about implicit conversions that may change the sign of
++// an integer; silencing it otherwise would require many explicit casts.
++# pragma GCC diagnostic ignored "-Wsign-conversion"
++# endif
++# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__
++# define FMT_HAS_GXX_CXX11 1
++# endif
++#else
++# define FMT_GCC_EXTENSION
++#endif
++
++#if defined(__INTEL_COMPILER)
++# define FMT_ICC_VERSION __INTEL_COMPILER
++#elif defined(__ICL)
++# define FMT_ICC_VERSION __ICL
++#endif
++
++#if defined(__clang__) && !defined(FMT_ICC_VERSION)
++# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
++# pragma clang diagnostic push
++# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
++# pragma clang diagnostic ignored "-Wpadded"
++#endif
++
++#ifdef __GNUC_LIBSTD__
++# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__)
++#endif
++
++#ifdef __has_feature
++# define FMT_HAS_FEATURE(x) __has_feature(x)
++#else
++# define FMT_HAS_FEATURE(x) 0
++#endif
++
++#ifdef __has_builtin
++# define FMT_HAS_BUILTIN(x) __has_builtin(x)
++#else
++# define FMT_HAS_BUILTIN(x) 0
++#endif
++
++#ifdef __has_cpp_attribute
++# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
++#else
++# define FMT_HAS_CPP_ATTRIBUTE(x) 0
++#endif
++
++#ifndef FMT_USE_VARIADIC_TEMPLATES
++// Variadic templates are available in GCC since version 4.4
++// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++
++// since version 2013.
++# define FMT_USE_VARIADIC_TEMPLATES \
++ (FMT_HAS_FEATURE(cxx_variadic_templates) || \
++ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800)
++#endif
++
++#ifndef FMT_USE_RVALUE_REFERENCES
++// Don't use rvalue references when compiling with clang and an old libstdc++
++// as the latter doesn't provide std::move.
++# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402
++# define FMT_USE_RVALUE_REFERENCES 0
++# else
++# define FMT_USE_RVALUE_REFERENCES \
++ (FMT_HAS_FEATURE(cxx_rvalue_references) || \
++ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600)
++# endif
++#endif
++
++// Check if exceptions are disabled.
++#if defined(__GNUC__) && !defined(__EXCEPTIONS)
++# define FMT_EXCEPTIONS 0
++#endif
++#if FMT_MSC_VER && !_HAS_EXCEPTIONS
++# define FMT_EXCEPTIONS 0
++#endif
++#ifndef FMT_EXCEPTIONS
++# define FMT_EXCEPTIONS 1
++#endif
++
++#ifndef FMT_THROW
++# if FMT_EXCEPTIONS
++# define FMT_THROW(x) throw x
++# else
++# define FMT_THROW(x) assert(false)
++# endif
++#endif
++
++// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
++#ifndef FMT_USE_NOEXCEPT
++# define FMT_USE_NOEXCEPT 0
++#endif
++
++#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
++ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
++ FMT_MSC_VER >= 1900
++# define FMT_DETECTED_NOEXCEPT noexcept
++#else
++# define FMT_DETECTED_NOEXCEPT throw()
++#endif
++
++#ifndef FMT_NOEXCEPT
++# if FMT_EXCEPTIONS
++# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
++# else
++# define FMT_NOEXCEPT
++# endif
++#endif
++
++// This is needed because GCC still uses throw() in its headers when exceptions
++// are disabled.
++#if FMT_GCC_VERSION
++# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT
++#else
++# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT
++#endif
++
++#ifndef FMT_OVERRIDE
++# if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || \
++ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
++ FMT_MSC_VER >= 1900
++# define FMT_OVERRIDE override
++# else
++# define FMT_OVERRIDE
++# endif
++#endif
++
++#ifndef FMT_NULL
++# if FMT_HAS_FEATURE(cxx_nullptr) || \
++ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
++ FMT_MSC_VER >= 1600
++# define FMT_NULL nullptr
++# else
++# define FMT_NULL NULL
++# endif
++#endif
++
++// A macro to disallow the copy constructor and operator= functions
++// This should be used in the private: declarations for a class
++#ifndef FMT_USE_DELETED_FUNCTIONS
++# define FMT_USE_DELETED_FUNCTIONS 0
++#endif
++
++#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \
++ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800
++# define FMT_DELETED_OR_UNDEFINED = delete
++# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \
++ TypeName(const TypeName&) = delete; \
++ TypeName& operator=(const TypeName&) = delete
++#else
++# define FMT_DELETED_OR_UNDEFINED
++# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \
++ TypeName(const TypeName&); \
++ TypeName& operator=(const TypeName&)
++#endif
++
++#ifndef FMT_USE_DEFAULTED_FUNCTIONS
++# define FMT_USE_DEFAULTED_FUNCTIONS 0
++#endif
++
++#ifndef FMT_DEFAULTED_COPY_CTOR
++# if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || \
++ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800
++# define FMT_DEFAULTED_COPY_CTOR(TypeName) \
++ TypeName(const TypeName&) = default;
++# else
++# define FMT_DEFAULTED_COPY_CTOR(TypeName)
++# endif
++#endif
++
++#ifndef FMT_USE_USER_DEFINED_LITERALS
++// All compilers which support UDLs also support variadic templates. This
++// makes the fmt::literals implementation easier. However, an explicit check
++// for variadic templates is added here just in case.
++// For Intel's compiler both it and the system gcc/msc must support UDLs.
++# define FMT_USE_USER_DEFINED_LITERALS \
++ FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \
++ (FMT_HAS_FEATURE(cxx_user_literals) || \
++ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \
++ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500)
++#endif
++
++#ifndef FMT_USE_EXTERN_TEMPLATES
++# define FMT_USE_EXTERN_TEMPLATES \
++ (FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11))
++#endif
++
++#ifdef FMT_HEADER_ONLY
++// If header only do not use extern templates.
++# undef FMT_USE_EXTERN_TEMPLATES
++# define FMT_USE_EXTERN_TEMPLATES 0
++#endif
++
++#ifndef FMT_ASSERT
++# define FMT_ASSERT(condition, message) assert((condition) && message)
++#endif
++
++// __builtin_clz is broken in clang with Microsoft CodeGen:
++// https://github.com/fmtlib/fmt/issues/519
++#ifndef _MSC_VER
++# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz)
++# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
++# endif
++
++# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll)
++# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
++# endif
++#endif
++
++// Some compilers masquerade as both MSVC and GCC-likes or
++// otherwise support __builtin_clz and __builtin_clzll, so
++// only define FMT_BUILTIN_CLZ using the MSVC intrinsics
++// if the clz and clzll builtins are not available.
++#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED)
++# include <intrin.h> // _BitScanReverse, _BitScanReverse64
++
++namespace fmt {
++namespace internal {
++# pragma intrinsic(_BitScanReverse)
++inline uint32_t clz(uint32_t x) {
++ unsigned long r = 0;
++ _BitScanReverse(&r, x);
++
++ assert(x != 0);
++ // Static analysis complains about using uninitialized data
++ // "r", but the only way that can happen is if "x" is 0,
++ // which the callers guarantee to not happen.
++# pragma warning(suppress: 6102)
++ return 31 - r;
++}
++# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
++
++# ifdef _WIN64
++# pragma intrinsic(_BitScanReverse64)
++# endif
++
++inline uint32_t clzll(uint64_t x) {
++ unsigned long r = 0;
++# ifdef _WIN64
++ _BitScanReverse64(&r, x);
++# else
++ // Scan the high 32 bits.
++ if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
++ return 63 - (r + 32);
++
++ // Scan the low 32 bits.
++ _BitScanReverse(&r, static_cast<uint32_t>(x));
++# endif
++
++ assert(x != 0);
++ // Static analysis complains about using uninitialized data
++ // "r", but the only way that can happen is if "x" is 0,
++ // which the callers guarantee to not happen.
++# pragma warning(suppress: 6102)
++ return 63 - r;
++}
++# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n)
++}
++}
++#endif
++
++namespace fmt {
++namespace internal {
++struct DummyInt {
++ int data[2];
++ operator int() const { return 0; }
++};
++typedef std::numeric_limits<fmt::internal::DummyInt> FPUtil;
++
++// Dummy implementations of system functions such as signbit and ecvt called
++// if the latter are not available.
++inline DummyInt signbit(...) { return DummyInt(); }
++inline DummyInt _ecvt_s(...) { return DummyInt(); }
++inline DummyInt isinf(...) { return DummyInt(); }
++inline DummyInt _finite(...) { return DummyInt(); }
++inline DummyInt isnan(...) { return DummyInt(); }
++inline DummyInt _isnan(...) { return DummyInt(); }
++
++// A helper function to suppress bogus "conditional expression is constant"
++// warnings.
++template <typename T>
++inline T const_check(T value) { return value; }
++}
++} // namespace fmt
++
++namespace std {
++// Standard permits specialization of std::numeric_limits. This specialization
++// is used to resolve ambiguity between isinf and std::isinf in glibc:
++// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891
++// and the same for isnan and signbit.
++template <>
++class numeric_limits<fmt::internal::DummyInt> :
++ public std::numeric_limits<int> {
++ public:
++ // Portable version of isinf.
++ template <typename T>
++ static bool isinfinity(T x) {
++ using namespace fmt::internal;
++ // The resolution "priority" is:
++ // isinf macro > std::isinf > ::isinf > fmt::internal::isinf
++ if (const_check(sizeof(isinf(x)) == sizeof(bool) ||
++ sizeof(isinf(x)) == sizeof(int))) {
++ return isinf(x) != 0;
++ }
++ return !_finite(static_cast<double>(x));
++ }
++
++ // Portable version of isnan.
++ template <typename T>
++ static bool isnotanumber(T x) {
++ using namespace fmt::internal;
++ if (const_check(sizeof(isnan(x)) == sizeof(bool) ||
++ sizeof(isnan(x)) == sizeof(int))) {
++ return isnan(x) != 0;
++ }
++ return _isnan(static_cast<double>(x)) != 0;
++ }
++
++ // Portable version of signbit.
++ static bool isnegative(double x) {
++ using namespace fmt::internal;
++ if (const_check(sizeof(signbit(x)) == sizeof(bool) ||
++ sizeof(signbit(x)) == sizeof(int))) {
++ return signbit(x) != 0;
++ }
++ if (x < 0) return true;
++ if (!isnotanumber(x)) return false;
++ int dec = 0, sign = 0;
++ char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail.
++ _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign);
++ return sign != 0;
++ }
++};
++} // namespace std
++
++namespace fmt {
++
++// Fix the warning about long long on older versions of GCC
++// that don't support the diagnostic pragma.
++FMT_GCC_EXTENSION typedef long long LongLong;
++FMT_GCC_EXTENSION typedef unsigned long long ULongLong;
++
++#if FMT_USE_RVALUE_REFERENCES
++using std::move;
++#endif
++
++template <typename Char>
++class BasicWriter;
++
++typedef BasicWriter<char> Writer;
++typedef BasicWriter<wchar_t> WWriter;
++
++template <typename Char>
++class ArgFormatter;
++
++struct FormatSpec;
++
++template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
++class BasicPrintfArgFormatter;
++
++template <typename CharType,
++ typename ArgFormatter = fmt::ArgFormatter<CharType> >
++class BasicFormatter;
++
++/**
++ \rst
++ A string reference. It can be constructed from a C string or
++ ``std::basic_string``.
++
++ You can use one of the following typedefs for common character types:
++
++ +------------+-------------------------+
++ | Type | Definition |
++ +============+=========================+
++ | StringRef | BasicStringRef<char> |
++ +------------+-------------------------+
++ | WStringRef | BasicStringRef<wchar_t> |
++ +------------+-------------------------+
++
++ This class is most useful as a parameter type to allow passing
++ different types of strings to a function, for example::
++
++ template <typename... Args>
++ std::string format(StringRef format_str, const Args & ... args);
++
++ format("{}", 42);
++ format(std::string("{}"), 42);
++ \endrst
++ */
++template <typename Char>
++class BasicStringRef {
++ private:
++ const Char *data_;
++ std::size_t size_;
++
++ public:
++ /** Constructs a string reference object from a C string and a size. */
++ BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {}
++
++ /**
++ \rst
++ Constructs a string reference object from a C string computing
++ the size with ``std::char_traits<Char>::length``.
++ \endrst
++ */
++ BasicStringRef(const Char *s)
++ : data_(s), size_(std::char_traits<Char>::length(s)) {}
++
++ /**
++ \rst
++ Constructs a string reference from a ``std::basic_string`` object.
++ \endrst
++ */
++ template <typename Allocator>
++ BasicStringRef(
++ const std::basic_string<Char, std::char_traits<Char>, Allocator> &s)
++ : data_(s.c_str()), size_(s.size()) {}
++
++ /**
++ \rst
++ Converts a string reference to an ``std::string`` object.
++ \endrst
++ */
++ std::basic_string<Char> to_string() const {
++ return std::basic_string<Char>(data_, size_);
++ }
++
++ /** Returns a pointer to the string data. */
++ const Char *data() const { return data_; }
++
++ /** Returns the string size. */
++ std::size_t size() const { return size_; }
++
++ // Lexicographically compare this string reference to other.
++ int compare(BasicStringRef other) const {
++ std::size_t size = size_ < other.size_ ? size_ : other.size_;
++ int result = std::char_traits<Char>::compare(data_, other.data_, size);
++ if (result == 0)
++ result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
++ return result;
++ }
++
++ friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) {
++ return lhs.compare(rhs) == 0;
++ }
++ friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) {
++ return lhs.compare(rhs) != 0;
++ }
++ friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) {
++ return lhs.compare(rhs) < 0;
++ }
++ friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) {
++ return lhs.compare(rhs) <= 0;
++ }
++ friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) {
++ return lhs.compare(rhs) > 0;
++ }
++ friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) {
++ return lhs.compare(rhs) >= 0;
++ }
++};
++
++typedef BasicStringRef<char> StringRef;
++typedef BasicStringRef<wchar_t> WStringRef;
++
++/**
++ \rst
++ A reference to a null terminated string. It can be constructed from a C
++ string or ``std::basic_string``.
++
++ You can use one of the following typedefs for common character types:
++
++ +-------------+--------------------------+
++ | Type | Definition |
++ +=============+==========================+
++ | CStringRef | BasicCStringRef<char> |
++ +-------------+--------------------------+
++ | WCStringRef | BasicCStringRef<wchar_t> |
++ +-------------+--------------------------+
++
++ This class is most useful as a parameter type to allow passing
++ different types of strings to a function, for example::
++
++ template <typename... Args>
++ std::string format(CStringRef format_str, const Args & ... args);
++
++ format("{}", 42);
++ format(std::string("{}"), 42);
++ \endrst
++ */
++template <typename Char>
++class BasicCStringRef {
++ private:
++ const Char *data_;
++
++ public:
++ /** Constructs a string reference object from a C string. */
++ BasicCStringRef(const Char *s) : data_(s) {}
++
++ /**
++ \rst
++ Constructs a string reference from a ``std::basic_string`` object.
++ \endrst
++ */
++ template <typename Allocator>
++ BasicCStringRef(
++ const std::basic_string<Char, std::char_traits<Char>, Allocator> &s)
++ : data_(s.c_str()) {}
++
++ /** Returns the pointer to a C string. */
++ const Char *c_str() const { return data_; }
++};
++
++typedef BasicCStringRef<char> CStringRef;
++typedef BasicCStringRef<wchar_t> WCStringRef;
++
++/** A formatting error such as invalid format string. */
++class FormatError : public std::runtime_error {
++ public:
++ explicit FormatError(CStringRef message)
++ : std::runtime_error(message.c_str()) {}
++ FormatError(const FormatError &ferr) : std::runtime_error(ferr) {}
++ FMT_API ~FormatError() FMT_DTOR_NOEXCEPT;
++};
++
++namespace internal {
++
++// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T.
++template <typename T>
++struct MakeUnsigned { typedef T Type; };
++
++#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \
++ template <> \
++ struct MakeUnsigned<T> { typedef U Type; }
++
++FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char);
++FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char);
++FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short);
++FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned);
++FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long);
++FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong);
++
++// Casts nonnegative integer to unsigned.
++template <typename Int>
++inline typename MakeUnsigned<Int>::Type to_unsigned(Int value) {
++ FMT_ASSERT(value >= 0, "negative value");
++ return static_cast<typename MakeUnsigned<Int>::Type>(value);
++}
++
++// The number of characters to store in the MemoryBuffer object itself
++// to avoid dynamic memory allocation.
++enum { INLINE_BUFFER_SIZE = 500 };
++
++#if FMT_SECURE_SCL
++// Use checked iterator to avoid warnings on MSVC.
++template <typename T>
++inline stdext::checked_array_iterator<T*> make_ptr(T *ptr, std::size_t size) {
++ return stdext::checked_array_iterator<T*>(ptr, size);
++}
++#else
++template <typename T>
++inline T *make_ptr(T *ptr, std::size_t) { return ptr; }
++#endif
++} // namespace internal
++
++/**
++ \rst
++ A buffer supporting a subset of ``std::vector``'s operations.
++ \endrst
++ */
++template <typename T>
++class Buffer {
++ private:
++ FMT_DISALLOW_COPY_AND_ASSIGN(Buffer);
++
++ protected:
++ T *ptr_;
++ std::size_t size_;
++ std::size_t capacity_;
++
++ Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0)
++ : ptr_(ptr), size_(0), capacity_(capacity) {}
++
++ /**
++ \rst
++ Increases the buffer capacity to hold at least *size* elements updating
++ ``ptr_`` and ``capacity_``.
++ \endrst
++ */
++ virtual void grow(std::size_t size) = 0;
++
++ public:
++ virtual ~Buffer() {}
++
++ /** Returns the size of this buffer. */
++ std::size_t size() const { return size_; }
++
++ /** Returns the capacity of this buffer. */
++ std::size_t capacity() const { return capacity_; }
++
++ /**
++ Resizes the buffer. If T is a POD type new elements may not be initialized.
++ */
++ void resize(std::size_t new_size) {
++ if (new_size > capacity_)
++ grow(new_size);
++ size_ = new_size;
++ }
++
++ /**
++ \rst
++ Reserves space to store at least *capacity* elements.
++ \endrst
++ */
++ void reserve(std::size_t capacity) {
++ if (capacity > capacity_)
++ grow(capacity);
++ }
++
++ void clear() FMT_NOEXCEPT { size_ = 0; }
++
++ void push_back(const T &value) {
++ if (size_ == capacity_)
++ grow(size_ + 1);
++ ptr_[size_++] = value;
++ }
++
++ /** Appends data to the end of the buffer. */
++ template <typename U>
++ void append(const U *begin, const U *end);
++
++ T &operator[](std::size_t index) { return ptr_[index]; }
++ const T &operator[](std::size_t index) const { return ptr_[index]; }
++};
++
++template <typename T>
++template <typename U>
++void Buffer<T>::append(const U *begin, const U *end) {
++ FMT_ASSERT(end >= begin, "negative value");
++ std::size_t new_size = size_ + (end - begin);
++ if (new_size > capacity_)
++ grow(new_size);
++ std::uninitialized_copy(begin, end,
++ internal::make_ptr(ptr_, capacity_) + size_);
++ size_ = new_size;
++}
++
++namespace internal {
++
++// A memory buffer for trivially copyable/constructible types with the first
++// SIZE elements stored in the object itself.
++template <typename T, std::size_t SIZE, typename Allocator = std::allocator<T> >
++class MemoryBuffer : private Allocator, public Buffer<T> {
++ private:
++ T data_[SIZE];
++
++ // Deallocate memory allocated by the buffer.
++ void deallocate() {
++ if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_);
++ }
++
++ protected:
++ void grow(std::size_t size) FMT_OVERRIDE;
++
++ public:
++ explicit MemoryBuffer(const Allocator &alloc = Allocator())
++ : Allocator(alloc), Buffer<T>(data_, SIZE) {}
++ ~MemoryBuffer() { deallocate(); }
++
++#if FMT_USE_RVALUE_REFERENCES
++ private:
++ // Move data from other to this buffer.
++ void move(MemoryBuffer &other) {
++ Allocator &this_alloc = *this, &other_alloc = other;
++ this_alloc = std::move(other_alloc);
++ this->size_ = other.size_;
++ this->capacity_ = other.capacity_;
++ if (other.ptr_ == other.data_) {
++ this->ptr_ = data_;
++ std::uninitialized_copy(other.data_, other.data_ + this->size_,
++ make_ptr(data_, this->capacity_));
++ } else {
++ this->ptr_ = other.ptr_;
++ // Set pointer to the inline array so that delete is not called
++ // when deallocating.
++ other.ptr_ = other.data_;
++ }
++ }
++
++ public:
++ MemoryBuffer(MemoryBuffer &&other) {
++ move(other);
++ }
++
++ MemoryBuffer &operator=(MemoryBuffer &&other) {
++ assert(this != &other);
++ deallocate();
++ move(other);
++ return *this;
++ }
++#endif
++
++ // Returns a copy of the allocator associated with this buffer.
++ Allocator get_allocator() const { return *this; }
++};
++
++template <typename T, std::size_t SIZE, typename Allocator>
++void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size) {
++ std::size_t new_capacity = this->capacity_ + this->capacity_ / 2;
++ if (size > new_capacity)
++ new_capacity = size;
++ T *new_ptr = this->allocate(new_capacity, FMT_NULL);
++ // The following code doesn't throw, so the raw pointer above doesn't leak.
++ std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_,
++ make_ptr(new_ptr, new_capacity));
++ std::size_t old_capacity = this->capacity_;
++ T *old_ptr = this->ptr_;
++ this->capacity_ = new_capacity;
++ this->ptr_ = new_ptr;
++ // deallocate may throw (at least in principle), but it doesn't matter since
++ // the buffer already uses the new storage and will deallocate it in case
++ // of exception.
++ if (old_ptr != data_)
++ Allocator::deallocate(old_ptr, old_capacity);
++}
++
++// A fixed-size buffer.
++template <typename Char>
++class FixedBuffer : public fmt::Buffer<Char> {
++ public:
++ FixedBuffer(Char *array, std::size_t size) : fmt::Buffer<Char>(array, size) {}
++
++ protected:
++ FMT_API void grow(std::size_t size) FMT_OVERRIDE;
++};
++
++template <typename Char>
++class BasicCharTraits {
++ public:
++#if FMT_SECURE_SCL
++ typedef stdext::checked_array_iterator<Char*> CharPtr;
++#else
++ typedef Char *CharPtr;
++#endif
++ static Char cast(int value) { return static_cast<Char>(value); }
++};
++
++template <typename Char>
++class CharTraits;
++
++template <>
++class CharTraits<char> : public BasicCharTraits<char> {
++ private:
++ // Conversion from wchar_t to char is not allowed.
++ static char convert(wchar_t);
++
++ public:
++ static char convert(char value) { return value; }
++
++ // Formats a floating-point number.
++ template <typename T>
++ FMT_API static int format_float(char *buffer, std::size_t size,
++ const char *format, unsigned width, int precision, T value);
++};
++
++#if FMT_USE_EXTERN_TEMPLATES
++extern template int CharTraits<char>::format_float<double>
++ (char *buffer, std::size_t size,
++ const char* format, unsigned width, int precision, double value);
++extern template int CharTraits<char>::format_float<long double>
++ (char *buffer, std::size_t size,
++ const char* format, unsigned width, int precision, long double value);
++#endif
++
++template <>
++class CharTraits<wchar_t> : public BasicCharTraits<wchar_t> {
++ public:
++ static wchar_t convert(char value) { return value; }
++ static wchar_t convert(wchar_t value) { return value; }
++
++ template <typename T>
++ FMT_API static int format_float(wchar_t *buffer, std::size_t size,
++ const wchar_t *format, unsigned width, int precision, T value);
++};
++
++#if FMT_USE_EXTERN_TEMPLATES
++extern template int CharTraits<wchar_t>::format_float<double>
++ (wchar_t *buffer, std::size_t size,
++ const wchar_t* format, unsigned width, int precision, double value);
++extern template int CharTraits<wchar_t>::format_float<long double>
++ (wchar_t *buffer, std::size_t size,
++ const wchar_t* format, unsigned width, int precision, long double value);
++#endif
++
++// Checks if a number is negative - used to avoid warnings.
++template <bool IsSigned>
++struct SignChecker {
++ template <typename T>
++ static bool is_negative(T value) { return value < 0; }
++};
++
++template <>
++struct SignChecker<false> {
++ template <typename T>
++ static bool is_negative(T) { return false; }
++};
++
++// Returns true if value is negative, false otherwise.
++// Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
++template <typename T>
++inline bool is_negative(T value) {
++ return SignChecker<std::numeric_limits<T>::is_signed>::is_negative(value);
++}
++
++// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise.
++template <bool FitsIn32Bits>
++struct TypeSelector { typedef uint32_t Type; };
++
++template <>
++struct TypeSelector<false> { typedef uint64_t Type; };
++
++template <typename T>
++struct IntTraits {
++ // Smallest of uint32_t and uint64_t that is large enough to represent
++ // all values of T.
++ typedef typename
++ TypeSelector<std::numeric_limits<T>::digits <= 32>::Type MainType;
++};
++
++FMT_API void report_unknown_type(char code, const char *type);
++
++// Static data is placed in this class template to allow header-only
++// configuration.
++template <typename T = void>
++struct FMT_API BasicData {
++ static const uint32_t POWERS_OF_10_32[];
++ static const uint64_t POWERS_OF_10_64[];
++ static const char DIGITS[];
++};
++
++#if FMT_USE_EXTERN_TEMPLATES
++extern template struct BasicData<void>;
++#endif
++
++typedef BasicData<> Data;
++
++#ifdef FMT_BUILTIN_CLZLL
++// Returns the number of decimal digits in n. Leading zeros are not counted
++// except for n == 0 in which case count_digits returns 1.
++inline unsigned count_digits(uint64_t n) {
++ // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
++ // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
++ int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
++ return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1;
++}
++#else
++// Fallback version of count_digits used when __builtin_clz is not available.
++inline unsigned count_digits(uint64_t n) {
++ unsigned count = 1;
++ for (;;) {
++ // Integer division is slow so do it for a group of four digits instead
++ // of for every digit. The idea comes from the talk by Alexandrescu
++ // "Three Optimization Tips for C++". See speed-test for a comparison.
++ if (n < 10) return count;
++ if (n < 100) return count + 1;
++ if (n < 1000) return count + 2;
++ if (n < 10000) return count + 3;
++ n /= 10000u;
++ count += 4;
++ }
++}
++#endif
++
++#ifdef FMT_BUILTIN_CLZ
++// Optional version of count_digits for better performance on 32-bit platforms.
++inline unsigned count_digits(uint32_t n) {
++ int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
++ return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1;
++}
++#endif
++
++// A functor that doesn't add a thousands separator.
++struct NoThousandsSep {
++ template <typename Char>
++ void operator()(Char *) {}
++};
++
++// A functor that adds a thousands separator.
++class ThousandsSep {
++ private:
++ fmt::StringRef sep_;
++
++ // Index of a decimal digit with the least significant digit having index 0.
++ unsigned digit_index_;
++
++ public:
++ explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {}
++
++ template <typename Char>
++ void operator()(Char *&buffer) {
++ if (++digit_index_ % 3 != 0)
++ return;
++ buffer -= sep_.size();
++ std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(),
++ internal::make_ptr(buffer, sep_.size()));
++ }
++};
++
++// Formats a decimal unsigned integer value writing into buffer.
++// thousands_sep is a functor that is called after writing each char to
++// add a thousands separator if necessary.
++template <typename UInt, typename Char, typename ThousandsSep>
++inline void format_decimal(Char *buffer, UInt value, unsigned num_digits,
++ ThousandsSep thousands_sep) {
++ buffer += num_digits;
++ while (value >= 100) {
++ // Integer division is slow so do it for a group of two digits instead
++ // of for every digit. The idea comes from the talk by Alexandrescu
++ // "Three Optimization Tips for C++". See speed-test for a comparison.
++ unsigned index = static_cast<unsigned>((value % 100) * 2);
++ value /= 100;
++ *--buffer = Data::DIGITS[index + 1];
++ thousands_sep(buffer);
++ *--buffer = Data::DIGITS[index];
++ thousands_sep(buffer);
++ }
++ if (value < 10) {
++ *--buffer = static_cast<char>('0' + value);
++ return;
++ }
++ unsigned index = static_cast<unsigned>(value * 2);
++ *--buffer = Data::DIGITS[index + 1];
++ thousands_sep(buffer);
++ *--buffer = Data::DIGITS[index];
++}
++
++template <typename UInt, typename Char>
++inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) {
++ format_decimal(buffer, value, num_digits, NoThousandsSep());
++ return;
++}
++
++#ifndef _WIN32
++# define FMT_USE_WINDOWS_H 0
++#elif !defined(FMT_USE_WINDOWS_H)
++# define FMT_USE_WINDOWS_H 1
++#endif
++
++// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h.
++// All the functionality that relies on it will be disabled too.
++#if FMT_USE_WINDOWS_H
++// A converter from UTF-8 to UTF-16.
++// It is only provided for Windows since other systems support UTF-8 natively.
++class UTF8ToUTF16 {
++ private:
++ MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer_;
++
++ public:
++ FMT_API explicit UTF8ToUTF16(StringRef s);
++ operator WStringRef() const { return WStringRef(&buffer_[0], size()); }
++ size_t size() const { return buffer_.size() - 1; }
++ const wchar_t *c_str() const { return &buffer_[0]; }
++ std::wstring str() const { return std::wstring(&buffer_[0], size()); }
++};
++
++// A converter from UTF-16 to UTF-8.
++// It is only provided for Windows since other systems support UTF-8 natively.
++class UTF16ToUTF8 {
++ private:
++ MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer_;
++
++ public:
++ UTF16ToUTF8() {}
++ FMT_API explicit UTF16ToUTF8(WStringRef s);
++ operator StringRef() const { return StringRef(&buffer_[0], size()); }
++ size_t size() const { return buffer_.size() - 1; }
++ const char *c_str() const { return &buffer_[0]; }
++ std::string str() const { return std::string(&buffer_[0], size()); }
++
++ // Performs conversion returning a system error code instead of
++ // throwing exception on conversion error. This method may still throw
++ // in case of memory allocation error.
++ FMT_API int convert(WStringRef s);
++};
++
++FMT_API void format_windows_error(fmt::Writer &out, int error_code,
++ fmt::StringRef message) FMT_NOEXCEPT;
++#endif
++
++// A formatting argument value.
++struct Value {
++ template <typename Char>
++ struct StringValue {
++ const Char *value;
++ std::size_t size;
++ };
++
++ typedef void (*FormatFunc)(
++ void *formatter, const void *arg, void *format_str_ptr);
++
++ struct CustomValue {
++ const void *value;
++ FormatFunc format;
++ };
++
++ union {
++ int int_value;
++ unsigned uint_value;
++ LongLong long_long_value;
++ ULongLong ulong_long_value;
++ double double_value;
++ long double long_double_value;
++ const void *pointer;
++ StringValue<char> string;
++ StringValue<signed char> sstring;
++ StringValue<unsigned char> ustring;
++ StringValue<wchar_t> wstring;
++ CustomValue custom;
++ };
++
++ enum Type {
++ NONE, NAMED_ARG,
++ // Integer types should go first,
++ INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR,
++ // followed by floating-point types.
++ DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE,
++ CSTRING, STRING, WSTRING, POINTER, CUSTOM
++ };
++};
++
++// A formatting argument. It is a trivially copyable/constructible type to
++// allow storage in internal::MemoryBuffer.
++struct Arg : Value {
++ Type type;
++};
++
++template <typename Char>
++struct NamedArg;
++template <typename Char, typename T>
++struct NamedArgWithType;
++
++template <typename T = void>
++struct Null {};
++
++// A helper class template to enable or disable overloads taking wide
++// characters and strings in MakeValue.
++template <typename T, typename Char>
++struct WCharHelper {
++ typedef Null<T> Supported;
++ typedef T Unsupported;
++};
++
++template <typename T>
++struct WCharHelper<T, wchar_t> {
++ typedef T Supported;
++ typedef Null<T> Unsupported;
++};
++
++typedef char Yes[1];
++typedef char No[2];
++
++template <typename T>
++T &get();
++
++// These are non-members to workaround an overload resolution bug in bcc32.
++Yes &convert(fmt::ULongLong);
++No &convert(...);
++
++template<typename T, bool ENABLE_CONVERSION>
++struct ConvertToIntImpl {
++ enum { value = ENABLE_CONVERSION };
++};
++
++template<typename T, bool ENABLE_CONVERSION>
++struct ConvertToIntImpl2 {
++ enum { value = false };
++};
++
++template<typename T>
++struct ConvertToIntImpl2<T, true> {
++ enum {
++ // Don't convert numeric types.
++ value = ConvertToIntImpl<T, !std::numeric_limits<T>::is_specialized>::value
++ };
++};
++
++template<typename T>
++struct ConvertToInt {
++ enum {
++ enable_conversion = sizeof(fmt::internal::convert(get<T>())) == sizeof(Yes)
++ };
++ enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
++};
++
++#define FMT_DISABLE_CONVERSION_TO_INT(Type) \
++ template <> \
++ struct ConvertToInt<Type> { enum { value = 0 }; }
++
++// Silence warnings about convering float to int.
++FMT_DISABLE_CONVERSION_TO_INT(float);
++FMT_DISABLE_CONVERSION_TO_INT(double);
++FMT_DISABLE_CONVERSION_TO_INT(long double);
++
++template<bool B, class T = void>
++struct EnableIf {};
++
++template<class T>
++struct EnableIf<true, T> { typedef T type; };
++
++template<bool B, class T, class F>
++struct Conditional { typedef T type; };
++
++template<class T, class F>
++struct Conditional<false, T, F> { typedef F type; };
++
++// For bcc32 which doesn't understand ! in template arguments.
++template <bool>
++struct Not { enum { value = 0 }; };
++
++template <>
++struct Not<false> { enum { value = 1 }; };
++
++template <typename T>
++struct FalseType { enum { value = 0 }; };
++
++template <typename T, T> struct LConvCheck {
++ LConvCheck(int) {}
++};
++
++// Returns the thousands separator for the current locale.
++// We check if ``lconv`` contains ``thousands_sep`` because on Android
++// ``lconv`` is stubbed as an empty struct.
++template <typename LConv>
++inline StringRef thousands_sep(
++ LConv *lc, LConvCheck<char *LConv::*, &LConv::thousands_sep> = 0) {
++ return lc->thousands_sep;
++}
++
++inline fmt::StringRef thousands_sep(...) { return ""; }
++
++#define FMT_CONCAT(a, b) a##b
++
++#if FMT_GCC_VERSION >= 303
++# define FMT_UNUSED __attribute__((unused))
++#else
++# define FMT_UNUSED
++#endif
++
++#ifndef FMT_USE_STATIC_ASSERT
++# define FMT_USE_STATIC_ASSERT 0
++#endif
++
++#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
++ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
++# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
++#else
++# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
++# define FMT_STATIC_ASSERT(cond, message) \
++ typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
++#endif
++
++template <typename Formatter, typename Char, typename T>
++void format_arg(Formatter &, const Char *, const T &) {
++ FMT_STATIC_ASSERT(FalseType<T>::value,
++ "Cannot format argument. To enable the use of ostream "
++ "operator<< include fmt/ostream.h. Otherwise provide "
++ "an overload of format_arg.");
++}
++
++// Makes an Arg object from any type.
++template <typename Formatter>
++class MakeValue : public Arg {
++ public:
++ typedef typename Formatter::Char Char;
++
++ private:
++ // The following two methods are private to disallow formatting of
++ // arbitrary pointers. If you want to output a pointer cast it to
++ // "void *" or "const void *". In particular, this forbids formatting
++ // of "[const] volatile char *" which is printed as bool by iostreams.
++ // Do not implement!
++ template <typename T>
++ MakeValue(const T *value);
++ template <typename T>
++ MakeValue(T *value);
++
++ // The following methods are private to disallow formatting of wide
++ // characters and strings into narrow strings as in
++ // fmt::format("{}", L"test");
++ // To fix this, use a wide format string: fmt::format(L"{}", L"test").
++#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED)
++ MakeValue(typename WCharHelper<wchar_t, Char>::Unsupported);
++#endif
++ MakeValue(typename WCharHelper<wchar_t *, Char>::Unsupported);
++ MakeValue(typename WCharHelper<const wchar_t *, Char>::Unsupported);
++ MakeValue(typename WCharHelper<const std::wstring &, Char>::Unsupported);
++ MakeValue(typename WCharHelper<WStringRef, Char>::Unsupported);
++
++ void set_string(StringRef str) {
++ string.value = str.data();
++ string.size = str.size();
++ }
++
++ void set_string(WStringRef str) {
++ wstring.value = str.data();
++ wstring.size = str.size();
++ }
++
++ // Formats an argument of a custom type, such as a user-defined class.
++ template <typename T>
++ static void format_custom_arg(
++ void *formatter, const void *arg, void *format_str_ptr) {
++ format_arg(*static_cast<Formatter*>(formatter),
++ *static_cast<const Char**>(format_str_ptr),
++ *static_cast<const T*>(arg));
++ }
++
++ public:
++ MakeValue() {}
++
++#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \
++ MakeValue(Type value) { field = rhs; } \
++ static uint64_t type(Type) { return Arg::TYPE; }
++
++#define FMT_MAKE_VALUE(Type, field, TYPE) \
++ FMT_MAKE_VALUE_(Type, field, TYPE, value)
++
++ FMT_MAKE_VALUE(bool, int_value, BOOL)
++ FMT_MAKE_VALUE(short, int_value, INT)
++ FMT_MAKE_VALUE(unsigned short, uint_value, UINT)
++ FMT_MAKE_VALUE(int, int_value, INT)
++ FMT_MAKE_VALUE(unsigned, uint_value, UINT)
++
++ MakeValue(long value) {
++ // To minimize the number of types we need to deal with, long is
++ // translated either to int or to long long depending on its size.
++ if (const_check(sizeof(long) == sizeof(int)))
++ int_value = static_cast<int>(value);
++ else
++ long_long_value = value;
++ }
++ static uint64_t type(long) {
++ return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG;
++ }
++
++ MakeValue(unsigned long value) {
++ if (const_check(sizeof(unsigned long) == sizeof(unsigned)))
++ uint_value = static_cast<unsigned>(value);
++ else
++ ulong_long_value = value;
++ }
++ static uint64_t type(unsigned long) {
++ return sizeof(unsigned long) == sizeof(unsigned) ?
++ Arg::UINT : Arg::ULONG_LONG;
++ }
++
++ FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG)
++ FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG)
++ FMT_MAKE_VALUE(float, double_value, DOUBLE)
++ FMT_MAKE_VALUE(double, double_value, DOUBLE)
++ FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE)
++ FMT_MAKE_VALUE(signed char, int_value, INT)
++ FMT_MAKE_VALUE(unsigned char, uint_value, UINT)
++ FMT_MAKE_VALUE(char, int_value, CHAR)
++
++#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
++ MakeValue(typename WCharHelper<wchar_t, Char>::Supported value) {
++ int_value = value;
++ }
++ static uint64_t type(wchar_t) { return Arg::CHAR; }
++#endif
++
++#define FMT_MAKE_STR_VALUE(Type, TYPE) \
++ MakeValue(Type value) { set_string(value); } \
++ static uint64_t type(Type) { return Arg::TYPE; }
++
++ FMT_MAKE_VALUE(char *, string.value, CSTRING)
++ FMT_MAKE_VALUE(const char *, string.value, CSTRING)
++ FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING)
++ FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING)
++ FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING)
++ FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING)
++ FMT_MAKE_STR_VALUE(const std::string &, STRING)
++ FMT_MAKE_STR_VALUE(StringRef, STRING)
++ FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str())
++
++#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \
++ MakeValue(typename WCharHelper<Type, Char>::Supported value) { \
++ set_string(value); \
++ } \
++ static uint64_t type(Type) { return Arg::TYPE; }
++
++ FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING)
++ FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING)
++ FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING)
++ FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING)
++
++ FMT_MAKE_VALUE(void *, pointer, POINTER)
++ FMT_MAKE_VALUE(const void *, pointer, POINTER)
++
++ template <typename T>
++ MakeValue(const T &value,
++ typename EnableIf<Not<
++ ConvertToInt<T>::value>::value, int>::type = 0) {
++ custom.value = &value;
++ custom.format = &format_custom_arg<T>;
++ }
++
++ template <typename T>
++ static typename EnableIf<Not<ConvertToInt<T>::value>::value, uint64_t>::type
++ type(const T &) {
++ return Arg::CUSTOM;
++ }
++
++ // Additional template param `Char_` is needed here because make_type always
++ // uses char.
++ template <typename Char_>
++ MakeValue(const NamedArg<Char_> &value) { pointer = &value; }
++ template <typename Char_, typename T>
++ MakeValue(const NamedArgWithType<Char_, T> &value) { pointer = &value; }
++
++ template <typename Char_>
++ static uint64_t type(const NamedArg<Char_> &) { return Arg::NAMED_ARG; }
++ template <typename Char_, typename T>
++ static uint64_t type(const NamedArgWithType<Char_, T> &) { return Arg::NAMED_ARG; }
++};
++
++template <typename Formatter>
++class MakeArg : public Arg {
++public:
++ MakeArg() {
++ type = Arg::NONE;
++ }
++
++ template <typename T>
++ MakeArg(const T &value)
++ : Arg(MakeValue<Formatter>(value)) {
++ type = static_cast<Arg::Type>(MakeValue<Formatter>::type(value));
++ }
++};
++
++template <typename Char>
++struct NamedArg : Arg {
++ BasicStringRef<Char> name;
++
++ template <typename T>
++ NamedArg(BasicStringRef<Char> argname, const T &value)
++ : Arg(MakeArg< BasicFormatter<Char> >(value)), name(argname) {}
++};
++
++template <typename Char, typename T>
++struct NamedArgWithType : NamedArg<Char> {
++ NamedArgWithType(BasicStringRef<Char> argname, const T &value)
++ : NamedArg<Char>(argname, value) {}
++};
++
++class RuntimeError : public std::runtime_error {
++ protected:
++ RuntimeError() : std::runtime_error("") {}
++ RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {}
++ FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT;
++};
++
++template <typename Char>
++class ArgMap;
++} // namespace internal
++
++/** An argument list. */
++class ArgList {
++ private:
++ // To reduce compiled code size per formatting function call, types of first
++ // MAX_PACKED_ARGS arguments are passed in the types_ field.
++ uint64_t types_;
++ union {
++ // If the number of arguments is less than MAX_PACKED_ARGS, the argument
++ // values are stored in values_, otherwise they are stored in args_.
++ // This is done to reduce compiled code size as storing larger objects
++ // may require more code (at least on x86-64) even if the same amount of
++ // data is actually copied to stack. It saves ~10% on the bloat test.
++ const internal::Value *values_;
++ const internal::Arg *args_;
++ };
++
++ internal::Arg::Type type(unsigned index) const {
++ return type(types_, index);
++ }
++
++ template <typename Char>
++ friend class internal::ArgMap;
++
++ public:
++ // Maximum number of arguments with packed types.
++ enum { MAX_PACKED_ARGS = 16 };
++
++ ArgList() : types_(0) {}
++
++ ArgList(ULongLong types, const internal::Value *values)
++ : types_(types), values_(values) {}
++ ArgList(ULongLong types, const internal::Arg *args)
++ : types_(types), args_(args) {}
++
++ uint64_t types() const { return types_; }
++
++ /** Returns the argument at specified index. */
++ internal::Arg operator[](unsigned index) const {
++ using internal::Arg;
++ Arg arg;
++ bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE;
++ if (index < MAX_PACKED_ARGS) {
++ Arg::Type arg_type = type(index);
++ internal::Value &val = arg;
++ if (arg_type != Arg::NONE)
++ val = use_values ? values_[index] : args_[index];
++ arg.type = arg_type;
++ return arg;
++ }
++ if (use_values) {
++ // The index is greater than the number of arguments that can be stored
++ // in values, so return a "none" argument.
++ arg.type = Arg::NONE;
++ return arg;
++ }
++ for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) {
++ if (args_[i].type == Arg::NONE)
++ return args_[i];
++ }
++ return args_[index];
++ }
++
++ static internal::Arg::Type type(uint64_t types, unsigned index) {
++ unsigned shift = index * 4;
++ uint64_t mask = 0xf;
++ return static_cast<internal::Arg::Type>(
++ (types & (mask << shift)) >> shift);
++ }
++};
++
++#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
++
++/**
++ \rst
++ An argument visitor based on the `curiously recurring template pattern
++ <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
++
++ To use `~fmt::ArgVisitor` define a subclass that implements some or all of the
++ visit methods with the same signatures as the methods in `~fmt::ArgVisitor`,
++ for example, `~fmt::ArgVisitor::visit_int()`.
++ Pass the subclass as the *Impl* template parameter. Then calling
++ `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method
++ specific to the argument type. For example, if the argument type is
++ ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
++ will be called. If the subclass doesn't contain a method with this signature,
++ then a corresponding method of `~fmt::ArgVisitor` will be called.
++
++ **Example**::
++
++ class MyArgVisitor : public fmt::ArgVisitor<MyArgVisitor, void> {
++ public:
++ void visit_int(int value) { fmt::print("{}", value); }
++ void visit_double(double value) { fmt::print("{}", value ); }
++ };
++ \endrst
++ */
++template <typename Impl, typename Result>
++class ArgVisitor {
++ private:
++ typedef internal::Arg Arg;
++
++ public:
++ void report_unhandled_arg() {}
++
++ Result visit_unhandled_arg() {
++ FMT_DISPATCH(report_unhandled_arg());
++ return Result();
++ }
++
++ /** Visits an ``int`` argument. **/
++ Result visit_int(int value) {
++ return FMT_DISPATCH(visit_any_int(value));
++ }
++
++ /** Visits a ``long long`` argument. **/
++ Result visit_long_long(LongLong value) {
++ return FMT_DISPATCH(visit_any_int(value));
++ }
++
++ /** Visits an ``unsigned`` argument. **/
++ Result visit_uint(unsigned value) {
++ return FMT_DISPATCH(visit_any_int(value));
++ }
++
++ /** Visits an ``unsigned long long`` argument. **/
++ Result visit_ulong_long(ULongLong value) {
++ return FMT_DISPATCH(visit_any_int(value));
++ }
++
++ /** Visits a ``bool`` argument. **/
++ Result visit_bool(bool value) {
++ return FMT_DISPATCH(visit_any_int(value));
++ }
++
++ /** Visits a ``char`` or ``wchar_t`` argument. **/
++ Result visit_char(int value) {
++ return FMT_DISPATCH(visit_any_int(value));
++ }
++
++ /** Visits an argument of any integral type. **/
++ template <typename T>
++ Result visit_any_int(T) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /** Visits a ``double`` argument. **/
++ Result visit_double(double value) {
++ return FMT_DISPATCH(visit_any_double(value));
++ }
++
++ /** Visits a ``long double`` argument. **/
++ Result visit_long_double(long double value) {
++ return FMT_DISPATCH(visit_any_double(value));
++ }
++
++ /** Visits a ``double`` or ``long double`` argument. **/
++ template <typename T>
++ Result visit_any_double(T) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /** Visits a null-terminated C string (``const char *``) argument. **/
++ Result visit_cstring(const char *) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /** Visits a string argument. **/
++ Result visit_string(Arg::StringValue<char>) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /** Visits a wide string argument. **/
++ Result visit_wstring(Arg::StringValue<wchar_t>) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /** Visits a pointer argument. **/
++ Result visit_pointer(const void *) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /** Visits an argument of a custom (user-defined) type. **/
++ Result visit_custom(Arg::CustomValue) {
++ return FMT_DISPATCH(visit_unhandled_arg());
++ }
++
++ /**
++ \rst
++ Visits an argument dispatching to the appropriate visit method based on
++ the argument type. For example, if the argument type is ``double`` then
++ the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be
++ called.
++ \endrst
++ */
++ Result visit(const Arg &arg) {
++ switch (arg.type) {
++ case Arg::NONE:
++ case Arg::NAMED_ARG:
++ FMT_ASSERT(false, "invalid argument type");
++ break;
++ case Arg::INT:
++ return FMT_DISPATCH(visit_int(arg.int_value));
++ case Arg::UINT:
++ return FMT_DISPATCH(visit_uint(arg.uint_value));
++ case Arg::LONG_LONG:
++ return FMT_DISPATCH(visit_long_long(arg.long_long_value));
++ case Arg::ULONG_LONG:
++ return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value));
++ case Arg::BOOL:
++ return FMT_DISPATCH(visit_bool(arg.int_value != 0));
++ case Arg::CHAR:
++ return FMT_DISPATCH(visit_char(arg.int_value));
++ case Arg::DOUBLE:
++ return FMT_DISPATCH(visit_double(arg.double_value));
++ case Arg::LONG_DOUBLE:
++ return FMT_DISPATCH(visit_long_double(arg.long_double_value));
++ case Arg::CSTRING:
++ return FMT_DISPATCH(visit_cstring(arg.string.value));
++ case Arg::STRING:
++ return FMT_DISPATCH(visit_string(arg.string));
++ case Arg::WSTRING:
++ return FMT_DISPATCH(visit_wstring(arg.wstring));
++ case Arg::POINTER:
++ return FMT_DISPATCH(visit_pointer(arg.pointer));
++ case Arg::CUSTOM:
++ return FMT_DISPATCH(visit_custom(arg.custom));
++ }
++ return Result();
++ }
++};
++
++enum Alignment {
++ ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
++};
++
++// Flags.
++enum {
++ SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8,
++ CHAR_FLAG = 0x10 // Argument has char type - used in error reporting.
++};
++
++// An empty format specifier.
++struct EmptySpec {};
++
++// A type specifier.
++template <char TYPE>
++struct TypeSpec : EmptySpec {
++ Alignment align() const { return ALIGN_DEFAULT; }
++ unsigned width() const { return 0; }
++ int precision() const { return -1; }
++ bool flag(unsigned) const { return false; }
++ char type() const { return TYPE; }
++ char type_prefix() const { return TYPE; }
++ char fill() const { return ' '; }
++};
++
++// A width specifier.
++struct WidthSpec {
++ unsigned width_;
++ // Fill is always wchar_t and cast to char if necessary to avoid having
++ // two specialization of WidthSpec and its subclasses.
++ wchar_t fill_;
++
++ WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {}
++
++ unsigned width() const { return width_; }
++ wchar_t fill() const { return fill_; }
++};
++
++// An alignment specifier.
++struct AlignSpec : WidthSpec {
++ Alignment align_;
++
++ AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT)
++ : WidthSpec(width, fill), align_(align) {}
++
++ Alignment align() const { return align_; }
++
++ int precision() const { return -1; }
++};
++
++// An alignment and type specifier.
++template <char TYPE>
++struct AlignTypeSpec : AlignSpec {
++ AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {}
++
++ bool flag(unsigned) const { return false; }
++ char type() const { return TYPE; }
++ char type_prefix() const { return TYPE; }
++};
++
++// A full format specifier.
++struct FormatSpec : AlignSpec {
++ unsigned flags_;
++ int precision_;
++ char type_;
++
++ FormatSpec(
++ unsigned width = 0, char type = 0, wchar_t fill = ' ')
++ : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {}
++
++ bool flag(unsigned f) const { return (flags_ & f) != 0; }
++ int precision() const { return precision_; }
++ char type() const { return type_; }
++ char type_prefix() const { return type_; }
++};
++
++// An integer format specifier.
++template <typename T, typename SpecT = TypeSpec<0>, typename Char = char>
++class IntFormatSpec : public SpecT {
++ private:
++ T value_;
++
++ public:
++ IntFormatSpec(T val, const SpecT &spec = SpecT())
++ : SpecT(spec), value_(val) {}
++
++ T value() const { return value_; }
++};
++
++// A string format specifier.
++template <typename Char>
++class StrFormatSpec : public AlignSpec {
++ private:
++ const Char *str_;
++
++ public:
++ template <typename FillChar>
++ StrFormatSpec(const Char *str, unsigned width, FillChar fill)
++ : AlignSpec(width, fill), str_(str) {
++ internal::CharTraits<Char>::convert(FillChar());
++ }
++
++ const Char *str() const { return str_; }
++};
++
++/**
++ Returns an integer format specifier to format the value in base 2.
++ */
++IntFormatSpec<int, TypeSpec<'b'> > bin(int value);
++
++/**
++ Returns an integer format specifier to format the value in base 8.
++ */
++IntFormatSpec<int, TypeSpec<'o'> > oct(int value);
++
++/**
++ Returns an integer format specifier to format the value in base 16 using
++ lower-case letters for the digits above 9.
++ */
++IntFormatSpec<int, TypeSpec<'x'> > hex(int value);
++
++/**
++ Returns an integer formatter format specifier to format in base 16 using
++ upper-case letters for the digits above 9.
++ */
++IntFormatSpec<int, TypeSpec<'X'> > hexu(int value);
++
++/**
++ \rst
++ Returns an integer format specifier to pad the formatted argument with the
++ fill character to the specified width using the default (right) numeric
++ alignment.
++
++ **Example**::
++
++ MemoryWriter out;
++ out << pad(hex(0xcafe), 8, '0');
++ // out.str() == "0000cafe"
++
++ \endrst
++ */
++template <char TYPE_CODE, typename Char>
++IntFormatSpec<int, AlignTypeSpec<TYPE_CODE>, Char> pad(
++ int value, unsigned width, Char fill = ' ');
++
++#define FMT_DEFINE_INT_FORMATTERS(TYPE) \
++inline IntFormatSpec<TYPE, TypeSpec<'b'> > bin(TYPE value) { \
++ return IntFormatSpec<TYPE, TypeSpec<'b'> >(value, TypeSpec<'b'>()); \
++} \
++ \
++inline IntFormatSpec<TYPE, TypeSpec<'o'> > oct(TYPE value) { \
++ return IntFormatSpec<TYPE, TypeSpec<'o'> >(value, TypeSpec<'o'>()); \
++} \
++ \
++inline IntFormatSpec<TYPE, TypeSpec<'x'> > hex(TYPE value) { \
++ return IntFormatSpec<TYPE, TypeSpec<'x'> >(value, TypeSpec<'x'>()); \
++} \
++ \
++inline IntFormatSpec<TYPE, TypeSpec<'X'> > hexu(TYPE value) { \
++ return IntFormatSpec<TYPE, TypeSpec<'X'> >(value, TypeSpec<'X'>()); \
++} \
++ \
++template <char TYPE_CODE> \
++inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE> > pad( \
++ IntFormatSpec<TYPE, TypeSpec<TYPE_CODE> > f, unsigned width) { \
++ return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE> >( \
++ f.value(), AlignTypeSpec<TYPE_CODE>(width, ' ')); \
++} \
++ \
++/* For compatibility with older compilers we provide two overloads for pad, */ \
++/* one that takes a fill character and one that doesn't. In the future this */ \
++/* can be replaced with one overload making the template argument Char */ \
++/* default to char (C++11). */ \
++template <char TYPE_CODE, typename Char> \
++inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char> pad( \
++ IntFormatSpec<TYPE, TypeSpec<TYPE_CODE>, Char> f, \
++ unsigned width, Char fill) { \
++ return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char>( \
++ f.value(), AlignTypeSpec<TYPE_CODE>(width, fill)); \
++} \
++ \
++inline IntFormatSpec<TYPE, AlignTypeSpec<0> > pad( \
++ TYPE value, unsigned width) { \
++ return IntFormatSpec<TYPE, AlignTypeSpec<0> >( \
++ value, AlignTypeSpec<0>(width, ' ')); \
++} \
++ \
++template <typename Char> \
++inline IntFormatSpec<TYPE, AlignTypeSpec<0>, Char> pad( \
++ TYPE value, unsigned width, Char fill) { \
++ return IntFormatSpec<TYPE, AlignTypeSpec<0>, Char>( \
++ value, AlignTypeSpec<0>(width, fill)); \
++}
++
++FMT_DEFINE_INT_FORMATTERS(int)
++FMT_DEFINE_INT_FORMATTERS(long)
++FMT_DEFINE_INT_FORMATTERS(unsigned)
++FMT_DEFINE_INT_FORMATTERS(unsigned long)
++FMT_DEFINE_INT_FORMATTERS(LongLong)
++FMT_DEFINE_INT_FORMATTERS(ULongLong)
++
++/**
++ \rst
++ Returns a string formatter that pads the formatted argument with the fill
++ character to the specified width using the default (left) string alignment.
++
++ **Example**::
++
++ std::string s = str(MemoryWriter() << pad("abc", 8));
++ // s == "abc "
++
++ \endrst
++ */
++template <typename Char>
++inline StrFormatSpec<Char> pad(
++ const Char *str, unsigned width, Char fill = ' ') {
++ return StrFormatSpec<Char>(str, width, fill);
++}
++
++inline StrFormatSpec<wchar_t> pad(
++ const wchar_t *str, unsigned width, char fill = ' ') {
++ return StrFormatSpec<wchar_t>(str, width, fill);
++}
++
++namespace internal {
++
++template <typename Char>
++class ArgMap {
++ private:
++ typedef std::vector<
++ std::pair<fmt::BasicStringRef<Char>, internal::Arg> > MapType;
++ typedef typename MapType::value_type Pair;
++
++ MapType map_;
++
++ public:
++ FMT_API void init(const ArgList &args);
++
++ const internal::Arg *find(const fmt::BasicStringRef<Char> &name) const {
++ // The list is unsorted, so just return the first matching name.
++ for (typename MapType::const_iterator it = map_.begin(), end = map_.end();
++ it != end; ++it) {
++ if (it->first == name)
++ return &it->second;
++ }
++ return FMT_NULL;
++ }
++};
++
++template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
++class ArgFormatterBase : public ArgVisitor<Impl, void> {
++ private:
++ BasicWriter<Char> &writer_;
++ Spec &spec_;
++
++ FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase);
++
++ void write_pointer(const void *p) {
++ spec_.flags_ = HASH_FLAG;
++ spec_.type_ = 'x';
++ writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_);
++ }
++
++ // workaround MSVC two-phase lookup issue
++ typedef internal::Arg Arg;
++
++ protected:
++ BasicWriter<Char> &writer() { return writer_; }
++ Spec &spec() { return spec_; }
++
++ void write(bool value) {
++ const char *str_value = value ? "true" : "false";
++ Arg::StringValue<char> str = { str_value, std::strlen(str_value) };
++ writer_.write_str(str, spec_);
++ }
++
++ void write(const char *value) {
++ Arg::StringValue<char> str = {value, value ? std::strlen(value) : 0};
++ writer_.write_str(str, spec_);
++ }
++
++ public:
++ typedef Spec SpecType;
++
++ ArgFormatterBase(BasicWriter<Char> &w, Spec &s)
++ : writer_(w), spec_(s) {}
++
++ template <typename T>
++ void visit_any_int(T value) { writer_.write_int(value, spec_); }
++
++ template <typename T>
++ void visit_any_double(T value) { writer_.write_double(value, spec_); }
++
++ void visit_bool(bool value) {
++ if (spec_.type_) {
++ visit_any_int(value);
++ return;
++ }
++ write(value);
++ }
++
++ void visit_char(int value) {
++ if (spec_.type_ && spec_.type_ != 'c') {
++ spec_.flags_ |= CHAR_FLAG;
++ writer_.write_int(value, spec_);
++ return;
++ }
++ if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
++ FMT_THROW(FormatError("invalid format specifier for char"));
++ typedef typename BasicWriter<Char>::CharPtr CharPtr;
++ Char fill = internal::CharTraits<Char>::cast(spec_.fill());
++ CharPtr out = CharPtr();
++ const unsigned CHAR_SIZE = 1;
++ if (spec_.width_ > CHAR_SIZE) {
++ out = writer_.grow_buffer(spec_.width_);
++ if (spec_.align_ == ALIGN_RIGHT) {
++ std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill);
++ out += spec_.width_ - CHAR_SIZE;
++ } else if (spec_.align_ == ALIGN_CENTER) {
++ out = writer_.fill_padding(out, spec_.width_,
++ internal::const_check(CHAR_SIZE), fill);
++ } else {
++ std::uninitialized_fill_n(out + CHAR_SIZE,
++ spec_.width_ - CHAR_SIZE, fill);
++ }
++ } else {
++ out = writer_.grow_buffer(CHAR_SIZE);
++ }
++ *out = internal::CharTraits<Char>::cast(value);
++ }
++
++ void visit_cstring(const char *value) {
++ if (spec_.type_ == 'p')
++ return write_pointer(value);
++ write(value);
++ }
++
++ // Qualification with "internal" here and below is a workaround for nvcc.
++ void visit_string(internal::Arg::StringValue<char> value) {
++ writer_.write_str(value, spec_);
++ }
++
++ using ArgVisitor<Impl, void>::visit_wstring;
++
++ void visit_wstring(internal::Arg::StringValue<Char> value) {
++ writer_.write_str(value, spec_);
++ }
++
++ void visit_pointer(const void *value) {
++ if (spec_.type_ && spec_.type_ != 'p')
++ report_unknown_type(spec_.type_, "pointer");
++ write_pointer(value);
++ }
++};
++
++class FormatterBase {
++ private:
++ ArgList args_;
++ int next_arg_index_;
++
++ // Returns the argument with specified index.
++ FMT_API Arg do_get_arg(unsigned arg_index, const char *&error);
++
++ protected:
++ const ArgList &args() const { return args_; }
++
++ explicit FormatterBase(const ArgList &args) {
++ args_ = args;
++ next_arg_index_ = 0;
++ }
++
++ // Returns the next argument.
++ Arg next_arg(const char *&error) {
++ if (next_arg_index_ >= 0)
++ return do_get_arg(internal::to_unsigned(next_arg_index_++), error);
++ error = "cannot switch from manual to automatic argument indexing";
++ return Arg();
++ }
++
++ // Checks if manual indexing is used and returns the argument with
++ // specified index.
++ Arg get_arg(unsigned arg_index, const char *&error) {
++ return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg();
++ }
++
++ bool check_no_auto_index(const char *&error) {
++ if (next_arg_index_ > 0) {
++ error = "cannot switch from automatic to manual argument indexing";
++ return false;
++ }
++ next_arg_index_ = -1;
++ return true;
++ }
++
++ template <typename Char>
++ void write(BasicWriter<Char> &w, const Char *start, const Char *end) {
++ if (start != end)
++ w << BasicStringRef<Char>(start, internal::to_unsigned(end - start));
++ }
++};
++} // namespace internal
++
++/**
++ \rst
++ An argument formatter based on the `curiously recurring template pattern
++ <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
++
++ To use `~fmt::BasicArgFormatter` define a subclass that implements some or
++ all of the visit methods with the same signatures as the methods in
++ `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
++ Pass the subclass as the *Impl* template parameter. When a formatting
++ function processes an argument, it will dispatch to a visit method
++ specific to the argument type. For example, if the argument type is
++ ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
++ will be called. If the subclass doesn't contain a method with this signature,
++ then a corresponding method of `~fmt::BasicArgFormatter` or its superclass
++ will be called.
++ \endrst
++ */
++template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
++class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec> {
++ private:
++ BasicFormatter<Char, Impl> &formatter_;
++ const Char *format_;
++
++ public:
++ /**
++ \rst
++ Constructs an argument formatter object.
++ *formatter* is a reference to the main formatter object, *spec* contains
++ format specifier information for standard argument types, and *fmt* points
++ to the part of the format string being parsed for custom argument types.
++ \endrst
++ */
++ BasicArgFormatter(BasicFormatter<Char, Impl> &formatter,
++ Spec &spec, const Char *fmt)
++ : internal::ArgFormatterBase<Impl, Char, Spec>(formatter.writer(), spec),
++ formatter_(formatter), format_(fmt) {}
++
++ /** Formats an argument of a custom (user-defined) type. */
++ void visit_custom(internal::Arg::CustomValue c) {
++ c.format(&formatter_, c.value, &format_);
++ }
++};
++
++/** The default argument formatter. */
++template <typename Char>
++class ArgFormatter :
++ public BasicArgFormatter<ArgFormatter<Char>, Char, FormatSpec> {
++ public:
++ /** Constructs an argument formatter object. */
++ ArgFormatter(BasicFormatter<Char> &formatter,
++ FormatSpec &spec, const Char *fmt)
++ : BasicArgFormatter<ArgFormatter<Char>,
++ Char, FormatSpec>(formatter, spec, fmt) {}
++};
++
++/** This template formats data and writes the output to a writer. */
++template <typename CharType, typename ArgFormatter>
++class BasicFormatter : private internal::FormatterBase {
++ public:
++ /** The character type for the output. */
++ typedef CharType Char;
++
++ private:
++ BasicWriter<Char> &writer_;
++ internal::ArgMap<Char> map_;
++
++ FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter);
++
++ using internal::FormatterBase::get_arg;
++
++ // Checks if manual indexing is used and returns the argument with
++ // specified name.
++ internal::Arg get_arg(BasicStringRef<Char> arg_name, const char *&error);
++
++ // Parses argument index and returns corresponding argument.
++ internal::Arg parse_arg_index(const Char *&s);
++
++ // Parses argument name and returns corresponding argument.
++ internal::Arg parse_arg_name(const Char *&s);
++
++ public:
++ /**
++ \rst
++ Constructs a ``BasicFormatter`` object. References to the arguments and
++ the writer are stored in the formatter object so make sure they have
++ appropriate lifetimes.
++ \endrst
++ */
++ BasicFormatter(const ArgList &args, BasicWriter<Char> &w)
++ : internal::FormatterBase(args), writer_(w) {}
++
++ /** Returns a reference to the writer associated with this formatter. */
++ BasicWriter<Char> &writer() { return writer_; }
++
++ /** Formats stored arguments and writes the output to the writer. */
++ void format(BasicCStringRef<Char> format_str);
++
++ // Formats a single argument and advances format_str, a format string pointer.
++ const Char *format(const Char *&format_str, const internal::Arg &arg);
++};
++
++// Generates a comma-separated list with results of applying f to
++// numbers 0..n-1.
++# define FMT_GEN(n, f) FMT_GEN##n(f)
++# define FMT_GEN1(f) f(0)
++# define FMT_GEN2(f) FMT_GEN1(f), f(1)
++# define FMT_GEN3(f) FMT_GEN2(f), f(2)
++# define FMT_GEN4(f) FMT_GEN3(f), f(3)
++# define FMT_GEN5(f) FMT_GEN4(f), f(4)
++# define FMT_GEN6(f) FMT_GEN5(f), f(5)
++# define FMT_GEN7(f) FMT_GEN6(f), f(6)
++# define FMT_GEN8(f) FMT_GEN7(f), f(7)
++# define FMT_GEN9(f) FMT_GEN8(f), f(8)
++# define FMT_GEN10(f) FMT_GEN9(f), f(9)
++# define FMT_GEN11(f) FMT_GEN10(f), f(10)
++# define FMT_GEN12(f) FMT_GEN11(f), f(11)
++# define FMT_GEN13(f) FMT_GEN12(f), f(12)
++# define FMT_GEN14(f) FMT_GEN13(f), f(13)
++# define FMT_GEN15(f) FMT_GEN14(f), f(14)
++
++namespace internal {
++inline uint64_t make_type() { return 0; }
++
++template <typename T>
++inline uint64_t make_type(const T &arg) {
++ return MakeValue< BasicFormatter<char> >::type(arg);
++}
++
++template <std::size_t N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)>
++struct ArgArray;
++
++template <std::size_t N>
++struct ArgArray<N, true/*IsPacked*/> {
++ typedef Value Type[N > 0 ? N : 1];
++
++ template <typename Formatter, typename T>
++ static Value make(const T &value) {
++#ifdef __clang__
++ Value result = MakeValue<Formatter>(value);
++ // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang:
++ // https://github.com/fmtlib/fmt/issues/276
++ (void)result.custom.format;
++ return result;
++#else
++ return MakeValue<Formatter>(value);
++#endif
++ }
++};
++
++template <std::size_t N>
++struct ArgArray<N, false/*IsPacked*/> {
++ typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE
++
++ template <typename Formatter, typename T>
++ static Arg make(const T &value) { return MakeArg<Formatter>(value); }
++};
++
++#if FMT_USE_VARIADIC_TEMPLATES
++template <typename Arg, typename... Args>
++inline uint64_t make_type(const Arg &first, const Args & ... tail) {
++ return make_type(first) | (make_type(tail...) << 4);
++}
++
++#else
++
++struct ArgType {
++ uint64_t type;
++
++ ArgType() : type(0) {}
++
++ template <typename T>
++ ArgType(const T &arg) : type(make_type(arg)) {}
++};
++
++# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType()
++
++inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
++ return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) |
++ (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) |
++ (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) |
++ (t12.type << 48) | (t13.type << 52) | (t14.type << 56);
++}
++#endif
++} // namespace internal
++
++# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n
++# define FMT_MAKE_ARG_TYPE(n) T##n
++# define FMT_MAKE_ARG(n) const T##n &v##n
++# define FMT_ASSIGN_char(n) \
++ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter<char> >(v##n)
++# define FMT_ASSIGN_wchar_t(n) \
++ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter<wchar_t> >(v##n)
++
++#if FMT_USE_VARIADIC_TEMPLATES
++// Defines a variadic function returning void.
++# define FMT_VARIADIC_VOID(func, arg_type) \
++ template <typename... Args> \
++ void func(arg_type arg0, const Args & ... args) { \
++ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
++ typename ArgArray::Type array{ \
++ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
++ func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \
++ }
++
++// Defines a variadic constructor.
++# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
++ template <typename... Args> \
++ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \
++ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
++ typename ArgArray::Type array{ \
++ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
++ func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \
++ }
++
++#else
++
++# define FMT_MAKE_REF(n) \
++ fmt::internal::MakeValue< fmt::BasicFormatter<Char> >(v##n)
++# define FMT_MAKE_REF2(n) v##n
++
++// Defines a wrapper for a function taking one argument of type arg_type
++// and n additional arguments of arbitrary types.
++# define FMT_WRAP1(func, arg_type, n) \
++ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
++ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
++ const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \
++ func(arg1, fmt::ArgList( \
++ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \
++ }
++
++// Emulates a variadic function returning void on a pre-C++11 compiler.
++# define FMT_VARIADIC_VOID(func, arg_type) \
++ inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \
++ FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \
++ FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \
++ FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \
++ FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \
++ FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10)
++
++# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \
++ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
++ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
++ const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \
++ func(arg0, arg1, fmt::ArgList( \
++ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \
++ }
++
++// Emulates a variadic constructor on a pre-C++11 compiler.
++# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \
++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 10)
++#endif
++
++// Generates a comma-separated list with results of applying f to pairs
++// (argument, index).
++#define FMT_FOR_EACH1(f, x0) f(x0, 0)
++#define FMT_FOR_EACH2(f, x0, x1) \
++ FMT_FOR_EACH1(f, x0), f(x1, 1)
++#define FMT_FOR_EACH3(f, x0, x1, x2) \
++ FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2)
++#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \
++ FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3)
++#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \
++ FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4)
++#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \
++ FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5)
++#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \
++ FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6)
++#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \
++ FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7)
++#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \
++ FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8)
++#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \
++ FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9)
++
++/**
++ An error returned by an operating system or a language runtime,
++ for example a file opening error.
++*/
++class SystemError : public internal::RuntimeError {
++ private:
++ FMT_API void init(int err_code, CStringRef format_str, ArgList args);
++
++ protected:
++ int error_code_;
++
++ typedef char Char; // For FMT_VARIADIC_CTOR.
++
++ SystemError() {}
++
++ public:
++ /**
++ \rst
++ Constructs a :class:`fmt::SystemError` object with a description
++ formatted with `fmt::format_system_error`. *message* and additional
++ arguments passed into the constructor are formatted similarly to
++ `fmt::format`.
++
++ **Example**::
++
++ // This throws a SystemError with the description
++ // cannot open file 'madeup': No such file or directory
++ // or similar (system message may vary).
++ const char *filename = "madeup";
++ std::FILE *file = std::fopen(filename, "r");
++ if (!file)
++ throw fmt::SystemError(errno, "cannot open file '{}'", filename);
++ \endrst
++ */
++ SystemError(int error_code, CStringRef message) {
++ init(error_code, message, ArgList());
++ }
++ FMT_DEFAULTED_COPY_CTOR(SystemError)
++ FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef)
++
++ FMT_API ~SystemError() FMT_DTOR_NOEXCEPT;
++
++ int error_code() const { return error_code_; }
++};
++
++/**
++ \rst
++ Formats an error returned by an operating system or a language runtime,
++ for example a file opening error, and writes it to *out* in the following
++ form:
++
++ .. parsed-literal::
++ *<message>*: *<system-message>*
++
++ where *<message>* is the passed message and *<system-message>* is
++ the system message corresponding to the error code.
++ *error_code* is a system error code as given by ``errno``.
++ If *error_code* is not a valid error code such as -1, the system message
++ may look like "Unknown error -1" and is platform-dependent.
++ \endrst
++ */
++FMT_API void format_system_error(fmt::Writer &out, int error_code,
++ fmt::StringRef message) FMT_NOEXCEPT;
++
++/**
++ \rst
++ This template provides operations for formatting and writing data into
++ a character stream. The output is stored in a buffer provided by a subclass
++ such as :class:`fmt::BasicMemoryWriter`.
++
++ You can use one of the following typedefs for common character types:
++
++ +---------+----------------------+
++ | Type | Definition |
++ +=========+======================+
++ | Writer | BasicWriter<char> |
++ +---------+----------------------+
++ | WWriter | BasicWriter<wchar_t> |
++ +---------+----------------------+
++
++ \endrst
++ */
++template <typename Char>
++class BasicWriter {
++ private:
++ // Output buffer.
++ Buffer<Char> &buffer_;
++
++ FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter);
++
++ typedef typename internal::CharTraits<Char>::CharPtr CharPtr;
++
++#if FMT_SECURE_SCL
++ // Returns pointer value.
++ static Char *get(CharPtr p) { return p.base(); }
++#else
++ static Char *get(Char *p) { return p; }
++#endif
++
++ // Fills the padding around the content and returns the pointer to the
++ // content area.
++ static CharPtr fill_padding(CharPtr buffer,
++ unsigned total_size, std::size_t content_size, wchar_t fill);
++
++ // Grows the buffer by n characters and returns a pointer to the newly
++ // allocated area.
++ CharPtr grow_buffer(std::size_t n) {
++ std::size_t size = buffer_.size();
++ buffer_.resize(size + n);
++ return internal::make_ptr(&buffer_[size], n);
++ }
++
++ // Writes an unsigned decimal integer.
++ template <typename UInt>
++ Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) {
++ unsigned num_digits = internal::count_digits(value);
++ Char *ptr = get(grow_buffer(prefix_size + num_digits));
++ internal::format_decimal(ptr + prefix_size, value, num_digits);
++ return ptr;
++ }
++
++ // Writes a decimal integer.
++ template <typename Int>
++ void write_decimal(Int value) {
++ typedef typename internal::IntTraits<Int>::MainType MainType;
++ MainType abs_value = static_cast<MainType>(value);
++ if (internal::is_negative(value)) {
++ abs_value = 0 - abs_value;
++ *write_unsigned_decimal(abs_value, 1) = '-';
++ } else {
++ write_unsigned_decimal(abs_value, 0);
++ }
++ }
++
++ // Prepare a buffer for integer formatting.
++ CharPtr prepare_int_buffer(unsigned num_digits,
++ const EmptySpec &, const char *prefix, unsigned prefix_size) {
++ unsigned size = prefix_size + num_digits;
++ CharPtr p = grow_buffer(size);
++ std::uninitialized_copy(prefix, prefix + prefix_size, p);
++ return p + size - 1;
++ }
++
++ template <typename Spec>
++ CharPtr prepare_int_buffer(unsigned num_digits,
++ const Spec &spec, const char *prefix, unsigned prefix_size);
++
++ // Formats an integer.
++ template <typename T, typename Spec>
++ void write_int(T value, Spec spec);
++
++ // Formats a floating-point number (double or long double).
++ template <typename T, typename Spec>
++ void write_double(T value, const Spec &spec);
++
++ // Writes a formatted string.
++ template <typename StrChar>
++ CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec);
++
++ template <typename StrChar, typename Spec>
++ void write_str(const internal::Arg::StringValue<StrChar> &str,
++ const Spec &spec);
++
++ // This following methods are private to disallow writing wide characters
++ // and strings to a char stream. If you want to print a wide string as a
++ // pointer as std::ostream does, cast it to const void*.
++ // Do not implement!
++ void operator<<(typename internal::WCharHelper<wchar_t, Char>::Unsupported);
++ void operator<<(
++ typename internal::WCharHelper<const wchar_t *, Char>::Unsupported);
++
++ // Appends floating-point length specifier to the format string.
++ // The second argument is only used for overload resolution.
++ void append_float_length(Char *&format_ptr, long double) {
++ *format_ptr++ = 'L';
++ }
++
++ template<typename T>
++ void append_float_length(Char *&, T) {}
++
++ template <typename Impl, typename Char_, typename Spec_>
++ friend class internal::ArgFormatterBase;
++
++ template <typename Impl, typename Char_, typename Spec_>
++ friend class BasicPrintfArgFormatter;
++
++ protected:
++ /**
++ Constructs a ``BasicWriter`` object.
++ */
++ explicit BasicWriter(Buffer<Char> &b) : buffer_(b) {}
++
++ public:
++ /**
++ \rst
++ Destroys a ``BasicWriter`` object.
++ \endrst
++ */
++ virtual ~BasicWriter() {}
++
++ /**
++ Returns the total number of characters written.
++ */
++ std::size_t size() const { return buffer_.size(); }
++
++ /**
++ Returns a pointer to the output buffer content. No terminating null
++ character is appended.
++ */
++ const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; }
++
++ /**
++ Returns a pointer to the output buffer content with terminating null
++ character appended.
++ */
++ const Char *c_str() const {
++ std::size_t size = buffer_.size();
++ buffer_.reserve(size + 1);
++ buffer_[size] = '\0';
++ return &buffer_[0];
++ }
++
++ /**
++ \rst
++ Returns the content of the output buffer as an `std::string`.
++ \endrst
++ */
++ std::basic_string<Char> str() const {
++ return std::basic_string<Char>(&buffer_[0], buffer_.size());
++ }
++
++ /**
++ \rst
++ Writes formatted data.
++
++ *args* is an argument list representing arbitrary arguments.
++
++ **Example**::
++
++ MemoryWriter out;
++ out.write("Current point:\n");
++ out.write("({:+f}, {:+f})", -3.14, 3.14);
++
++ This will write the following output to the ``out`` object:
++
++ .. code-block:: none
++
++ Current point:
++ (-3.140000, +3.140000)
++
++ The output can be accessed using :func:`data()`, :func:`c_str` or
++ :func:`str` methods.
++
++ See also :ref:`syntax`.
++ \endrst
++ */
++ void write(BasicCStringRef<Char> format, ArgList args) {
++ BasicFormatter<Char>(args, *this).format(format);
++ }
++ FMT_VARIADIC_VOID(write, BasicCStringRef<Char>)
++
++ BasicWriter &operator<<(int value) {
++ write_decimal(value);
++ return *this;
++ }
++ BasicWriter &operator<<(unsigned value) {
++ return *this << IntFormatSpec<unsigned>(value);
++ }
++ BasicWriter &operator<<(long value) {
++ write_decimal(value);
++ return *this;
++ }
++ BasicWriter &operator<<(unsigned long value) {
++ return *this << IntFormatSpec<unsigned long>(value);
++ }
++ BasicWriter &operator<<(LongLong value) {
++ write_decimal(value);
++ return *this;
++ }
++
++ /**
++ \rst
++ Formats *value* and writes it to the stream.
++ \endrst
++ */
++ BasicWriter &operator<<(ULongLong value) {
++ return *this << IntFormatSpec<ULongLong>(value);
++ }
++
++ BasicWriter &operator<<(double value) {
++ write_double(value, FormatSpec());
++ return *this;
++ }
++
++ /**
++ \rst
++ Formats *value* using the general format for floating-point numbers
++ (``'g'``) and writes it to the stream.
++ \endrst
++ */
++ BasicWriter &operator<<(long double value) {
++ write_double(value, FormatSpec());
++ return *this;
++ }
++
++ /**
++ Writes a character to the stream.
++ */
++ BasicWriter &operator<<(char value) {
++ buffer_.push_back(value);
++ return *this;
++ }
++
++ BasicWriter &operator<<(
++ typename internal::WCharHelper<wchar_t, Char>::Supported value) {
++ buffer_.push_back(value);
++ return *this;
++ }
++
++ /**
++ \rst
++ Writes *value* to the stream.
++ \endrst
++ */
++ BasicWriter &operator<<(fmt::BasicStringRef<Char> value) {
++ const Char *str = value.data();
++ buffer_.append(str, str + value.size());
++ return *this;
++ }
++
++ BasicWriter &operator<<(
++ typename internal::WCharHelper<StringRef, Char>::Supported value) {
++ const char *str = value.data();
++ buffer_.append(str, str + value.size());
++ return *this;
++ }
++
++ template <typename T, typename Spec, typename FillChar>
++ BasicWriter &operator<<(IntFormatSpec<T, Spec, FillChar> spec) {
++ internal::CharTraits<Char>::convert(FillChar());
++ write_int(spec.value(), spec);
++ return *this;
++ }
++
++ template <typename StrChar>
++ BasicWriter &operator<<(const StrFormatSpec<StrChar> &spec) {
++ const StrChar *s = spec.str();
++ write_str(s, std::char_traits<Char>::length(s), spec);
++ return *this;
++ }
++
++ void clear() FMT_NOEXCEPT { buffer_.clear(); }
++
++ Buffer<Char> &buffer() FMT_NOEXCEPT { return buffer_; }
++};
++
++template <typename Char>
++template <typename StrChar>
++typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(
++ const StrChar *s, std::size_t size, const AlignSpec &spec) {
++ CharPtr out = CharPtr();
++ if (spec.width() > size) {
++ out = grow_buffer(spec.width());
++ Char fill = internal::CharTraits<Char>::cast(spec.fill());
++ if (spec.align() == ALIGN_RIGHT) {
++ std::uninitialized_fill_n(out, spec.width() - size, fill);
++ out += spec.width() - size;
++ } else if (spec.align() == ALIGN_CENTER) {
++ out = fill_padding(out, spec.width(), size, fill);
++ } else {
++ std::uninitialized_fill_n(out + size, spec.width() - size, fill);
++ }
++ } else {
++ out = grow_buffer(size);
++ }
++ std::uninitialized_copy(s, s + size, out);
++ return out;
++}
++
++template <typename Char>
++template <typename StrChar, typename Spec>
++void BasicWriter<Char>::write_str(
++ const internal::Arg::StringValue<StrChar> &s, const Spec &spec) {
++ // Check if StrChar is convertible to Char.
++ internal::CharTraits<Char>::convert(StrChar());
++ if (spec.type_ && spec.type_ != 's')
++ internal::report_unknown_type(spec.type_, "string");
++ const StrChar *str_value = s.value;
++ std::size_t str_size = s.size;
++ if (str_size == 0) {
++ if (!str_value) {
++ FMT_THROW(FormatError("string pointer is null"));
++ }
++ }
++ std::size_t precision = static_cast<std::size_t>(spec.precision_);
++ if (spec.precision_ >= 0 && precision < str_size)
++ str_size = precision;
++ write_str(str_value, str_size, spec);
++}
++
++template <typename Char>
++typename BasicWriter<Char>::CharPtr
++ BasicWriter<Char>::fill_padding(
++ CharPtr buffer, unsigned total_size,
++ std::size_t content_size, wchar_t fill) {
++ std::size_t padding = total_size - content_size;
++ std::size_t left_padding = padding / 2;
++ Char fill_char = internal::CharTraits<Char>::cast(fill);
++ std::uninitialized_fill_n(buffer, left_padding, fill_char);
++ buffer += left_padding;
++ CharPtr content = buffer;
++ std::uninitialized_fill_n(buffer + content_size,
++ padding - left_padding, fill_char);
++ return content;
++}
++
++template <typename Char>
++template <typename Spec>
++typename BasicWriter<Char>::CharPtr
++ BasicWriter<Char>::prepare_int_buffer(
++ unsigned num_digits, const Spec &spec,
++ const char *prefix, unsigned prefix_size) {
++ unsigned width = spec.width();
++ Alignment align = spec.align();
++ Char fill = internal::CharTraits<Char>::cast(spec.fill());
++ if (spec.precision() > static_cast<int>(num_digits)) {
++ // Octal prefix '0' is counted as a digit, so ignore it if precision
++ // is specified.
++ if (prefix_size > 0 && prefix[prefix_size - 1] == '0')
++ --prefix_size;
++ unsigned number_size =
++ prefix_size + internal::to_unsigned(spec.precision());
++ AlignSpec subspec(number_size, '0', ALIGN_NUMERIC);
++ if (number_size >= width)
++ return prepare_int_buffer(num_digits, subspec, prefix, prefix_size);
++ buffer_.reserve(width);
++ unsigned fill_size = width - number_size;
++ if (align != ALIGN_LEFT) {
++ CharPtr p = grow_buffer(fill_size);
++ std::uninitialized_fill(p, p + fill_size, fill);
++ }
++ CharPtr result = prepare_int_buffer(
++ num_digits, subspec, prefix, prefix_size);
++ if (align == ALIGN_LEFT) {
++ CharPtr p = grow_buffer(fill_size);
++ std::uninitialized_fill(p, p + fill_size, fill);
++ }
++ return result;
++ }
++ unsigned size = prefix_size + num_digits;
++ if (width <= size) {
++ CharPtr p = grow_buffer(size);
++ std::uninitialized_copy(prefix, prefix + prefix_size, p);
++ return p + size - 1;
++ }
++ CharPtr p = grow_buffer(width);
++ CharPtr end = p + width;
++ if (align == ALIGN_LEFT) {
++ std::uninitialized_copy(prefix, prefix + prefix_size, p);
++ p += size;
++ std::uninitialized_fill(p, end, fill);
++ } else if (align == ALIGN_CENTER) {
++ p = fill_padding(p, width, size, fill);
++ std::uninitialized_copy(prefix, prefix + prefix_size, p);
++ p += size;
++ } else {
++ if (align == ALIGN_NUMERIC) {
++ if (prefix_size != 0) {
++ p = std::uninitialized_copy(prefix, prefix + prefix_size, p);
++ size -= prefix_size;
++ }
++ } else {
++ std::uninitialized_copy(prefix, prefix + prefix_size, end - size);
++ }
++ std::uninitialized_fill(p, end - size, fill);
++ p = end;
++ }
++ return p - 1;
++}
++
++template <typename Char>
++template <typename T, typename Spec>
++void BasicWriter<Char>::write_int(T value, Spec spec) {
++ unsigned prefix_size = 0;
++ typedef typename internal::IntTraits<T>::MainType UnsignedType;
++ UnsignedType abs_value = static_cast<UnsignedType>(value);
++ char prefix[4] = "";
++ if (internal::is_negative(value)) {
++ prefix[0] = '-';
++ ++prefix_size;
++ abs_value = 0 - abs_value;
++ } else if (spec.flag(SIGN_FLAG)) {
++ prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
++ ++prefix_size;
++ }
++ switch (spec.type()) {
++ case 0: case 'd': {
++ unsigned num_digits = internal::count_digits(abs_value);
++ CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1;
++ internal::format_decimal(get(p), abs_value, 0);
++ break;
++ }
++ case 'x': case 'X': {
++ UnsignedType n = abs_value;
++ if (spec.flag(HASH_FLAG)) {
++ prefix[prefix_size++] = '0';
++ prefix[prefix_size++] = spec.type_prefix();
++ }
++ unsigned num_digits = 0;
++ do {
++ ++num_digits;
++ } while ((n >>= 4) != 0);
++ Char *p = get(prepare_int_buffer(
++ num_digits, spec, prefix, prefix_size));
++ n = abs_value;
++ const char *digits = spec.type() == 'x' ?
++ "0123456789abcdef" : "0123456789ABCDEF";
++ do {
++ *p-- = digits[n & 0xf];
++ } while ((n >>= 4) != 0);
++ break;
++ }
++ case 'b': case 'B': {
++ UnsignedType n = abs_value;
++ if (spec.flag(HASH_FLAG)) {
++ prefix[prefix_size++] = '0';
++ prefix[prefix_size++] = spec.type_prefix();
++ }
++ unsigned num_digits = 0;
++ do {
++ ++num_digits;
++ } while ((n >>= 1) != 0);
++ Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size));
++ n = abs_value;
++ do {
++ *p-- = static_cast<Char>('0' + (n & 1));
++ } while ((n >>= 1) != 0);
++ break;
++ }
++ case 'o': {
++ UnsignedType n = abs_value;
++ if (spec.flag(HASH_FLAG))
++ prefix[prefix_size++] = '0';
++ unsigned num_digits = 0;
++ do {
++ ++num_digits;
++ } while ((n >>= 3) != 0);
++ Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size));
++ n = abs_value;
++ do {
++ *p-- = static_cast<Char>('0' + (n & 7));
++ } while ((n >>= 3) != 0);
++ break;
++ }
++ case 'n': {
++ unsigned num_digits = internal::count_digits(abs_value);
++ fmt::StringRef sep = "";
++#if !(defined(ANDROID) || defined(__ANDROID__))
++ sep = internal::thousands_sep(std::localeconv());
++#endif
++ unsigned size = static_cast<unsigned>(
++ num_digits + sep.size() * ((num_digits - 1) / 3));
++ CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
++ internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep));
++ break;
++ }
++ default:
++ internal::report_unknown_type(
++ spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer");
++ break;
++ }
++}
++
++template <typename Char>
++template <typename T, typename Spec>
++void BasicWriter<Char>::write_double(T value, const Spec &spec) {
++ // Check type.
++ char type = spec.type();
++ bool upper = false;
++ switch (type) {
++ case 0:
++ type = 'g';
++ break;
++ case 'e': case 'f': case 'g': case 'a':
++ break;
++ case 'F':
++#if FMT_MSC_VER
++ // MSVC's printf doesn't support 'F'.
++ type = 'f';
++#endif
++ // Fall through.
++ case 'E': case 'G': case 'A':
++ upper = true;
++ break;
++ default:
++ internal::report_unknown_type(type, "double");
++ break;
++ }
++
++ char sign = 0;
++ // Use isnegative instead of value < 0 because the latter is always
++ // false for NaN.
++ if (internal::FPUtil::isnegative(static_cast<double>(value))) {
++ sign = '-';
++ value = -value;
++ } else if (spec.flag(SIGN_FLAG)) {
++ sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
++ }
++
++ if (internal::FPUtil::isnotanumber(value)) {
++ // Format NaN ourselves because sprintf's output is not consistent
++ // across platforms.
++ std::size_t nan_size = 4;
++ const char *nan = upper ? " NAN" : " nan";
++ if (!sign) {
++ --nan_size;
++ ++nan;
++ }
++ CharPtr out = write_str(nan, nan_size, spec);
++ if (sign)
++ *out = sign;
++ return;
++ }
++
++ if (internal::FPUtil::isinfinity(value)) {
++ // Format infinity ourselves because sprintf's output is not consistent
++ // across platforms.
++ std::size_t inf_size = 4;
++ const char *inf = upper ? " INF" : " inf";
++ if (!sign) {
++ --inf_size;
++ ++inf;
++ }
++ CharPtr out = write_str(inf, inf_size, spec);
++ if (sign)
++ *out = sign;
++ return;
++ }
++
++ std::size_t offset = buffer_.size();
++ unsigned width = spec.width();
++ if (sign) {
++ buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u));
++ if (width > 0)
++ --width;
++ ++offset;
++ }
++
++ // Build format string.
++ enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
++ Char format[MAX_FORMAT_SIZE];
++ Char *format_ptr = format;
++ *format_ptr++ = '%';
++ unsigned width_for_sprintf = width;
++ if (spec.flag(HASH_FLAG))
++ *format_ptr++ = '#';
++ if (spec.align() == ALIGN_CENTER) {
++ width_for_sprintf = 0;
++ } else {
++ if (spec.align() == ALIGN_LEFT)
++ *format_ptr++ = '-';
++ if (width != 0)
++ *format_ptr++ = '*';
++ }
++ if (spec.precision() >= 0) {
++ *format_ptr++ = '.';
++ *format_ptr++ = '*';
++ }
++
++ append_float_length(format_ptr, value);
++ *format_ptr++ = type;
++ *format_ptr = '\0';
++
++ // Format using snprintf.
++ Char fill = internal::CharTraits<Char>::cast(spec.fill());
++ unsigned n = 0;
++ Char *start = FMT_NULL;
++ for (;;) {
++ std::size_t buffer_size = buffer_.capacity() - offset;
++#if FMT_MSC_VER
++ // MSVC's vsnprintf_s doesn't work with zero size, so reserve
++ // space for at least one extra character to make the size non-zero.
++ // Note that the buffer's capacity will increase by more than 1.
++ if (buffer_size == 0) {
++ buffer_.reserve(offset + 1);
++ buffer_size = buffer_.capacity() - offset;
++ }
++#endif
++ start = &buffer_[offset];
++ int result = internal::CharTraits<Char>::format_float(
++ start, buffer_size, format, width_for_sprintf, spec.precision(), value);
++ if (result >= 0) {
++ n = internal::to_unsigned(result);
++ if (offset + n < buffer_.capacity())
++ break; // The buffer is large enough - continue with formatting.
++ buffer_.reserve(offset + n + 1);
++ } else {
++ // If result is negative we ask to increase the capacity by at least 1,
++ // but as std::vector, the buffer grows exponentially.
++ buffer_.reserve(buffer_.capacity() + 1);
++ }
++ }
++ if (sign) {
++ if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
++ *start != ' ') {
++ *(start - 1) = sign;
++ sign = 0;
++ } else {
++ *(start - 1) = fill;
++ }
++ ++n;
++ }
++ if (spec.align() == ALIGN_CENTER && spec.width() > n) {
++ width = spec.width();
++ CharPtr p = grow_buffer(width);
++ std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char));
++ fill_padding(p, spec.width(), n, fill);
++ return;
++ }
++ if (spec.fill() != ' ' || sign) {
++ while (*start == ' ')
++ *start++ = fill;
++ if (sign)
++ *(start - 1) = sign;
++ }
++ grow_buffer(n);
++}
++
++/**
++ \rst
++ This class template provides operations for formatting and writing data
++ into a character stream. The output is stored in a memory buffer that grows
++ dynamically.
++
++ You can use one of the following typedefs for common character types
++ and the standard allocator:
++
++ +---------------+-----------------------------------------------------+
++ | Type | Definition |
++ +===============+=====================================================+
++ | MemoryWriter | BasicMemoryWriter<char, std::allocator<char>> |
++ +---------------+-----------------------------------------------------+
++ | WMemoryWriter | BasicMemoryWriter<wchar_t, std::allocator<wchar_t>> |
++ +---------------+-----------------------------------------------------+
++
++ **Example**::
++
++ MemoryWriter out;
++ out << "The answer is " << 42 << "\n";
++ out.write("({:+f}, {:+f})", -3.14, 3.14);
++
++ This will write the following output to the ``out`` object:
++
++ .. code-block:: none
++
++ The answer is 42
++ (-3.140000, +3.140000)
++
++ The output can be converted to an ``std::string`` with ``out.str()`` or
++ accessed as a C string with ``out.c_str()``.
++ \endrst
++ */
++template <typename Char, typename Allocator = std::allocator<Char> >
++class BasicMemoryWriter : public BasicWriter<Char> {
++ private:
++ internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE, Allocator> buffer_;
++
++ public:
++ explicit BasicMemoryWriter(const Allocator& alloc = Allocator())
++ : BasicWriter<Char>(buffer_), buffer_(alloc) {}
++
++#if FMT_USE_RVALUE_REFERENCES
++ /**
++ \rst
++ Constructs a :class:`fmt::BasicMemoryWriter` object moving the content
++ of the other object to it.
++ \endrst
++ */
++ BasicMemoryWriter(BasicMemoryWriter &&other)
++ : BasicWriter<Char>(buffer_), buffer_(std::move(other.buffer_)) {
++ }
++
++ /**
++ \rst
++ Moves the content of the other ``BasicMemoryWriter`` object to this one.
++ \endrst
++ */
++ BasicMemoryWriter &operator=(BasicMemoryWriter &&other) {
++ buffer_ = std::move(other.buffer_);
++ return *this;
++ }
++#endif
++};
++
++typedef BasicMemoryWriter<char> MemoryWriter;
++typedef BasicMemoryWriter<wchar_t> WMemoryWriter;
++
++/**
++ \rst
++ This class template provides operations for formatting and writing data
++ into a fixed-size array. For writing into a dynamically growing buffer
++ use :class:`fmt::BasicMemoryWriter`.
++
++ Any write method will throw ``std::runtime_error`` if the output doesn't fit
++ into the array.
++
++ You can use one of the following typedefs for common character types:
++
++ +--------------+---------------------------+
++ | Type | Definition |
++ +==============+===========================+
++ | ArrayWriter | BasicArrayWriter<char> |
++ +--------------+---------------------------+
++ | WArrayWriter | BasicArrayWriter<wchar_t> |
++ +--------------+---------------------------+
++ \endrst
++ */
++template <typename Char>
++class BasicArrayWriter : public BasicWriter<Char> {
++ private:
++ internal::FixedBuffer<Char> buffer_;
++
++ public:
++ /**
++ \rst
++ Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the
++ given size.
++ \endrst
++ */
++ BasicArrayWriter(Char *array, std::size_t size)
++ : BasicWriter<Char>(buffer_), buffer_(array, size) {}
++
++ /**
++ \rst
++ Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the
++ size known at compile time.
++ \endrst
++ */
++ template <std::size_t SIZE>
++ explicit BasicArrayWriter(Char (&array)[SIZE])
++ : BasicWriter<Char>(buffer_), buffer_(array, SIZE) {}
++};
++
++typedef BasicArrayWriter<char> ArrayWriter;
++typedef BasicArrayWriter<wchar_t> WArrayWriter;
++
++// Reports a system error without throwing an exception.
++// Can be used to report errors from destructors.
++FMT_API void report_system_error(int error_code,
++ StringRef message) FMT_NOEXCEPT;
++
++#if FMT_USE_WINDOWS_H
++
++/** A Windows error. */
++class WindowsError : public SystemError {
++ private:
++ FMT_API void init(int error_code, CStringRef format_str, ArgList args);
++
++ public:
++ /**
++ \rst
++ Constructs a :class:`fmt::WindowsError` object with the description
++ of the form
++
++ .. parsed-literal::
++ *<message>*: *<system-message>*
++
++ where *<message>* is the formatted message and *<system-message>* is the
++ system message corresponding to the error code.
++ *error_code* is a Windows error code as given by ``GetLastError``.
++ If *error_code* is not a valid error code such as -1, the system message
++ will look like "error -1".
++
++ **Example**::
++
++ // This throws a WindowsError with the description
++ // cannot open file 'madeup': The system cannot find the file specified.
++ // or similar (system message may vary).
++ const char *filename = "madeup";
++ LPOFSTRUCT of = LPOFSTRUCT();
++ HFILE file = OpenFile(filename, &of, OF_READ);
++ if (file == HFILE_ERROR) {
++ throw fmt::WindowsError(GetLastError(),
++ "cannot open file '{}'", filename);
++ }
++ \endrst
++ */
++ WindowsError(int error_code, CStringRef message) {
++ init(error_code, message, ArgList());
++ }
++ FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef)
++};
++
++// Reports a Windows error without throwing an exception.
++// Can be used to report errors from destructors.
++FMT_API void report_windows_error(int error_code,
++ StringRef message) FMT_NOEXCEPT;
++
++#endif
++
++enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
++
++/**
++ Formats a string and prints it to stdout using ANSI escape sequences
++ to specify color (experimental).
++ Example:
++ print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
++ */
++FMT_API void print_colored(Color c, CStringRef format, ArgList args);
++
++/**
++ \rst
++ Formats arguments and returns the result as a string.
++
++ **Example**::
++
++ std::string message = format("The answer is {}", 42);
++ \endrst
++*/
++inline std::string format(CStringRef format_str, ArgList args) {
++ MemoryWriter w;
++ w.write(format_str, args);
++ return w.str();
++}
++
++inline std::wstring format(WCStringRef format_str, ArgList args) {
++ WMemoryWriter w;
++ w.write(format_str, args);
++ return w.str();
++}
++
++/**
++ \rst
++ Prints formatted data to the file *f*.
++
++ **Example**::
++
++ print(stderr, "Don't {}!", "panic");
++ \endrst
++ */
++FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args);
++
++/**
++ \rst
++ Prints formatted data to ``stdout``.
++
++ **Example**::
++
++ print("Elapsed time: {0:.2f} seconds", 1.23);
++ \endrst
++ */
++FMT_API void print(CStringRef format_str, ArgList args);
++
++/**
++ Fast integer formatter.
++ */
++class FormatInt {
++ private:
++ // Buffer should be large enough to hold all digits (digits10 + 1),
++ // a sign and a null character.
++ enum {BUFFER_SIZE = std::numeric_limits<ULongLong>::digits10 + 3};
++ mutable char buffer_[BUFFER_SIZE];
++ char *str_;
++
++ // Formats value in reverse and returns the number of digits.
++ char *format_decimal(ULongLong value) {
++ char *buffer_end = buffer_ + BUFFER_SIZE - 1;
++ while (value >= 100) {
++ // Integer division is slow so do it for a group of two digits instead
++ // of for every digit. The idea comes from the talk by Alexandrescu
++ // "Three Optimization Tips for C++". See speed-test for a comparison.
++ unsigned index = static_cast<unsigned>((value % 100) * 2);
++ value /= 100;
++ *--buffer_end = internal::Data::DIGITS[index + 1];
++ *--buffer_end = internal::Data::DIGITS[index];
++ }
++ if (value < 10) {
++ *--buffer_end = static_cast<char>('0' + value);
++ return buffer_end;
++ }
++ unsigned index = static_cast<unsigned>(value * 2);
++ *--buffer_end = internal::Data::DIGITS[index + 1];
++ *--buffer_end = internal::Data::DIGITS[index];
++ return buffer_end;
++ }
++
++ void FormatSigned(LongLong value) {
++ ULongLong abs_value = static_cast<ULongLong>(value);
++ bool negative = value < 0;
++ if (negative)
++ abs_value = 0 - abs_value;
++ str_ = format_decimal(abs_value);
++ if (negative)
++ *--str_ = '-';
++ }
++
++ public:
++ explicit FormatInt(int value) { FormatSigned(value); }
++ explicit FormatInt(long value) { FormatSigned(value); }
++ explicit FormatInt(LongLong value) { FormatSigned(value); }
++ explicit FormatInt(unsigned value) : str_(format_decimal(value)) {}
++ explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}
++ explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {}
++
++ /** Returns the number of characters written to the output buffer. */
++ std::size_t size() const {
++ return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1);
++ }
++
++ /**
++ Returns a pointer to the output buffer content. No terminating null
++ character is appended.
++ */
++ const char *data() const { return str_; }
++
++ /**
++ Returns a pointer to the output buffer content with terminating null
++ character appended.
++ */
++ const char *c_str() const {
++ buffer_[BUFFER_SIZE - 1] = '\0';
++ return str_;
++ }
++
++ /**
++ \rst
++ Returns the content of the output buffer as an ``std::string``.
++ \endrst
++ */
++ std::string str() const { return std::string(str_, size()); }
++};
++
++// Formats a decimal integer value writing into buffer and returns
++// a pointer to the end of the formatted string. This function doesn't
++// write a terminating null character.
++template <typename T>
++inline void format_decimal(char *&buffer, T value) {
++ typedef typename internal::IntTraits<T>::MainType MainType;
++ MainType abs_value = static_cast<MainType>(value);
++ if (internal::is_negative(value)) {
++ *buffer++ = '-';
++ abs_value = 0 - abs_value;
++ }
++ if (abs_value < 100) {
++ if (abs_value < 10) {
++ *buffer++ = static_cast<char>('0' + abs_value);
++ return;
++ }
++ unsigned index = static_cast<unsigned>(abs_value * 2);
++ *buffer++ = internal::Data::DIGITS[index];
++ *buffer++ = internal::Data::DIGITS[index + 1];
++ return;
++ }
++ unsigned num_digits = internal::count_digits(abs_value);
++ internal::format_decimal(buffer, abs_value, num_digits);
++ buffer += num_digits;
++}
++
++/**
++ \rst
++ Returns a named argument for formatting functions.
++
++ **Example**::
++
++ print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
++
++ \endrst
++ */
++template <typename T>
++inline internal::NamedArgWithType<char, T> arg(StringRef name, const T &arg) {
++ return internal::NamedArgWithType<char, T>(name, arg);
++}
++
++template <typename T>
++inline internal::NamedArgWithType<wchar_t, T> arg(WStringRef name, const T &arg) {
++ return internal::NamedArgWithType<wchar_t, T>(name, arg);
++}
++
++// The following two functions are deleted intentionally to disable
++// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
++template <typename Char>
++void arg(StringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
++template <typename Char>
++void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
++}
++
++#if FMT_GCC_VERSION
++// Use the system_header pragma to suppress warnings about variadic macros
++// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't
++// work. It is used at the end because we want to suppress as little warnings
++// as possible.
++# pragma GCC system_header
++#endif
++
++// This is used to work around VC++ bugs in handling variadic macros.
++#define FMT_EXPAND(args) args
++
++// Returns the number of arguments.
++// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s.
++#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N())
++#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__))
++#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
++#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
++
++#define FMT_FOR_EACH_(N, f, ...) \
++ FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__))
++#define FMT_FOR_EACH(f, ...) \
++ FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__))
++
++#define FMT_ADD_ARG_NAME(type, index) type arg##index
++#define FMT_GET_ARG_NAME(type, index) arg##index
++
++#if FMT_USE_VARIADIC_TEMPLATES
++# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \
++ template <typename... Args> \
++ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
++ const Args & ... args) { \
++ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
++ typename ArgArray::Type array{ \
++ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
++ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \
++ fmt::ArgList(fmt::internal::make_type(args...), array)); \
++ }
++#else
++// Defines a wrapper for a function taking __VA_ARGS__ arguments
++// and n additional arguments of arbitrary types.
++# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \
++ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
++ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
++ FMT_GEN(n, FMT_MAKE_ARG)) { \
++ fmt::internal::ArgArray<n>::Type arr; \
++ FMT_GEN(n, FMT_ASSIGN_##Char); \
++ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \
++ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \
++ }
++
++# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \
++ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \
++ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \
++ } \
++ FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \
++ FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__)
++#endif // FMT_USE_VARIADIC_TEMPLATES
++
++/**
++ \rst
++ Defines a variadic function with the specified return type, function name
++ and argument types passed as variable arguments to this macro.
++
++ **Example**::
++
++ void print_error(const char *file, int line, const char *format,
++ fmt::ArgList args) {
++ fmt::print("{}: {}: ", file, line);
++ fmt::print(format, args);
++ }
++ FMT_VARIADIC(void, print_error, const char *, int, const char *)
++
++ ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that
++ don't implement variadic templates. You don't have to use this macro if
++ you don't need legacy compiler support and can use variadic templates
++ directly::
++
++ template <typename... Args>
++ void print_error(const char *file, int line, const char *format,
++ const Args & ... args) {
++ fmt::print("{}: {}: ", file, line);
++ fmt::print(format, args...);
++ }
++ \endrst
++ */
++#define FMT_VARIADIC(ReturnType, func, ...) \
++ FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__)
++
++#define FMT_VARIADIC_W(ReturnType, func, ...) \
++ FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__)
++
++#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id)
++
++#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id)
++
++/**
++ \rst
++ Convenient macro to capture the arguments' names and values into several
++ ``fmt::arg(name, value)``.
++
++ **Example**::
++
++ int x = 1, y = 2;
++ print("point: ({x}, {y})", FMT_CAPTURE(x, y));
++ // same as:
++ // print("point: ({x}, {y})", arg("x", x), arg("y", y));
++
++ \endrst
++ */
++#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__)
++
++#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__)
++
++namespace fmt {
++FMT_VARIADIC(std::string, format, CStringRef)
++FMT_VARIADIC_W(std::wstring, format, WCStringRef)
++FMT_VARIADIC(void, print, CStringRef)
++FMT_VARIADIC(void, print, std::FILE *, CStringRef)
++FMT_VARIADIC(void, print_colored, Color, CStringRef)
++
++namespace internal {
++template <typename Char>
++inline bool is_name_start(Char c) {
++ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
++}
++
++// Parses an unsigned integer advancing s to the end of the parsed input.
++// This function assumes that the first character of s is a digit.
++template <typename Char>
++unsigned parse_nonnegative_int(const Char *&s) {
++ assert('0' <= *s && *s <= '9');
++ unsigned value = 0;
++ do {
++ unsigned new_value = value * 10 + (*s++ - '0');
++ // Check if value wrapped around.
++ if (new_value < value) {
++ value = (std::numeric_limits<unsigned>::max)();
++ break;
++ }
++ value = new_value;
++ } while ('0' <= *s && *s <= '9');
++ // Convert to unsigned to prevent a warning.
++ unsigned max_int = (std::numeric_limits<int>::max)();
++ if (value > max_int)
++ FMT_THROW(FormatError("number is too big"));
++ return value;
++}
++
++inline void require_numeric_argument(const Arg &arg, char spec) {
++ if (arg.type > Arg::LAST_NUMERIC_TYPE) {
++ std::string message =
++ fmt::format("format specifier '{}' requires numeric argument", spec);
++ FMT_THROW(fmt::FormatError(message));
++ }
++}
++
++template <typename Char>
++void check_sign(const Char *&s, const Arg &arg) {
++ char sign = static_cast<char>(*s);
++ require_numeric_argument(arg, sign);
++ if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
++ FMT_THROW(FormatError(fmt::format(
++ "format specifier '{}' requires signed argument", sign)));
++ }
++ ++s;
++}
++} // namespace internal
++
++template <typename Char, typename AF>
++inline internal::Arg BasicFormatter<Char, AF>::get_arg(
++ BasicStringRef<Char> arg_name, const char *&error) {
++ if (check_no_auto_index(error)) {
++ map_.init(args());
++ const internal::Arg *arg = map_.find(arg_name);
++ if (arg)
++ return *arg;
++ error = "argument not found";
++ }
++ return internal::Arg();
++}
++
++template <typename Char, typename AF>
++inline internal::Arg BasicFormatter<Char, AF>::parse_arg_index(const Char *&s) {
++ const char *error = FMT_NULL;
++ internal::Arg arg = *s < '0' || *s > '9' ?
++ next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error);
++ if (error) {
++ FMT_THROW(FormatError(
++ *s != '}' && *s != ':' ? "invalid format string" : error));
++ }
++ return arg;
++}
++
++template <typename Char, typename AF>
++inline internal::Arg BasicFormatter<Char, AF>::parse_arg_name(const Char *&s) {
++ assert(internal::is_name_start(*s));
++ const Char *start = s;
++ Char c;
++ do {
++ c = *++s;
++ } while (internal::is_name_start(c) || ('0' <= c && c <= '9'));
++ const char *error = FMT_NULL;
++ internal::Arg arg = get_arg(BasicStringRef<Char>(start, s - start), error);
++ if (error)
++ FMT_THROW(FormatError(error));
++ return arg;
++}
++
++template <typename Char, typename ArgFormatter>
++const Char *BasicFormatter<Char, ArgFormatter>::format(
++ const Char *&format_str, const internal::Arg &arg) {
++ using internal::Arg;
++ const Char *s = format_str;
++ typename ArgFormatter::SpecType spec;
++ if (*s == ':') {
++ if (arg.type == Arg::CUSTOM) {
++ arg.custom.format(this, arg.custom.value, &s);
++ return s;
++ }
++ ++s;
++ // Parse fill and alignment.
++ if (Char c = *s) {
++ const Char *p = s + 1;
++ spec.align_ = ALIGN_DEFAULT;
++ do {
++ switch (*p) {
++ case '<':
++ spec.align_ = ALIGN_LEFT;
++ break;
++ case '>':
++ spec.align_ = ALIGN_RIGHT;
++ break;
++ case '=':
++ spec.align_ = ALIGN_NUMERIC;
++ break;
++ case '^':
++ spec.align_ = ALIGN_CENTER;
++ break;
++ }
++ if (spec.align_ != ALIGN_DEFAULT) {
++ if (p != s) {
++ if (c == '}') break;
++ if (c == '{')
++ FMT_THROW(FormatError("invalid fill character '{'"));
++ s += 2;
++ spec.fill_ = c;
++ } else ++s;
++ if (spec.align_ == ALIGN_NUMERIC)
++ require_numeric_argument(arg, '=');
++ break;
++ }
++ } while (--p >= s);
++ }
++
++ // Parse sign.
++ switch (*s) {
++ case '+':
++ check_sign(s, arg);
++ spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
++ break;
++ case '-':
++ check_sign(s, arg);
++ spec.flags_ |= MINUS_FLAG;
++ break;
++ case ' ':
++ check_sign(s, arg);
++ spec.flags_ |= SIGN_FLAG;
++ break;
++ }
++
++ if (*s == '#') {
++ require_numeric_argument(arg, '#');
++ spec.flags_ |= HASH_FLAG;
++ ++s;
++ }
++
++ // Parse zero flag.
++ if (*s == '0') {
++ require_numeric_argument(arg, '0');
++ spec.align_ = ALIGN_NUMERIC;
++ spec.fill_ = '0';
++ ++s;
++ }
++
++ // Parse width.
++ if ('0' <= *s && *s <= '9') {
++ spec.width_ = internal::parse_nonnegative_int(s);
++ } else if (*s == '{') {
++ ++s;
++ Arg width_arg = internal::is_name_start(*s) ?
++ parse_arg_name(s) : parse_arg_index(s);
++ if (*s++ != '}')
++ FMT_THROW(FormatError("invalid format string"));
++ ULongLong value = 0;
++ switch (width_arg.type) {
++ case Arg::INT:
++ if (width_arg.int_value < 0)
++ FMT_THROW(FormatError("negative width"));
++ value = width_arg.int_value;
++ break;
++ case Arg::UINT:
++ value = width_arg.uint_value;
++ break;
++ case Arg::LONG_LONG:
++ if (width_arg.long_long_value < 0)
++ FMT_THROW(FormatError("negative width"));
++ value = width_arg.long_long_value;
++ break;
++ case Arg::ULONG_LONG:
++ value = width_arg.ulong_long_value;
++ break;
++ default:
++ FMT_THROW(FormatError("width is not integer"));
++ }
++ if (value > (std::numeric_limits<int>::max)())
++ FMT_THROW(FormatError("number is too big"));
++ spec.width_ = static_cast<int>(value);
++ }
++
++ // Parse precision.
++ if (*s == '.') {
++ ++s;
++ spec.precision_ = 0;
++ if ('0' <= *s && *s <= '9') {
++ spec.precision_ = internal::parse_nonnegative_int(s);
++ } else if (*s == '{') {
++ ++s;
++ Arg precision_arg = internal::is_name_start(*s) ?
++ parse_arg_name(s) : parse_arg_index(s);
++ if (*s++ != '}')
++ FMT_THROW(FormatError("invalid format string"));
++ ULongLong value = 0;
++ switch (precision_arg.type) {
++ case Arg::INT:
++ if (precision_arg.int_value < 0)
++ FMT_THROW(FormatError("negative precision"));
++ value = precision_arg.int_value;
++ break;
++ case Arg::UINT:
++ value = precision_arg.uint_value;
++ break;
++ case Arg::LONG_LONG:
++ if (precision_arg.long_long_value < 0)
++ FMT_THROW(FormatError("negative precision"));
++ value = precision_arg.long_long_value;
++ break;
++ case Arg::ULONG_LONG:
++ value = precision_arg.ulong_long_value;
++ break;
++ default:
++ FMT_THROW(FormatError("precision is not integer"));
++ }
++ if (value > (std::numeric_limits<int>::max)())
++ FMT_THROW(FormatError("number is too big"));
++ spec.precision_ = static_cast<int>(value);
++ } else {
++ FMT_THROW(FormatError("missing precision specifier"));
++ }
++ if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) {
++ FMT_THROW(FormatError(
++ fmt::format("precision not allowed in {} format specifier",
++ arg.type == Arg::POINTER ? "pointer" : "integer")));
++ }
++ }
++
++ // Parse type.
++ if (*s != '}' && *s)
++ spec.type_ = static_cast<char>(*s++);
++ }
++
++ if (*s++ != '}')
++ FMT_THROW(FormatError("missing '}' in format string"));
++
++ // Format argument.
++ ArgFormatter(*this, spec, s - 1).visit(arg);
++ return s;
++}
++
++template <typename Char, typename AF>
++void BasicFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
++ const Char *s = format_str.c_str();
++ const Char *start = s;
++ while (*s) {
++ Char c = *s++;
++ if (c != '{' && c != '}') continue;
++ if (*s == c) {
++ write(writer_, start, s);
++ start = ++s;
++ continue;
++ }
++ if (c == '}')
++ FMT_THROW(FormatError("unmatched '}' in format string"));
++ write(writer_, start, s - 1);
++ internal::Arg arg = internal::is_name_start(*s) ?
++ parse_arg_name(s) : parse_arg_index(s);
++ start = s = format(s, arg);
++ }
++ write(writer_, start, s);
++}
++
++template <typename Char, typename It>
++struct ArgJoin {
++ It first;
++ It last;
++ BasicCStringRef<Char> sep;
++
++ ArgJoin(It first, It last, const BasicCStringRef<Char>& sep) :
++ first(first),
++ last(last),
++ sep(sep) {}
++};
++
++template <typename It>
++ArgJoin<char, It> join(It first, It last, const BasicCStringRef<char>& sep) {
++ return ArgJoin<char, It>(first, last, sep);
++}
++
++template <typename It>
++ArgJoin<wchar_t, It> join(It first, It last, const BasicCStringRef<wchar_t>& sep) {
++ return ArgJoin<wchar_t, It>(first, last, sep);
++}
++
++#if FMT_HAS_GXX_CXX11
++template <typename Range>
++auto join(const Range& range, const BasicCStringRef<char>& sep)
++ -> ArgJoin<char, decltype(std::begin(range))> {
++ return join(std::begin(range), std::end(range), sep);
++}
++
++template <typename Range>
++auto join(const Range& range, const BasicCStringRef<wchar_t>& sep)
++ -> ArgJoin<wchar_t, decltype(std::begin(range))> {
++ return join(std::begin(range), std::end(range), sep);
++}
++#endif
++
++template <typename ArgFormatter, typename Char, typename It>
++void format_arg(fmt::BasicFormatter<Char, ArgFormatter> &f,
++ const Char *&format_str, const ArgJoin<Char, It>& e) {
++ const Char* end = format_str;
++ if (*end == ':')
++ ++end;
++ while (*end && *end != '}')
++ ++end;
++ if (*end != '}')
++ FMT_THROW(FormatError("missing '}' in format string"));
++
++ It it = e.first;
++ if (it != e.last) {
++ const Char* save = format_str;
++ f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter> >(*it++));
++ while (it != e.last) {
++ f.writer().write(e.sep);
++ format_str = save;
++ f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter> >(*it++));
++ }
++ }
++ format_str = end + 1;
++}
++} // namespace fmt
++
++#if FMT_USE_USER_DEFINED_LITERALS
++namespace fmt {
++namespace internal {
++
++template <typename Char>
++struct UdlFormat {
++ const Char *str;
++
++ template <typename... Args>
++ auto operator()(Args && ... args) const
++ -> decltype(format(str, std::forward<Args>(args)...)) {
++ return format(str, std::forward<Args>(args)...);
++ }
++};
++
++template <typename Char>
++struct UdlArg {
++ const Char *str;
++
++ template <typename T>
++ NamedArgWithType<Char, T> operator=(T &&value) const {
++ return {str, std::forward<T>(value)};
++ }
++};
++
++} // namespace internal
++
++inline namespace literals {
++
++/**
++ \rst
++ C++11 literal equivalent of :func:`fmt::format`.
++
++ **Example**::
++
++ using namespace fmt::literals;
++ std::string message = "The answer is {}"_format(42);
++ \endrst
++ */
++inline internal::UdlFormat<char>
++operator"" _format(const char *s, std::size_t) { return {s}; }
++inline internal::UdlFormat<wchar_t>
++operator"" _format(const wchar_t *s, std::size_t) { return {s}; }
++
++/**
++ \rst
++ C++11 literal equivalent of :func:`fmt::arg`.
++
++ **Example**::
++
++ using namespace fmt::literals;
++ print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
++ \endrst
++ */
++inline internal::UdlArg<char>
++operator"" _a(const char *s, std::size_t) { return {s}; }
++inline internal::UdlArg<wchar_t>
++operator"" _a(const wchar_t *s, std::size_t) { return {s}; }
++
++} // inline namespace literals
++} // namespace fmt
++#endif // FMT_USE_USER_DEFINED_LITERALS
++
++// Restore warnings.
++#if FMT_GCC_VERSION >= 406
++# pragma GCC diagnostic pop
++#endif
++
++#if defined(__clang__) && !defined(FMT_ICC_VERSION)
++# pragma clang diagnostic pop
++#endif
++
++#ifdef FMT_HEADER_ONLY
++# define FMT_FUNC inline
++# include "format.cc"
++#else
++# define FMT_FUNC
++#endif
++
++#endif // FMT_FORMAT_H_
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc
+new file mode 100644
+index 00000000..2d443f73
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc
+@@ -0,0 +1,35 @@
++/*
++ Formatting library for C++ - std::ostream support
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ For the license information refer to format.h.
++ */
++
++#include "ostream.h"
++
++namespace fmt {
++
++namespace internal {
++FMT_FUNC void write(std::ostream &os, Writer &w) {
++ const char *data = w.data();
++ typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
++ UnsignedStreamSize size = w.size();
++ UnsignedStreamSize max_size =
++ internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
++ do {
++ UnsignedStreamSize n = size <= max_size ? size : max_size;
++ os.write(data, static_cast<std::streamsize>(n));
++ data += n;
++ size -= n;
++ } while (size != 0);
++}
++}
++
++FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
++ MemoryWriter w;
++ w.write(format_str, args);
++ internal::write(os, w);
++}
++} // namespace fmt
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h
+new file mode 100644
+index 00000000..84a02d17
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h
+@@ -0,0 +1,105 @@
++/*
++ Formatting library for C++ - std::ostream support
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ For the license information refer to format.h.
++ */
++
++#ifndef FMT_OSTREAM_H_
++#define FMT_OSTREAM_H_
++
++#include "format.h"
++#include <ostream>
++
++namespace fmt {
++
++namespace internal {
++
++template <class Char>
++class FormatBuf : public std::basic_streambuf<Char> {
++ private:
++ typedef typename std::basic_streambuf<Char>::int_type int_type;
++ typedef typename std::basic_streambuf<Char>::traits_type traits_type;
++
++ Buffer<Char> &buffer_;
++
++ public:
++ FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
++
++ protected:
++ // The put-area is actually always empty. This makes the implementation
++ // simpler and has the advantage that the streambuf and the buffer are always
++ // in sync and sputc never writes into uninitialized memory. The obvious
++ // disadvantage is that each call to sputc always results in a (virtual) call
++ // to overflow. There is no disadvantage here for sputn since this always
++ // results in a call to xsputn.
++
++ int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
++ if (!traits_type::eq_int_type(ch, traits_type::eof()))
++ buffer_.push_back(static_cast<Char>(ch));
++ return ch;
++ }
++
++ std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
++ buffer_.append(s, s + count);
++ return count;
++ }
++};
++
++Yes &convert(std::ostream &);
++
++struct DummyStream : std::ostream {
++ DummyStream(); // Suppress a bogus warning in MSVC.
++ // Hide all operator<< overloads from std::ostream.
++ void operator<<(Null<>);
++};
++
++No &operator<<(std::ostream &, int);
++
++template<typename T>
++struct ConvertToIntImpl<T, true> {
++ // Convert to int only if T doesn't have an overloaded operator<<.
++ enum {
++ value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
++ };
++};
++
++// Write the content of w to os.
++FMT_API void write(std::ostream &os, Writer &w);
++} // namespace internal
++
++// Formats a value.
++template <typename Char, typename ArgFormatter_, typename T>
++void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
++ const Char *&format_str, const T &value) {
++ internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
++
++ internal::FormatBuf<Char> format_buf(buffer);
++ std::basic_ostream<Char> output(&format_buf);
++ output << value;
++
++ BasicStringRef<Char> str(&buffer[0], buffer.size());
++ typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
++ format_str = f.format(format_str, MakeArg(str));
++}
++
++/**
++ \rst
++ Prints formatted data to the stream *os*.
++
++ **Example**::
++
++ print(cerr, "Don't {}!", "panic");
++ \endrst
++ */
++FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
++FMT_VARIADIC(void, print, std::ostream &, CStringRef)
++} // namespace fmt
++
++#ifdef FMT_HEADER_ONLY
++# include "ostream.cc"
++#endif
++
++#endif // FMT_OSTREAM_H_
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc
+new file mode 100644
+index 00000000..356668c1
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc
+@@ -0,0 +1,241 @@
++/*
++ A C++ interface to POSIX functions.
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ For the license information refer to format.h.
++ */
++
++// Disable bogus MSVC warnings.
++#ifndef _CRT_SECURE_NO_WARNINGS
++# define _CRT_SECURE_NO_WARNINGS
++#endif
++
++#include "posix.h"
++
++#include <limits.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#ifndef _WIN32
++# include <unistd.h>
++#else
++# ifndef WIN32_LEAN_AND_MEAN
++# define WIN32_LEAN_AND_MEAN
++# endif
++# include <windows.h>
++# include <io.h>
++
++# define O_CREAT _O_CREAT
++# define O_TRUNC _O_TRUNC
++
++# ifndef S_IRUSR
++# define S_IRUSR _S_IREAD
++# endif
++
++# ifndef S_IWUSR
++# define S_IWUSR _S_IWRITE
++# endif
++
++# ifdef __MINGW32__
++# define _SH_DENYNO 0x40
++# endif
++
++#endif // _WIN32
++
++#ifdef fileno
++# undef fileno
++#endif
++
++namespace {
++#ifdef _WIN32
++// Return type of read and write functions.
++typedef int RWResult;
++
++// On Windows the count argument to read and write is unsigned, so convert
++// it from size_t preventing integer overflow.
++inline unsigned convert_rwcount(std::size_t count) {
++ return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
++}
++#else
++// Return type of read and write functions.
++typedef ssize_t RWResult;
++
++inline std::size_t convert_rwcount(std::size_t count) { return count; }
++#endif
++}
++
++fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
++ if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
++ fmt::report_system_error(errno, "cannot close file");
++}
++
++fmt::BufferedFile::BufferedFile(
++ fmt::CStringRef filename, fmt::CStringRef mode) {
++ FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
++ if (!file_)
++ FMT_THROW(SystemError(errno, "cannot open file {}", filename));
++}
++
++void fmt::BufferedFile::close() {
++ if (!file_)
++ return;
++ int result = FMT_SYSTEM(fclose(file_));
++ file_ = FMT_NULL;
++ if (result != 0)
++ FMT_THROW(SystemError(errno, "cannot close file"));
++}
++
++// A macro used to prevent expansion of fileno on broken versions of MinGW.
++#define FMT_ARGS
++
++int fmt::BufferedFile::fileno() const {
++ int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
++ if (fd == -1)
++ FMT_THROW(SystemError(errno, "cannot get file descriptor"));
++ return fd;
++}
++
++fmt::File::File(fmt::CStringRef path, int oflag) {
++ int mode = S_IRUSR | S_IWUSR;
++#if defined(_WIN32) && !defined(__MINGW32__)
++ fd_ = -1;
++ FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
++#else
++ FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
++#endif
++ if (fd_ == -1)
++ FMT_THROW(SystemError(errno, "cannot open file {}", path));
++}
++
++fmt::File::~File() FMT_NOEXCEPT {
++ // Don't retry close in case of EINTR!
++ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
++ if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
++ fmt::report_system_error(errno, "cannot close file");
++}
++
++void fmt::File::close() {
++ if (fd_ == -1)
++ return;
++ // Don't retry close in case of EINTR!
++ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
++ int result = FMT_POSIX_CALL(close(fd_));
++ fd_ = -1;
++ if (result != 0)
++ FMT_THROW(SystemError(errno, "cannot close file"));
++}
++
++fmt::LongLong fmt::File::size() const {
++#ifdef _WIN32
++ // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
++ // is less than 0x0500 as is the case with some default MinGW builds.
++ // Both functions support large file sizes.
++ DWORD size_upper = 0;
++ HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
++ DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
++ if (size_lower == INVALID_FILE_SIZE) {
++ DWORD error = GetLastError();
++ if (error != NO_ERROR)
++ FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
++ }
++ fmt::ULongLong long_size = size_upper;
++ return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
++#else
++ typedef struct stat Stat;
++ Stat file_stat = Stat();
++ if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
++ FMT_THROW(SystemError(errno, "cannot get file attributes"));
++ FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
++ "return type of File::size is not large enough");
++ return file_stat.st_size;
++#endif
++}
++
++std::size_t fmt::File::read(void *buffer, std::size_t count) {
++ RWResult result = 0;
++ FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
++ if (result < 0)
++ FMT_THROW(SystemError(errno, "cannot read from file"));
++ return internal::to_unsigned(result);
++}
++
++std::size_t fmt::File::write(const void *buffer, std::size_t count) {
++ RWResult result = 0;
++ FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
++ if (result < 0)
++ FMT_THROW(SystemError(errno, "cannot write to file"));
++ return internal::to_unsigned(result);
++}
++
++fmt::File fmt::File::dup(int fd) {
++ // Don't retry as dup doesn't return EINTR.
++ // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
++ int new_fd = FMT_POSIX_CALL(dup(fd));
++ if (new_fd == -1)
++ FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
++ return File(new_fd);
++}
++
++void fmt::File::dup2(int fd) {
++ int result = 0;
++ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
++ if (result == -1) {
++ FMT_THROW(SystemError(errno,
++ "cannot duplicate file descriptor {} to {}", fd_, fd));
++ }
++}
++
++void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
++ int result = 0;
++ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
++ if (result == -1)
++ ec = ErrorCode(errno);
++}
++
++void fmt::File::pipe(File &read_end, File &write_end) {
++ // Close the descriptors first to make sure that assignments don't throw
++ // and there are no leaks.
++ read_end.close();
++ write_end.close();
++ int fds[2] = {};
++#ifdef _WIN32
++ // Make the default pipe capacity same as on Linux 2.6.11+.
++ enum { DEFAULT_CAPACITY = 65536 };
++ int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
++#else
++ // Don't retry as the pipe function doesn't return EINTR.
++ // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
++ int result = FMT_POSIX_CALL(pipe(fds));
++#endif
++ if (result != 0)
++ FMT_THROW(SystemError(errno, "cannot create pipe"));
++ // The following assignments don't throw because read_fd and write_fd
++ // are closed.
++ read_end = File(fds[0]);
++ write_end = File(fds[1]);
++}
++
++fmt::BufferedFile fmt::File::fdopen(const char *mode) {
++ // Don't retry as fdopen doesn't return EINTR.
++ FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
++ if (!f)
++ FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
++ BufferedFile file(f);
++ fd_ = -1;
++ return file;
++}
++
++long fmt::getpagesize() {
++#ifdef _WIN32
++ SYSTEM_INFO si;
++ GetSystemInfo(&si);
++ return si.dwPageSize;
++#else
++ long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
++ if (size < 0)
++ FMT_THROW(SystemError(errno, "cannot get memory page size"));
++ return size;
++#endif
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h
+new file mode 100644
+index 00000000..88512de5
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h
+@@ -0,0 +1,367 @@
++/*
++ A C++ interface to POSIX functions.
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ For the license information refer to format.h.
++ */
++
++#ifndef FMT_POSIX_H_
++#define FMT_POSIX_H_
++
++#if defined(__MINGW32__) || defined(__CYGWIN__)
++// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
++# undef __STRICT_ANSI__
++#endif
++
++#include <errno.h>
++#include <fcntl.h> // for O_RDONLY
++#include <locale.h> // for locale_t
++#include <stdio.h>
++#include <stdlib.h> // for strtod_l
++
++#include <cstddef>
++
++#if defined __APPLE__ || defined(__FreeBSD__)
++# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
++#endif
++
++#include "format.h"
++
++#ifndef FMT_POSIX
++# if defined(_WIN32) && !defined(__MINGW32__)
++// Fix warnings about deprecated symbols.
++# define FMT_POSIX(call) _##call
++# else
++# define FMT_POSIX(call) call
++# endif
++#endif
++
++// Calls to system functions are wrapped in FMT_SYSTEM for testability.
++#ifdef FMT_SYSTEM
++# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
++#else
++# define FMT_SYSTEM(call) call
++# ifdef _WIN32
++// Fix warnings about deprecated symbols.
++# define FMT_POSIX_CALL(call) ::_##call
++# else
++# define FMT_POSIX_CALL(call) ::call
++# endif
++#endif
++
++// Retries the expression while it evaluates to error_result and errno
++// equals to EINTR.
++#ifndef _WIN32
++# define FMT_RETRY_VAL(result, expression, error_result) \
++ do { \
++ result = (expression); \
++ } while (result == error_result && errno == EINTR)
++#else
++# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
++#endif
++
++#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
++
++namespace fmt {
++
++// An error code.
++class ErrorCode {
++ private:
++ int value_;
++
++ public:
++ explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
++
++ int get() const FMT_NOEXCEPT { return value_; }
++};
++
++// A buffered file.
++class BufferedFile {
++ private:
++ FILE *file_;
++
++ friend class File;
++
++ explicit BufferedFile(FILE *f) : file_(f) {}
++
++ public:
++ // Constructs a BufferedFile object which doesn't represent any file.
++ BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
++
++ // Destroys the object closing the file it represents if any.
++ FMT_API ~BufferedFile() FMT_NOEXCEPT;
++
++#if !FMT_USE_RVALUE_REFERENCES
++ // Emulate a move constructor and a move assignment operator if rvalue
++ // references are not supported.
++
++ private:
++ // A proxy object to emulate a move constructor.
++ // It is private to make it impossible call operator Proxy directly.
++ struct Proxy {
++ FILE *file;
++ };
++
++public:
++ // A "move constructor" for moving from a temporary.
++ BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
++
++ // A "move constructor" for moving from an lvalue.
++ BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
++ f.file_ = FMT_NULL;
++ }
++
++ // A "move assignment operator" for moving from a temporary.
++ BufferedFile &operator=(Proxy p) {
++ close();
++ file_ = p.file;
++ return *this;
++ }
++
++ // A "move assignment operator" for moving from an lvalue.
++ BufferedFile &operator=(BufferedFile &other) {
++ close();
++ file_ = other.file_;
++ other.file_ = FMT_NULL;
++ return *this;
++ }
++
++ // Returns a proxy object for moving from a temporary:
++ // BufferedFile file = BufferedFile(...);
++ operator Proxy() FMT_NOEXCEPT {
++ Proxy p = {file_};
++ file_ = FMT_NULL;
++ return p;
++ }
++
++#else
++ private:
++ FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
++
++ public:
++ BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
++ other.file_ = FMT_NULL;
++ }
++
++ BufferedFile& operator=(BufferedFile &&other) {
++ close();
++ file_ = other.file_;
++ other.file_ = FMT_NULL;
++ return *this;
++ }
++#endif
++
++ // Opens a file.
++ FMT_API BufferedFile(CStringRef filename, CStringRef mode);
++
++ // Closes the file.
++ FMT_API void close();
++
++ // Returns the pointer to a FILE object representing this file.
++ FILE *get() const FMT_NOEXCEPT { return file_; }
++
++ // We place parentheses around fileno to workaround a bug in some versions
++ // of MinGW that define fileno as a macro.
++ FMT_API int (fileno)() const;
++
++ void print(CStringRef format_str, const ArgList &args) {
++ fmt::print(file_, format_str, args);
++ }
++ FMT_VARIADIC(void, print, CStringRef)
++};
++
++// A file. Closed file is represented by a File object with descriptor -1.
++// Methods that are not declared with FMT_NOEXCEPT may throw
++// fmt::SystemError in case of failure. Note that some errors such as
++// closing the file multiple times will cause a crash on Windows rather
++// than an exception. You can get standard behavior by overriding the
++// invalid parameter handler with _set_invalid_parameter_handler.
++class File {
++ private:
++ int fd_; // File descriptor.
++
++ // Constructs a File object with a given descriptor.
++ explicit File(int fd) : fd_(fd) {}
++
++ public:
++ // Possible values for the oflag argument to the constructor.
++ enum {
++ RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
++ WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
++ RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
++ };
++
++ // Constructs a File object which doesn't represent any file.
++ File() FMT_NOEXCEPT : fd_(-1) {}
++
++ // Opens a file and constructs a File object representing this file.
++ FMT_API File(CStringRef path, int oflag);
++
++#if !FMT_USE_RVALUE_REFERENCES
++ // Emulate a move constructor and a move assignment operator if rvalue
++ // references are not supported.
++
++ private:
++ // A proxy object to emulate a move constructor.
++ // It is private to make it impossible call operator Proxy directly.
++ struct Proxy {
++ int fd;
++ };
++
++ public:
++ // A "move constructor" for moving from a temporary.
++ File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
++
++ // A "move constructor" for moving from an lvalue.
++ File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
++ other.fd_ = -1;
++ }
++
++ // A "move assignment operator" for moving from a temporary.
++ File &operator=(Proxy p) {
++ close();
++ fd_ = p.fd;
++ return *this;
++ }
++
++ // A "move assignment operator" for moving from an lvalue.
++ File &operator=(File &other) {
++ close();
++ fd_ = other.fd_;
++ other.fd_ = -1;
++ return *this;
++ }
++
++ // Returns a proxy object for moving from a temporary:
++ // File file = File(...);
++ operator Proxy() FMT_NOEXCEPT {
++ Proxy p = {fd_};
++ fd_ = -1;
++ return p;
++ }
++
++#else
++ private:
++ FMT_DISALLOW_COPY_AND_ASSIGN(File);
++
++ public:
++ File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
++ other.fd_ = -1;
++ }
++
++ File& operator=(File &&other) {
++ close();
++ fd_ = other.fd_;
++ other.fd_ = -1;
++ return *this;
++ }
++#endif
++
++ // Destroys the object closing the file it represents if any.
++ FMT_API ~File() FMT_NOEXCEPT;
++
++ // Returns the file descriptor.
++ int descriptor() const FMT_NOEXCEPT { return fd_; }
++
++ // Closes the file.
++ FMT_API void close();
++
++ // Returns the file size. The size has signed type for consistency with
++ // stat::st_size.
++ FMT_API LongLong size() const;
++
++ // Attempts to read count bytes from the file into the specified buffer.
++ FMT_API std::size_t read(void *buffer, std::size_t count);
++
++ // Attempts to write count bytes from the specified buffer to the file.
++ FMT_API std::size_t write(const void *buffer, std::size_t count);
++
++ // Duplicates a file descriptor with the dup function and returns
++ // the duplicate as a file object.
++ FMT_API static File dup(int fd);
++
++ // Makes fd be the copy of this file descriptor, closing fd first if
++ // necessary.
++ FMT_API void dup2(int fd);
++
++ // Makes fd be the copy of this file descriptor, closing fd first if
++ // necessary.
++ FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
++
++ // Creates a pipe setting up read_end and write_end file objects for reading
++ // and writing respectively.
++ FMT_API static void pipe(File &read_end, File &write_end);
++
++ // Creates a BufferedFile object associated with this file and detaches
++ // this File object from the file.
++ FMT_API BufferedFile fdopen(const char *mode);
++};
++
++// Returns the memory page size.
++long getpagesize();
++
++#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
++ !defined(__ANDROID__) && !defined(__CYGWIN__)
++# define FMT_LOCALE
++#endif
++
++#ifdef FMT_LOCALE
++// A "C" numeric locale.
++class Locale {
++ private:
++# ifdef _MSC_VER
++ typedef _locale_t locale_t;
++
++ enum { LC_NUMERIC_MASK = LC_NUMERIC };
++
++ static locale_t newlocale(int category_mask, const char *locale, locale_t) {
++ return _create_locale(category_mask, locale);
++ }
++
++ static void freelocale(locale_t locale) {
++ _free_locale(locale);
++ }
++
++ static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
++ return _strtod_l(nptr, endptr, locale);
++ }
++# endif
++
++ locale_t locale_;
++
++ FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
++
++ public:
++ typedef locale_t Type;
++
++ Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
++ if (!locale_)
++ FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
++ }
++ ~Locale() { freelocale(locale_); }
++
++ Type get() const { return locale_; }
++
++ // Converts string to floating-point number and advances str past the end
++ // of the parsed input.
++ double strtod(const char *&str) const {
++ char *end = FMT_NULL;
++ double result = strtod_l(str, &end, locale_);
++ str = end;
++ return result;
++ }
++};
++#endif // FMT_LOCALE
++} // namespace fmt
++
++#if !FMT_USE_RVALUE_REFERENCES
++namespace std {
++// For compatibility with C++98.
++inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; }
++inline fmt::File &move(fmt::File &f) { return f; }
++}
++#endif
++
++#endif // FMT_POSIX_H_
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h
+new file mode 100644
+index 00000000..c98b0e01
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h
+@@ -0,0 +1,143 @@
++/*
++ Formatting library for C++ - time formatting
++
++ Copyright (c) 2012 - 2016, Victor Zverovich
++ All rights reserved.
++
++ For the license information refer to format.h.
++ */
++
++#ifndef FMT_TIME_H_
++#define FMT_TIME_H_
++
++#include "format.h"
++#include <ctime>
++
++#ifdef _MSC_VER
++# pragma warning(push)
++# pragma warning(disable: 4702) // unreachable code
++# pragma warning(disable: 4996) // "deprecated" functions
++#endif
++
++namespace fmt {
++template <typename ArgFormatter>
++void format_arg(BasicFormatter<char, ArgFormatter> &f,
++ const char *&format_str, const std::tm &tm) {
++ if (*format_str == ':')
++ ++format_str;
++ const char *end = format_str;
++ while (*end && *end != '}')
++ ++end;
++ if (*end != '}')
++ FMT_THROW(FormatError("missing '}' in format string"));
++ internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
++ format.append(format_str, end + 1);
++ format[format.size() - 1] = '\0';
++ Buffer<char> &buffer = f.writer().buffer();
++ std::size_t start = buffer.size();
++ for (;;) {
++ std::size_t size = buffer.capacity() - start;
++ std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
++ if (count != 0) {
++ buffer.resize(start + count);
++ break;
++ }
++ if (size >= format.size() * 256) {
++ // If the buffer is 256 times larger than the format string, assume
++ // that `strftime` gives an empty result. There doesn't seem to be a
++ // better way to distinguish the two cases:
++ // https://github.com/fmtlib/fmt/issues/367
++ break;
++ }
++ const std::size_t MIN_GROWTH = 10;
++ buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
++ }
++ format_str = end + 1;
++}
++
++namespace internal{
++inline Null<> localtime_r(...) { return Null<>(); }
++inline Null<> localtime_s(...) { return Null<>(); }
++inline Null<> gmtime_r(...) { return Null<>(); }
++inline Null<> gmtime_s(...) { return Null<>(); }
++}
++
++// Thread-safe replacement for std::localtime
++inline std::tm localtime(std::time_t time) {
++ struct LocalTime {
++ std::time_t time_;
++ std::tm tm_;
++
++ LocalTime(std::time_t t): time_(t) {}
++
++ bool run() {
++ using namespace fmt::internal;
++ return handle(localtime_r(&time_, &tm_));
++ }
++
++ bool handle(std::tm *tm) { return tm != FMT_NULL; }
++
++ bool handle(internal::Null<>) {
++ using namespace fmt::internal;
++ return fallback(localtime_s(&tm_, &time_));
++ }
++
++ bool fallback(int res) { return res == 0; }
++
++ bool fallback(internal::Null<>) {
++ using namespace fmt::internal;
++ std::tm *tm = std::localtime(&time_);
++ if (tm) tm_ = *tm;
++ return tm != FMT_NULL;
++ }
++ };
++ LocalTime lt(time);
++ if (lt.run())
++ return lt.tm_;
++ // Too big time values may be unsupported.
++ FMT_THROW(fmt::FormatError("time_t value out of range"));
++ return std::tm();
++}
++
++// Thread-safe replacement for std::gmtime
++inline std::tm gmtime(std::time_t time) {
++ struct GMTime {
++ std::time_t time_;
++ std::tm tm_;
++
++ GMTime(std::time_t t): time_(t) {}
++
++ bool run() {
++ using namespace fmt::internal;
++ return handle(gmtime_r(&time_, &tm_));
++ }
++
++ bool handle(std::tm *tm) { return tm != FMT_NULL; }
++
++ bool handle(internal::Null<>) {
++ using namespace fmt::internal;
++ return fallback(gmtime_s(&tm_, &time_));
++ }
++
++ bool fallback(int res) { return res == 0; }
++
++ bool fallback(internal::Null<>) {
++ std::tm *tm = std::gmtime(&time_);
++ if (tm != FMT_NULL) tm_ = *tm;
++ return tm != FMT_NULL;
++ }
++ };
++ GMTime gt(time);
++ if (gt.run())
++ return gt.tm_;
++ // Too big time values may be unsupported.
++ FMT_THROW(fmt::FormatError("time_t value out of range"));
++ return std::tm();
++}
++} //namespace fmt
++
++#ifdef _MSC_VER
++# pragma warning(pop)
++#endif
++
++#endif // FMT_TIME_H_
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/fmt.h b/external/spdlog-0.14.0/include/spdlog/fmt/fmt.h
+new file mode 100644
+index 00000000..a4ee4673
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/fmt.h
+@@ -0,0 +1,28 @@
++//
++// Copyright(c) 2016 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++//
++// Include a bundled header-only copy of fmtlib or an external one.
++// By default spdlog include its own copy.
++//
++
++#if !defined(SPDLOG_FMT_EXTERNAL)
++
++#ifndef FMT_HEADER_ONLY
++#define FMT_HEADER_ONLY
++#endif
++#ifndef FMT_USE_WINDOWS_H
++#define FMT_USE_WINDOWS_H 0
++#endif
++#include "spdlog/fmt/bundled/format.h"
++
++#else //external fmtlib
++
++#include <fmt/format.h>
++
++#endif
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/ostr.h b/external/spdlog-0.14.0/include/spdlog/fmt/ostr.h
+new file mode 100644
+index 00000000..49b5e98c
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/fmt/ostr.h
+@@ -0,0 +1,17 @@
++//
++// Copyright(c) 2016 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++// include external or bundled copy of fmtlib's ostream support
++//
++#if !defined(SPDLOG_FMT_EXTERNAL)
++#include "spdlog/fmt/fmt.h"
++#include "spdlog/fmt/bundled/ostream.h"
++#else
++#include <fmt/ostream.h>
++#endif
++
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/formatter.h b/external/spdlog-0.14.0/include/spdlog/formatter.h
+new file mode 100644
+index 00000000..6bba9025
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/formatter.h
+@@ -0,0 +1,47 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/details/log_msg.h"
++
++#include <vector>
++#include <string>
++#include <memory>
++
++namespace spdlog
++{
++namespace details
++{
++class flag_formatter;
++}
++
++class formatter
++{
++public:
++ virtual ~formatter() {}
++ virtual void format(details::log_msg& msg) = 0;
++};
++
++class pattern_formatter SPDLOG_FINAL : public formatter
++{
++
++public:
++ explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local);
++ pattern_formatter(const pattern_formatter&) = delete;
++ pattern_formatter& operator=(const pattern_formatter&) = delete;
++ void format(details::log_msg& msg) override;
++private:
++ const std::string _pattern;
++ const pattern_time_type _pattern_time;
++ std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
++ std::tm get_time(details::log_msg& msg);
++ void handle_flag(char flag);
++ void compile_pattern(const std::string& pattern);
++};
++}
++
++#include "spdlog/details/pattern_formatter_impl.h"
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/logger.h b/external/spdlog-0.14.0/include/spdlog/logger.h
+new file mode 100644
+index 00000000..642208eb
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/logger.h
+@@ -0,0 +1,132 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler())
++// Has name, log level, vector of std::shared sink pointers and formatter
++// Upon each log write the logger:
++// 1. Checks if its log level is enough to log the message
++// 2. Format the message using the formatter function
++// 3. Pass the formatted message to its sinks to performa the actual logging
++
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/common.h"
++
++#include <vector>
++#include <memory>
++#include <string>
++
++namespace spdlog
++{
++
++class logger
++{
++public:
++ logger(const std::string& logger_name, sink_ptr single_sink);
++ logger(const std::string& name, sinks_init_list);
++ template<class It>
++ logger(const std::string& name, const It& begin, const It& end);
++
++ virtual ~logger();
++ logger(const logger&) = delete;
++ logger& operator=(const logger&) = delete;
++
++
++ template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
++ template <typename... Args> void log(level::level_enum lvl, const char* msg);
++ template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
++
++ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* fmt, const Args&... args);
++ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* msg);
++ template <typename Arg1, typename... Args> void trace_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void debug_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void info_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void warn_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void error_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
++ template <typename Arg1, typename... Args> void critical_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
++
++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
++ template <typename... Args> void log(level::level_enum lvl, const wchar_t* msg);
++ template <typename... Args> void log(level::level_enum lvl, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void trace(const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void debug(const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void info(const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void warn(const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void error(const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void critical(const wchar_t* fmt, const Args&... args);
++
++ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* msg);
++ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void trace_if(const bool flag, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void debug_if(const bool flag, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void info_if(const bool flag, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void warn_if(const bool flag, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void error_if(const bool flag, const wchar_t* fmt, const Args&... args);
++ template <typename... Args> void critical_if(const bool flag, const wchar_t* fmt, const Args&... args);
++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
++
++ template <typename T> void log(level::level_enum lvl, const T&);
++ template <typename T> void trace(const T&);
++ template <typename T> void debug(const T&);
++ template <typename T> void info(const T&);
++ template <typename T> void warn(const T&);
++ template <typename T> void error(const T&);
++ template <typename T> void critical(const T&);
++
++ template <typename T> void log_if(const bool flag, level::level_enum lvl, const T&);
++ template <typename T> void trace_if(const bool flag, const T&);
++ template <typename T> void debug_if(const bool flag, const T&);
++ template <typename T> void info_if(const bool flag, const T&);
++ template <typename T> void warn_if(const bool flag, const T&);
++ template <typename T> void error_if(const bool flag, const T&);
++ template <typename T> void critical_if(const bool flag, const T&);
++
++ bool should_log(level::level_enum) const;
++ void set_level(level::level_enum);
++ level::level_enum level() const;
++ const std::string& name() const;
++ void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
++ void set_formatter(formatter_ptr);
++
++ // automatically call flush() if message level >= log_level
++ void flush_on(level::level_enum log_level);
++
++ virtual void flush();
++
++ const std::vector<sink_ptr>& sinks() const;
++
++ // error handler
++ virtual void set_error_handler(log_err_handler);
++ virtual log_err_handler error_handler();
++
++protected:
++ virtual void _sink_it(details::log_msg&);
++ virtual void _set_pattern(const std::string&, pattern_time_type);
++ virtual void _set_formatter(formatter_ptr);
++
++ // default error handler: print the error to stderr with the max rate of 1 message/minute
++ virtual void _default_err_handler(const std::string &msg);
++
++ // return true if the given message level should trigger a flush
++ bool _should_flush_on(const details::log_msg&);
++
++ const std::string _name;
++ std::vector<sink_ptr> _sinks;
++ formatter_ptr _formatter;
++ spdlog::level_t _level;
++ spdlog::level_t _flush_level;
++ log_err_handler _err_handler;
++ std::atomic<time_t> _last_err_time;
++ std::atomic<size_t> _msg_counter;
++};
++}
++
++#include "spdlog/details/logger_impl.h"
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h
+new file mode 100644
+index 00000000..239f2d28
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h
+@@ -0,0 +1,90 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#if defined(__ANDROID__)
++
++#include "spdlog/sinks/sink.h"
++
++#include <mutex>
++#include <string>
++#include <android/log.h>
++#include <thread>
++#include <chrono>
++
++#if !defined(SPDLOG_ANDROID_RETRIES)
++#define SPDLOG_ANDROID_RETRIES 2
++#endif
++
++namespace spdlog
++{
++namespace sinks
++{
++
++/*
++* Android sink (logging using __android_log_write)
++* __android_log_write is thread-safe. No lock is needed.
++*/
++class android_sink : public sink
++{
++public:
++ explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {}
++
++ void log(const details::log_msg& msg) override
++ {
++ const android_LogPriority priority = convert_to_android(msg.level);
++ const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str());
++
++ // See system/core/liblog/logger_write.c for explanation of return value
++ int ret = __android_log_write(priority, _tag.c_str(), msg_output);
++ int retry_count = 0;
++ while ((ret == -11/*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
++ {
++ std::this_thread::sleep_for(std::chrono::milliseconds(5));
++ ret = __android_log_write(priority, _tag.c_str(), msg_output);
++ retry_count++;
++ }
++
++ if (ret < 0)
++ {
++ throw spdlog_ex("__android_log_write() failed", ret);
++ }
++ }
++
++ void flush() override
++ {
++ }
++
++private:
++ static android_LogPriority convert_to_android(spdlog::level::level_enum level)
++ {
++ switch(level)
++ {
++ case spdlog::level::trace:
++ return ANDROID_LOG_VERBOSE;
++ case spdlog::level::debug:
++ return ANDROID_LOG_DEBUG;
++ case spdlog::level::info:
++ return ANDROID_LOG_INFO;
++ case spdlog::level::warn:
++ return ANDROID_LOG_WARN;
++ case spdlog::level::err:
++ return ANDROID_LOG_ERROR;
++ case spdlog::level::critical:
++ return ANDROID_LOG_FATAL;
++ default:
++ return ANDROID_LOG_DEFAULT;
++ }
++ }
++
++ std::string _tag;
++ bool _use_raw_msg;
++};
++
++}
++}
++
++#endif
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h
+new file mode 100644
+index 00000000..56fd3fd5
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h
+@@ -0,0 +1,133 @@
++//
++// Copyright(c) 2017 spdlog authors.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/common.h"
++#include "spdlog/details/os.h"
++
++#include <string>
++#include <map>
++
++namespace spdlog
++{
++namespace sinks
++{
++
++/**
++ * This sink prefixes the output with an ANSI escape sequence color code depending on the severity
++ * of the message.
++ * If no color terminal detected, omit the escape codes.
++ */
++template <class Mutex>
++class ansicolor_sink: public base_sink<Mutex>
++{
++public:
++ ansicolor_sink(FILE* file): target_file_(file)
++ {
++ should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal();
++ colors_[level::trace] = cyan;
++ colors_[level::debug] = cyan;
++ colors_[level::info] = reset;
++ colors_[level::warn] = yellow + bold;
++ colors_[level::err] = red + bold;
++ colors_[level::critical] = bold + on_red;
++ colors_[level::off] = reset;
++ }
++ virtual ~ansicolor_sink()
++ {
++ _flush();
++ }
++
++ void set_color(level::level_enum color_level, const std::string& color)
++ {
++ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
++ colors_[color_level] = color;
++ }
++
++ /// Formatting codes
++ const std::string reset = "\033[00m";
++ const std::string bold = "\033[1m";
++ const std::string dark = "\033[2m";
++ const std::string underline = "\033[4m";
++ const std::string blink = "\033[5m";
++ const std::string reverse = "\033[7m";
++ const std::string concealed = "\033[8m";
++
++ // Foreground colors
++ const std::string grey = "\033[30m";
++ const std::string red = "\033[31m";
++ const std::string green = "\033[32m";
++ const std::string yellow = "\033[33m";
++ const std::string blue = "\033[34m";
++ const std::string magenta = "\033[35m";
++ const std::string cyan = "\033[36m";
++ const std::string white = "\033[37m";
++
++ /// Background colors
++ const std::string on_grey = "\033[40m";
++ const std::string on_red = "\033[41m";
++ const std::string on_green = "\033[42m";
++ const std::string on_yellow = "\033[43m";
++ const std::string on_blue = "\033[44m";
++ const std::string on_magenta = "\033[45m";
++ const std::string on_cyan = "\033[46m";
++ const std::string on_white = "\033[47m";
++
++protected:
++ virtual void _sink_it(const details::log_msg& msg) override
++ {
++ // Wrap the originally formatted message in color codes.
++ // If color is not supported in the terminal, log as is instead.
++ if (should_do_colors_)
++ {
++ const std::string& prefix = colors_[msg.level];
++ fwrite(prefix.data(), sizeof(char), prefix.size(), target_file_);
++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
++ fwrite(reset.data(), sizeof(char), reset.size(), target_file_);
++ }
++ else
++ {
++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
++ }
++ _flush();
++ }
++
++ void _flush() override
++ {
++ fflush(target_file_);
++ }
++ FILE* target_file_;
++ bool should_do_colors_;
++ std::map<level::level_enum, std::string> colors_;
++};
++
++
++template<class Mutex>
++class ansicolor_stdout_sink: public ansicolor_sink<Mutex>
++{
++public:
++ ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
++ {}
++};
++
++template<class Mutex>
++class ansicolor_stderr_sink: public ansicolor_sink<Mutex>
++{
++public:
++ ansicolor_stderr_sink(): ansicolor_sink<Mutex>(stderr)
++ {}
++};
++
++typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
++typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
++
++typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
++typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
++
++} // namespace sinks
++} // namespace spdlog
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h
+new file mode 100644
+index 00000000..926f4931
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h
+@@ -0,0 +1,50 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++//
++// base sink templated over a mutex (either dummy or real)
++// concrete implementation should only override the _sink_it method.
++// all locking is taken care of here so no locking needed by the implementers..
++//
++
++#include "spdlog/sinks/sink.h"
++#include "spdlog/formatter.h"
++#include "spdlog/common.h"
++#include "spdlog/details/log_msg.h"
++
++#include <mutex>
++
++namespace spdlog
++{
++namespace sinks
++{
++template<class Mutex>
++class base_sink:public sink
++{
++public:
++ base_sink():_mutex() {}
++ virtual ~base_sink() = default;
++
++ base_sink(const base_sink&) = delete;
++ base_sink& operator=(const base_sink&) = delete;
++
++ void log(const details::log_msg& msg) SPDLOG_FINAL override
++ {
++ std::lock_guard<Mutex> lock(_mutex);
++ _sink_it(msg);
++ }
++ void flush() SPDLOG_FINAL override
++ {
++ _flush();
++ }
++
++protected:
++ virtual void _sink_it(const details::log_msg& msg) = 0;
++ virtual void _flush() = 0;
++ Mutex _mutex;
++};
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h
+new file mode 100644
+index 00000000..4d4b6b61
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h
+@@ -0,0 +1,73 @@
++//
++// Copyright (c) 2015 David Schury, Gabi Melman
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/details/log_msg.h"
++#include "spdlog/details/null_mutex.h"
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/sinks/sink.h"
++
++#include <algorithm>
++#include <mutex>
++#include <memory>
++#include <vector>
++
++// Distribution sink (mux). Stores a vector of sinks which get called when log is called
++
++namespace spdlog
++{
++namespace sinks
++{
++template<class Mutex>
++class dist_sink: public base_sink<Mutex>
++{
++public:
++ explicit dist_sink() :_sinks() {}
++ dist_sink(const dist_sink&) = delete;
++ dist_sink& operator=(const dist_sink&) = delete;
++ virtual ~dist_sink() = default;
++
++protected:
++ std::vector<std::shared_ptr<sink>> _sinks;
++
++ void _sink_it(const details::log_msg& msg) override
++ {
++ for (auto &sink : _sinks)
++ {
++ if( sink->should_log( msg.level))
++ {
++ sink->log(msg);
++ }
++ }
++ }
++
++ void _flush() override
++ {
++ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
++ for (auto &sink : _sinks)
++ sink->flush();
++ }
++
++public:
++
++
++ void add_sink(std::shared_ptr<sink> sink)
++ {
++ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
++ _sinks.push_back(sink);
++ }
++
++ void remove_sink(std::shared_ptr<sink> sink)
++ {
++ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
++ _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
++ }
++};
++
++typedef dist_sink<std::mutex> dist_sink_mt;
++typedef dist_sink<details::null_mutex> dist_sink_st;
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h b/external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h
+new file mode 100644
+index 00000000..421acc8a
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h
+@@ -0,0 +1,242 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/details/null_mutex.h"
++#include "spdlog/details/file_helper.h"
++#include "spdlog/fmt/fmt.h"
++
++#include <algorithm>
++#include <chrono>
++#include <cstdio>
++#include <ctime>
++#include <mutex>
++#include <string>
++#include <cerrno>
++
++namespace spdlog
++{
++namespace sinks
++{
++/*
++ * Trivial file sink with single file as target
++ */
++template<class Mutex>
++class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
++{
++public:
++ explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
++ {
++ _file_helper.open(filename, truncate);
++ }
++
++ void set_force_flush(bool force_flush)
++ {
++ _force_flush = force_flush;
++ }
++
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ _file_helper.write(msg);
++ if(_force_flush)
++ _file_helper.flush();
++ }
++ void _flush() override
++ {
++ _file_helper.flush();
++ }
++private:
++ details::file_helper _file_helper;
++ bool _force_flush;
++};
++
++typedef simple_file_sink<std::mutex> simple_file_sink_mt;
++typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
++
++/*
++ * Rotating file sink based on size
++ */
++template<class Mutex>
++class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
++{
++public:
++ rotating_file_sink(const filename_t &base_filename,
++ std::size_t max_size, std::size_t max_files) :
++ _base_filename(base_filename),
++ _max_size(max_size),
++ _max_files(max_files),
++ _current_size(0),
++ _file_helper()
++ {
++ _file_helper.open(calc_filename(_base_filename, 0));
++ _current_size = _file_helper.size(); //expensive. called only once
++ }
++
++
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ _current_size += msg.formatted.size();
++ if (_current_size > _max_size)
++ {
++ _rotate();
++ _current_size = msg.formatted.size();
++ }
++ _file_helper.write(msg);
++ }
++
++ void _flush() override
++ {
++ _file_helper.flush();
++ }
++
++private:
++ static filename_t calc_filename(const filename_t& filename, std::size_t index)
++ {
++ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
++ if (index)
++ w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
++ else
++ w.write(SPDLOG_FILENAME_T("{}"), filename);
++ return w.str();
++ }
++
++ // Rotate files:
++ // log.txt -> log.txt.1
++ // log.txt.1 -> log.txt.2
++ // log.txt.2 -> log.txt.3
++ // lo3.txt.3 -> delete
++
++ void _rotate()
++ {
++ using details::os::filename_to_str;
++ _file_helper.close();
++ for (auto i = _max_files; i > 0; --i)
++ {
++ filename_t src = calc_filename(_base_filename, i - 1);
++ filename_t target = calc_filename(_base_filename, i);
++
++ if (details::file_helper::file_exists(target))
++ {
++ if (details::os::remove(target) != 0)
++ {
++ throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
++ }
++ }
++ if (details::file_helper::file_exists(src) && details::os::rename(src, target))
++ {
++ throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
++ }
++ }
++ _file_helper.reopen(true);
++ }
++ filename_t _base_filename;
++ std::size_t _max_size;
++ std::size_t _max_files;
++ std::size_t _current_size;
++ details::file_helper _file_helper;
++};
++
++typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
++typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
++
++/*
++ * Default generator of daily log file names.
++ */
++struct default_daily_file_name_calculator
++{
++ // Create filename for the form basename.YYYY-MM-DD_hh-mm
++ static filename_t calc_filename(const filename_t& basename)
++ {
++ std::tm tm = spdlog::details::os::localtime();
++ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
++ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
++ return w.str();
++ }
++};
++
++/*
++ * Generator of daily log file names in format basename.YYYY-MM-DD
++ */
++struct dateonly_daily_file_name_calculator
++{
++ // Create filename for the form basename.YYYY-MM-DD
++ static filename_t calc_filename(const filename_t& basename)
++ {
++ std::tm tm = spdlog::details::os::localtime();
++ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
++ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++ return w.str();
++ }
++};
++
++/*
++ * Rotating file sink based on date. rotates at midnight
++ */
++template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
++class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex >
++{
++public:
++ //create daily file sink which rotates on given time
++ daily_file_sink(
++ const filename_t& base_filename,
++ int rotation_hour,
++ int rotation_minute) : _base_filename(base_filename),
++ _rotation_h(rotation_hour),
++ _rotation_m(rotation_minute)
++ {
++ if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
++ throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
++ _rotation_tp = _next_rotation_tp();
++ _file_helper.open(FileNameCalc::calc_filename(_base_filename));
++ }
++
++
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ if (std::chrono::system_clock::now() >= _rotation_tp)
++ {
++ _file_helper.open(FileNameCalc::calc_filename(_base_filename));
++ _rotation_tp = _next_rotation_tp();
++ }
++ _file_helper.write(msg);
++ }
++
++ void _flush() override
++ {
++ _file_helper.flush();
++ }
++
++private:
++ std::chrono::system_clock::time_point _next_rotation_tp()
++ {
++ auto now = std::chrono::system_clock::now();
++ time_t tnow = std::chrono::system_clock::to_time_t(now);
++ tm date = spdlog::details::os::localtime(tnow);
++ date.tm_hour = _rotation_h;
++ date.tm_min = _rotation_m;
++ date.tm_sec = 0;
++ auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
++ if (rotation_time > now)
++ return rotation_time;
++ else
++ return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24));
++ }
++
++ filename_t _base_filename;
++ int _rotation_h;
++ int _rotation_m;
++ std::chrono::system_clock::time_point _rotation_tp;
++ details::file_helper _file_helper;
++};
++
++typedef daily_file_sink<std::mutex> daily_file_sink_mt;
++typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h
+new file mode 100644
+index 00000000..68b02556
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h
+@@ -0,0 +1,51 @@
++//
++// Copyright(c) 2016 Alexander Dalshov.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#if defined(_MSC_VER)
++
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/details/null_mutex.h"
++
++#include <WinBase.h>
++
++#include <mutex>
++#include <string>
++
++namespace spdlog
++{
++namespace sinks
++{
++/*
++* MSVC sink (logging using OutputDebugStringA)
++*/
++template<class Mutex>
++class msvc_sink : public base_sink < Mutex >
++{
++public:
++ explicit msvc_sink()
++ {
++ }
++
++
++
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ OutputDebugStringA(msg.formatted.c_str());
++ }
++
++ void _flush() override
++ {}
++};
++
++typedef msvc_sink<std::mutex> msvc_sink_mt;
++typedef msvc_sink<details::null_mutex> msvc_sink_st;
++
++}
++}
++
++#endif
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h
+new file mode 100644
+index 00000000..ed4b5e47
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h
+@@ -0,0 +1,34 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/details/null_mutex.h"
++
++#include <mutex>
++
++namespace spdlog
++{
++namespace sinks
++{
++
++template <class Mutex>
++class null_sink : public base_sink < Mutex >
++{
++protected:
++ void _sink_it(const details::log_msg&) override
++ {}
++
++ void _flush() override
++ {}
++
++};
++typedef null_sink<details::null_mutex> null_sink_st;
++typedef null_sink<details::null_mutex> null_sink_mt;
++
++}
++}
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h
+new file mode 100644
+index 00000000..f056107f
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h
+@@ -0,0 +1,47 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/details/null_mutex.h"
++#include "spdlog/sinks/base_sink.h"
++
++#include <ostream>
++#include <mutex>
++
++namespace spdlog
++{
++namespace sinks
++{
++template<class Mutex>
++class ostream_sink: public base_sink<Mutex>
++{
++public:
++ explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {}
++ ostream_sink(const ostream_sink&) = delete;
++ ostream_sink& operator=(const ostream_sink&) = delete;
++ virtual ~ostream_sink() = default;
++
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ _ostream.write(msg.formatted.data(), msg.formatted.size());
++ if (_force_flush)
++ _ostream.flush();
++ }
++
++ void _flush() override
++ {
++ _ostream.flush();
++ }
++
++ std::ostream& _ostream;
++ bool _force_flush;
++};
++
++typedef ostream_sink<std::mutex> ostream_sink_mt;
++typedef ostream_sink<details::null_mutex> ostream_sink_st;
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/sink.h
+new file mode 100644
+index 00000000..0974f337
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/sink.h
+@@ -0,0 +1,53 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++
++#pragma once
++
++#include "spdlog/details/log_msg.h"
++
++namespace spdlog
++{
++namespace sinks
++{
++class sink
++{
++public:
++ sink()
++ {
++ _level = level::trace;
++ }
++
++ virtual ~sink() {}
++ virtual void log(const details::log_msg& msg) = 0;
++ virtual void flush() = 0;
++
++ bool should_log(level::level_enum msg_level) const;
++ void set_level(level::level_enum log_level);
++ level::level_enum level() const;
++
++private:
++ level_t _level;
++
++};
++
++inline bool sink::should_log(level::level_enum msg_level) const
++{
++ return msg_level >= _level.load(std::memory_order_relaxed);
++}
++
++inline void sink::set_level(level::level_enum log_level)
++{
++ _level.store(log_level);
++}
++
++inline level::level_enum sink::level() const
++{
++ return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
++}
++
++}
++}
++
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h b/external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h
+new file mode 100644
+index 00000000..dcdcc7c1
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h
+@@ -0,0 +1,77 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/details/null_mutex.h"
++#include "spdlog/sinks/base_sink.h"
++
++#include <cstdio>
++#include <memory>
++#include <mutex>
++
++namespace spdlog
++{
++namespace sinks
++{
++
++template <class Mutex>
++class stdout_sink SPDLOG_FINAL : public base_sink<Mutex>
++{
++ using MyType = stdout_sink<Mutex>;
++public:
++ stdout_sink()
++ {}
++ static std::shared_ptr<MyType> instance()
++ {
++ static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
++ return instance;
++ }
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
++ _flush();
++ }
++
++ void _flush() override
++ {
++ fflush(stdout);
++ }
++};
++
++typedef stdout_sink<details::null_mutex> stdout_sink_st;
++typedef stdout_sink<std::mutex> stdout_sink_mt;
++
++
++template <class Mutex>
++class stderr_sink SPDLOG_FINAL : public base_sink<Mutex>
++{
++ using MyType = stderr_sink<Mutex>;
++public:
++ stderr_sink()
++ {}
++ static std::shared_ptr<MyType> instance()
++ {
++ static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
++ return instance;
++ }
++protected:
++ void _sink_it(const details::log_msg& msg) override
++ {
++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
++ _flush();
++ }
++
++ void _flush() override
++ {
++ fflush(stderr);
++ }
++};
++
++typedef stderr_sink<std::mutex> stderr_sink_mt;
++typedef stderr_sink<details::null_mutex> stderr_sink_st;
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h
+new file mode 100644
+index 00000000..0b509c4a
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h
+@@ -0,0 +1,81 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/common.h"
++
++#ifdef SPDLOG_ENABLE_SYSLOG
++
++#include "spdlog/sinks/sink.h"
++#include "spdlog/details/log_msg.h"
++
++#include <array>
++#include <string>
++#include <syslog.h>
++
++
++namespace spdlog
++{
++namespace sinks
++{
++/**
++ * Sink that write to syslog using the `syscall()` library call.
++ *
++ * Locking is not needed, as `syslog()` itself is thread-safe.
++ */
++class syslog_sink : public sink
++{
++public:
++ //
++ syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER):
++ _ident(ident)
++ {
++ _priorities[static_cast<int>(level::trace)] = LOG_DEBUG;
++ _priorities[static_cast<int>(level::debug)] = LOG_DEBUG;
++ _priorities[static_cast<int>(level::info)] = LOG_INFO;
++ _priorities[static_cast<int>(level::warn)] = LOG_WARNING;
++ _priorities[static_cast<int>(level::err)] = LOG_ERR;
++ _priorities[static_cast<int>(level::critical)] = LOG_CRIT;
++ _priorities[static_cast<int>(level::off)] = LOG_INFO;
++
++ //set ident to be program name if empty
++ ::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility);
++ }
++ ~syslog_sink()
++ {
++ ::closelog();
++ }
++
++ syslog_sink(const syslog_sink&) = delete;
++ syslog_sink& operator=(const syslog_sink&) = delete;
++
++ void log(const details::log_msg &msg) override
++ {
++ ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
++ }
++
++ void flush() override
++ {
++ }
++
++
++private:
++ std::array<int, 7> _priorities;
++ //must store the ident because the man says openlog might use the pointer as is and not a string copy
++ const std::string _ident;
++
++ //
++ // Simply maps spdlog's log level to syslog priority level.
++ //
++ int syslog_prio_from_level(const details::log_msg &msg) const
++ {
++ return _priorities[static_cast<int>(msg.level)];
++ }
++};
++}
++}
++
++#endif
+diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h
+new file mode 100644
+index 00000000..5b92bf8a
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h
+@@ -0,0 +1,121 @@
++//
++// Copyright(c) 2016 spdlog
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++#include "spdlog/sinks/base_sink.h"
++#include "spdlog/details/null_mutex.h"
++#include "spdlog/common.h"
++
++#include <mutex>
++#include <string>
++#include <map>
++#include <wincon.h>
++
++namespace spdlog
++{
++namespace sinks
++{
++/*
++ * Windows color console sink. Uses WriteConsoleA to write to the console with colors
++ */
++template<class Mutex>
++class wincolor_sink: public base_sink<Mutex>
++{
++public:
++ const WORD BOLD = FOREGROUND_INTENSITY;
++ const WORD RED = FOREGROUND_RED;
++ const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
++ const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
++ const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
++
++ wincolor_sink(HANDLE std_handle): out_handle_(std_handle)
++ {
++ colors_[level::trace] = CYAN;
++ colors_[level::debug] = CYAN;
++ colors_[level::info] = WHITE | BOLD;
++ colors_[level::warn] = YELLOW | BOLD;
++ colors_[level::err] = RED | BOLD; // red bold
++ colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
++ colors_[level::off] = 0;
++ }
++
++ virtual ~wincolor_sink()
++ {
++ this->flush();
++ }
++
++ wincolor_sink(const wincolor_sink& other) = delete;
++ wincolor_sink& operator=(const wincolor_sink& other) = delete;
++
++protected:
++ virtual void _sink_it(const details::log_msg& msg) override
++ {
++ auto color = colors_[msg.level];
++ auto orig_attribs = set_console_attribs(color);
++ WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
++ SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
++ }
++
++ virtual void _flush() override
++ {
++ // windows console always flushed?
++ }
++
++ // change the color for the given level
++ void set_color(level::level_enum level, WORD color)
++ {
++ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
++ colors_[level] = color;
++ }
++
++private:
++ HANDLE out_handle_;
++ std::map<level::level_enum, WORD> colors_;
++
++ // set color and return the orig console attributes (for resetting later)
++ WORD set_console_attribs(WORD attribs)
++ {
++ CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
++ GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
++ WORD back_color = orig_buffer_info.wAttributes;
++ // retrieve the current background color
++ back_color &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
++ // keep the background color unchanged
++ SetConsoleTextAttribute(out_handle_, attribs | back_color);
++ return orig_buffer_info.wAttributes; //return orig attribs
++ }
++};
++
++//
++// windows color console to stdout
++//
++template<class Mutex>
++class wincolor_stdout_sink: public wincolor_sink<Mutex>
++{
++public:
++ wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
++ {}
++};
++
++typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt;
++typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st;
++
++//
++// windows color console to stderr
++//
++template<class Mutex>
++class wincolor_stderr_sink: public wincolor_sink<Mutex>
++{
++public:
++ wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
++ {}
++};
++
++typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt;
++typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st;
++
++}
++}
+diff --git a/external/spdlog-0.14.0/include/spdlog/spdlog.h b/external/spdlog-0.14.0/include/spdlog/spdlog.h
+new file mode 100644
+index 00000000..8fec6431
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/spdlog.h
+@@ -0,0 +1,189 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++// spdlog main header file.
++// see example.cpp for usage example
++
++#pragma once
++
++#define SPDLOG_VERSION "0.14.0"
++
++#include "spdlog/tweakme.h"
++#include "spdlog/common.h"
++#include "spdlog/logger.h"
++
++#include <memory>
++#include <functional>
++#include <chrono>
++#include <string>
++
++namespace spdlog
++{
++
++//
++// Return an existing logger or nullptr if a logger with such name doesn't exist.
++// example: spdlog::get("my_logger")->info("hello {}", "world");
++//
++std::shared_ptr<logger> get(const std::string& name);
++
++
++//
++// Set global formatting
++// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
++//
++void set_pattern(const std::string& format_string);
++void set_formatter(formatter_ptr f);
++
++//
++// Set global logging level for
++//
++void set_level(level::level_enum log_level);
++
++//
++// Set global error handler
++//
++void set_error_handler(log_err_handler);
++
++//
++// Turn on async mode (off by default) and set the queue size for each async_logger.
++// effective only for loggers created after this call.
++// queue_size: size of queue (must be power of 2):
++// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction.
++//
++// async_overflow_policy (optional, block_retry by default):
++// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry.
++// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
++//
++// worker_warmup_cb (optional):
++// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
++//
++// worker_teardown_cb (optional):
++// callback function that will be called in worker thread upon exit
++//
++void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
++
++// Turn off async mode
++void set_sync_mode();
++
++
++//
++// Create and register multi/single threaded basic file logger.
++// Basic logger simply writes to given file without any limitations or rotations.
++//
++std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
++std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
++
++//
++// Create and register multi/single threaded rotating file logger
++//
++std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
++std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
++
++//
++// Create file logger which creates new file on the given time (default in midnight):
++//
++std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
++std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
++
++//
++// Create and register stdout/stderr loggers
++//
++std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
++std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
++std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
++std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
++//
++// Create and register colored stdout/stderr loggers
++//
++std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
++std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
++std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
++std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
++
++
++//
++// Create and register a syslog logger
++//
++#ifdef SPDLOG_ENABLE_SYSLOG
++std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
++#endif
++
++#if defined(__ANDROID__)
++std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
++#endif
++
++// Create and register a logger with a single sink
++std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
++
++// Create and register a logger with multiple sinks
++std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
++template<class It>
++std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
++
++
++// Create and register a logger with templated sink type
++// Example:
++// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
++template <typename Sink, typename... Args>
++std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
++
++// Create and register an async logger with a single sink
++std::shared_ptr<logger> create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
++
++// Create and register an async logger with multiple sinks
++std::shared_ptr<logger> create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
++template<class It>
++std::shared_ptr<logger> create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
++
++// Register the given logger with the given name
++void register_logger(std::shared_ptr<logger> logger);
++
++// Apply a user defined function on all registered loggers
++// Example:
++// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
++void apply_all(std::function<void(std::shared_ptr<logger>)> fun);
++
++// Drop the reference to the given logger
++void drop(const std::string &name);
++
++// Drop all references from the registry
++void drop_all();
++
++
++///////////////////////////////////////////////////////////////////////////////
++//
++// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
++// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
++// SPDLOG_TRACE(..) will also print current file and line.
++//
++// Example:
++// spdlog::set_level(spdlog::level::trace);
++// SPDLOG_TRACE(my_logger, "some trace message");
++// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
++// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
++// SPDLOG_DEBUG_IF(my_logger, true, "some debug message {} {}", 3, 4);
++///////////////////////////////////////////////////////////////////////////////
++
++#ifdef SPDLOG_TRACE_ON
++#define SPDLOG_STR_H(x) #x
++#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
++#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
++#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
++#else
++#define SPDLOG_TRACE(logger, ...)
++#define SPDLOG_TRACE_IF(logger, flag, ...)
++#endif
++
++#ifdef SPDLOG_DEBUG_ON
++#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
++#define SPDLOG_DEBUG_IF(logger, flag, ...) logger->debug_if(flag, __VA_ARGS__)
++#else
++#define SPDLOG_DEBUG(logger, ...)
++#define SPDLOG_DEBUG_IF(logger, flag, ...)
++#endif
++
++}
++
++
++#include "spdlog/details/spdlog_impl.h"
+diff --git a/external/spdlog-0.14.0/include/spdlog/tweakme.h b/external/spdlog-0.14.0/include/spdlog/tweakme.h
+new file mode 100644
+index 00000000..53f5cf7e
+--- /dev/null
++++ b/external/spdlog-0.14.0/include/spdlog/tweakme.h
+@@ -0,0 +1,141 @@
++//
++// Copyright(c) 2015 Gabi Melman.
++// Distributed under the MIT License (http://opensource.org/licenses/MIT)
++//
++
++#pragma once
++
++///////////////////////////////////////////////////////////////////////////////
++//
++// Edit this file to squeeze more performance, and to customize supported features
++//
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
++// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
++// Uncomment to use it instead of the regular clock.
++//
++// #define SPDLOG_CLOCK_COARSE
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment if date/time logging is not needed and never appear in the log pattern.
++// This will prevent spdlog from quering the clock on each log call.
++//
++// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
++// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
++//
++// #define SPDLOG_NO_DATETIME
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
++// This will prevent spdlog from quering the thread id on each log call.
++//
++// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
++//
++// #define SPDLOG_NO_THREAD_ID
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment if logger name logging is not needed.
++// This will prevent spdlog from copying the logger name on each log call.
++//
++// #define SPDLOG_NO_NAME
++///////////////////////////////////////////////////////////////////////////////
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
++//
++// #define SPDLOG_DEBUG_ON
++// #define SPDLOG_TRACE_ON
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
++// Use only if your code never modifes concurrently the registry.
++// Note that upon creating a logger the registry is modified by spdlog..
++//
++// #define SPDLOG_NO_REGISTRY_MUTEX
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to avoid spdlog's usage of atomic log levels
++// Use only if your code never modifies a logger's log levels concurrently by different threads.
++//
++// #define SPDLOG_NO_ATOMIC_LEVELS
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to enable usage of wchar_t for file names on Windows.
++//
++// #define SPDLOG_WCHAR_FILENAMES
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
++//
++// #define SPDLOG_EOL ";-)\n"
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
++// In this case spdlog will try to include <fmt/format.h> so set your -I flag accordingly.
++//
++// #define SPDLOG_FMT_EXTERNAL
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to enable syslog (disabled by default)
++//
++// #define SPDLOG_ENABLE_SYSLOG
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to enable wchar_t support (convert to utf8)
++//
++// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to prevent child processes from inheriting log file descriptors
++//
++// #define SPDLOG_PREVENT_CHILD_FD
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to mark some types as final, allowing more optimizations in release
++// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types
++// for instance.
++//
++// #define SPDLOG_FINAL final
++///////////////////////////////////////////////////////////////////////////////
++
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to enable message counting feature. Adds %i logger pattern that
++// prints log message sequence id.
++//
++// #define SPDLOG_ENABLE_MESSAGE_COUNTER
++///////////////////////////////////////////////////////////////////////////////
++
++///////////////////////////////////////////////////////////////////////////////
++// Uncomment to enable user defined tag names
++//
++// #define SPDLOG_LEVEL_NAMES { " TRACE", " DEBUG", " INFO",
++// " WARNING", " ERROR", "CRITICAL", "OFF" };
++///////////////////////////////////////////////////////////////////////////////
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/tests/CMakeLists.txt b/external/spdlog-0.14.0/tests/CMakeLists.txt
+new file mode 100644
+index 00000000..22329b4e
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/CMakeLists.txt
+@@ -0,0 +1,19 @@
++#
++# Tests
++#
++
++enable_testing()
++
++find_package(Threads)
++
++# Build Catch unit tests
++add_library(catch INTERFACE)
++target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
++
++file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp)
++
++add_executable(catch_tests ${catch_tests})
++target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT})
++add_test(NAME catch_tests COMMAND catch_tests)
++file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
++
+diff --git a/external/spdlog-0.14.0/tests/catch.hpp b/external/spdlog-0.14.0/tests/catch.hpp
+new file mode 100644
+index 00000000..925c6bff
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/catch.hpp
+@@ -0,0 +1,9427 @@
++/*
++ * CATCH v1.1 build 1 (master branch)
++ * Generated: 2015-03-27 18:00:16.346230
++ * ----------------------------------------------------------
++ * This file has been merged from multiple headers. Please don't edit it directly
++ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
++ *
++ * Distributed under the Boost Software License, Version 1.0. (See accompanying
++ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++ */
++#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
++#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
++
++#define TWOBLUECUBES_CATCH_HPP_INCLUDED
++
++// #included from: internal/catch_suppress_warnings.h
++
++#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
++
++#ifdef __clang__
++# ifdef __ICC // icpc defines the __clang__ macro
++# pragma warning(push)
++# pragma warning(disable: 161 1682)
++# else // __ICC
++# pragma clang diagnostic ignored "-Wglobal-constructors"
++# pragma clang diagnostic ignored "-Wvariadic-macros"
++# pragma clang diagnostic ignored "-Wc99-extensions"
++# pragma clang diagnostic ignored "-Wunused-variable"
++# pragma clang diagnostic push
++# pragma clang diagnostic ignored "-Wpadded"
++# pragma clang diagnostic ignored "-Wc++98-compat"
++# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
++# endif
++#elif defined __GNUC__
++# pragma GCC diagnostic ignored "-Wvariadic-macros"
++# pragma GCC diagnostic ignored "-Wunused-variable"
++# pragma GCC diagnostic push
++# pragma GCC diagnostic ignored "-Wpadded"
++#endif
++
++#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
++# define CATCH_IMPL
++#endif
++
++#ifdef CATCH_IMPL
++# ifndef CLARA_CONFIG_MAIN
++# define CLARA_CONFIG_MAIN_NOT_DEFINED
++# define CLARA_CONFIG_MAIN
++# endif
++#endif
++
++// #included from: internal/catch_notimplemented_exception.h
++#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
++
++// #included from: catch_common.h
++#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
++
++#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
++#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
++#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
++
++#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
++#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
++
++#include <sstream>
++#include <stdexcept>
++#include <algorithm>
++
++// #included from: catch_compiler_capabilities.h
++#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
++
++// Much of the following code is based on Boost (1.53)
++
++#ifdef __clang__
++
++# if __has_feature(cxx_nullptr)
++# define CATCH_CONFIG_CPP11_NULLPTR
++# endif
++
++# if __has_feature(cxx_noexcept)
++# define CATCH_CONFIG_CPP11_NOEXCEPT
++# endif
++
++#endif // __clang__
++
++////////////////////////////////////////////////////////////////////////////////
++// Borland
++#ifdef __BORLANDC__
++
++#if (__BORLANDC__ > 0x582 )
++//#define CATCH_CONFIG_SFINAE // Not confirmed
++#endif
++
++#endif // __BORLANDC__
++
++////////////////////////////////////////////////////////////////////////////////
++// EDG
++#ifdef __EDG_VERSION__
++
++#if (__EDG_VERSION__ > 238 )
++//#define CATCH_CONFIG_SFINAE // Not confirmed
++#endif
++
++#endif // __EDG_VERSION__
++
++////////////////////////////////////////////////////////////////////////////////
++// Digital Mars
++#ifdef __DMC__
++
++#if (__DMC__ > 0x840 )
++//#define CATCH_CONFIG_SFINAE // Not confirmed
++#endif
++
++#endif // __DMC__
++
++////////////////////////////////////////////////////////////////////////////////
++// GCC
++#ifdef __GNUC__
++
++#if __GNUC__ < 3
++
++#if (__GNUC_MINOR__ >= 96 )
++//#define CATCH_CONFIG_SFINAE
++#endif
++
++#elif __GNUC__ >= 3
++
++// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
++
++#endif // __GNUC__ < 3
++
++#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
++
++#define CATCH_CONFIG_CPP11_NULLPTR
++#endif
++
++#endif // __GNUC__
++
++////////////////////////////////////////////////////////////////////////////////
++// Visual C++
++#ifdef _MSC_VER
++
++#if (_MSC_VER >= 1600)
++#define CATCH_CONFIG_CPP11_NULLPTR
++#endif
++
++#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
++//#define CATCH_CONFIG_SFINAE // Not confirmed
++#endif
++
++#endif // _MSC_VER
++
++// Use variadic macros if the compiler supports them
++#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
++ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
++ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
++ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
++
++#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
++#define CATCH_CONFIG_VARIADIC_MACROS
++#endif
++
++#endif
++
++////////////////////////////////////////////////////////////////////////////////
++// C++ language feature support
++
++// detect language version:
++#if (__cplusplus == 201103L)
++# define CATCH_CPP11
++# define CATCH_CPP11_OR_GREATER
++#elif (__cplusplus >= 201103L)
++# define CATCH_CPP11_OR_GREATER
++#endif
++
++// noexcept support:
++#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
++# define CATCH_NOEXCEPT noexcept
++# define CATCH_NOEXCEPT_IS(x) noexcept(x)
++#else
++# define CATCH_NOEXCEPT throw()
++# define CATCH_NOEXCEPT_IS(x)
++#endif
++
++namespace Catch {
++
++ class NonCopyable {
++#ifdef CATCH_CPP11_OR_GREATER
++ NonCopyable( NonCopyable const& ) = delete;
++ NonCopyable( NonCopyable && ) = delete;
++ NonCopyable& operator = ( NonCopyable const& ) = delete;
++ NonCopyable& operator = ( NonCopyable && ) = delete;
++#else
++ NonCopyable( NonCopyable const& info );
++ NonCopyable& operator = ( NonCopyable const& );
++#endif
++
++ protected:
++ NonCopyable() {}
++ virtual ~NonCopyable();
++ };
++
++ class SafeBool {
++ public:
++ typedef void (SafeBool::*type)() const;
++
++ static type makeSafe( bool value ) {
++ return value ? &SafeBool::trueValue : 0;
++ }
++ private:
++ void trueValue() const {}
++ };
++
++ template<typename ContainerT>
++ inline void deleteAll( ContainerT& container ) {
++ typename ContainerT::const_iterator it = container.begin();
++ typename ContainerT::const_iterator itEnd = container.end();
++ for(; it != itEnd; ++it )
++ delete *it;
++ }
++ template<typename AssociativeContainerT>
++ inline void deleteAllValues( AssociativeContainerT& container ) {
++ typename AssociativeContainerT::const_iterator it = container.begin();
++ typename AssociativeContainerT::const_iterator itEnd = container.end();
++ for(; it != itEnd; ++it )
++ delete it->second;
++ }
++
++ bool startsWith( std::string const& s, std::string const& prefix );
++ bool endsWith( std::string const& s, std::string const& suffix );
++ bool contains( std::string const& s, std::string const& infix );
++ void toLowerInPlace( std::string& s );
++ std::string toLower( std::string const& s );
++ std::string trim( std::string const& str );
++ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
++
++ struct pluralise {
++ pluralise( std::size_t count, std::string const& label );
++
++ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
++
++ std::size_t m_count;
++ std::string m_label;
++ };
++
++ struct SourceLineInfo {
++
++ SourceLineInfo();
++ SourceLineInfo( char const* _file, std::size_t _line );
++ SourceLineInfo( SourceLineInfo const& other );
++# ifdef CATCH_CPP11_OR_GREATER
++ SourceLineInfo( SourceLineInfo && ) = default;
++ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
++ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
++# endif
++ bool empty() const;
++ bool operator == ( SourceLineInfo const& other ) const;
++ bool operator < ( SourceLineInfo const& other ) const;
++
++ std::string file;
++ std::size_t line;
++ };
++
++ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
++
++ // This is just here to avoid compiler warnings with macro constants and boolean literals
++ inline bool isTrue( bool value ){ return value; }
++ inline bool alwaysTrue() { return true; }
++ inline bool alwaysFalse() { return false; }
++
++ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
++
++ // Use this in variadic streaming macros to allow
++ // >> +StreamEndStop
++ // as well as
++ // >> stuff +StreamEndStop
++ struct StreamEndStop {
++ std::string operator+() {
++ return std::string();
++ }
++ };
++ template<typename T>
++ T const& operator + ( T const& value, StreamEndStop ) {
++ return value;
++ }
++}
++
++#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
++#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
++
++#include <ostream>
++
++namespace Catch {
++
++ class NotImplementedException : public std::exception
++ {
++ public:
++ NotImplementedException( SourceLineInfo const& lineInfo );
++ NotImplementedException( NotImplementedException const& ) {}
++
++ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
++
++ virtual const char* what() const CATCH_NOEXCEPT;
++
++ private:
++ std::string m_what;
++ SourceLineInfo m_lineInfo;
++ };
++
++} // end namespace Catch
++
++///////////////////////////////////////////////////////////////////////////////
++#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
++
++// #included from: internal/catch_context.h
++#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
++
++// #included from: catch_interfaces_generators.h
++#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
++
++#include <string>
++
++namespace Catch {
++
++ struct IGeneratorInfo {
++ virtual ~IGeneratorInfo();
++ virtual bool moveNext() = 0;
++ virtual std::size_t getCurrentIndex() const = 0;
++ };
++
++ struct IGeneratorsForTest {
++ virtual ~IGeneratorsForTest();
++
++ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
++ virtual bool moveNext() = 0;
++ };
++
++ IGeneratorsForTest* createGeneratorsForTest();
++
++} // end namespace Catch
++
++// #included from: catch_ptr.hpp
++#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wpadded"
++#endif
++
++namespace Catch {
++
++ // An intrusive reference counting smart pointer.
++ // T must implement addRef() and release() methods
++ // typically implementing the IShared interface
++ template<typename T>
++ class Ptr {
++ public:
++ Ptr() : m_p( NULL ){}
++ Ptr( T* p ) : m_p( p ){
++ if( m_p )
++ m_p->addRef();
++ }
++ Ptr( Ptr const& other ) : m_p( other.m_p ){
++ if( m_p )
++ m_p->addRef();
++ }
++ ~Ptr(){
++ if( m_p )
++ m_p->release();
++ }
++ void reset() {
++ if( m_p )
++ m_p->release();
++ m_p = NULL;
++ }
++ Ptr& operator = ( T* p ){
++ Ptr temp( p );
++ swap( temp );
++ return *this;
++ }
++ Ptr& operator = ( Ptr const& other ){
++ Ptr temp( other );
++ swap( temp );
++ return *this;
++ }
++ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
++ T* get() { return m_p; }
++ const T* get() const{ return m_p; }
++ T& operator*() const { return *m_p; }
++ T* operator->() const { return m_p; }
++ bool operator !() const { return m_p == NULL; }
++ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
++
++ private:
++ T* m_p;
++ };
++
++ struct IShared : NonCopyable {
++ virtual ~IShared();
++ virtual void addRef() const = 0;
++ virtual void release() const = 0;
++ };
++
++ template<typename T = IShared>
++ struct SharedImpl : T {
++
++ SharedImpl() : m_rc( 0 ){}
++
++ virtual void addRef() const {
++ ++m_rc;
++ }
++ virtual void release() const {
++ if( --m_rc == 0 )
++ delete this;
++ }
++
++ mutable unsigned int m_rc;
++ };
++
++} // end namespace Catch
++
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++
++#include <memory>
++#include <vector>
++#include <stdlib.h>
++
++namespace Catch {
++
++ class TestCase;
++ class Stream;
++ struct IResultCapture;
++ struct IRunner;
++ struct IGeneratorsForTest;
++ struct IConfig;
++
++ struct IContext
++ {
++ virtual ~IContext();
++
++ virtual IResultCapture* getResultCapture() = 0;
++ virtual IRunner* getRunner() = 0;
++ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
++ virtual bool advanceGeneratorsForCurrentTest() = 0;
++ virtual Ptr<IConfig const> getConfig() const = 0;
++ };
++
++ struct IMutableContext : IContext
++ {
++ virtual ~IMutableContext();
++ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
++ virtual void setRunner( IRunner* runner ) = 0;
++ virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
++ };
++
++ IContext& getCurrentContext();
++ IMutableContext& getCurrentMutableContext();
++ void cleanUpContext();
++ Stream createStream( std::string const& streamName );
++
++}
++
++// #included from: internal/catch_test_registry.hpp
++#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
++
++// #included from: catch_interfaces_testcase.h
++#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
++
++#include <vector>
++
++namespace Catch {
++
++ class TestSpec;
++
++ struct ITestCase : IShared {
++ virtual void invoke () const = 0;
++ protected:
++ virtual ~ITestCase();
++ };
++
++ class TestCase;
++ struct IConfig;
++
++ struct ITestCaseRegistry {
++ virtual ~ITestCaseRegistry();
++ virtual std::vector<TestCase> const& getAllTests() const = 0;
++ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
++
++ };
++}
++
++namespace Catch {
++
++template<typename C>
++class MethodTestCase : public SharedImpl<ITestCase> {
++
++public:
++ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
++
++ virtual void invoke() const {
++ C obj;
++ (obj.*m_method)();
++ }
++
++private:
++ virtual ~MethodTestCase() {}
++
++ void (C::*m_method)();
++};
++
++typedef void(*TestFunction)();
++
++struct NameAndDesc {
++ NameAndDesc( const char* _name = "", const char* _description= "" )
++ : name( _name ), description( _description )
++ {}
++
++ const char* name;
++ const char* description;
++};
++
++struct AutoReg {
++
++ AutoReg( TestFunction function,
++ SourceLineInfo const& lineInfo,
++ NameAndDesc const& nameAndDesc );
++
++ template<typename C>
++ AutoReg( void (C::*method)(),
++ char const* className,
++ NameAndDesc const& nameAndDesc,
++ SourceLineInfo const& lineInfo ) {
++ registerTestCase( new MethodTestCase<C>( method ),
++ className,
++ nameAndDesc,
++ lineInfo );
++ }
++
++ void registerTestCase( ITestCase* testCase,
++ char const* className,
++ NameAndDesc const& nameAndDesc,
++ SourceLineInfo const& lineInfo );
++
++ ~AutoReg();
++
++private:
++ AutoReg( AutoReg const& );
++ void operator= ( AutoReg const& );
++};
++
++} // end namespace Catch
++
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++ ///////////////////////////////////////////////////////////////////////////////
++ #define INTERNAL_CATCH_TESTCASE( ... ) \
++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
++
++ ///////////////////////////////////////////////////////////////////////////////
++ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
++
++ ///////////////////////////////////////////////////////////////////////////////
++ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
++ namespace{ \
++ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
++ void test(); \
++ }; \
++ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
++ } \
++ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
++
++#else
++ ///////////////////////////////////////////////////////////////////////////////
++ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
++
++ ///////////////////////////////////////////////////////////////////////////////
++ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
++
++ ///////////////////////////////////////////////////////////////////////////////
++ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
++ namespace{ \
++ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
++ void test(); \
++ }; \
++ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
++ } \
++ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
++
++#endif
++
++// #included from: internal/catch_capture.hpp
++#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
++
++// #included from: catch_result_builder.h
++#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
++
++// #included from: catch_result_type.h
++#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
++
++namespace Catch {
++
++ // ResultWas::OfType enum
++ struct ResultWas { enum OfType {
++ Unknown = -1,
++ Ok = 0,
++ Info = 1,
++ Warning = 2,
++
++ FailureBit = 0x10,
++
++ ExpressionFailed = FailureBit | 1,
++ ExplicitFailure = FailureBit | 2,
++
++ Exception = 0x100 | FailureBit,
++
++ ThrewException = Exception | 1,
++ DidntThrowException = Exception | 2,
++
++ FatalErrorCondition = 0x200 | FailureBit
++
++ }; };
++
++ inline bool isOk( ResultWas::OfType resultType ) {
++ return ( resultType & ResultWas::FailureBit ) == 0;
++ }
++ inline bool isJustInfo( int flags ) {
++ return flags == ResultWas::Info;
++ }
++
++ // ResultDisposition::Flags enum
++ struct ResultDisposition { enum Flags {
++ Normal = 0x00,
++
++ ContinueOnFailure = 0x01, // Failures fail test, but execution continues
++ FalseTest = 0x02, // Prefix expression with !
++ SuppressFail = 0x04 // Failures are reported but do not fail the test
++ }; };
++
++ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
++ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
++ }
++
++ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
++ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
++ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
++
++} // end namespace Catch
++
++// #included from: catch_assertionresult.h
++#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
++
++#include <string>
++
++namespace Catch {
++
++ struct AssertionInfo
++ {
++ AssertionInfo() {}
++ AssertionInfo( std::string const& _macroName,
++ SourceLineInfo const& _lineInfo,
++ std::string const& _capturedExpression,
++ ResultDisposition::Flags _resultDisposition );
++
++ std::string macroName;
++ SourceLineInfo lineInfo;
++ std::string capturedExpression;
++ ResultDisposition::Flags resultDisposition;
++ };
++
++ struct AssertionResultData
++ {
++ AssertionResultData() : resultType( ResultWas::Unknown ) {}
++
++ std::string reconstructedExpression;
++ std::string message;
++ ResultWas::OfType resultType;
++ };
++
++ class AssertionResult {
++ public:
++ AssertionResult();
++ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
++ ~AssertionResult();
++# ifdef CATCH_CPP11_OR_GREATER
++ AssertionResult( AssertionResult const& ) = default;
++ AssertionResult( AssertionResult && ) = default;
++ AssertionResult& operator = ( AssertionResult const& ) = default;
++ AssertionResult& operator = ( AssertionResult && ) = default;
++# endif
++
++ bool isOk() const;
++ bool succeeded() const;
++ ResultWas::OfType getResultType() const;
++ bool hasExpression() const;
++ bool hasMessage() const;
++ std::string getExpression() const;
++ std::string getExpressionInMacro() const;
++ bool hasExpandedExpression() const;
++ std::string getExpandedExpression() const;
++ std::string getMessage() const;
++ SourceLineInfo getSourceInfo() const;
++ std::string getTestMacroName() const;
++
++ protected:
++ AssertionInfo m_info;
++ AssertionResultData m_resultData;
++ };
++
++} // end namespace Catch
++
++namespace Catch {
++
++ struct TestFailureException{};
++
++ template<typename T> class ExpressionLhs;
++
++ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
++
++ struct CopyableStream {
++ CopyableStream() {}
++ CopyableStream( CopyableStream const& other ) {
++ oss << other.oss.str();
++ }
++ CopyableStream& operator=( CopyableStream const& other ) {
++ oss.str("");
++ oss << other.oss.str();
++ return *this;
++ }
++ std::ostringstream oss;
++ };
++
++ class ResultBuilder {
++ public:
++ ResultBuilder( char const* macroName,
++ SourceLineInfo const& lineInfo,
++ char const* capturedExpression,
++ ResultDisposition::Flags resultDisposition );
++
++ template<typename T>
++ ExpressionLhs<T const&> operator->* ( T const& operand );
++ ExpressionLhs<bool> operator->* ( bool value );
++
++ template<typename T>
++ ResultBuilder& operator << ( T const& value ) {
++ m_stream.oss << value;
++ return *this;
++ }
++
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
++
++ ResultBuilder& setResultType( ResultWas::OfType result );
++ ResultBuilder& setResultType( bool result );
++ ResultBuilder& setLhs( std::string const& lhs );
++ ResultBuilder& setRhs( std::string const& rhs );
++ ResultBuilder& setOp( std::string const& op );
++
++ void endExpression();
++
++ std::string reconstructExpression() const;
++ AssertionResult build() const;
++
++ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
++ void captureResult( ResultWas::OfType resultType );
++ void captureExpression();
++ void react();
++ bool shouldDebugBreak() const;
++ bool allowThrows() const;
++
++ private:
++ AssertionInfo m_assertionInfo;
++ AssertionResultData m_data;
++ struct ExprComponents {
++ ExprComponents() : testFalse( false ) {}
++ bool testFalse;
++ std::string lhs, rhs, op;
++ } m_exprComponents;
++ CopyableStream m_stream;
++
++ bool m_shouldDebugBreak;
++ bool m_shouldThrow;
++ };
++
++} // namespace Catch
++
++// Include after due to circular dependency:
++// #included from: catch_expression_lhs.hpp
++#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
++
++// #included from: catch_evaluate.hpp
++#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning(push)
++#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
++#endif
++
++#include <cstddef>
++
++namespace Catch {
++namespace Internal {
++
++ enum Operator {
++ IsEqualTo,
++ IsNotEqualTo,
++ IsLessThan,
++ IsGreaterThan,
++ IsLessThanOrEqualTo,
++ IsGreaterThanOrEqualTo
++ };
++
++ template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
++ template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
++ template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
++ template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
++ template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
++ template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
++ template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
++
++ template<typename T>
++ inline T& opCast(T const& t) { return const_cast<T&>(t); }
++
++// nullptr_t support based on pull request #154 from Konstantin Baumann
++#ifdef CATCH_CONFIG_CPP11_NULLPTR
++ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
++#endif // CATCH_CONFIG_CPP11_NULLPTR
++
++ // So the compare overloads can be operator agnostic we convey the operator as a template
++ // enum, which is used to specialise an Evaluator for doing the comparison.
++ template<typename T1, typename T2, Operator Op>
++ class Evaluator{};
++
++ template<typename T1, typename T2>
++ struct Evaluator<T1, T2, IsEqualTo> {
++ static bool evaluate( T1 const& lhs, T2 const& rhs) {
++ return opCast( lhs ) == opCast( rhs );
++ }
++ };
++ template<typename T1, typename T2>
++ struct Evaluator<T1, T2, IsNotEqualTo> {
++ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
++ return opCast( lhs ) != opCast( rhs );
++ }
++ };
++ template<typename T1, typename T2>
++ struct Evaluator<T1, T2, IsLessThan> {
++ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
++ return opCast( lhs ) < opCast( rhs );
++ }
++ };
++ template<typename T1, typename T2>
++ struct Evaluator<T1, T2, IsGreaterThan> {
++ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
++ return opCast( lhs ) > opCast( rhs );
++ }
++ };
++ template<typename T1, typename T2>
++ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
++ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
++ return opCast( lhs ) >= opCast( rhs );
++ }
++ };
++ template<typename T1, typename T2>
++ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
++ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
++ return opCast( lhs ) <= opCast( rhs );
++ }
++ };
++
++ template<Operator Op, typename T1, typename T2>
++ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
++ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
++ }
++
++ // This level of indirection allows us to specialise for integer types
++ // to avoid signed/ unsigned warnings
++
++ // "base" overload
++ template<Operator Op, typename T1, typename T2>
++ bool compare( T1 const& lhs, T2 const& rhs ) {
++ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
++ }
++
++ // unsigned X to int
++ template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
++ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
++ }
++ template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
++ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
++ }
++ template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
++ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
++ }
++
++ // unsigned X to long
++ template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
++ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
++ }
++ template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
++ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
++ }
++ template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
++ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
++ }
++
++ // int to unsigned X
++ template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
++ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
++ }
++ template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
++ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
++ }
++ template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
++ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
++ }
++
++ // long to unsigned X
++ template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
++ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
++ }
++ template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
++ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
++ }
++ template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
++ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
++ }
++
++ // pointer to long (when comparing against NULL)
++ template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
++ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
++ }
++ template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
++ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
++ }
++
++ // pointer to int (when comparing against NULL)
++ template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
++ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
++ }
++ template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
++ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
++ }
++
++#ifdef CATCH_CONFIG_CPP11_NULLPTR
++ // pointer to nullptr_t (when comparing against nullptr)
++ template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
++ return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
++ }
++ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
++ return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
++ }
++#endif // CATCH_CONFIG_CPP11_NULLPTR
++
++} // end of namespace Internal
++} // end of namespace Catch
++
++#ifdef _MSC_VER
++#pragma warning(pop)
++#endif
++
++// #included from: catch_tostring.h
++#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
++
++// #included from: catch_sfinae.hpp
++#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
++
++// Try to detect if the current compiler supports SFINAE
++
++namespace Catch {
++
++ struct TrueType {
++ static const bool value = true;
++ typedef void Enable;
++ char sizer[1];
++ };
++ struct FalseType {
++ static const bool value = false;
++ typedef void Disable;
++ char sizer[2];
++ };
++
++#ifdef CATCH_CONFIG_SFINAE
++
++ template<bool> struct NotABooleanExpression;
++
++ template<bool c> struct If : NotABooleanExpression<c> {};
++ template<> struct If<true> : TrueType {};
++ template<> struct If<false> : FalseType {};
++
++ template<int size> struct SizedIf;
++ template<> struct SizedIf<sizeof(TrueType)> : TrueType {};
++ template<> struct SizedIf<sizeof(FalseType)> : FalseType {};
++
++#endif // CATCH_CONFIG_SFINAE
++
++} // end namespace Catch
++
++#include <sstream>
++#include <iomanip>
++#include <limits>
++#include <vector>
++#include <cstddef>
++
++#ifdef __OBJC__
++// #included from: catch_objc_arc.hpp
++#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
++
++#import <Foundation/Foundation.h>
++
++#ifdef __has_feature
++#define CATCH_ARC_ENABLED __has_feature(objc_arc)
++#else
++#define CATCH_ARC_ENABLED 0
++#endif
++
++void arcSafeRelease( NSObject* obj );
++id performOptionalSelector( id obj, SEL sel );
++
++#if !CATCH_ARC_ENABLED
++inline void arcSafeRelease( NSObject* obj ) {
++ [obj release];
++}
++inline id performOptionalSelector( id obj, SEL sel ) {
++ if( [obj respondsToSelector: sel] )
++ return [obj performSelector: sel];
++ return nil;
++}
++#define CATCH_UNSAFE_UNRETAINED
++#define CATCH_ARC_STRONG
++#else
++inline void arcSafeRelease( NSObject* ){}
++inline id performOptionalSelector( id obj, SEL sel ) {
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
++#endif
++ if( [obj respondsToSelector: sel] )
++ return [obj performSelector: sel];
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++ return nil;
++}
++#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
++#define CATCH_ARC_STRONG __strong
++#endif
++
++#endif
++
++#ifdef CATCH_CPP11_OR_GREATER
++#include <tuple>
++#include <type_traits>
++#endif
++
++namespace Catch {
++
++// Why we're here.
++template<typename T>
++std::string toString( T const& value );
++
++// Built in overloads
++
++std::string toString( std::string const& value );
++std::string toString( std::wstring const& value );
++std::string toString( const char* const value );
++std::string toString( char* const value );
++std::string toString( const wchar_t* const value );
++std::string toString( wchar_t* const value );
++std::string toString( int value );
++std::string toString( unsigned long value );
++std::string toString( unsigned int value );
++std::string toString( const double value );
++std::string toString( const float value );
++std::string toString( bool value );
++std::string toString( char value );
++std::string toString( signed char value );
++std::string toString( unsigned char value );
++
++#ifdef CATCH_CONFIG_CPP11_NULLPTR
++std::string toString( std::nullptr_t );
++#endif
++
++#ifdef __OBJC__
++ std::string toString( NSString const * const& nsstring );
++ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
++ std::string toString( NSObject* const& nsObject );
++#endif
++
++namespace Detail {
++
++ extern std::string unprintableString;
++
++// SFINAE is currently disabled by default for all compilers.
++// If the non SFINAE version of IsStreamInsertable is ambiguous for you
++// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
++#ifdef CATCH_CONFIG_SFINAE
++
++ template<typename T>
++ class IsStreamInsertableHelper {
++ template<int N> struct TrueIfSizeable : TrueType {};
++
++ template<typename T2>
++ static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*);
++ static FalseType dummy(...);
++
++ public:
++ typedef SizedIf<sizeof(dummy((T*)0))> type;
++ };
++
++ template<typename T>
++ struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {};
++
++#else
++
++ struct BorgType {
++ template<typename T> BorgType( T const& );
++ };
++
++ TrueType& testStreamable( std::ostream& );
++ FalseType testStreamable( FalseType );
++
++ FalseType operator<<( std::ostream const&, BorgType const& );
++
++ template<typename T>
++ struct IsStreamInsertable {
++ static std::ostream &s;
++ static T const&t;
++ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
++ };
++
++#endif
++
++#if defined(CATCH_CPP11_OR_GREATER)
++ template<typename T,
++ bool IsEnum = std::is_enum<T>::value
++ >
++ struct EnumStringMaker
++ {
++ static std::string convert( T const& ) { return unprintableString; }
++ };
++
++ template<typename T>
++ struct EnumStringMaker<T,true>
++ {
++ static std::string convert( T const& v )
++ {
++ return ::Catch::toString(
++ static_cast<typename std::underlying_type<T>::type>(v)
++ );
++ }
++ };
++#endif
++ template<bool C>
++ struct StringMakerBase {
++#if defined(CATCH_CPP11_OR_GREATER)
++ template<typename T>
++ static std::string convert( T const& v )
++ {
++ return EnumStringMaker<T>::convert( v );
++ }
++#else
++ template<typename T>
++ static std::string convert( T const& ) { return unprintableString; }
++#endif
++ };
++
++ template<>
++ struct StringMakerBase<true> {
++ template<typename T>
++ static std::string convert( T const& _value ) {
++ std::ostringstream oss;
++ oss << _value;
++ return oss.str();
++ }
++ };
++
++ std::string rawMemoryToString( const void *object, std::size_t size );
++
++ template<typename T>
++ inline std::string rawMemoryToString( const T& object ) {
++ return rawMemoryToString( &object, sizeof(object) );
++ }
++
++} // end namespace Detail
++
++template<typename T>
++struct StringMaker :
++ Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
++
++template<typename T>
++struct StringMaker<T*> {
++ template<typename U>
++ static std::string convert( U* p ) {
++ if( !p )
++ return INTERNAL_CATCH_STRINGIFY( NULL );
++ else
++ return Detail::rawMemoryToString( p );
++ }
++};
++
++template<typename R, typename C>
++struct StringMaker<R C::*> {
++ static std::string convert( R C::* p ) {
++ if( !p )
++ return INTERNAL_CATCH_STRINGIFY( NULL );
++ else
++ return Detail::rawMemoryToString( p );
++ }
++};
++
++namespace Detail {
++ template<typename InputIterator>
++ std::string rangeToString( InputIterator first, InputIterator last );
++}
++
++//template<typename T, typename Allocator>
++//struct StringMaker<std::vector<T, Allocator> > {
++// static std::string convert( std::vector<T,Allocator> const& v ) {
++// return Detail::rangeToString( v.begin(), v.end() );
++// }
++//};
++
++template<typename T, typename Allocator>
++std::string toString( std::vector<T,Allocator> const& v ) {
++ return Detail::rangeToString( v.begin(), v.end() );
++}
++
++#ifdef CATCH_CPP11_OR_GREATER
++
++// toString for tuples
++namespace TupleDetail {
++ template<
++ typename Tuple,
++ std::size_t N = 0,
++ bool = (N < std::tuple_size<Tuple>::value)
++ >
++ struct ElementPrinter {
++ static void print( const Tuple& tuple, std::ostream& os )
++ {
++ os << ( N ? ", " : " " )
++ << Catch::toString(std::get<N>(tuple));
++ ElementPrinter<Tuple,N+1>::print(tuple,os);
++ }
++ };
++
++ template<
++ typename Tuple,
++ std::size_t N
++ >
++ struct ElementPrinter<Tuple,N,false> {
++ static void print( const Tuple&, std::ostream& ) {}
++ };
++
++}
++
++template<typename ...Types>
++struct StringMaker<std::tuple<Types...>> {
++
++ static std::string convert( const std::tuple<Types...>& tuple )
++ {
++ std::ostringstream os;
++ os << '{';
++ TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
++ os << " }";
++ return os.str();
++ }
++};
++#endif
++
++namespace Detail {
++ template<typename T>
++ std::string makeString( T const& value ) {
++ return StringMaker<T>::convert( value );
++ }
++} // end namespace Detail
++
++/// \brief converts any type to a string
++///
++/// The default template forwards on to ostringstream - except when an
++/// ostringstream overload does not exist - in which case it attempts to detect
++/// that and writes {?}.
++/// Overload (not specialise) this template for custom typs that you don't want
++/// to provide an ostream overload for.
++template<typename T>
++std::string toString( T const& value ) {
++ return StringMaker<T>::convert( value );
++}
++
++ namespace Detail {
++ template<typename InputIterator>
++ std::string rangeToString( InputIterator first, InputIterator last ) {
++ std::ostringstream oss;
++ oss << "{ ";
++ if( first != last ) {
++ oss << Catch::toString( *first );
++ for( ++first ; first != last ; ++first )
++ oss << ", " << Catch::toString( *first );
++ }
++ oss << " }";
++ return oss.str();
++ }
++}
++
++} // end namespace Catch
++
++namespace Catch {
++
++// Wraps the LHS of an expression and captures the operator and RHS (if any) -
++// wrapping them all in a ResultBuilder object
++template<typename T>
++class ExpressionLhs {
++ ExpressionLhs& operator = ( ExpressionLhs const& );
++# ifdef CATCH_CPP11_OR_GREATER
++ ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
++# endif
++
++public:
++ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
++# ifdef CATCH_CPP11_OR_GREATER
++ ExpressionLhs( ExpressionLhs const& ) = default;
++ ExpressionLhs( ExpressionLhs && ) = default;
++# endif
++
++ template<typename RhsT>
++ ResultBuilder& operator == ( RhsT const& rhs ) {
++ return captureExpression<Internal::IsEqualTo>( rhs );
++ }
++
++ template<typename RhsT>
++ ResultBuilder& operator != ( RhsT const& rhs ) {
++ return captureExpression<Internal::IsNotEqualTo>( rhs );
++ }
++
++ template<typename RhsT>
++ ResultBuilder& operator < ( RhsT const& rhs ) {
++ return captureExpression<Internal::IsLessThan>( rhs );
++ }
++
++ template<typename RhsT>
++ ResultBuilder& operator > ( RhsT const& rhs ) {
++ return captureExpression<Internal::IsGreaterThan>( rhs );
++ }
++
++ template<typename RhsT>
++ ResultBuilder& operator <= ( RhsT const& rhs ) {
++ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
++ }
++
++ template<typename RhsT>
++ ResultBuilder& operator >= ( RhsT const& rhs ) {
++ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
++ }
++
++ ResultBuilder& operator == ( bool rhs ) {
++ return captureExpression<Internal::IsEqualTo>( rhs );
++ }
++
++ ResultBuilder& operator != ( bool rhs ) {
++ return captureExpression<Internal::IsNotEqualTo>( rhs );
++ }
++
++ void endExpression() {
++ bool value = m_lhs ? true : false;
++ m_rb
++ .setLhs( Catch::toString( value ) )
++ .setResultType( value )
++ .endExpression();
++ }
++
++ // Only simple binary expressions are allowed on the LHS.
++ // If more complex compositions are required then place the sub expression in parentheses
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
++ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
++
++private:
++ template<Internal::Operator Op, typename RhsT>
++ ResultBuilder& captureExpression( RhsT const& rhs ) {
++ return m_rb
++ .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
++ .setLhs( Catch::toString( m_lhs ) )
++ .setRhs( Catch::toString( rhs ) )
++ .setOp( Internal::OperatorTraits<Op>::getName() );
++ }
++
++private:
++ ResultBuilder& m_rb;
++ T m_lhs;
++};
++
++} // end namespace Catch
++
++
++namespace Catch {
++
++ template<typename T>
++ inline ExpressionLhs<T const&> ResultBuilder::operator->* ( T const& operand ) {
++ return ExpressionLhs<T const&>( *this, operand );
++ }
++
++ inline ExpressionLhs<bool> ResultBuilder::operator->* ( bool value ) {
++ return ExpressionLhs<bool>( *this, value );
++ }
++
++} // namespace Catch
++
++// #included from: catch_message.h
++#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
++
++#include <string>
++
++namespace Catch {
++
++ struct MessageInfo {
++ MessageInfo( std::string const& _macroName,
++ SourceLineInfo const& _lineInfo,
++ ResultWas::OfType _type );
++
++ std::string macroName;
++ SourceLineInfo lineInfo;
++ ResultWas::OfType type;
++ std::string message;
++ unsigned int sequence;
++
++ bool operator == ( MessageInfo const& other ) const {
++ return sequence == other.sequence;
++ }
++ bool operator < ( MessageInfo const& other ) const {
++ return sequence < other.sequence;
++ }
++ private:
++ static unsigned int globalCount;
++ };
++
++ struct MessageBuilder {
++ MessageBuilder( std::string const& macroName,
++ SourceLineInfo const& lineInfo,
++ ResultWas::OfType type )
++ : m_info( macroName, lineInfo, type )
++ {}
++
++ template<typename T>
++ MessageBuilder& operator << ( T const& value ) {
++ m_stream << value;
++ return *this;
++ }
++
++ MessageInfo m_info;
++ std::ostringstream m_stream;
++ };
++
++ class ScopedMessage {
++ public:
++ ScopedMessage( MessageBuilder const& builder );
++ ScopedMessage( ScopedMessage const& other );
++ ~ScopedMessage();
++
++ MessageInfo m_info;
++ };
++
++} // end namespace Catch
++
++// #included from: catch_interfaces_capture.h
++#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
++
++#include <string>
++
++namespace Catch {
++
++ class TestCase;
++ class AssertionResult;
++ struct AssertionInfo;
++ struct SectionInfo;
++ struct MessageInfo;
++ class ScopedMessageBuilder;
++ struct Counts;
++
++ struct IResultCapture {
++
++ virtual ~IResultCapture();
++
++ virtual void assertionEnded( AssertionResult const& result ) = 0;
++ virtual bool sectionStarted( SectionInfo const& sectionInfo,
++ Counts& assertions ) = 0;
++ virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
++ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
++ virtual void popScopedMessage( MessageInfo const& message ) = 0;
++
++ virtual std::string getCurrentTestName() const = 0;
++ virtual const AssertionResult* getLastResult() const = 0;
++
++ virtual void handleFatalErrorCondition( std::string const& message ) = 0;
++ };
++
++ IResultCapture& getResultCapture();
++}
++
++// #included from: catch_debugger.h
++#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
++
++// #included from: catch_platform.h
++#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
++
++#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
++#define CATCH_PLATFORM_MAC
++#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
++#define CATCH_PLATFORM_IPHONE
++#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
++#define CATCH_PLATFORM_WINDOWS
++#endif
++
++#include <string>
++
++namespace Catch{
++
++ bool isDebuggerActive();
++ void writeToDebugConsole( std::string const& text );
++}
++
++#ifdef CATCH_PLATFORM_MAC
++
++ // The following code snippet based on:
++ // http://cocoawithlove.com/2008/03/break-into-debugger.html
++ #ifdef DEBUG
++ #if defined(__ppc64__) || defined(__ppc__)
++ #define CATCH_BREAK_INTO_DEBUGGER() \
++ if( Catch::isDebuggerActive() ) { \
++ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
++ : : : "memory","r0","r3","r4" ); \
++ }
++ #else
++ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
++ #endif
++ #endif
++
++#elif defined(_MSC_VER)
++ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
++#elif defined(__MINGW32__)
++ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
++ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
++#endif
++
++#ifndef CATCH_BREAK_INTO_DEBUGGER
++#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
++#endif
++
++// #included from: catch_interfaces_runner.h
++#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
++
++namespace Catch {
++ class TestCase;
++
++ struct IRunner {
++ virtual ~IRunner();
++ virtual bool aborting() const = 0;
++ };
++}
++
++///////////////////////////////////////////////////////////////////////////////
++// In the event of a failure works out if the debugger needs to be invoked
++// and/or an exception thrown and takes appropriate action.
++// This needs to be done as a macro so the debugger will stop in the user
++// source code rather than in Catch library code
++#define INTERNAL_CATCH_REACT( resultBuilder ) \
++ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
++ resultBuilder.react();
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
++ try { \
++ ( __catchResult->*expr ).endExpression(); \
++ } \
++ catch( ... ) { \
++ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
++ } \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
++ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
++ if( Catch::getResultCapture().getLastResult()->succeeded() )
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
++ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
++ if( !Catch::getResultCapture().getLastResult()->succeeded() )
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
++ try { \
++ expr; \
++ __catchResult.captureResult( Catch::ResultWas::Ok ); \
++ } \
++ catch( ... ) { \
++ __catchResult.useActiveException( resultDisposition ); \
++ } \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::alwaysFalse() )
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
++ if( __catchResult.allowThrows() ) \
++ try { \
++ expr; \
++ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
++ } \
++ catch( ... ) { \
++ __catchResult.captureResult( Catch::ResultWas::Ok ); \
++ } \
++ else \
++ __catchResult.captureResult( Catch::ResultWas::Ok ); \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::alwaysFalse() )
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
++ if( __catchResult.allowThrows() ) \
++ try { \
++ expr; \
++ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
++ } \
++ catch( exceptionType ) { \
++ __catchResult.captureResult( Catch::ResultWas::Ok ); \
++ } \
++ catch( ... ) { \
++ __catchResult.useActiveException( resultDisposition ); \
++ } \
++ else \
++ __catchResult.captureResult( Catch::ResultWas::Ok ); \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::alwaysFalse() )
++
++///////////////////////////////////////////////////////////////////////////////
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
++ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
++ __catchResult.captureResult( messageType ); \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::alwaysFalse() )
++#else
++ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
++ __catchResult << log + ::Catch::StreamEndStop(); \
++ __catchResult.captureResult( messageType ); \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::alwaysFalse() )
++#endif
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_INFO( log, macroName ) \
++ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
++ do { \
++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
++ try { \
++ std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
++ __catchResult \
++ .setLhs( Catch::toString( arg ) ) \
++ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
++ .setOp( "matches" ) \
++ .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
++ __catchResult.captureExpression(); \
++ } catch( ... ) { \
++ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
++ } \
++ INTERNAL_CATCH_REACT( __catchResult ) \
++ } while( Catch::alwaysFalse() )
++
++// #included from: internal/catch_section.h
++#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
++
++// #included from: catch_section_info.h
++#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
++
++namespace Catch {
++
++ struct SectionInfo {
++ SectionInfo
++ ( SourceLineInfo const& _lineInfo,
++ std::string const& _name,
++ std::string const& _description = std::string() );
++
++ std::string name;
++ std::string description;
++ SourceLineInfo lineInfo;
++ };
++
++} // end namespace Catch
++
++// #included from: catch_totals.hpp
++#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
++
++#include <cstddef>
++
++namespace Catch {
++
++ struct Counts {
++ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
++
++ Counts operator - ( Counts const& other ) const {
++ Counts diff;
++ diff.passed = passed - other.passed;
++ diff.failed = failed - other.failed;
++ diff.failedButOk = failedButOk - other.failedButOk;
++ return diff;
++ }
++ Counts& operator += ( Counts const& other ) {
++ passed += other.passed;
++ failed += other.failed;
++ failedButOk += other.failedButOk;
++ return *this;
++ }
++
++ std::size_t total() const {
++ return passed + failed + failedButOk;
++ }
++ bool allPassed() const {
++ return failed == 0 && failedButOk == 0;
++ }
++ bool allOk() const {
++ return failed == 0;
++ }
++
++ std::size_t passed;
++ std::size_t failed;
++ std::size_t failedButOk;
++ };
++
++ struct Totals {
++
++ Totals operator - ( Totals const& other ) const {
++ Totals diff;
++ diff.assertions = assertions - other.assertions;
++ diff.testCases = testCases - other.testCases;
++ return diff;
++ }
++
++ Totals delta( Totals const& prevTotals ) const {
++ Totals diff = *this - prevTotals;
++ if( diff.assertions.failed > 0 )
++ ++diff.testCases.failed;
++ else if( diff.assertions.failedButOk > 0 )
++ ++diff.testCases.failedButOk;
++ else
++ ++diff.testCases.passed;
++ return diff;
++ }
++
++ Totals& operator += ( Totals const& other ) {
++ assertions += other.assertions;
++ testCases += other.testCases;
++ return *this;
++ }
++
++ Counts assertions;
++ Counts testCases;
++ };
++}
++
++// #included from: catch_timer.h
++#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
++
++#ifdef CATCH_PLATFORM_WINDOWS
++typedef unsigned long long uint64_t;
++#else
++#include <stdint.h>
++#endif
++
++namespace Catch {
++
++ class Timer {
++ public:
++ Timer() : m_ticks( 0 ) {}
++ void start();
++ unsigned int getElapsedMicroseconds() const;
++ unsigned int getElapsedMilliseconds() const;
++ double getElapsedSeconds() const;
++
++ private:
++ uint64_t m_ticks;
++ };
++
++} // namespace Catch
++
++#include <string>
++
++namespace Catch {
++
++ class Section : NonCopyable {
++ public:
++ Section( SectionInfo const& info );
++ ~Section();
++
++ // This indicates whether the section should be executed or not
++ operator bool() const;
++
++ private:
++ SectionInfo m_info;
++
++ std::string m_name;
++ Counts m_assertions;
++ bool m_sectionIncluded;
++ Timer m_timer;
++ };
++
++} // end namespace Catch
++
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++ #define INTERNAL_CATCH_SECTION( ... ) \
++ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
++#else
++ #define INTERNAL_CATCH_SECTION( name, desc ) \
++ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
++#endif
++
++// #included from: internal/catch_generators.hpp
++#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
++
++#include <iterator>
++#include <vector>
++#include <string>
++#include <stdlib.h>
++
++namespace Catch {
++
++template<typename T>
++struct IGenerator {
++ virtual ~IGenerator() {}
++ virtual T getValue( std::size_t index ) const = 0;
++ virtual std::size_t size () const = 0;
++};
++
++template<typename T>
++class BetweenGenerator : public IGenerator<T> {
++public:
++ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
++
++ virtual T getValue( std::size_t index ) const {
++ return m_from+static_cast<int>( index );
++ }
++
++ virtual std::size_t size() const {
++ return static_cast<std::size_t>( 1+m_to-m_from );
++ }
++
++private:
++
++ T m_from;
++ T m_to;
++};
++
++template<typename T>
++class ValuesGenerator : public IGenerator<T> {
++public:
++ ValuesGenerator(){}
++
++ void add( T value ) {
++ m_values.push_back( value );
++ }
++
++ virtual T getValue( std::size_t index ) const {
++ return m_values[index];
++ }
++
++ virtual std::size_t size() const {
++ return m_values.size();
++ }
++
++private:
++ std::vector<T> m_values;
++};
++
++template<typename T>
++class CompositeGenerator {
++public:
++ CompositeGenerator() : m_totalSize( 0 ) {}
++
++ // *** Move semantics, similar to auto_ptr ***
++ CompositeGenerator( CompositeGenerator& other )
++ : m_fileInfo( other.m_fileInfo ),
++ m_totalSize( 0 )
++ {
++ move( other );
++ }
++
++ CompositeGenerator& setFileInfo( const char* fileInfo ) {
++ m_fileInfo = fileInfo;
++ return *this;
++ }
++
++ ~CompositeGenerator() {
++ deleteAll( m_composed );
++ }
++
++ operator T () const {
++ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
++
++ typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
++ typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
++ for( size_t index = 0; it != itEnd; ++it )
++ {
++ const IGenerator<T>* generator = *it;
++ if( overallIndex >= index && overallIndex < index + generator->size() )
++ {
++ return generator->getValue( overallIndex-index );
++ }
++ index += generator->size();
++ }
++ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
++ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
++ }
++
++ void add( const IGenerator<T>* generator ) {
++ m_totalSize += generator->size();
++ m_composed.push_back( generator );
++ }
++
++ CompositeGenerator& then( CompositeGenerator& other ) {
++ move( other );
++ return *this;
++ }
++
++ CompositeGenerator& then( T value ) {
++ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
++ valuesGen->add( value );
++ add( valuesGen );
++ return *this;
++ }
++
++private:
++
++ void move( CompositeGenerator& other ) {
++ std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
++ m_totalSize += other.m_totalSize;
++ other.m_composed.clear();
++ }
++
++ std::vector<const IGenerator<T>*> m_composed;
++ std::string m_fileInfo;
++ size_t m_totalSize;
++};
++
++namespace Generators
++{
++ template<typename T>
++ CompositeGenerator<T> between( T from, T to ) {
++ CompositeGenerator<T> generators;
++ generators.add( new BetweenGenerator<T>( from, to ) );
++ return generators;
++ }
++
++ template<typename T>
++ CompositeGenerator<T> values( T val1, T val2 ) {
++ CompositeGenerator<T> generators;
++ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
++ valuesGen->add( val1 );
++ valuesGen->add( val2 );
++ generators.add( valuesGen );
++ return generators;
++ }
++
++ template<typename T>
++ CompositeGenerator<T> values( T val1, T val2, T val3 ){
++ CompositeGenerator<T> generators;
++ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
++ valuesGen->add( val1 );
++ valuesGen->add( val2 );
++ valuesGen->add( val3 );
++ generators.add( valuesGen );
++ return generators;
++ }
++
++ template<typename T>
++ CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
++ CompositeGenerator<T> generators;
++ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
++ valuesGen->add( val1 );
++ valuesGen->add( val2 );
++ valuesGen->add( val3 );
++ valuesGen->add( val4 );
++ generators.add( valuesGen );
++ return generators;
++ }
++
++} // end namespace Generators
++
++using namespace Generators;
++
++} // end namespace Catch
++
++#define INTERNAL_CATCH_LINESTR2( line ) #line
++#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
++
++#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
++
++// #included from: internal/catch_interfaces_exception.h
++#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
++
++#include <string>
++// #included from: catch_interfaces_registry_hub.h
++#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
++
++#include <string>
++
++namespace Catch {
++
++ class TestCase;
++ struct ITestCaseRegistry;
++ struct IExceptionTranslatorRegistry;
++ struct IExceptionTranslator;
++ struct IReporterRegistry;
++ struct IReporterFactory;
++
++ struct IRegistryHub {
++ virtual ~IRegistryHub();
++
++ virtual IReporterRegistry const& getReporterRegistry() const = 0;
++ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
++ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
++ };
++
++ struct IMutableRegistryHub {
++ virtual ~IMutableRegistryHub();
++ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
++ virtual void registerTest( TestCase const& testInfo ) = 0;
++ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
++ };
++
++ IRegistryHub& getRegistryHub();
++ IMutableRegistryHub& getMutableRegistryHub();
++ void cleanUp();
++ std::string translateActiveException();
++
++}
++
++
++namespace Catch {
++
++ typedef std::string(*exceptionTranslateFunction)();
++
++ struct IExceptionTranslator {
++ virtual ~IExceptionTranslator();
++ virtual std::string translate() const = 0;
++ };
++
++ struct IExceptionTranslatorRegistry {
++ virtual ~IExceptionTranslatorRegistry();
++
++ virtual std::string translateActiveException() const = 0;
++ };
++
++ class ExceptionTranslatorRegistrar {
++ template<typename T>
++ class ExceptionTranslator : public IExceptionTranslator {
++ public:
++
++ ExceptionTranslator( std::string(*translateFunction)( T& ) )
++ : m_translateFunction( translateFunction )
++ {}
++
++ virtual std::string translate() const {
++ try {
++ throw;
++ }
++ catch( T& ex ) {
++ return m_translateFunction( ex );
++ }
++ }
++
++ protected:
++ std::string(*m_translateFunction)( T& );
++ };
++
++ public:
++ template<typename T>
++ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
++ getMutableRegistryHub().registerTranslator
++ ( new ExceptionTranslator<T>( translateFunction ) );
++ }
++ };
++}
++
++///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
++ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
++ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
++ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature )
++
++// #included from: internal/catch_approx.hpp
++#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
++
++#include <cmath>
++#include <limits>
++
++namespace Catch {
++namespace Detail {
++
++ class Approx {
++ public:
++ explicit Approx ( double value )
++ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
++ m_scale( 1.0 ),
++ m_value( value )
++ {}
++
++ Approx( Approx const& other )
++ : m_epsilon( other.m_epsilon ),
++ m_scale( other.m_scale ),
++ m_value( other.m_value )
++ {}
++
++ static Approx custom() {
++ return Approx( 0 );
++ }
++
++ Approx operator()( double value ) {
++ Approx approx( value );
++ approx.epsilon( m_epsilon );
++ approx.scale( m_scale );
++ return approx;
++ }
++
++ friend bool operator == ( double lhs, Approx const& rhs ) {
++ // Thanks to Richard Harris for his help refining this formula
++ return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
++ }
++
++ friend bool operator == ( Approx const& lhs, double rhs ) {
++ return operator==( rhs, lhs );
++ }
++
++ friend bool operator != ( double lhs, Approx const& rhs ) {
++ return !operator==( lhs, rhs );
++ }
++
++ friend bool operator != ( Approx const& lhs, double rhs ) {
++ return !operator==( rhs, lhs );
++ }
++
++ Approx& epsilon( double newEpsilon ) {
++ m_epsilon = newEpsilon;
++ return *this;
++ }
++
++ Approx& scale( double newScale ) {
++ m_scale = newScale;
++ return *this;
++ }
++
++ std::string toString() const {
++ std::ostringstream oss;
++ oss << "Approx( " << Catch::toString( m_value ) << " )";
++ return oss.str();
++ }
++
++ private:
++ double m_epsilon;
++ double m_scale;
++ double m_value;
++ };
++}
++
++template<>
++inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
++ return value.toString();
++}
++
++} // end namespace Catch
++
++// #included from: internal/catch_matchers.hpp
++#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
++
++namespace Catch {
++namespace Matchers {
++ namespace Impl {
++
++ template<typename ExpressionT>
++ struct Matcher : SharedImpl<IShared>
++ {
++ typedef ExpressionT ExpressionType;
++
++ virtual ~Matcher() {}
++ virtual Ptr<Matcher> clone() const = 0;
++ virtual bool match( ExpressionT const& expr ) const = 0;
++ virtual std::string toString() const = 0;
++ };
++
++ template<typename DerivedT, typename ExpressionT>
++ struct MatcherImpl : Matcher<ExpressionT> {
++
++ virtual Ptr<Matcher<ExpressionT> > clone() const {
++ return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
++ }
++ };
++
++ namespace Generic {
++
++ template<typename ExpressionT>
++ class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
++ public:
++
++ AllOf() {}
++ AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
++
++ AllOf& add( Matcher<ExpressionT> const& matcher ) {
++ m_matchers.push_back( matcher.clone() );
++ return *this;
++ }
++ virtual bool match( ExpressionT const& expr ) const
++ {
++ for( std::size_t i = 0; i < m_matchers.size(); ++i )
++ if( !m_matchers[i]->match( expr ) )
++ return false;
++ return true;
++ }
++ virtual std::string toString() const {
++ std::ostringstream oss;
++ oss << "( ";
++ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
++ if( i != 0 )
++ oss << " and ";
++ oss << m_matchers[i]->toString();
++ }
++ oss << " )";
++ return oss.str();
++ }
++
++ private:
++ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
++ };
++
++ template<typename ExpressionT>
++ class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
++ public:
++
++ AnyOf() {}
++ AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
++
++ AnyOf& add( Matcher<ExpressionT> const& matcher ) {
++ m_matchers.push_back( matcher.clone() );
++ return *this;
++ }
++ virtual bool match( ExpressionT const& expr ) const
++ {
++ for( std::size_t i = 0; i < m_matchers.size(); ++i )
++ if( m_matchers[i]->match( expr ) )
++ return true;
++ return false;
++ }
++ virtual std::string toString() const {
++ std::ostringstream oss;
++ oss << "( ";
++ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
++ if( i != 0 )
++ oss << " or ";
++ oss << m_matchers[i]->toString();
++ }
++ oss << " )";
++ return oss.str();
++ }
++
++ private:
++ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
++ };
++
++ }
++
++ namespace StdString {
++
++ inline std::string makeString( std::string const& str ) { return str; }
++ inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
++
++ struct Equals : MatcherImpl<Equals, std::string> {
++ Equals( std::string const& str ) : m_str( str ){}
++ Equals( Equals const& other ) : m_str( other.m_str ){}
++
++ virtual ~Equals();
++
++ virtual bool match( std::string const& expr ) const {
++ return m_str == expr;
++ }
++ virtual std::string toString() const {
++ return "equals: \"" + m_str + "\"";
++ }
++
++ std::string m_str;
++ };
++
++ struct Contains : MatcherImpl<Contains, std::string> {
++ Contains( std::string const& substr ) : m_substr( substr ){}
++ Contains( Contains const& other ) : m_substr( other.m_substr ){}
++
++ virtual ~Contains();
++
++ virtual bool match( std::string const& expr ) const {
++ return expr.find( m_substr ) != std::string::npos;
++ }
++ virtual std::string toString() const {
++ return "contains: \"" + m_substr + "\"";
++ }
++
++ std::string m_substr;
++ };
++
++ struct StartsWith : MatcherImpl<StartsWith, std::string> {
++ StartsWith( std::string const& substr ) : m_substr( substr ){}
++ StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
++
++ virtual ~StartsWith();
++
++ virtual bool match( std::string const& expr ) const {
++ return expr.find( m_substr ) == 0;
++ }
++ virtual std::string toString() const {
++ return "starts with: \"" + m_substr + "\"";
++ }
++
++ std::string m_substr;
++ };
++
++ struct EndsWith : MatcherImpl<EndsWith, std::string> {
++ EndsWith( std::string const& substr ) : m_substr( substr ){}
++ EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
++
++ virtual ~EndsWith();
++
++ virtual bool match( std::string const& expr ) const {
++ return expr.find( m_substr ) == expr.size() - m_substr.size();
++ }
++ virtual std::string toString() const {
++ return "ends with: \"" + m_substr + "\"";
++ }
++
++ std::string m_substr;
++ };
++ } // namespace StdString
++ } // namespace Impl
++
++ // The following functions create the actual matcher objects.
++ // This allows the types to be inferred
++ template<typename ExpressionT>
++ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
++ Impl::Matcher<ExpressionT> const& m2 ) {
++ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
++ }
++ template<typename ExpressionT>
++ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
++ Impl::Matcher<ExpressionT> const& m2,
++ Impl::Matcher<ExpressionT> const& m3 ) {
++ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
++ }
++ template<typename ExpressionT>
++ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
++ Impl::Matcher<ExpressionT> const& m2 ) {
++ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
++ }
++ template<typename ExpressionT>
++ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
++ Impl::Matcher<ExpressionT> const& m2,
++ Impl::Matcher<ExpressionT> const& m3 ) {
++ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
++ }
++
++ inline Impl::StdString::Equals Equals( std::string const& str ) {
++ return Impl::StdString::Equals( str );
++ }
++ inline Impl::StdString::Equals Equals( const char* str ) {
++ return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
++ }
++ inline Impl::StdString::Contains Contains( std::string const& substr ) {
++ return Impl::StdString::Contains( substr );
++ }
++ inline Impl::StdString::Contains Contains( const char* substr ) {
++ return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
++ }
++ inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) {
++ return Impl::StdString::StartsWith( substr );
++ }
++ inline Impl::StdString::StartsWith StartsWith( const char* substr ) {
++ return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
++ }
++ inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) {
++ return Impl::StdString::EndsWith( substr );
++ }
++ inline Impl::StdString::EndsWith EndsWith( const char* substr ) {
++ return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
++ }
++
++} // namespace Matchers
++
++using namespace Matchers;
++
++} // namespace Catch
++
++// #included from: internal/catch_interfaces_tag_alias_registry.h
++#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
++
++// #included from: catch_tag_alias.h
++#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
++
++#include <string>
++
++namespace Catch {
++
++ struct TagAlias {
++ TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
++
++ std::string tag;
++ SourceLineInfo lineInfo;
++ };
++
++ struct RegistrarForTagAliases {
++ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
++ };
++
++} // end namespace Catch
++
++#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
++// #included from: catch_option.hpp
++#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
++
++namespace Catch {
++
++ // An optional type
++ template<typename T>
++ class Option {
++ public:
++ Option() : nullableValue( NULL ) {}
++ Option( T const& _value )
++ : nullableValue( new( storage ) T( _value ) )
++ {}
++ Option( Option const& _other )
++ : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
++ {}
++
++ ~Option() {
++ reset();
++ }
++
++ Option& operator= ( Option const& _other ) {
++ if( &_other != this ) {
++ reset();
++ if( _other )
++ nullableValue = new( storage ) T( *_other );
++ }
++ return *this;
++ }
++ Option& operator = ( T const& _value ) {
++ reset();
++ nullableValue = new( storage ) T( _value );
++ return *this;
++ }
++
++ void reset() {
++ if( nullableValue )
++ nullableValue->~T();
++ nullableValue = NULL;
++ }
++
++ T& operator*() { return *nullableValue; }
++ T const& operator*() const { return *nullableValue; }
++ T* operator->() { return nullableValue; }
++ const T* operator->() const { return nullableValue; }
++
++ T valueOr( T const& defaultValue ) const {
++ return nullableValue ? *nullableValue : defaultValue;
++ }
++
++ bool some() const { return nullableValue != NULL; }
++ bool none() const { return nullableValue == NULL; }
++
++ bool operator !() const { return nullableValue == NULL; }
++ operator SafeBool::type() const {
++ return SafeBool::makeSafe( some() );
++ }
++
++ private:
++ T* nullableValue;
++ char storage[sizeof(T)];
++ };
++
++} // end namespace Catch
++
++namespace Catch {
++
++ struct ITagAliasRegistry {
++ virtual ~ITagAliasRegistry();
++ virtual Option<TagAlias> find( std::string const& alias ) const = 0;
++ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
++
++ static ITagAliasRegistry const& get();
++ };
++
++} // end namespace Catch
++
++// These files are included here so the single_include script doesn't put them
++// in the conditionally compiled sections
++// #included from: internal/catch_test_case_info.h
++#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
++
++#include <string>
++#include <set>
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wpadded"
++#endif
++
++namespace Catch {
++
++ struct ITestCase;
++
++ struct TestCaseInfo {
++ enum SpecialProperties{
++ None = 0,
++ IsHidden = 1 << 1,
++ ShouldFail = 1 << 2,
++ MayFail = 1 << 3,
++ Throws = 1 << 4
++ };
++
++ TestCaseInfo( std::string const& _name,
++ std::string const& _className,
++ std::string const& _description,
++ std::set<std::string> const& _tags,
++ SourceLineInfo const& _lineInfo );
++
++ TestCaseInfo( TestCaseInfo const& other );
++
++ bool isHidden() const;
++ bool throws() const;
++ bool okToFail() const;
++ bool expectedToFail() const;
++
++ std::string name;
++ std::string className;
++ std::string description;
++ std::set<std::string> tags;
++ std::set<std::string> lcaseTags;
++ std::string tagsAsString;
++ SourceLineInfo lineInfo;
++ SpecialProperties properties;
++ };
++
++ class TestCase : public TestCaseInfo {
++ public:
++
++ TestCase( ITestCase* testCase, TestCaseInfo const& info );
++ TestCase( TestCase const& other );
++
++ TestCase withName( std::string const& _newName ) const;
++
++ void invoke() const;
++
++ TestCaseInfo const& getTestCaseInfo() const;
++
++ void swap( TestCase& other );
++ bool operator == ( TestCase const& other ) const;
++ bool operator < ( TestCase const& other ) const;
++ TestCase& operator = ( TestCase const& other );
++
++ private:
++ Ptr<ITestCase> test;
++ };
++
++ TestCase makeTestCase( ITestCase* testCase,
++ std::string const& className,
++ std::string const& name,
++ std::string const& description,
++ SourceLineInfo const& lineInfo );
++}
++
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++
++
++#ifdef __OBJC__
++// #included from: internal/catch_objc.hpp
++#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
++
++#import <objc/runtime.h>
++
++#include <string>
++
++// NB. Any general catch headers included here must be included
++// in catch.hpp first to make sure they are included by the single
++// header for non obj-usage
++
++///////////////////////////////////////////////////////////////////////////////
++// This protocol is really only here for (self) documenting purposes, since
++// all its methods are optional.
++@protocol OcFixture
++
++@optional
++
++-(void) setUp;
++-(void) tearDown;
++
++@end
++
++namespace Catch {
++
++ class OcMethod : public SharedImpl<ITestCase> {
++
++ public:
++ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
++
++ virtual void invoke() const {
++ id obj = [[m_cls alloc] init];
++
++ performOptionalSelector( obj, @selector(setUp) );
++ performOptionalSelector( obj, m_sel );
++ performOptionalSelector( obj, @selector(tearDown) );
++
++ arcSafeRelease( obj );
++ }
++ private:
++ virtual ~OcMethod() {}
++
++ Class m_cls;
++ SEL m_sel;
++ };
++
++ namespace Detail{
++
++ inline std::string getAnnotation( Class cls,
++ std::string const& annotationName,
++ std::string const& testCaseName ) {
++ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
++ SEL sel = NSSelectorFromString( selStr );
++ arcSafeRelease( selStr );
++ id value = performOptionalSelector( cls, sel );
++ if( value )
++ return [(NSString*)value UTF8String];
++ return "";
++ }
++ }
++
++ inline size_t registerTestMethods() {
++ size_t noTestMethods = 0;
++ int noClasses = objc_getClassList( NULL, 0 );
++
++ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
++ objc_getClassList( classes, noClasses );
++
++ for( int c = 0; c < noClasses; c++ ) {
++ Class cls = classes[c];
++ {
++ u_int count;
++ Method* methods = class_copyMethodList( cls, &count );
++ for( u_int m = 0; m < count ; m++ ) {
++ SEL selector = method_getName(methods[m]);
++ std::string methodName = sel_getName(selector);
++ if( startsWith( methodName, "Catch_TestCase_" ) ) {
++ std::string testCaseName = methodName.substr( 15 );
++ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
++ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
++ const char* className = class_getName( cls );
++
++ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
++ noTestMethods++;
++ }
++ }
++ free(methods);
++ }
++ }
++ return noTestMethods;
++ }
++
++ namespace Matchers {
++ namespace Impl {
++ namespace NSStringMatchers {
++
++ template<typename MatcherT>
++ struct StringHolder : MatcherImpl<MatcherT, NSString*>{
++ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
++ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
++ StringHolder() {
++ arcSafeRelease( m_substr );
++ }
++
++ NSString* m_substr;
++ };
++
++ struct Equals : StringHolder<Equals> {
++ Equals( NSString* substr ) : StringHolder( substr ){}
++
++ virtual bool match( ExpressionType const& str ) const {
++ return (str != nil || m_substr == nil ) &&
++ [str isEqualToString:m_substr];
++ }
++
++ virtual std::string toString() const {
++ return "equals string: " + Catch::toString( m_substr );
++ }
++ };
++
++ struct Contains : StringHolder<Contains> {
++ Contains( NSString* substr ) : StringHolder( substr ){}
++
++ virtual bool match( ExpressionType const& str ) const {
++ return (str != nil || m_substr == nil ) &&
++ [str rangeOfString:m_substr].location != NSNotFound;
++ }
++
++ virtual std::string toString() const {
++ return "contains string: " + Catch::toString( m_substr );
++ }
++ };
++
++ struct StartsWith : StringHolder<StartsWith> {
++ StartsWith( NSString* substr ) : StringHolder( substr ){}
++
++ virtual bool match( ExpressionType const& str ) const {
++ return (str != nil || m_substr == nil ) &&
++ [str rangeOfString:m_substr].location == 0;
++ }
++
++ virtual std::string toString() const {
++ return "starts with: " + Catch::toString( m_substr );
++ }
++ };
++ struct EndsWith : StringHolder<EndsWith> {
++ EndsWith( NSString* substr ) : StringHolder( substr ){}
++
++ virtual bool match( ExpressionType const& str ) const {
++ return (str != nil || m_substr == nil ) &&
++ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
++ }
++
++ virtual std::string toString() const {
++ return "ends with: " + Catch::toString( m_substr );
++ }
++ };
++
++ } // namespace NSStringMatchers
++ } // namespace Impl
++
++ inline Impl::NSStringMatchers::Equals
++ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
++
++ inline Impl::NSStringMatchers::Contains
++ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
++
++ inline Impl::NSStringMatchers::StartsWith
++ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
++
++ inline Impl::NSStringMatchers::EndsWith
++ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
++
++ } // namespace Matchers
++
++ using namespace Matchers;
++
++} // namespace Catch
++
++///////////////////////////////////////////////////////////////////////////////
++#define OC_TEST_CASE( name, desc )\
+++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
++{\
++return @ name; \
++}\
+++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
++{ \
++return @ desc; \
++} \
++-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
++
++#endif
++
++#ifdef CATCH_IMPL
++// #included from: internal/catch_impl.hpp
++#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
++
++// Collect all the implementation files together here
++// These are the equivalent of what would usually be cpp files
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wweak-vtables"
++#endif
++
++// #included from: ../catch_runner.hpp
++#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
++
++// #included from: internal/catch_commandline.hpp
++#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
++
++// #included from: catch_config.hpp
++#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
++
++// #included from: catch_test_spec_parser.hpp
++#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wpadded"
++#endif
++
++// #included from: catch_test_spec.hpp
++#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wpadded"
++#endif
++
++#include <string>
++#include <vector>
++
++namespace Catch {
++
++ class TestSpec {
++ struct Pattern : SharedImpl<> {
++ virtual ~Pattern();
++ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
++ };
++ class NamePattern : public Pattern {
++ enum WildcardPosition {
++ NoWildcard = 0,
++ WildcardAtStart = 1,
++ WildcardAtEnd = 2,
++ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
++ };
++
++ public:
++ NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
++ if( startsWith( m_name, "*" ) ) {
++ m_name = m_name.substr( 1 );
++ m_wildcard = WildcardAtStart;
++ }
++ if( endsWith( m_name, "*" ) ) {
++ m_name = m_name.substr( 0, m_name.size()-1 );
++ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
++ }
++ }
++ virtual ~NamePattern();
++ virtual bool matches( TestCaseInfo const& testCase ) const {
++ switch( m_wildcard ) {
++ case NoWildcard:
++ return m_name == toLower( testCase.name );
++ case WildcardAtStart:
++ return endsWith( toLower( testCase.name ), m_name );
++ case WildcardAtEnd:
++ return startsWith( toLower( testCase.name ), m_name );
++ case WildcardAtBothEnds:
++ return contains( toLower( testCase.name ), m_name );
++ }
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wunreachable-code"
++#endif
++ throw std::logic_error( "Unknown enum" );
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++ }
++ private:
++ std::string m_name;
++ WildcardPosition m_wildcard;
++ };
++ class TagPattern : public Pattern {
++ public:
++ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
++ virtual ~TagPattern();
++ virtual bool matches( TestCaseInfo const& testCase ) const {
++ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
++ }
++ private:
++ std::string m_tag;
++ };
++ class ExcludedPattern : public Pattern {
++ public:
++ ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
++ virtual ~ExcludedPattern();
++ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
++ private:
++ Ptr<Pattern> m_underlyingPattern;
++ };
++
++ struct Filter {
++ std::vector<Ptr<Pattern> > m_patterns;
++
++ bool matches( TestCaseInfo const& testCase ) const {
++ // All patterns in a filter must match for the filter to be a match
++ for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
++ if( !(*it)->matches( testCase ) )
++ return false;
++ return true;
++ }
++ };
++
++ public:
++ bool hasFilters() const {
++ return !m_filters.empty();
++ }
++ bool matches( TestCaseInfo const& testCase ) const {
++ // A TestSpec matches if any filter matches
++ for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
++ if( it->matches( testCase ) )
++ return true;
++ return false;
++ }
++
++ private:
++ std::vector<Filter> m_filters;
++
++ friend class TestSpecParser;
++ };
++}
++
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++
++namespace Catch {
++
++ class TestSpecParser {
++ enum Mode{ None, Name, QuotedName, Tag };
++ Mode m_mode;
++ bool m_exclusion;
++ std::size_t m_start, m_pos;
++ std::string m_arg;
++ TestSpec::Filter m_currentFilter;
++ TestSpec m_testSpec;
++ ITagAliasRegistry const* m_tagAliases;
++
++ public:
++ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
++
++ TestSpecParser& parse( std::string const& arg ) {
++ m_mode = None;
++ m_exclusion = false;
++ m_start = std::string::npos;
++ m_arg = m_tagAliases->expandAliases( arg );
++ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
++ visitChar( m_arg[m_pos] );
++ if( m_mode == Name )
++ addPattern<TestSpec::NamePattern>();
++ return *this;
++ }
++ TestSpec testSpec() {
++ addFilter();
++ return m_testSpec;
++ }
++ private:
++ void visitChar( char c ) {
++ if( m_mode == None ) {
++ switch( c ) {
++ case ' ': return;
++ case '~': m_exclusion = true; return;
++ case '[': return startNewMode( Tag, ++m_pos );
++ case '"': return startNewMode( QuotedName, ++m_pos );
++ default: startNewMode( Name, m_pos ); break;
++ }
++ }
++ if( m_mode == Name ) {
++ if( c == ',' ) {
++ addPattern<TestSpec::NamePattern>();
++ addFilter();
++ }
++ else if( c == '[' ) {
++ if( subString() == "exclude:" )
++ m_exclusion = true;
++ else
++ addPattern<TestSpec::NamePattern>();
++ startNewMode( Tag, ++m_pos );
++ }
++ }
++ else if( m_mode == QuotedName && c == '"' )
++ addPattern<TestSpec::NamePattern>();
++ else if( m_mode == Tag && c == ']' )
++ addPattern<TestSpec::TagPattern>();
++ }
++ void startNewMode( Mode mode, std::size_t start ) {
++ m_mode = mode;
++ m_start = start;
++ }
++ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
++ template<typename T>
++ void addPattern() {
++ std::string token = subString();
++ if( startsWith( token, "exclude:" ) ) {
++ m_exclusion = true;
++ token = token.substr( 8 );
++ }
++ if( !token.empty() ) {
++ Ptr<TestSpec::Pattern> pattern = new T( token );
++ if( m_exclusion )
++ pattern = new TestSpec::ExcludedPattern( pattern );
++ m_currentFilter.m_patterns.push_back( pattern );
++ }
++ m_exclusion = false;
++ m_mode = None;
++ }
++ void addFilter() {
++ if( !m_currentFilter.m_patterns.empty() ) {
++ m_testSpec.m_filters.push_back( m_currentFilter );
++ m_currentFilter = TestSpec::Filter();
++ }
++ }
++ };
++ inline TestSpec parseTestSpec( std::string const& arg ) {
++ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
++ }
++
++} // namespace Catch
++
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++
++// #included from: catch_interfaces_config.h
++#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
++
++#include <iostream>
++#include <string>
++#include <vector>
++
++namespace Catch {
++
++ struct Verbosity { enum Level {
++ NoOutput = 0,
++ Quiet,
++ Normal
++ }; };
++
++ struct WarnAbout { enum What {
++ Nothing = 0x00,
++ NoAssertions = 0x01
++ }; };
++
++ struct ShowDurations { enum OrNot {
++ DefaultForReporter,
++ Always,
++ Never
++ }; };
++ struct RunTests { enum InWhatOrder {
++ InDeclarationOrder,
++ InLexicographicalOrder,
++ InRandomOrder
++ }; };
++
++ class TestSpec;
++
++ struct IConfig : IShared {
++
++ virtual ~IConfig();
++
++ virtual bool allowThrows() const = 0;
++ virtual std::ostream& stream() const = 0;
++ virtual std::string name() const = 0;
++ virtual bool includeSuccessfulResults() const = 0;
++ virtual bool shouldDebugBreak() const = 0;
++ virtual bool warnAboutMissingAssertions() const = 0;
++ virtual int abortAfter() const = 0;
++ virtual bool showInvisibles() const = 0;
++ virtual ShowDurations::OrNot showDurations() const = 0;
++ virtual TestSpec const& testSpec() const = 0;
++ virtual RunTests::InWhatOrder runOrder() const = 0;
++ virtual unsigned int rngSeed() const = 0;
++ virtual bool forceColour() const = 0;
++ };
++}
++
++// #included from: catch_stream.h
++#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
++
++#include <streambuf>
++
++#ifdef __clang__
++#pragma clang diagnostic ignored "-Wpadded"
++#endif
++
++namespace Catch {
++
++ class Stream {
++ public:
++ Stream();
++ Stream( std::streambuf* _streamBuf, bool _isOwned );
++ void release();
++
++ std::streambuf* streamBuf;
++
++ private:
++ bool isOwned;
++ };
++
++ std::ostream& cout();
++ std::ostream& cerr();
++}
++
++#include <memory>
++#include <vector>
++#include <string>
++#include <iostream>
++#include <ctime>
++
++#ifndef CATCH_CONFIG_CONSOLE_WIDTH
++#define CATCH_CONFIG_CONSOLE_WIDTH 80
++#endif
++
++namespace Catch {
++
++ struct ConfigData {
++
++ ConfigData()
++ : listTests( false ),
++ listTags( false ),
++ listReporters( false ),
++ listTestNamesOnly( false ),
++ showSuccessfulTests( false ),
++ shouldDebugBreak( false ),
++ noThrow( false ),
++ showHelp( false ),
++ showInvisibles( false ),
++ forceColour( false ),
++ abortAfter( -1 ),
++ rngSeed( 0 ),
++ verbosity( Verbosity::Normal ),
++ warnings( WarnAbout::Nothing ),
++ showDurations( ShowDurations::DefaultForReporter ),
++ runOrder( RunTests::InDeclarationOrder )
++ {}
++
++ bool listTests;
++ bool listTags;
++ bool listReporters;
++ bool listTestNamesOnly;
++
++ bool showSuccessfulTests;
++ bool shouldDebugBreak;
++ bool noThrow;
++ bool showHelp;
++ bool showInvisibles;
++ bool forceColour;
++
++ int abortAfter;
++ unsigned int rngSeed;
++
++ Verbosity::Level verbosity;
++ WarnAbout::What warnings;
++ ShowDurations::OrNot showDurations;
++ RunTests::InWhatOrder runOrder;
++
++ std::string reporterName;
++ std::string outputFilename;
++ std::string name;
++ std::string processName;
++
++ std::vector<std::string> testsOrTags;
++ };
++
++ class Config : public SharedImpl<IConfig> {
++ private:
++ Config( Config const& other );
++ Config& operator = ( Config const& other );
++ virtual void dummy();
++ public:
++
++ Config()
++ : m_os( Catch::cout().rdbuf() )
++ {}
++
++ Config( ConfigData const& data )
++ : m_data( data ),
++ m_os( Catch::cout().rdbuf() )
++ {
++ if( !data.testsOrTags.empty() ) {
++ TestSpecParser parser( ITagAliasRegistry::get() );
++ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
++ parser.parse( data.testsOrTags[i] );
++ m_testSpec = parser.testSpec();
++ }
++ }
++
++ virtual ~Config() {
++ m_os.rdbuf( Catch::cout().rdbuf() );
++ m_stream.release();
++ }
++
++ void setFilename( std::string const& filename ) {
++ m_data.outputFilename = filename;
++ }
++
++ std::string const& getFilename() const {
++ return m_data.outputFilename ;
++ }
++
++ bool listTests() const { return m_data.listTests; }
++ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
++ bool listTags() const { return m_data.listTags; }
++ bool listReporters() const { return m_data.listReporters; }
++
++ std::string getProcessName() const { return m_data.processName; }
++
++ bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
++
++ void setStreamBuf( std::streambuf* buf ) {
++ m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
++ }
++
++ void useStream( std::string const& streamName ) {
++ Stream stream = createStream( streamName );
++ setStreamBuf( stream.streamBuf );
++ m_stream.release();
++ m_stream = stream;
++ }
++
++ std::string getReporterName() const { return m_data.reporterName; }
++
++ int abortAfter() const { return m_data.abortAfter; }
++
++ TestSpec const& testSpec() const { return m_testSpec; }
++
++ bool showHelp() const { return m_data.showHelp; }
++ bool showInvisibles() const { return m_data.showInvisibles; }
++
++ // IConfig interface
++ virtual bool allowThrows() const { return !m_data.noThrow; }
++ virtual std::ostream& stream() const { return m_os; }
++ virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
++ virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
++ virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
++ virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
++ virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; }
++ virtual unsigned int rngSeed() const { return m_data.rngSeed; }
++ virtual bool forceColour() const { return m_data.forceColour; }
++
++ private:
++ ConfigData m_data;
++
++ Stream m_stream;
++ mutable std::ostream m_os;
++ TestSpec m_testSpec;
++ };
++
++} // end namespace Catch
++
++// #included from: catch_clara.h
++#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
++
++// Use Catch's value for console width (store Clara's off to the side, if present)
++#ifdef CLARA_CONFIG_CONSOLE_WIDTH
++#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
++#undef CLARA_CONFIG_CONSOLE_WIDTH
++#endif
++#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
++
++// Declare Clara inside the Catch namespace
++#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
++// #included from: ../external/clara.h
++
++// Only use header guard if we are not using an outer namespace
++#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
++
++#ifndef STITCH_CLARA_OPEN_NAMESPACE
++#define TWOBLUECUBES_CLARA_H_INCLUDED
++#define STITCH_CLARA_OPEN_NAMESPACE
++#define STITCH_CLARA_CLOSE_NAMESPACE
++#else
++#define STITCH_CLARA_CLOSE_NAMESPACE }
++#endif
++
++#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
++
++// ----------- #included from tbc_text_format.h -----------
++
++// Only use header guard if we are not using an outer namespace
++#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
++#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++#define TBC_TEXT_FORMAT_H_INCLUDED
++#endif
++
++#include <string>
++#include <vector>
++#include <sstream>
++
++// Use optional outer namespace
++#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
++#endif
++
++namespace Tbc {
++
++#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
++ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
++#else
++ const unsigned int consoleWidth = 80;
++#endif
++
++ struct TextAttributes {
++ TextAttributes()
++ : initialIndent( std::string::npos ),
++ indent( 0 ),
++ width( consoleWidth-1 ),
++ tabChar( '\t' )
++ {}
++
++ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
++ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
++ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
++ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
++
++ std::size_t initialIndent; // indent of first line, or npos
++ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
++ std::size_t width; // maximum width of text, including indent. Longer text will wrap
++ char tabChar; // If this char is seen the indent is changed to current pos
++ };
++
++ class Text {
++ public:
++ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
++ : attr( _attr )
++ {
++ std::string wrappableChars = " [({.,/|\\-";
++ std::size_t indent = _attr.initialIndent != std::string::npos
++ ? _attr.initialIndent
++ : _attr.indent;
++ std::string remainder = _str;
++
++ while( !remainder.empty() ) {
++ if( lines.size() >= 1000 ) {
++ lines.push_back( "... message truncated due to excessive size" );
++ return;
++ }
++ std::size_t tabPos = std::string::npos;
++ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
++ std::size_t pos = remainder.find_first_of( '\n' );
++ if( pos <= width ) {
++ width = pos;
++ }
++ pos = remainder.find_last_of( _attr.tabChar, width );
++ if( pos != std::string::npos ) {
++ tabPos = pos;
++ if( remainder[width] == '\n' )
++ width--;
++ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
++ }
++
++ if( width == remainder.size() ) {
++ spliceLine( indent, remainder, width );
++ }
++ else if( remainder[width] == '\n' ) {
++ spliceLine( indent, remainder, width );
++ if( width <= 1 || remainder.size() != 1 )
++ remainder = remainder.substr( 1 );
++ indent = _attr.indent;
++ }
++ else {
++ pos = remainder.find_last_of( wrappableChars, width );
++ if( pos != std::string::npos && pos > 0 ) {
++ spliceLine( indent, remainder, pos );
++ if( remainder[0] == ' ' )
++ remainder = remainder.substr( 1 );
++ }
++ else {
++ spliceLine( indent, remainder, width-1 );
++ lines.back() += "-";
++ }
++ if( lines.size() == 1 )
++ indent = _attr.indent;
++ if( tabPos != std::string::npos )
++ indent += tabPos;
++ }
++ }
++ }
++
++ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
++ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
++ _remainder = _remainder.substr( _pos );
++ }
++
++ typedef std::vector<std::string>::const_iterator const_iterator;
++
++ const_iterator begin() const { return lines.begin(); }
++ const_iterator end() const { return lines.end(); }
++ std::string const& last() const { return lines.back(); }
++ std::size_t size() const { return lines.size(); }
++ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
++ std::string toString() const {
++ std::ostringstream oss;
++ oss << *this;
++ return oss.str();
++ }
++
++ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
++ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
++ it != itEnd; ++it ) {
++ if( it != _text.begin() )
++ _stream << "\n";
++ _stream << *it;
++ }
++ return _stream;
++ }
++
++ private:
++ std::string str;
++ TextAttributes attr;
++ std::vector<std::string> lines;
++ };
++
++} // end namespace Tbc
++
++#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++} // end outer namespace
++#endif
++
++#endif // TBC_TEXT_FORMAT_H_INCLUDED
++
++// ----------- end of #include from tbc_text_format.h -----------
++// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
++
++#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
++
++#include <map>
++#include <algorithm>
++#include <stdexcept>
++#include <memory>
++
++// Use optional outer namespace
++#ifdef STITCH_CLARA_OPEN_NAMESPACE
++STITCH_CLARA_OPEN_NAMESPACE
++#endif
++
++namespace Clara {
++
++ struct UnpositionalTag {};
++
++ extern UnpositionalTag _;
++
++#ifdef CLARA_CONFIG_MAIN
++ UnpositionalTag _;
++#endif
++
++ namespace Detail {
++
++#ifdef CLARA_CONSOLE_WIDTH
++ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
++#else
++ const unsigned int consoleWidth = 80;
++#endif
++
++ using namespace Tbc;
++
++ inline bool startsWith( std::string const& str, std::string const& prefix ) {
++ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
++ }
++
++ template<typename T> struct RemoveConstRef{ typedef T type; };
++ template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
++ template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
++ template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
++
++ template<typename T> struct IsBool { static const bool value = false; };
++ template<> struct IsBool<bool> { static const bool value = true; };
++
++ template<typename T>
++ void convertInto( std::string const& _source, T& _dest ) {
++ std::stringstream ss;
++ ss << _source;
++ ss >> _dest;
++ if( ss.fail() )
++ throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
++ }
++ inline void convertInto( std::string const& _source, std::string& _dest ) {
++ _dest = _source;
++ }
++ inline void convertInto( std::string const& _source, bool& _dest ) {
++ std::string sourceLC = _source;
++ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
++ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
++ _dest = true;
++ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
++ _dest = false;
++ else
++ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
++ }
++ inline void convertInto( bool _source, bool& _dest ) {
++ _dest = _source;
++ }
++ template<typename T>
++ inline void convertInto( bool, T& ) {
++ throw std::runtime_error( "Invalid conversion" );
++ }
++
++ template<typename ConfigT>
++ struct IArgFunction {
++ virtual ~IArgFunction() {}
++# ifdef CATCH_CPP11_OR_GREATER
++ IArgFunction() = default;
++ IArgFunction( IArgFunction const& ) = default;
++# endif
++ virtual void set( ConfigT& config, std::string const& value ) const = 0;
++ virtual void setFlag( ConfigT& config ) const = 0;
++ virtual bool takesArg() const = 0;
++ virtual IArgFunction* clone() const = 0;
++ };
++
++ template<typename ConfigT>
++ class BoundArgFunction {
++ public:
++ BoundArgFunction() : functionObj( NULL ) {}
++ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
++ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
++ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
++ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
++ delete functionObj;
++ functionObj = newFunctionObj;
++ return *this;
++ }
++ ~BoundArgFunction() { delete functionObj; }
++
++ void set( ConfigT& config, std::string const& value ) const {
++ functionObj->set( config, value );
++ }
++ void setFlag( ConfigT& config ) const {
++ functionObj->setFlag( config );
++ }
++ bool takesArg() const { return functionObj->takesArg(); }
++
++ bool isSet() const {
++ return functionObj != NULL;
++ }
++ private:
++ IArgFunction<ConfigT>* functionObj;
++ };
++
++ template<typename C>
++ struct NullBinder : IArgFunction<C>{
++ virtual void set( C&, std::string const& ) const {}
++ virtual void setFlag( C& ) const {}
++ virtual bool takesArg() const { return true; }
++ virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
++ };
++
++ template<typename C, typename M>
++ struct BoundDataMember : IArgFunction<C>{
++ BoundDataMember( M C::* _member ) : member( _member ) {}
++ virtual void set( C& p, std::string const& stringValue ) const {
++ convertInto( stringValue, p.*member );
++ }
++ virtual void setFlag( C& p ) const {
++ convertInto( true, p.*member );
++ }
++ virtual bool takesArg() const { return !IsBool<M>::value; }
++ virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
++ M C::* member;
++ };
++ template<typename C, typename M>
++ struct BoundUnaryMethod : IArgFunction<C>{
++ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
++ virtual void set( C& p, std::string const& stringValue ) const {
++ typename RemoveConstRef<M>::type value;
++ convertInto( stringValue, value );
++ (p.*member)( value );
++ }
++ virtual void setFlag( C& p ) const {
++ typename RemoveConstRef<M>::type value;
++ convertInto( true, value );
++ (p.*member)( value );
++ }
++ virtual bool takesArg() const { return !IsBool<M>::value; }
++ virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
++ void (C::*member)( M );
++ };
++ template<typename C>
++ struct BoundNullaryMethod : IArgFunction<C>{
++ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
++ virtual void set( C& p, std::string const& stringValue ) const {
++ bool value;
++ convertInto( stringValue, value );
++ if( value )
++ (p.*member)();
++ }
++ virtual void setFlag( C& p ) const {
++ (p.*member)();
++ }
++ virtual bool takesArg() const { return false; }
++ virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
++ void (C::*member)();
++ };
++
++ template<typename C>
++ struct BoundUnaryFunction : IArgFunction<C>{
++ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
++ virtual void set( C& obj, std::string const& stringValue ) const {
++ bool value;
++ convertInto( stringValue, value );
++ if( value )
++ function( obj );
++ }
++ virtual void setFlag( C& p ) const {
++ function( p );
++ }
++ virtual bool takesArg() const { return false; }
++ virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
++ void (*function)( C& );
++ };
++
++ template<typename C, typename T>
++ struct BoundBinaryFunction : IArgFunction<C>{
++ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
++ virtual void set( C& obj, std::string const& stringValue ) const {
++ typename RemoveConstRef<T>::type value;
++ convertInto( stringValue, value );
++ function( obj, value );
++ }
++ virtual void setFlag( C& obj ) const {
++ typename RemoveConstRef<T>::type value;
++ convertInto( true, value );
++ function( obj, value );
++ }
++ virtual bool takesArg() const { return !IsBool<T>::value; }
++ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
++ void (*function)( C&, T );
++ };
++
++ } // namespace Detail
++
++ struct Parser {
++ Parser() : separators( " \t=:" ) {}
++
++ struct Token {
++ enum Type { Positional, ShortOpt, LongOpt };
++ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
++ Type type;
++ std::string data;
++ };
++
++ void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
++ const std::string doubleDash = "--";
++ for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
++ parseIntoTokens( argv[i] , tokens);
++ }
++ void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
++ while( !arg.empty() ) {
++ Parser::Token token( Parser::Token::Positional, arg );
++ arg = "";
++ if( token.data[0] == '-' ) {
++ if( token.data.size() > 1 && token.data[1] == '-' ) {
++ token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
++ }
++ else {
++ token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
++ if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
++ arg = "-" + token.data.substr( 1 );
++ token.data = token.data.substr( 0, 1 );
++ }
++ }
++ }
++ if( token.type != Parser::Token::Positional ) {
++ std::size_t pos = token.data.find_first_of( separators );
++ if( pos != std::string::npos ) {
++ arg = token.data.substr( pos+1 );
++ token.data = token.data.substr( 0, pos );
++ }
++ }
++ tokens.push_back( token );
++ }
++ }
++ std::string separators;
++ };
++
++ template<typename ConfigT>
++ struct CommonArgProperties {
++ CommonArgProperties() {}
++ CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
++
++ Detail::BoundArgFunction<ConfigT> boundField;
++ std::string description;
++ std::string detail;
++ std::string placeholder; // Only value if boundField takes an arg
++
++ bool takesArg() const {
++ return !placeholder.empty();
++ }
++ void validate() const {
++ if( !boundField.isSet() )
++ throw std::logic_error( "option not bound" );
++ }
++ };
++ struct OptionArgProperties {
++ std::vector<std::string> shortNames;
++ std::string longName;
++
++ bool hasShortName( std::string const& shortName ) const {
++ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
++ }
++ bool hasLongName( std::string const& _longName ) const {
++ return _longName == longName;
++ }
++ };
++ struct PositionalArgProperties {
++ PositionalArgProperties() : position( -1 ) {}
++ int position; // -1 means non-positional (floating)
++
++ bool isFixedPositional() const {
++ return position != -1;
++ }
++ };
++
++ template<typename ConfigT>
++ class CommandLine {
++
++ struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
++ Arg() {}
++ Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
++
++ using CommonArgProperties<ConfigT>::placeholder; // !TBD
++
++ std::string dbgName() const {
++ if( !longName.empty() )
++ return "--" + longName;
++ if( !shortNames.empty() )
++ return "-" + shortNames[0];
++ return "positional args";
++ }
++ std::string commands() const {
++ std::ostringstream oss;
++ bool first = true;
++ std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
++ for(; it != itEnd; ++it ) {
++ if( first )
++ first = false;
++ else
++ oss << ", ";
++ oss << "-" << *it;
++ }
++ if( !longName.empty() ) {
++ if( !first )
++ oss << ", ";
++ oss << "--" << longName;
++ }
++ if( !placeholder.empty() )
++ oss << " <" << placeholder << ">";
++ return oss.str();
++ }
++ };
++
++ // NOTE: std::auto_ptr is deprecated in c++11/c++0x
++#if defined(__cplusplus) && __cplusplus > 199711L
++ typedef std::unique_ptr<Arg> ArgAutoPtr;
++#else
++ typedef std::auto_ptr<Arg> ArgAutoPtr;
++#endif
++
++ friend void addOptName( Arg& arg, std::string const& optName )
++ {
++ if( optName.empty() )
++ return;
++ if( Detail::startsWith( optName, "--" ) ) {
++ if( !arg.longName.empty() )
++ throw std::logic_error( "Only one long opt may be specified. '"
++ + arg.longName
++ + "' already specified, now attempting to add '"
++ + optName + "'" );
++ arg.longName = optName.substr( 2 );
++ }
++ else if( Detail::startsWith( optName, "-" ) )
++ arg.shortNames.push_back( optName.substr( 1 ) );
++ else
++ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
++ }
++ friend void setPositionalArg( Arg& arg, int position )
++ {
++ arg.position = position;
++ }
++
++ class ArgBuilder {
++ public:
++ ArgBuilder( Arg* arg ) : m_arg( arg ) {}
++
++ // Bind a non-boolean data member (requires placeholder string)
++ template<typename C, typename M>
++ void bind( M C::* field, std::string const& placeholder ) {
++ m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
++ m_arg->placeholder = placeholder;
++ }
++ // Bind a boolean data member (no placeholder required)
++ template<typename C>
++ void bind( bool C::* field ) {
++ m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
++ }
++
++ // Bind a method taking a single, non-boolean argument (requires a placeholder string)
++ template<typename C, typename M>
++ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
++ m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
++ m_arg->placeholder = placeholder;
++ }
++
++ // Bind a method taking a single, boolean argument (no placeholder string required)
++ template<typename C>
++ void bind( void (C::* unaryMethod)( bool ) ) {
++ m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
++ }
++
++ // Bind a method that takes no arguments (will be called if opt is present)
++ template<typename C>
++ void bind( void (C::* nullaryMethod)() ) {
++ m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
++ }
++
++ // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
++ template<typename C>
++ void bind( void (* unaryFunction)( C& ) ) {
++ m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
++ }
++
++ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
++ template<typename C, typename T>
++ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
++ m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
++ m_arg->placeholder = placeholder;
++ }
++
++ ArgBuilder& describe( std::string const& description ) {
++ m_arg->description = description;
++ return *this;
++ }
++ ArgBuilder& detail( std::string const& detail ) {
++ m_arg->detail = detail;
++ return *this;
++ }
++
++ protected:
++ Arg* m_arg;
++ };
++
++ class OptBuilder : public ArgBuilder {
++ public:
++ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
++ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
++
++ OptBuilder& operator[]( std::string const& optName ) {
++ addOptName( *ArgBuilder::m_arg, optName );
++ return *this;
++ }
++ };
++
++ public:
++
++ CommandLine()
++ : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
++ m_highestSpecifiedArgPosition( 0 ),
++ m_throwOnUnrecognisedTokens( false )
++ {}
++ CommandLine( CommandLine const& other )
++ : m_boundProcessName( other.m_boundProcessName ),
++ m_options ( other.m_options ),
++ m_positionalArgs( other.m_positionalArgs ),
++ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
++ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
++ {
++ if( other.m_floatingArg.get() )
++ m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
++ }
++
++ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
++ m_throwOnUnrecognisedTokens = shouldThrow;
++ return *this;
++ }
++
++ OptBuilder operator[]( std::string const& optName ) {
++ m_options.push_back( Arg() );
++ addOptName( m_options.back(), optName );
++ OptBuilder builder( &m_options.back() );
++ return builder;
++ }
++
++ ArgBuilder operator[]( int position ) {
++ m_positionalArgs.insert( std::make_pair( position, Arg() ) );
++ if( position > m_highestSpecifiedArgPosition )
++ m_highestSpecifiedArgPosition = position;
++ setPositionalArg( m_positionalArgs[position], position );
++ ArgBuilder builder( &m_positionalArgs[position] );
++ return builder;
++ }
++
++ // Invoke this with the _ instance
++ ArgBuilder operator[]( UnpositionalTag ) {
++ if( m_floatingArg.get() )
++ throw std::logic_error( "Only one unpositional argument can be added" );
++ m_floatingArg.reset( new Arg() );
++ ArgBuilder builder( m_floatingArg.get() );
++ return builder;
++ }
++
++ template<typename C, typename M>
++ void bindProcessName( M C::* field ) {
++ m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
++ }
++ template<typename C, typename M>
++ void bindProcessName( void (C::*_unaryMethod)( M ) ) {
++ m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
++ }
++
++ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
++ typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
++ std::size_t maxWidth = 0;
++ for( it = itBegin; it != itEnd; ++it )
++ maxWidth = (std::max)( maxWidth, it->commands().size() );
++
++ for( it = itBegin; it != itEnd; ++it ) {
++ Detail::Text usage( it->commands(), Detail::TextAttributes()
++ .setWidth( maxWidth+indent )
++ .setIndent( indent ) );
++ Detail::Text desc( it->description, Detail::TextAttributes()
++ .setWidth( width - maxWidth - 3 ) );
++
++ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
++ std::string usageCol = i < usage.size() ? usage[i] : "";
++ os << usageCol;
++
++ if( i < desc.size() && !desc[i].empty() )
++ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
++ << desc[i];
++ os << "\n";
++ }
++ }
++ }
++ std::string optUsage() const {
++ std::ostringstream oss;
++ optUsage( oss );
++ return oss.str();
++ }
++
++ void argSynopsis( std::ostream& os ) const {
++ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
++ if( i > 1 )
++ os << " ";
++ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
++ if( it != m_positionalArgs.end() )
++ os << "<" << it->second.placeholder << ">";
++ else if( m_floatingArg.get() )
++ os << "<" << m_floatingArg->placeholder << ">";
++ else
++ throw std::logic_error( "non consecutive positional arguments with no floating args" );
++ }
++ // !TBD No indication of mandatory args
++ if( m_floatingArg.get() ) {
++ if( m_highestSpecifiedArgPosition > 1 )
++ os << " ";
++ os << "[<" << m_floatingArg->placeholder << "> ...]";
++ }
++ }
++ std::string argSynopsis() const {
++ std::ostringstream oss;
++ argSynopsis( oss );
++ return oss.str();
++ }
++
++ void usage( std::ostream& os, std::string const& procName ) const {
++ validate();
++ os << "usage:\n " << procName << " ";
++ argSynopsis( os );
++ if( !m_options.empty() ) {
++ os << " [options]\n\nwhere options are: \n";
++ optUsage( os, 2 );
++ }
++ os << "\n";
++ }
++ std::string usage( std::string const& procName ) const {
++ std::ostringstream oss;
++ usage( oss, procName );
++ return oss.str();
++ }
++
++ ConfigT parse( int argc, char const * const * argv ) const {
++ ConfigT config;
++ parseInto( argc, argv, config );
++ return config;
++ }
++
++ std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
++ std::string processName = argv[0];
++ std::size_t lastSlash = processName.find_last_of( "/\\" );
++ if( lastSlash != std::string::npos )
++ processName = processName.substr( lastSlash+1 );
++ m_boundProcessName.set( config, processName );
++ std::vector<Parser::Token> tokens;
++ Parser parser;
++ parser.parseIntoTokens( argc, argv, tokens );
++ return populate( tokens, config );
++ }
++
++ std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
++ validate();
++ std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
++ unusedTokens = populateFixedArgs( unusedTokens, config );
++ unusedTokens = populateFloatingArgs( unusedTokens, config );
++ return unusedTokens;
++ }
++
++ std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
++ std::vector<Parser::Token> unusedTokens;
++ std::vector<std::string> errors;
++ for( std::size_t i = 0; i < tokens.size(); ++i ) {
++ Parser::Token const& token = tokens[i];
++ typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
++ for(; it != itEnd; ++it ) {
++ Arg const& arg = *it;
++
++ try {
++ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
++ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
++ if( arg.takesArg() ) {
++ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
++ errors.push_back( "Expected argument to option: " + token.data );
++ else
++ arg.boundField.set( config, tokens[++i].data );
++ }
++ else {
++ arg.boundField.setFlag( config );
++ }
++ break;
++ }
++ }
++ catch( std::exception& ex ) {
++ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
++ }
++ }
++ if( it == itEnd ) {
++ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
++ unusedTokens.push_back( token );
++ else if( errors.empty() && m_throwOnUnrecognisedTokens )
++ errors.push_back( "unrecognised option: " + token.data );
++ }
++ }
++ if( !errors.empty() ) {
++ std::ostringstream oss;
++ for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
++ it != itEnd;
++ ++it ) {
++ if( it != errors.begin() )
++ oss << "\n";
++ oss << *it;
++ }
++ throw std::runtime_error( oss.str() );
++ }
++ return unusedTokens;
++ }
++ std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
++ std::vector<Parser::Token> unusedTokens;
++ int position = 1;
++ for( std::size_t i = 0; i < tokens.size(); ++i ) {
++ Parser::Token const& token = tokens[i];
++ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
++ if( it != m_positionalArgs.end() )
++ it->second.boundField.set( config, token.data );
++ else
++ unusedTokens.push_back( token );
++ if( token.type == Parser::Token::Positional )
++ position++;
++ }
++ return unusedTokens;
++ }
++ std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
++ if( !m_floatingArg.get() )
++ return tokens;
++ std::vector<Parser::Token> unusedTokens;
++ for( std::size_t i = 0; i < tokens.size(); ++i ) {
++ Parser::Token const& token = tokens[i];
++ if( token.type == Parser::Token::Positional )
++ m_floatingArg->boundField.set( config, token.data );
++ else
++ unusedTokens.push_back( token );
++ }
++ return unusedTokens;
++ }
++
++ void validate() const
++ {
++ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
++ throw std::logic_error( "No options or arguments specified" );
++
++ for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
++ itEnd = m_options.end();
++ it != itEnd; ++it )
++ it->validate();
++ }
++
++ private:
++ Detail::BoundArgFunction<ConfigT> m_boundProcessName;
++ std::vector<Arg> m_options;
++ std::map<int, Arg> m_positionalArgs;
++ ArgAutoPtr m_floatingArg;
++ int m_highestSpecifiedArgPosition;
++ bool m_throwOnUnrecognisedTokens;
++ };
++
++} // end namespace Clara
++
++STITCH_CLARA_CLOSE_NAMESPACE
++#undef STITCH_CLARA_OPEN_NAMESPACE
++#undef STITCH_CLARA_CLOSE_NAMESPACE
++
++#endif // TWOBLUECUBES_CLARA_H_INCLUDED
++#undef STITCH_CLARA_OPEN_NAMESPACE
++
++// Restore Clara's value for console width, if present
++#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
++#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
++#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
++#endif
++
++#include <fstream>
++
++namespace Catch {
++
++ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
++ inline void abortAfterX( ConfigData& config, int x ) {
++ if( x < 1 )
++ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
++ config.abortAfter = x;
++ }
++ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
++
++ inline void addWarning( ConfigData& config, std::string const& _warning ) {
++ if( _warning == "NoAssertions" )
++ config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
++ else
++ throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
++ }
++ inline void setOrder( ConfigData& config, std::string const& order ) {
++ if( startsWith( "declared", order ) )
++ config.runOrder = RunTests::InDeclarationOrder;
++ else if( startsWith( "lexical", order ) )
++ config.runOrder = RunTests::InLexicographicalOrder;
++ else if( startsWith( "random", order ) )
++ config.runOrder = RunTests::InRandomOrder;
++ else
++ throw std::runtime_error( "Unrecognised ordering: '" + order + "'" );
++ }
++ inline void setRngSeed( ConfigData& config, std::string const& seed ) {
++ if( seed == "time" ) {
++ config.rngSeed = static_cast<unsigned int>( std::time(0) );
++ }
++ else {
++ std::stringstream ss;
++ ss << seed;
++ ss >> config.rngSeed;
++ if( ss.fail() )
++ throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" );
++ }
++ }
++ inline void setVerbosity( ConfigData& config, int level ) {
++ // !TBD: accept strings?
++ config.verbosity = static_cast<Verbosity::Level>( level );
++ }
++ inline void setShowDurations( ConfigData& config, bool _showDurations ) {
++ config.showDurations = _showDurations
++ ? ShowDurations::Always
++ : ShowDurations::Never;
++ }
++ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
++ std::ifstream f( _filename.c_str() );
++ if( !f.is_open() )
++ throw std::domain_error( "Unable to load input file: " + _filename );
++
++ std::string line;
++ while( std::getline( f, line ) ) {
++ line = trim(line);
++ if( !line.empty() && !startsWith( line, "#" ) )
++ addTestOrTags( config, "\"" + line + "\"," );
++ }
++ }
++
++ inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
++
++ using namespace Clara;
++ CommandLine<ConfigData> cli;
++
++ cli.bindProcessName( &ConfigData::processName );
++
++ cli["-?"]["-h"]["--help"]
++ .describe( "display usage information" )
++ .bind( &ConfigData::showHelp );
++
++ cli["-l"]["--list-tests"]
++ .describe( "list all/matching test cases" )
++ .bind( &ConfigData::listTests );
++
++ cli["-t"]["--list-tags"]
++ .describe( "list all/matching tags" )
++ .bind( &ConfigData::listTags );
++
++ cli["-s"]["--success"]
++ .describe( "include successful tests in output" )
++ .bind( &ConfigData::showSuccessfulTests );
++
++ cli["-b"]["--break"]
++ .describe( "break into debugger on failure" )
++ .bind( &ConfigData::shouldDebugBreak );
++
++ cli["-e"]["--nothrow"]
++ .describe( "skip exception tests" )
++ .bind( &ConfigData::noThrow );
++
++ cli["-i"]["--invisibles"]
++ .describe( "show invisibles (tabs, newlines)" )
++ .bind( &ConfigData::showInvisibles );
++
++ cli["-o"]["--out"]
++ .describe( "output filename" )
++ .bind( &ConfigData::outputFilename, "filename" );
++
++ cli["-r"]["--reporter"]
++// .placeholder( "name[:filename]" )
++ .describe( "reporter to use (defaults to console)" )
++ .bind( &ConfigData::reporterName, "name" );
++
++ cli["-n"]["--name"]
++ .describe( "suite name" )
++ .bind( &ConfigData::name, "name" );
++
++ cli["-a"]["--abort"]
++ .describe( "abort at first failure" )
++ .bind( &abortAfterFirst );
++
++ cli["-x"]["--abortx"]
++ .describe( "abort after x failures" )
++ .bind( &abortAfterX, "no. failures" );
++
++ cli["-w"]["--warn"]
++ .describe( "enable warnings" )
++ .bind( &addWarning, "warning name" );
++
++// - needs updating if reinstated
++// cli.into( &setVerbosity )
++// .describe( "level of verbosity (0=no output)" )
++// .shortOpt( "v")
++// .longOpt( "verbosity" )
++// .placeholder( "level" );
++
++ cli[_]
++ .describe( "which test or tests to use" )
++ .bind( &addTestOrTags, "test name, pattern or tags" );
++
++ cli["-d"]["--durations"]
++ .describe( "show test durations" )
++ .bind( &setShowDurations, "yes/no" );
++
++ cli["-f"]["--input-file"]
++ .describe( "load test names to run from a file" )
++ .bind( &loadTestNamesFromFile, "filename" );
++
++ // Less common commands which don't have a short form
++ cli["--list-test-names-only"]
++ .describe( "list all/matching test cases names only" )
++ .bind( &ConfigData::listTestNamesOnly );
++
++ cli["--list-reporters"]
++ .describe( "list all reporters" )
++ .bind( &ConfigData::listReporters );
++
++ cli["--order"]
++ .describe( "test case order (defaults to decl)" )
++ .bind( &setOrder, "decl|lex|rand" );
++
++ cli["--rng-seed"]
++ .describe( "set a specific seed for random numbers" )
++ .bind( &setRngSeed, "'time'|number" );
++
++ cli["--force-colour"]
++ .describe( "force colourised output" )
++ .bind( &ConfigData::forceColour );
++
++ return cli;
++ }
++
++} // end namespace Catch
++
++// #included from: internal/catch_list.hpp
++#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
++
++// #included from: catch_text.h
++#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
++
++#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
++
++#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
++// #included from: ../external/tbc_text_format.h
++// Only use header guard if we are not using an outer namespace
++#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
++# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
++# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
++# endif
++# else
++# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
++# endif
++#endif
++#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
++#include <string>
++#include <vector>
++#include <sstream>
++
++// Use optional outer namespace
++#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
++#endif
++
++namespace Tbc {
++
++#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
++ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
++#else
++ const unsigned int consoleWidth = 80;
++#endif
++
++ struct TextAttributes {
++ TextAttributes()
++ : initialIndent( std::string::npos ),
++ indent( 0 ),
++ width( consoleWidth-1 ),
++ tabChar( '\t' )
++ {}
++
++ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
++ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
++ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
++ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
++
++ std::size_t initialIndent; // indent of first line, or npos
++ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
++ std::size_t width; // maximum width of text, including indent. Longer text will wrap
++ char tabChar; // If this char is seen the indent is changed to current pos
++ };
++
++ class Text {
++ public:
++ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
++ : attr( _attr )
++ {
++ std::string wrappableChars = " [({.,/|\\-";
++ std::size_t indent = _attr.initialIndent != std::string::npos
++ ? _attr.initialIndent
++ : _attr.indent;
++ std::string remainder = _str;
++
++ while( !remainder.empty() ) {
++ if( lines.size() >= 1000 ) {
++ lines.push_back( "... message truncated due to excessive size" );
++ return;
++ }
++ std::size_t tabPos = std::string::npos;
++ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
++ std::size_t pos = remainder.find_first_of( '\n' );
++ if( pos <= width ) {
++ width = pos;
++ }
++ pos = remainder.find_last_of( _attr.tabChar, width );
++ if( pos != std::string::npos ) {
++ tabPos = pos;
++ if( remainder[width] == '\n' )
++ width--;
++ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
++ }
++
++ if( width == remainder.size() ) {
++ spliceLine( indent, remainder, width );
++ }
++ else if( remainder[width] == '\n' ) {
++ spliceLine( indent, remainder, width );
++ if( width <= 1 || remainder.size() != 1 )
++ remainder = remainder.substr( 1 );
++ indent = _attr.indent;
++ }
++ else {
++ pos = remainder.find_last_of( wrappableChars, width );
++ if( pos != std::string::npos && pos > 0 ) {
++ spliceLine( indent, remainder, pos );
++ if( remainder[0] == ' ' )
++ remainder = remainder.substr( 1 );
++ }
++ else {
++ spliceLine( indent, remainder, width-1 );
++ lines.back() += "-";
++ }
++ if( lines.size() == 1 )
++ indent = _attr.indent;
++ if( tabPos != std::string::npos )
++ indent += tabPos;
++ }
++ }
++ }
++
++ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
++ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
++ _remainder = _remainder.substr( _pos );
++ }
++
++ typedef std::vector<std::string>::const_iterator const_iterator;
++
++ const_iterator begin() const { return lines.begin(); }
++ const_iterator end() const { return lines.end(); }
++ std::string const& last() const { return lines.back(); }
++ std::size_t size() const { return lines.size(); }
++ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
++ std::string toString() const {
++ std::ostringstream oss;
++ oss << *this;
++ return oss.str();
++ }
++
++ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
++ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
++ it != itEnd; ++it ) {
++ if( it != _text.begin() )
++ _stream << "\n";
++ _stream << *it;
++ }
++ return _stream;
++ }
++
++ private:
++ std::string str;
++ TextAttributes attr;
++ std::vector<std::string> lines;
++ };
++
++} // end namespace Tbc
++
++#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++} // end outer namespace
++#endif
++
++#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
++#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
++
++namespace Catch {
++ using Tbc::Text;
++ using Tbc::TextAttributes;
++}
++
++// #included from: catch_console_colour.hpp
++#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
++
++namespace Catch {
++
++ struct Colour {
++ enum Code {
++ None = 0,
++
++ White,
++ Red,
++ Green,
++ Blue,
++ Cyan,
++ Yellow,
++ Grey,
++
++ Bright = 0x10,
++
++ BrightRed = Bright | Red,
++ BrightGreen = Bright | Green,
++ LightGrey = Bright | Grey,
++ BrightWhite = Bright | White,
++
++ // By intention
++ FileName = LightGrey,
++ Warning = Yellow,
++ ResultError = BrightRed,
++ ResultSuccess = BrightGreen,
++ ResultExpectedFailure = Warning,
++
++ Error = BrightRed,
++ Success = Green,
++
++ OriginalExpression = Cyan,
++ ReconstructedExpression = Yellow,
++
++ SecondaryText = LightGrey,
++ Headers = White
++ };
++
++ // Use constructed object for RAII guard
++ Colour( Code _colourCode );
++ Colour( Colour const& other );
++ ~Colour();
++
++ // Use static method for one-shot changes
++ static void use( Code _colourCode );
++
++ private:
++ bool m_moved;
++ };
++
++ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
++
++} // end namespace Catch
++
++// #included from: catch_interfaces_reporter.h
++#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
++
++#include <string>
++#include <ostream>
++#include <map>
++#include <assert.h>
++
++namespace Catch
++{
++ struct ReporterConfig {
++ explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
++ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
++
++ ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
++ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
++
++ std::ostream& stream() const { return *m_stream; }
++ Ptr<IConfig> fullConfig() const { return m_fullConfig; }
++
++ private:
++ std::ostream* m_stream;
++ Ptr<IConfig> m_fullConfig;
++ };
++
++ struct ReporterPreferences {
++ ReporterPreferences()
++ : shouldRedirectStdOut( false )
++ {}
++
++ bool shouldRedirectStdOut;
++ };
++
++ template<typename T>
++ struct LazyStat : Option<T> {
++ LazyStat() : used( false ) {}
++ LazyStat& operator=( T const& _value ) {
++ Option<T>::operator=( _value );
++ used = false;
++ return *this;
++ }
++ void reset() {
++ Option<T>::reset();
++ used = false;
++ }
++ bool used;
++ };
++
++ struct TestRunInfo {
++ TestRunInfo( std::string const& _name ) : name( _name ) {}
++ std::string name;
++ };
++ struct GroupInfo {
++ GroupInfo( std::string const& _name,
++ std::size_t _groupIndex,
++ std::size_t _groupsCount )
++ : name( _name ),
++ groupIndex( _groupIndex ),
++ groupsCounts( _groupsCount )
++ {}
++
++ std::string name;
++ std::size_t groupIndex;
++ std::size_t groupsCounts;
++ };
++
++ struct AssertionStats {
++ AssertionStats( AssertionResult const& _assertionResult,
++ std::vector<MessageInfo> const& _infoMessages,
++ Totals const& _totals )
++ : assertionResult( _assertionResult ),
++ infoMessages( _infoMessages ),
++ totals( _totals )
++ {
++ if( assertionResult.hasMessage() ) {
++ // Copy message into messages list.
++ // !TBD This should have been done earlier, somewhere
++ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
++ builder << assertionResult.getMessage();
++ builder.m_info.message = builder.m_stream.str();
++
++ infoMessages.push_back( builder.m_info );
++ }
++ }
++ virtual ~AssertionStats();
++
++# ifdef CATCH_CPP11_OR_GREATER
++ AssertionStats( AssertionStats const& ) = default;
++ AssertionStats( AssertionStats && ) = default;
++ AssertionStats& operator = ( AssertionStats const& ) = default;
++ AssertionStats& operator = ( AssertionStats && ) = default;
++# endif
++
++ AssertionResult assertionResult;
++ std::vector<MessageInfo> infoMessages;
++ Totals totals;
++ };
++
++ struct SectionStats {
++ SectionStats( SectionInfo const& _sectionInfo,
++ Counts const& _assertions,
++ double _durationInSeconds,
++ bool _missingAssertions )
++ : sectionInfo( _sectionInfo ),
++ assertions( _assertions ),
++ durationInSeconds( _durationInSeconds ),
++ missingAssertions( _missingAssertions )
++ {}
++ virtual ~SectionStats();
++# ifdef CATCH_CPP11_OR_GREATER
++ SectionStats( SectionStats const& ) = default;
++ SectionStats( SectionStats && ) = default;
++ SectionStats& operator = ( SectionStats const& ) = default;
++ SectionStats& operator = ( SectionStats && ) = default;
++# endif
++
++ SectionInfo sectionInfo;
++ Counts assertions;
++ double durationInSeconds;
++ bool missingAssertions;
++ };
++
++ struct TestCaseStats {
++ TestCaseStats( TestCaseInfo const& _testInfo,
++ Totals const& _totals,
++ std::string const& _stdOut,
++ std::string const& _stdErr,
++ bool _aborting )
++ : testInfo( _testInfo ),
++ totals( _totals ),
++ stdOut( _stdOut ),
++ stdErr( _stdErr ),
++ aborting( _aborting )
++ {}
++ virtual ~TestCaseStats();
++
++# ifdef CATCH_CPP11_OR_GREATER
++ TestCaseStats( TestCaseStats const& ) = default;
++ TestCaseStats( TestCaseStats && ) = default;
++ TestCaseStats& operator = ( TestCaseStats const& ) = default;
++ TestCaseStats& operator = ( TestCaseStats && ) = default;
++# endif
++
++ TestCaseInfo testInfo;
++ Totals totals;
++ std::string stdOut;
++ std::string stdErr;
++ bool aborting;
++ };
++
++ struct TestGroupStats {
++ TestGroupStats( GroupInfo const& _groupInfo,
++ Totals const& _totals,
++ bool _aborting )
++ : groupInfo( _groupInfo ),
++ totals( _totals ),
++ aborting( _aborting )
++ {}
++ TestGroupStats( GroupInfo const& _groupInfo )
++ : groupInfo( _groupInfo ),
++ aborting( false )
++ {}
++ virtual ~TestGroupStats();
++
++# ifdef CATCH_CPP11_OR_GREATER
++ TestGroupStats( TestGroupStats const& ) = default;
++ TestGroupStats( TestGroupStats && ) = default;
++ TestGroupStats& operator = ( TestGroupStats const& ) = default;
++ TestGroupStats& operator = ( TestGroupStats && ) = default;
++# endif
++
++ GroupInfo groupInfo;
++ Totals totals;
++ bool aborting;
++ };
++
++ struct TestRunStats {
++ TestRunStats( TestRunInfo const& _runInfo,
++ Totals const& _totals,
++ bool _aborting )
++ : runInfo( _runInfo ),
++ totals( _totals ),
++ aborting( _aborting )
++ {}
++ virtual ~TestRunStats();
++
++# ifndef CATCH_CPP11_OR_GREATER
++ TestRunStats( TestRunStats const& _other )
++ : runInfo( _other.runInfo ),
++ totals( _other.totals ),
++ aborting( _other.aborting )
++ {}
++# else
++ TestRunStats( TestRunStats const& ) = default;
++ TestRunStats( TestRunStats && ) = default;
++ TestRunStats& operator = ( TestRunStats const& ) = default;
++ TestRunStats& operator = ( TestRunStats && ) = default;
++# endif
++
++ TestRunInfo runInfo;
++ Totals totals;
++ bool aborting;
++ };
++
++ struct IStreamingReporter : IShared {
++ virtual ~IStreamingReporter();
++
++ // Implementing class must also provide the following static method:
++ // static std::string getDescription();
++
++ virtual ReporterPreferences getPreferences() const = 0;
++
++ virtual void noMatchingTestCases( std::string const& spec ) = 0;
++
++ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
++ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
++
++ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
++ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
++
++ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
++
++ // The return value indicates if the messages buffer should be cleared:
++ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
++ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
++ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
++
++ virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
++ };
++
++ struct IReporterFactory {
++ virtual ~IReporterFactory();
++ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
++ virtual std::string getDescription() const = 0;
++ };
++
++ struct IReporterRegistry {
++ typedef std::map<std::string, IReporterFactory*> FactoryMap;
++
++ virtual ~IReporterRegistry();
++ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
++ virtual FactoryMap const& getFactories() const = 0;
++ };
++
++}
++
++#include <limits>
++#include <algorithm>
++
++namespace Catch {
++
++ inline std::size_t listTests( Config const& config ) {
++
++ TestSpec testSpec = config.testSpec();
++ if( config.testSpec().hasFilters() )
++ Catch::cout() << "Matching test cases:\n";
++ else {
++ Catch::cout() << "All available test cases:\n";
++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
++ }
++
++ std::size_t matchedTests = 0;
++ TextAttributes nameAttr, tagsAttr;
++ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
++ tagsAttr.setIndent( 6 );
++
++ std::vector<TestCase> matchedTestCases;
++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
++ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
++ it != itEnd;
++ ++it ) {
++ matchedTests++;
++ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
++ Colour::Code colour = testCaseInfo.isHidden()
++ ? Colour::SecondaryText
++ : Colour::None;
++ Colour colourGuard( colour );
++
++ Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
++ if( !testCaseInfo.tags.empty() )
++ Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
++ }
++
++ if( !config.testSpec().hasFilters() )
++ Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
++ else
++ Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
++ return matchedTests;
++ }
++
++ inline std::size_t listTestsNamesOnly( Config const& config ) {
++ TestSpec testSpec = config.testSpec();
++ if( !config.testSpec().hasFilters() )
++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
++ std::size_t matchedTests = 0;
++ std::vector<TestCase> matchedTestCases;
++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
++ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
++ it != itEnd;
++ ++it ) {
++ matchedTests++;
++ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
++ Catch::cout() << testCaseInfo.name << std::endl;
++ }
++ return matchedTests;
++ }
++
++ struct TagInfo {
++ TagInfo() : count ( 0 ) {}
++ void add( std::string const& spelling ) {
++ ++count;
++ spellings.insert( spelling );
++ }
++ std::string all() const {
++ std::string out;
++ for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
++ it != itEnd;
++ ++it )
++ out += "[" + *it + "]";
++ return out;
++ }
++ std::set<std::string> spellings;
++ std::size_t count;
++ };
++
++ inline std::size_t listTags( Config const& config ) {
++ TestSpec testSpec = config.testSpec();
++ if( config.testSpec().hasFilters() )
++ Catch::cout() << "Tags for matching test cases:\n";
++ else {
++ Catch::cout() << "All available tags:\n";
++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
++ }
++
++ std::map<std::string, TagInfo> tagCounts;
++
++ std::vector<TestCase> matchedTestCases;
++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
++ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
++ it != itEnd;
++ ++it ) {
++ for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
++ tagItEnd = it->getTestCaseInfo().tags.end();
++ tagIt != tagItEnd;
++ ++tagIt ) {
++ std::string tagName = *tagIt;
++ std::string lcaseTagName = toLower( tagName );
++ std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
++ if( countIt == tagCounts.end() )
++ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
++ countIt->second.add( tagName );
++ }
++ }
++
++ for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
++ countItEnd = tagCounts.end();
++ countIt != countItEnd;
++ ++countIt ) {
++ std::ostringstream oss;
++ oss << " " << std::setw(2) << countIt->second.count << " ";
++ Text wrapper( countIt->second.all(), TextAttributes()
++ .setInitialIndent( 0 )
++ .setIndent( oss.str().size() )
++ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
++ Catch::cout() << oss.str() << wrapper << "\n";
++ }
++ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
++ return tagCounts.size();
++ }
++
++ inline std::size_t listReporters( Config const& /*config*/ ) {
++ Catch::cout() << "Available reporters:\n";
++ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
++ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
++ std::size_t maxNameLen = 0;
++ for(it = itBegin; it != itEnd; ++it )
++ maxNameLen = (std::max)( maxNameLen, it->first.size() );
++
++ for(it = itBegin; it != itEnd; ++it ) {
++ Text wrapper( it->second->getDescription(), TextAttributes()
++ .setInitialIndent( 0 )
++ .setIndent( 7+maxNameLen )
++ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
++ Catch::cout() << " "
++ << it->first
++ << ":"
++ << std::string( maxNameLen - it->first.size() + 2, ' ' )
++ << wrapper << "\n";
++ }
++ Catch::cout() << std::endl;
++ return factories.size();
++ }
++
++ inline Option<std::size_t> list( Config const& config ) {
++ Option<std::size_t> listedCount;
++ if( config.listTests() )
++ listedCount = listedCount.valueOr(0) + listTests( config );
++ if( config.listTestNamesOnly() )
++ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
++ if( config.listTags() )
++ listedCount = listedCount.valueOr(0) + listTags( config );
++ if( config.listReporters() )
++ listedCount = listedCount.valueOr(0) + listReporters( config );
++ return listedCount;
++ }
++
++} // end namespace Catch
++
++// #included from: internal/catch_runner_impl.hpp
++#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
++
++// #included from: catch_test_case_tracker.hpp
++#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
++
++#include <map>
++#include <string>
++#include <assert.h>
++
++namespace Catch {
++namespace SectionTracking {
++
++ class TrackedSection {
++
++ typedef std::map<std::string, TrackedSection> TrackedSections;
++
++ public:
++ enum RunState {
++ NotStarted,
++ Executing,
++ ExecutingChildren,
++ Completed
++ };
++
++ TrackedSection( std::string const& name, TrackedSection* parent )
++ : m_name( name ), m_runState( NotStarted ), m_parent( parent )
++ {}
++
++ RunState runState() const { return m_runState; }
++
++ TrackedSection* findChild( std::string const& childName ) {
++ TrackedSections::iterator it = m_children.find( childName );
++ return it != m_children.end()
++ ? &it->second
++ : NULL;
++ }
++ TrackedSection* acquireChild( std::string const& childName ) {
++ if( TrackedSection* child = findChild( childName ) )
++ return child;
++ m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
++ return findChild( childName );
++ }
++ void enter() {
++ if( m_runState == NotStarted )
++ m_runState = Executing;
++ }
++ void leave() {
++ for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
++ it != itEnd;
++ ++it )
++ if( it->second.runState() != Completed ) {
++ m_runState = ExecutingChildren;
++ return;
++ }
++ m_runState = Completed;
++ }
++ TrackedSection* getParent() {
++ return m_parent;
++ }
++ bool hasChildren() const {
++ return !m_children.empty();
++ }
++
++ private:
++ std::string m_name;
++ RunState m_runState;
++ TrackedSections m_children;
++ TrackedSection* m_parent;
++
++ };
++
++ class TestCaseTracker {
++ public:
++ TestCaseTracker( std::string const& testCaseName )
++ : m_testCase( testCaseName, NULL ),
++ m_currentSection( &m_testCase ),
++ m_completedASectionThisRun( false )
++ {}
++
++ bool enterSection( std::string const& name ) {
++ TrackedSection* child = m_currentSection->acquireChild( name );
++ if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
++ return false;
++
++ m_currentSection = child;
++ m_currentSection->enter();
++ return true;
++ }
++ void leaveSection() {
++ m_currentSection->leave();
++ m_currentSection = m_currentSection->getParent();
++ assert( m_currentSection != NULL );
++ m_completedASectionThisRun = true;
++ }
++
++ bool currentSectionHasChildren() const {
++ return m_currentSection->hasChildren();
++ }
++ bool isCompleted() const {
++ return m_testCase.runState() == TrackedSection::Completed;
++ }
++
++ class Guard {
++ public:
++ Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
++ m_tracker.enterTestCase();
++ }
++ ~Guard() {
++ m_tracker.leaveTestCase();
++ }
++ private:
++ Guard( Guard const& );
++ void operator = ( Guard const& );
++ TestCaseTracker& m_tracker;
++ };
++
++ private:
++ void enterTestCase() {
++ m_currentSection = &m_testCase;
++ m_completedASectionThisRun = false;
++ m_testCase.enter();
++ }
++ void leaveTestCase() {
++ m_testCase.leave();
++ }
++
++ TrackedSection m_testCase;
++ TrackedSection* m_currentSection;
++ bool m_completedASectionThisRun;
++ };
++
++} // namespace SectionTracking
++
++using SectionTracking::TestCaseTracker;
++
++} // namespace Catch
++
++// #included from: catch_fatal_condition.hpp
++#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
++
++namespace Catch {
++
++ // Report the error condition then exit the process
++ inline void fatal( std::string const& message, int exitCode ) {
++ IContext& context = Catch::getCurrentContext();
++ IResultCapture* resultCapture = context.getResultCapture();
++ resultCapture->handleFatalErrorCondition( message );
++
++ if( Catch::alwaysTrue() ) // avoids "no return" warnings
++ exit( exitCode );
++ }
++
++} // namespace Catch
++
++#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
++
++namespace Catch {
++
++ struct FatalConditionHandler {
++ void reset() {}
++ };
++
++} // namespace Catch
++
++#else // Not Windows - assumed to be POSIX compatible //////////////////////////
++
++#include <signal.h>
++
++namespace Catch {
++
++ struct SignalDefs { int id; const char* name; };
++ extern SignalDefs signalDefs[];
++ SignalDefs signalDefs[] = {
++ { SIGINT, "SIGINT - Terminal interrupt signal" },
++ { SIGILL, "SIGILL - Illegal instruction signal" },
++ { SIGFPE, "SIGFPE - Floating point error signal" },
++ { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
++ { SIGTERM, "SIGTERM - Termination request signal" },
++ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
++ };
++
++ struct FatalConditionHandler {
++
++ static void handleSignal( int sig ) {
++ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
++ if( sig == signalDefs[i].id )
++ fatal( signalDefs[i].name, -sig );
++ fatal( "<unknown signal>", -sig );
++ }
++
++ FatalConditionHandler() : m_isSet( true ) {
++ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
++ signal( signalDefs[i].id, handleSignal );
++ }
++ ~FatalConditionHandler() {
++ reset();
++ }
++ void reset() {
++ if( m_isSet ) {
++ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
++ signal( signalDefs[i].id, SIG_DFL );
++ m_isSet = false;
++ }
++ }
++
++ bool m_isSet;
++ };
++
++} // namespace Catch
++
++#endif // not Windows
++
++#include <set>
++#include <string>
++
++namespace Catch {
++
++ class StreamRedirect {
++
++ public:
++ StreamRedirect( std::ostream& stream, std::string& targetString )
++ : m_stream( stream ),
++ m_prevBuf( stream.rdbuf() ),
++ m_targetString( targetString )
++ {
++ stream.rdbuf( m_oss.rdbuf() );
++ }
++
++ ~StreamRedirect() {
++ m_targetString += m_oss.str();
++ m_stream.rdbuf( m_prevBuf );
++ }
++
++ private:
++ std::ostream& m_stream;
++ std::streambuf* m_prevBuf;
++ std::ostringstream m_oss;
++ std::string& m_targetString;
++ };
++
++ ///////////////////////////////////////////////////////////////////////////
++
++ class RunContext : public IResultCapture, public IRunner {
++
++ RunContext( RunContext const& );
++ void operator =( RunContext const& );
++
++ public:
++
++ explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
++ : m_runInfo( config->name() ),
++ m_context( getCurrentMutableContext() ),
++ m_activeTestCase( NULL ),
++ m_config( config ),
++ m_reporter( reporter ),
++ m_prevRunner( m_context.getRunner() ),
++ m_prevResultCapture( m_context.getResultCapture() ),
++ m_prevConfig( m_context.getConfig() )
++ {
++ m_context.setRunner( this );
++ m_context.setConfig( m_config );
++ m_context.setResultCapture( this );
++ m_reporter->testRunStarting( m_runInfo );
++ }
++
++ virtual ~RunContext() {
++ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
++ m_context.setRunner( m_prevRunner );
++ m_context.setConfig( NULL );
++ m_context.setResultCapture( m_prevResultCapture );
++ m_context.setConfig( m_prevConfig );
++ }
++
++ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
++ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
++ }
++ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
++ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
++ }
++
++ Totals runTest( TestCase const& testCase ) {
++ Totals prevTotals = m_totals;
++
++ std::string redirectedCout;
++ std::string redirectedCerr;
++
++ TestCaseInfo testInfo = testCase.getTestCaseInfo();
++
++ m_reporter->testCaseStarting( testInfo );
++
++ m_activeTestCase = &testCase;
++ m_testCaseTracker = TestCaseTracker( testInfo.name );
++
++ do {
++ do {
++ runCurrentTest( redirectedCout, redirectedCerr );
++ }
++ while( !m_testCaseTracker->isCompleted() && !aborting() );
++ }
++ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
++
++ Totals deltaTotals = m_totals.delta( prevTotals );
++ m_totals.testCases += deltaTotals.testCases;
++ m_reporter->testCaseEnded( TestCaseStats( testInfo,
++ deltaTotals,
++ redirectedCout,
++ redirectedCerr,
++ aborting() ) );
++
++ m_activeTestCase = NULL;
++ m_testCaseTracker.reset();
++
++ return deltaTotals;
++ }
++
++ Ptr<IConfig const> config() const {
++ return m_config;
++ }
++
++ private: // IResultCapture
++
++ virtual void assertionEnded( AssertionResult const& result ) {
++ if( result.getResultType() == ResultWas::Ok ) {
++ m_totals.assertions.passed++;
++ }
++ else if( !result.isOk() ) {
++ m_totals.assertions.failed++;
++ }
++
++ if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
++ m_messages.clear();
++
++ // Reset working state
++ m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
++ m_lastResult = result;
++ }
++
++ virtual bool sectionStarted (
++ SectionInfo const& sectionInfo,
++ Counts& assertions
++ )
++ {
++ std::ostringstream oss;
++ oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
++
++ if( !m_testCaseTracker->enterSection( oss.str() ) )
++ return false;
++
++ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
++
++ m_reporter->sectionStarting( sectionInfo );
++
++ assertions = m_totals.assertions;
++
++ return true;
++ }
++ bool testForMissingAssertions( Counts& assertions ) {
++ if( assertions.total() != 0 ||
++ !m_config->warnAboutMissingAssertions() ||
++ m_testCaseTracker->currentSectionHasChildren() )
++ return false;
++ m_totals.assertions.failed++;
++ assertions.failed++;
++ return true;
++ }
++
++ virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
++ if( std::uncaught_exception() ) {
++ m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
++ return;
++ }
++
++ Counts assertions = m_totals.assertions - prevAssertions;
++ bool missingAssertions = testForMissingAssertions( assertions );
++
++ m_testCaseTracker->leaveSection();
++
++ m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
++ m_messages.clear();
++ }
++
++ virtual void pushScopedMessage( MessageInfo const& message ) {
++ m_messages.push_back( message );
++ }
++
++ virtual void popScopedMessage( MessageInfo const& message ) {
++ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
++ }
++
++ virtual std::string getCurrentTestName() const {
++ return m_activeTestCase
++ ? m_activeTestCase->getTestCaseInfo().name
++ : "";
++ }
++
++ virtual const AssertionResult* getLastResult() const {
++ return &m_lastResult;
++ }
++
++ virtual void handleFatalErrorCondition( std::string const& message ) {
++ ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
++ resultBuilder.setResultType( ResultWas::FatalErrorCondition );
++ resultBuilder << message;
++ resultBuilder.captureExpression();
++
++ handleUnfinishedSections();
++
++ // Recreate section for test case (as we will lose the one that was in scope)
++ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
++ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
++
++ Counts assertions;
++ assertions.failed = 1;
++ SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
++ m_reporter->sectionEnded( testCaseSectionStats );
++
++ TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
++
++ Totals deltaTotals;
++ deltaTotals.testCases.failed = 1;
++ m_reporter->testCaseEnded( TestCaseStats( testInfo,
++ deltaTotals,
++ "",
++ "",
++ false ) );
++ m_totals.testCases.failed++;
++ testGroupEnded( "", m_totals, 1, 1 );
++ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
++ }
++
++ public:
++ // !TBD We need to do this another way!
++ bool aborting() const {
++ return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
++ }
++
++ private:
++
++ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
++ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
++ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
++ m_reporter->sectionStarting( testCaseSection );
++ Counts prevAssertions = m_totals.assertions;
++ double duration = 0;
++ try {
++ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
++ TestCaseTracker::Guard guard( *m_testCaseTracker );
++
++ Timer timer;
++ timer.start();
++ if( m_reporter->getPreferences().shouldRedirectStdOut ) {
++ StreamRedirect coutRedir( Catch::cout(), redirectedCout );
++ StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
++ invokeActiveTestCase();
++ }
++ else {
++ invokeActiveTestCase();
++ }
++ duration = timer.getElapsedSeconds();
++ }
++ catch( TestFailureException& ) {
++ // This just means the test was aborted due to failure
++ }
++ catch(...) {
++ makeUnexpectedResultBuilder().useActiveException();
++ }
++ handleUnfinishedSections();
++ m_messages.clear();
++
++ Counts assertions = m_totals.assertions - prevAssertions;
++ bool missingAssertions = testForMissingAssertions( assertions );
++
++ if( testCaseInfo.okToFail() ) {
++ std::swap( assertions.failedButOk, assertions.failed );
++ m_totals.assertions.failed -= assertions.failedButOk;
++ m_totals.assertions.failedButOk += assertions.failedButOk;
++ }
++
++ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
++ m_reporter->sectionEnded( testCaseSectionStats );
++ }
++
++ void invokeActiveTestCase() {
++ FatalConditionHandler fatalConditionHandler; // Handle signals
++ m_activeTestCase->invoke();
++ fatalConditionHandler.reset();
++ }
++
++ private:
++
++ ResultBuilder makeUnexpectedResultBuilder() const {
++ return ResultBuilder( m_lastAssertionInfo.macroName.c_str(),
++ m_lastAssertionInfo.lineInfo,
++ m_lastAssertionInfo.capturedExpression.c_str(),
++ m_lastAssertionInfo.resultDisposition );
++ }
++
++ void handleUnfinishedSections() {
++ // If sections ended prematurely due to an exception we stored their
++ // infos here so we can tear them down outside the unwind process.
++ for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
++ itEnd = m_unfinishedSections.rend();
++ it != itEnd;
++ ++it )
++ sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
++ m_unfinishedSections.clear();
++ }
++
++ struct UnfinishedSections {
++ UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
++ : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
++ {}
++
++ SectionInfo info;
++ Counts prevAssertions;
++ double durationInSeconds;
++ };
++
++ TestRunInfo m_runInfo;
++ IMutableContext& m_context;
++ TestCase const* m_activeTestCase;
++ Option<TestCaseTracker> m_testCaseTracker;
++ AssertionResult m_lastResult;
++
++ Ptr<IConfig const> m_config;
++ Totals m_totals;
++ Ptr<IStreamingReporter> m_reporter;
++ std::vector<MessageInfo> m_messages;
++ IRunner* m_prevRunner;
++ IResultCapture* m_prevResultCapture;
++ Ptr<IConfig const> m_prevConfig;
++ AssertionInfo m_lastAssertionInfo;
++ std::vector<UnfinishedSections> m_unfinishedSections;
++ };
++
++ IResultCapture& getResultCapture() {
++ if( IResultCapture* capture = getCurrentContext().getResultCapture() )
++ return *capture;
++ else
++ throw std::logic_error( "No result capture instance" );
++ }
++
++} // end namespace Catch
++
++// #included from: internal/catch_version.h
++#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
++
++namespace Catch {
++
++ // Versioning information
++ struct Version {
++ Version( unsigned int _majorVersion,
++ unsigned int _minorVersion,
++ unsigned int _buildNumber,
++ char const* const _branchName )
++ : majorVersion( _majorVersion ),
++ minorVersion( _minorVersion ),
++ buildNumber( _buildNumber ),
++ branchName( _branchName )
++ {}
++
++ unsigned int const majorVersion;
++ unsigned int const minorVersion;
++ unsigned int const buildNumber;
++ char const* const branchName;
++
++ private:
++ void operator=( Version const& );
++ };
++
++ extern Version libraryVersion;
++}
++
++#include <fstream>
++#include <stdlib.h>
++#include <limits>
++
++namespace Catch {
++
++ class Runner {
++
++ public:
++ Runner( Ptr<Config> const& config )
++ : m_config( config )
++ {
++ openStream();
++ makeReporter();
++ }
++
++ Totals runTests() {
++
++ RunContext context( m_config.get(), m_reporter );
++
++ Totals totals;
++
++ context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
++
++ TestSpec testSpec = m_config->testSpec();
++ if( !testSpec.hasFilters() )
++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
++
++ std::vector<TestCase> testCases;
++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
++
++ int testsRunForGroup = 0;
++ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
++ it != itEnd;
++ ++it ) {
++ testsRunForGroup++;
++ if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
++
++ if( context.aborting() )
++ break;
++
++ totals += context.runTest( *it );
++ m_testsAlreadyRun.insert( *it );
++ }
++ }
++ std::vector<TestCase> skippedTestCases;
++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
++
++ for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
++ it != itEnd;
++ ++it )
++ m_reporter->skipTest( *it );
++
++ context.testGroupEnded( "all tests", totals, 1, 1 );
++ return totals;
++ }
++
++ private:
++ void openStream() {
++ // Open output file, if specified
++ if( !m_config->getFilename().empty() ) {
++ m_ofs.open( m_config->getFilename().c_str() );
++ if( m_ofs.fail() ) {
++ std::ostringstream oss;
++ oss << "Unable to open file: '" << m_config->getFilename() << "'";
++ throw std::domain_error( oss.str() );
++ }
++ m_config->setStreamBuf( m_ofs.rdbuf() );
++ }
++ }
++ void makeReporter() {
++ std::string reporterName = m_config->getReporterName().empty()
++ ? "console"
++ : m_config->getReporterName();
++
++ m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
++ if( !m_reporter ) {
++ std::ostringstream oss;
++ oss << "No reporter registered with name: '" << reporterName << "'";
++ throw std::domain_error( oss.str() );
++ }
++ }
++
++ private:
++ Ptr<Config> m_config;
++ std::ofstream m_ofs;
++ Ptr<IStreamingReporter> m_reporter;
++ std::set<TestCase> m_testsAlreadyRun;
++ };
++
++ class Session : NonCopyable {
++ static bool alreadyInstantiated;
++
++ public:
++
++ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
++
++ Session()
++ : m_cli( makeCommandLineParser() ) {
++ if( alreadyInstantiated ) {
++ std::string msg = "Only one instance of Catch::Session can ever be used";
++ Catch::cerr() << msg << std::endl;
++ throw std::logic_error( msg );
++ }
++ alreadyInstantiated = true;
++ }
++ ~Session() {
++ Catch::cleanUp();
++ }
++
++ void showHelp( std::string const& processName ) {
++ Catch::cout() << "\nCatch v" << libraryVersion.majorVersion << "."
++ << libraryVersion.minorVersion << " build "
++ << libraryVersion.buildNumber;
++ if( libraryVersion.branchName != std::string( "master" ) )
++ Catch::cout() << " (" << libraryVersion.branchName << " branch)";
++ Catch::cout() << "\n";
++
++ m_cli.usage( Catch::cout(), processName );
++ Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
++ }
++
++ int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
++ try {
++ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
++ m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
++ if( m_configData.showHelp )
++ showHelp( m_configData.processName );
++ m_config.reset();
++ }
++ catch( std::exception& ex ) {
++ {
++ Colour colourGuard( Colour::Red );
++ Catch::cerr() << "\nError(s) in input:\n"
++ << Text( ex.what(), TextAttributes().setIndent(2) )
++ << "\n\n";
++ }
++ m_cli.usage( Catch::cout(), m_configData.processName );
++ return (std::numeric_limits<int>::max)();
++ }
++ return 0;
++ }
++
++ void useConfigData( ConfigData const& _configData ) {
++ m_configData = _configData;
++ m_config.reset();
++ }
++
++ int run( int argc, char* const argv[] ) {
++
++ int returnCode = applyCommandLine( argc, argv );
++ if( returnCode == 0 )
++ returnCode = run();
++ return returnCode;
++ }
++
++ int run() {
++ if( m_configData.showHelp )
++ return 0;
++
++ try
++ {
++ config(); // Force config to be constructed
++
++ std::srand( m_configData.rngSeed );
++
++ Runner runner( m_config );
++
++ // Handle list request
++ if( Option<std::size_t> listed = list( config() ) )
++ return static_cast<int>( *listed );
++
++ return static_cast<int>( runner.runTests().assertions.failed );
++ }
++ catch( std::exception& ex ) {
++ Catch::cerr() << ex.what() << std::endl;
++ return (std::numeric_limits<int>::max)();
++ }
++ }
++
++ Clara::CommandLine<ConfigData> const& cli() const {
++ return m_cli;
++ }
++ std::vector<Clara::Parser::Token> const& unusedTokens() const {
++ return m_unusedTokens;
++ }
++ ConfigData& configData() {
++ return m_configData;
++ }
++ Config& config() {
++ if( !m_config )
++ m_config = new Config( m_configData );
++ return *m_config;
++ }
++
++ private:
++ Clara::CommandLine<ConfigData> m_cli;
++ std::vector<Clara::Parser::Token> m_unusedTokens;
++ ConfigData m_configData;
++ Ptr<Config> m_config;
++ };
++
++ bool Session::alreadyInstantiated = false;
++
++} // end namespace Catch
++
++// #included from: catch_registry_hub.hpp
++#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
++
++// #included from: catch_test_case_registry_impl.hpp
++#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
++
++#include <vector>
++#include <set>
++#include <sstream>
++#include <iostream>
++#include <algorithm>
++
++namespace Catch {
++
++ class TestRegistry : public ITestCaseRegistry {
++ struct LexSort {
++ bool operator() (TestCase i,TestCase j) const { return (i<j);}
++ };
++ struct RandomNumberGenerator {
++ int operator()( int n ) const { return std::rand() % n; }
++ };
++
++ public:
++ TestRegistry() : m_unnamedCount( 0 ) {}
++ virtual ~TestRegistry();
++
++ virtual void registerTest( TestCase const& testCase ) {
++ std::string name = testCase.getTestCaseInfo().name;
++ if( name == "" ) {
++ std::ostringstream oss;
++ oss << "Anonymous test case " << ++m_unnamedCount;
++ return registerTest( testCase.withName( oss.str() ) );
++ }
++
++ if( m_functions.find( testCase ) == m_functions.end() ) {
++ m_functions.insert( testCase );
++ m_functionsInOrder.push_back( testCase );
++ if( !testCase.isHidden() )
++ m_nonHiddenFunctions.push_back( testCase );
++ }
++ else {
++ TestCase const& prev = *m_functions.find( testCase );
++ {
++ Colour colourGuard( Colour::Red );
++ Catch::cerr() << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
++ << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
++ << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
++ }
++ exit(1);
++ }
++ }
++
++ virtual std::vector<TestCase> const& getAllTests() const {
++ return m_functionsInOrder;
++ }
++
++ virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
++ return m_nonHiddenFunctions;
++ }
++
++ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
++
++ for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
++ itEnd = m_functionsInOrder.end();
++ it != itEnd;
++ ++it ) {
++ bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
++ if( includeTest != negated )
++ matchingTestCases.push_back( *it );
++ }
++ sortTests( config, matchingTestCases );
++ }
++
++ private:
++
++ static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
++
++ switch( config.runOrder() ) {
++ case RunTests::InLexicographicalOrder:
++ std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
++ break;
++ case RunTests::InRandomOrder:
++ {
++ RandomNumberGenerator rng;
++ std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
++ }
++ break;
++ case RunTests::InDeclarationOrder:
++ // already in declaration order
++ break;
++ }
++ }
++ std::set<TestCase> m_functions;
++ std::vector<TestCase> m_functionsInOrder;
++ std::vector<TestCase> m_nonHiddenFunctions;
++ size_t m_unnamedCount;
++ };
++
++ ///////////////////////////////////////////////////////////////////////////
++
++ class FreeFunctionTestCase : public SharedImpl<ITestCase> {
++ public:
++
++ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
++
++ virtual void invoke() const {
++ m_fun();
++ }
++
++ private:
++ virtual ~FreeFunctionTestCase();
++
++ TestFunction m_fun;
++ };
++
++ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
++ std::string className = classOrQualifiedMethodName;
++ if( startsWith( className, "&" ) )
++ {
++ std::size_t lastColons = className.rfind( "::" );
++ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
++ if( penultimateColons == std::string::npos )
++ penultimateColons = 1;
++ className = className.substr( penultimateColons, lastColons-penultimateColons );
++ }
++ return className;
++ }
++
++ ///////////////////////////////////////////////////////////////////////////
++
++ AutoReg::AutoReg( TestFunction function,
++ SourceLineInfo const& lineInfo,
++ NameAndDesc const& nameAndDesc ) {
++ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
++ }
++
++ AutoReg::~AutoReg() {}
++
++ void AutoReg::registerTestCase( ITestCase* testCase,
++ char const* classOrQualifiedMethodName,
++ NameAndDesc const& nameAndDesc,
++ SourceLineInfo const& lineInfo ) {
++
++ getMutableRegistryHub().registerTest
++ ( makeTestCase( testCase,
++ extractClassName( classOrQualifiedMethodName ),
++ nameAndDesc.name,
++ nameAndDesc.description,
++ lineInfo ) );
++ }
++
++} // end namespace Catch
++
++// #included from: catch_reporter_registry.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
++
++#include <map>
++
++namespace Catch {
++
++ class ReporterRegistry : public IReporterRegistry {
++
++ public:
++
++ virtual ~ReporterRegistry() {
++ deleteAllValues( m_factories );
++ }
++
++ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
++ FactoryMap::const_iterator it = m_factories.find( name );
++ if( it == m_factories.end() )
++ return NULL;
++ return it->second->create( ReporterConfig( config ) );
++ }
++
++ void registerReporter( std::string const& name, IReporterFactory* factory ) {
++ m_factories.insert( std::make_pair( name, factory ) );
++ }
++
++ FactoryMap const& getFactories() const {
++ return m_factories;
++ }
++
++ private:
++ FactoryMap m_factories;
++ };
++}
++
++// #included from: catch_exception_translator_registry.hpp
++#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
++
++#ifdef __OBJC__
++#import "Foundation/Foundation.h"
++#endif
++
++namespace Catch {
++
++ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
++ public:
++ ~ExceptionTranslatorRegistry() {
++ deleteAll( m_translators );
++ }
++
++ virtual void registerTranslator( const IExceptionTranslator* translator ) {
++ m_translators.push_back( translator );
++ }
++
++ virtual std::string translateActiveException() const {
++ try {
++#ifdef __OBJC__
++ // In Objective-C try objective-c exceptions first
++ @try {
++ throw;
++ }
++ @catch (NSException *exception) {
++ return Catch::toString( [exception description] );
++ }
++#else
++ throw;
++#endif
++ }
++ catch( TestFailureException& ) {
++ throw;
++ }
++ catch( std::exception& ex ) {
++ return ex.what();
++ }
++ catch( std::string& msg ) {
++ return msg;
++ }
++ catch( const char* msg ) {
++ return msg;
++ }
++ catch(...) {
++ return tryTranslators( m_translators.begin() );
++ }
++ }
++
++ std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
++ if( it == m_translators.end() )
++ return "Unknown exception";
++
++ try {
++ return (*it)->translate();
++ }
++ catch(...) {
++ return tryTranslators( it+1 );
++ }
++ }
++
++ private:
++ std::vector<const IExceptionTranslator*> m_translators;
++ };
++}
++
++namespace Catch {
++
++ namespace {
++
++ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
++
++ RegistryHub( RegistryHub const& );
++ void operator=( RegistryHub const& );
++
++ public: // IRegistryHub
++ RegistryHub() {
++ }
++ virtual IReporterRegistry const& getReporterRegistry() const {
++ return m_reporterRegistry;
++ }
++ virtual ITestCaseRegistry const& getTestCaseRegistry() const {
++ return m_testCaseRegistry;
++ }
++ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
++ return m_exceptionTranslatorRegistry;
++ }
++
++ public: // IMutableRegistryHub
++ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
++ m_reporterRegistry.registerReporter( name, factory );
++ }
++ virtual void registerTest( TestCase const& testInfo ) {
++ m_testCaseRegistry.registerTest( testInfo );
++ }
++ virtual void registerTranslator( const IExceptionTranslator* translator ) {
++ m_exceptionTranslatorRegistry.registerTranslator( translator );
++ }
++
++ private:
++ TestRegistry m_testCaseRegistry;
++ ReporterRegistry m_reporterRegistry;
++ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
++ };
++
++ // Single, global, instance
++ inline RegistryHub*& getTheRegistryHub() {
++ static RegistryHub* theRegistryHub = NULL;
++ if( !theRegistryHub )
++ theRegistryHub = new RegistryHub();
++ return theRegistryHub;
++ }
++ }
++
++ IRegistryHub& getRegistryHub() {
++ return *getTheRegistryHub();
++ }
++ IMutableRegistryHub& getMutableRegistryHub() {
++ return *getTheRegistryHub();
++ }
++ void cleanUp() {
++ delete getTheRegistryHub();
++ getTheRegistryHub() = NULL;
++ cleanUpContext();
++ }
++ std::string translateActiveException() {
++ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
++ }
++
++} // end namespace Catch
++
++// #included from: catch_notimplemented_exception.hpp
++#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
++
++#include <ostream>
++
++namespace Catch {
++
++ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
++ : m_lineInfo( lineInfo ) {
++ std::ostringstream oss;
++ oss << lineInfo << ": function ";
++ oss << "not implemented";
++ m_what = oss.str();
++ }
++
++ const char* NotImplementedException::what() const CATCH_NOEXCEPT {
++ return m_what.c_str();
++ }
++
++} // end namespace Catch
++
++// #included from: catch_context_impl.hpp
++#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
++
++// #included from: catch_stream.hpp
++#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
++
++// #included from: catch_streambuf.h
++#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
++
++#include <streambuf>
++
++namespace Catch {
++
++ class StreamBufBase : public std::streambuf {
++ public:
++ virtual ~StreamBufBase() CATCH_NOEXCEPT;
++ };
++}
++
++#include <stdexcept>
++#include <cstdio>
++#include <iostream>
++
++namespace Catch {
++
++ template<typename WriterF, size_t bufferSize=256>
++ class StreamBufImpl : public StreamBufBase {
++ char data[bufferSize];
++ WriterF m_writer;
++
++ public:
++ StreamBufImpl() {
++ setp( data, data + sizeof(data) );
++ }
++
++ ~StreamBufImpl() CATCH_NOEXCEPT {
++ sync();
++ }
++
++ private:
++ int overflow( int c ) {
++ sync();
++
++ if( c != EOF ) {
++ if( pbase() == epptr() )
++ m_writer( std::string( 1, static_cast<char>( c ) ) );
++ else
++ sputc( static_cast<char>( c ) );
++ }
++ return 0;
++ }
++
++ int sync() {
++ if( pbase() != pptr() ) {
++ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
++ setp( pbase(), epptr() );
++ }
++ return 0;
++ }
++ };
++
++ ///////////////////////////////////////////////////////////////////////////
++
++ struct OutputDebugWriter {
++
++ void operator()( std::string const&str ) {
++ writeToDebugConsole( str );
++ }
++ };
++
++ Stream::Stream()
++ : streamBuf( NULL ), isOwned( false )
++ {}
++
++ Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
++ : streamBuf( _streamBuf ), isOwned( _isOwned )
++ {}
++
++ void Stream::release() {
++ if( isOwned ) {
++ delete streamBuf;
++ streamBuf = NULL;
++ isOwned = false;
++ }
++ }
++
++#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
++ std::ostream& cout() {
++ return std::cout;
++ }
++ std::ostream& cerr() {
++ return std::cerr;
++ }
++#endif
++}
++
++namespace Catch {
++
++ class Context : public IMutableContext {
++
++ Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
++ Context( Context const& );
++ void operator=( Context const& );
++
++ public: // IContext
++ virtual IResultCapture* getResultCapture() {
++ return m_resultCapture;
++ }
++ virtual IRunner* getRunner() {
++ return m_runner;
++ }
++ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
++ return getGeneratorsForCurrentTest()
++ .getGeneratorInfo( fileInfo, totalSize )
++ .getCurrentIndex();
++ }
++ virtual bool advanceGeneratorsForCurrentTest() {
++ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
++ return generators && generators->moveNext();
++ }
++
++ virtual Ptr<IConfig const> getConfig() const {
++ return m_config;
++ }
++
++ public: // IMutableContext
++ virtual void setResultCapture( IResultCapture* resultCapture ) {
++ m_resultCapture = resultCapture;
++ }
++ virtual void setRunner( IRunner* runner ) {
++ m_runner = runner;
++ }
++ virtual void setConfig( Ptr<IConfig const> const& config ) {
++ m_config = config;
++ }
++
++ friend IMutableContext& getCurrentMutableContext();
++
++ private:
++ IGeneratorsForTest* findGeneratorsForCurrentTest() {
++ std::string testName = getResultCapture()->getCurrentTestName();
++
++ std::map<std::string, IGeneratorsForTest*>::const_iterator it =
++ m_generatorsByTestName.find( testName );
++ return it != m_generatorsByTestName.end()
++ ? it->second
++ : NULL;
++ }
++
++ IGeneratorsForTest& getGeneratorsForCurrentTest() {
++ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
++ if( !generators ) {
++ std::string testName = getResultCapture()->getCurrentTestName();
++ generators = createGeneratorsForTest();
++ m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
++ }
++ return *generators;
++ }
++
++ private:
++ Ptr<IConfig const> m_config;
++ IRunner* m_runner;
++ IResultCapture* m_resultCapture;
++ std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
++ };
++
++ namespace {
++ Context* currentContext = NULL;
++ }
++ IMutableContext& getCurrentMutableContext() {
++ if( !currentContext )
++ currentContext = new Context();
++ return *currentContext;
++ }
++ IContext& getCurrentContext() {
++ return getCurrentMutableContext();
++ }
++
++ Stream createStream( std::string const& streamName ) {
++ if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
++ if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
++ if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
++
++ throw std::domain_error( "Unknown stream: " + streamName );
++ }
++
++ void cleanUpContext() {
++ delete currentContext;
++ currentContext = NULL;
++ }
++}
++
++// #included from: catch_console_colour_impl.hpp
++#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
++
++namespace Catch {
++ namespace {
++
++ struct IColourImpl {
++ virtual ~IColourImpl() {}
++ virtual void use( Colour::Code _colourCode ) = 0;
++ };
++
++ struct NoColourImpl : IColourImpl {
++ void use( Colour::Code ) {}
++
++ static IColourImpl* instance() {
++ static NoColourImpl s_instance;
++ return &s_instance;
++ }
++ };
++
++ } // anon namespace
++} // namespace Catch
++
++#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
++# ifdef CATCH_PLATFORM_WINDOWS
++# define CATCH_CONFIG_COLOUR_WINDOWS
++# else
++# define CATCH_CONFIG_COLOUR_ANSI
++# endif
++#endif
++
++#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
++
++#ifndef NOMINMAX
++#define NOMINMAX
++#endif
++
++#ifdef __AFXDLL
++#include <AfxWin.h>
++#else
++#include <windows.h>
++#endif
++
++namespace Catch {
++namespace {
++
++ class Win32ColourImpl : public IColourImpl {
++ public:
++ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
++ {
++ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
++ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
++ originalAttributes = csbiInfo.wAttributes;
++ }
++
++ virtual void use( Colour::Code _colourCode ) {
++ switch( _colourCode ) {
++ case Colour::None: return setTextAttribute( originalAttributes );
++ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
++ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
++ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
++ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
++ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
++ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
++ case Colour::Grey: return setTextAttribute( 0 );
++
++ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
++ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
++ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
++ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
++
++ case Colour::Bright: throw std::logic_error( "not a colour" );
++ }
++ }
++
++ private:
++ void setTextAttribute( WORD _textAttribute ) {
++ SetConsoleTextAttribute( stdoutHandle, _textAttribute );
++ }
++ HANDLE stdoutHandle;
++ WORD originalAttributes;
++ };
++
++ IColourImpl* platformColourInstance() {
++ static Win32ColourImpl s_instance;
++ return &s_instance;
++ }
++
++} // end anon namespace
++} // end namespace Catch
++
++#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
++
++#include <unistd.h>
++
++namespace Catch {
++namespace {
++
++ // use POSIX/ ANSI console terminal codes
++ // Thanks to Adam Strzelecki for original contribution
++ // (http://github.com/nanoant)
++ // https://github.com/philsquared/Catch/pull/131
++ class PosixColourImpl : public IColourImpl {
++ public:
++ virtual void use( Colour::Code _colourCode ) {
++ switch( _colourCode ) {
++ case Colour::None:
++ case Colour::White: return setColour( "[0m" );
++ case Colour::Red: return setColour( "[0;31m" );
++ case Colour::Green: return setColour( "[0;32m" );
++ case Colour::Blue: return setColour( "[0:34m" );
++ case Colour::Cyan: return setColour( "[0;36m" );
++ case Colour::Yellow: return setColour( "[0;33m" );
++ case Colour::Grey: return setColour( "[1;30m" );
++
++ case Colour::LightGrey: return setColour( "[0;37m" );
++ case Colour::BrightRed: return setColour( "[1;31m" );
++ case Colour::BrightGreen: return setColour( "[1;32m" );
++ case Colour::BrightWhite: return setColour( "[1;37m" );
++
++ case Colour::Bright: throw std::logic_error( "not a colour" );
++ }
++ }
++ static IColourImpl* instance() {
++ static PosixColourImpl s_instance;
++ return &s_instance;
++ }
++
++ private:
++ void setColour( const char* _escapeCode ) {
++ Catch::cout() << '\033' << _escapeCode;
++ }
++ };
++
++ IColourImpl* platformColourInstance() {
++ Ptr<IConfig const> config = getCurrentContext().getConfig();
++ return (config && config->forceColour()) || isatty(STDOUT_FILENO)
++ ? PosixColourImpl::instance()
++ : NoColourImpl::instance();
++ }
++
++} // end anon namespace
++} // end namespace Catch
++
++#else // not Windows or ANSI ///////////////////////////////////////////////
++
++namespace Catch {
++
++ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
++
++} // end namespace Catch
++
++#endif // Windows/ ANSI/ None
++
++namespace Catch {
++
++ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
++ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
++ Colour::~Colour(){ if( !m_moved ) use( None ); }
++
++ void Colour::use( Code _colourCode ) {
++ static IColourImpl* impl = isDebuggerActive()
++ ? NoColourImpl::instance()
++ : platformColourInstance();
++ impl->use( _colourCode );
++ }
++
++} // end namespace Catch
++
++// #included from: catch_generators_impl.hpp
++#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
++
++#include <vector>
++#include <string>
++#include <map>
++
++namespace Catch {
++
++ struct GeneratorInfo : IGeneratorInfo {
++
++ GeneratorInfo( std::size_t size )
++ : m_size( size ),
++ m_currentIndex( 0 )
++ {}
++
++ bool moveNext() {
++ if( ++m_currentIndex == m_size ) {
++ m_currentIndex = 0;
++ return false;
++ }
++ return true;
++ }
++
++ std::size_t getCurrentIndex() const {
++ return m_currentIndex;
++ }
++
++ std::size_t m_size;
++ std::size_t m_currentIndex;
++ };
++
++ ///////////////////////////////////////////////////////////////////////////
++
++ class GeneratorsForTest : public IGeneratorsForTest {
++
++ public:
++ ~GeneratorsForTest() {
++ deleteAll( m_generatorsInOrder );
++ }
++
++ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
++ std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
++ if( it == m_generatorsByName.end() ) {
++ IGeneratorInfo* info = new GeneratorInfo( size );
++ m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
++ m_generatorsInOrder.push_back( info );
++ return *info;
++ }
++ return *it->second;
++ }
++
++ bool moveNext() {
++ std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
++ std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
++ for(; it != itEnd; ++it ) {
++ if( (*it)->moveNext() )
++ return true;
++ }
++ return false;
++ }
++
++ private:
++ std::map<std::string, IGeneratorInfo*> m_generatorsByName;
++ std::vector<IGeneratorInfo*> m_generatorsInOrder;
++ };
++
++ IGeneratorsForTest* createGeneratorsForTest()
++ {
++ return new GeneratorsForTest();
++ }
++
++} // end namespace Catch
++
++// #included from: catch_assertionresult.hpp
++#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
++
++namespace Catch {
++
++ AssertionInfo::AssertionInfo( std::string const& _macroName,
++ SourceLineInfo const& _lineInfo,
++ std::string const& _capturedExpression,
++ ResultDisposition::Flags _resultDisposition )
++ : macroName( _macroName ),
++ lineInfo( _lineInfo ),
++ capturedExpression( _capturedExpression ),
++ resultDisposition( _resultDisposition )
++ {}
++
++ AssertionResult::AssertionResult() {}
++
++ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
++ : m_info( info ),
++ m_resultData( data )
++ {}
++
++ AssertionResult::~AssertionResult() {}
++
++ // Result was a success
++ bool AssertionResult::succeeded() const {
++ return Catch::isOk( m_resultData.resultType );
++ }
++
++ // Result was a success, or failure is suppressed
++ bool AssertionResult::isOk() const {
++ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
++ }
++
++ ResultWas::OfType AssertionResult::getResultType() const {
++ return m_resultData.resultType;
++ }
++
++ bool AssertionResult::hasExpression() const {
++ return !m_info.capturedExpression.empty();
++ }
++
++ bool AssertionResult::hasMessage() const {
++ return !m_resultData.message.empty();
++ }
++
++ std::string AssertionResult::getExpression() const {
++ if( isFalseTest( m_info.resultDisposition ) )
++ return "!" + m_info.capturedExpression;
++ else
++ return m_info.capturedExpression;
++ }
++ std::string AssertionResult::getExpressionInMacro() const {
++ if( m_info.macroName.empty() )
++ return m_info.capturedExpression;
++ else
++ return m_info.macroName + "( " + m_info.capturedExpression + " )";
++ }
++
++ bool AssertionResult::hasExpandedExpression() const {
++ return hasExpression() && getExpandedExpression() != getExpression();
++ }
++
++ std::string AssertionResult::getExpandedExpression() const {
++ return m_resultData.reconstructedExpression;
++ }
++
++ std::string AssertionResult::getMessage() const {
++ return m_resultData.message;
++ }
++ SourceLineInfo AssertionResult::getSourceInfo() const {
++ return m_info.lineInfo;
++ }
++
++ std::string AssertionResult::getTestMacroName() const {
++ return m_info.macroName;
++ }
++
++} // end namespace Catch
++
++// #included from: catch_test_case_info.hpp
++#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
++
++namespace Catch {
++
++ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
++ if( startsWith( tag, "." ) ||
++ tag == "hide" ||
++ tag == "!hide" )
++ return TestCaseInfo::IsHidden;
++ else if( tag == "!throws" )
++ return TestCaseInfo::Throws;
++ else if( tag == "!shouldfail" )
++ return TestCaseInfo::ShouldFail;
++ else if( tag == "!mayfail" )
++ return TestCaseInfo::MayFail;
++ else
++ return TestCaseInfo::None;
++ }
++ inline bool isReservedTag( std::string const& tag ) {
++ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
++ }
++ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
++ if( isReservedTag( tag ) ) {
++ {
++ Colour colourGuard( Colour::Red );
++ Catch::cerr()
++ << "Tag name [" << tag << "] not allowed.\n"
++ << "Tag names starting with non alpha-numeric characters are reserved\n";
++ }
++ {
++ Colour colourGuard( Colour::FileName );
++ Catch::cerr() << _lineInfo << std::endl;
++ }
++ exit(1);
++ }
++ }
++
++ TestCase makeTestCase( ITestCase* _testCase,
++ std::string const& _className,
++ std::string const& _name,
++ std::string const& _descOrTags,
++ SourceLineInfo const& _lineInfo )
++ {
++ bool isHidden( startsWith( _name, "./" ) ); // Legacy support
++
++ // Parse out tags
++ std::set<std::string> tags;
++ std::string desc, tag;
++ bool inTag = false;
++ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
++ char c = _descOrTags[i];
++ if( !inTag ) {
++ if( c == '[' )
++ inTag = true;
++ else
++ desc += c;
++ }
++ else {
++ if( c == ']' ) {
++ TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
++ if( prop == TestCaseInfo::IsHidden )
++ isHidden = true;
++ else if( prop == TestCaseInfo::None )
++ enforceNotReservedTag( tag, _lineInfo );
++
++ tags.insert( tag );
++ tag.clear();
++ inTag = false;
++ }
++ else
++ tag += c;
++ }
++ }
++ if( isHidden ) {
++ tags.insert( "hide" );
++ tags.insert( "." );
++ }
++
++ TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
++ return TestCase( _testCase, info );
++ }
++
++ TestCaseInfo::TestCaseInfo( std::string const& _name,
++ std::string const& _className,
++ std::string const& _description,
++ std::set<std::string> const& _tags,
++ SourceLineInfo const& _lineInfo )
++ : name( _name ),
++ className( _className ),
++ description( _description ),
++ tags( _tags ),
++ lineInfo( _lineInfo ),
++ properties( None )
++ {
++ std::ostringstream oss;
++ for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
++ oss << "[" << *it << "]";
++ std::string lcaseTag = toLower( *it );
++ properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
++ lcaseTags.insert( lcaseTag );
++ }
++ tagsAsString = oss.str();
++ }
++
++ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
++ : name( other.name ),
++ className( other.className ),
++ description( other.description ),
++ tags( other.tags ),
++ lcaseTags( other.lcaseTags ),
++ tagsAsString( other.tagsAsString ),
++ lineInfo( other.lineInfo ),
++ properties( other.properties )
++ {}
++
++ bool TestCaseInfo::isHidden() const {
++ return ( properties & IsHidden ) != 0;
++ }
++ bool TestCaseInfo::throws() const {
++ return ( properties & Throws ) != 0;
++ }
++ bool TestCaseInfo::okToFail() const {
++ return ( properties & (ShouldFail | MayFail ) ) != 0;
++ }
++ bool TestCaseInfo::expectedToFail() const {
++ return ( properties & (ShouldFail ) ) != 0;
++ }
++
++ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
++
++ TestCase::TestCase( TestCase const& other )
++ : TestCaseInfo( other ),
++ test( other.test )
++ {}
++
++ TestCase TestCase::withName( std::string const& _newName ) const {
++ TestCase other( *this );
++ other.name = _newName;
++ return other;
++ }
++
++ void TestCase::swap( TestCase& other ) {
++ test.swap( other.test );
++ name.swap( other.name );
++ className.swap( other.className );
++ description.swap( other.description );
++ tags.swap( other.tags );
++ lcaseTags.swap( other.lcaseTags );
++ tagsAsString.swap( other.tagsAsString );
++ std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
++ std::swap( lineInfo, other.lineInfo );
++ }
++
++ void TestCase::invoke() const {
++ test->invoke();
++ }
++
++ bool TestCase::operator == ( TestCase const& other ) const {
++ return test.get() == other.test.get() &&
++ name == other.name &&
++ className == other.className;
++ }
++
++ bool TestCase::operator < ( TestCase const& other ) const {
++ return name < other.name;
++ }
++ TestCase& TestCase::operator = ( TestCase const& other ) {
++ TestCase temp( other );
++ swap( temp );
++ return *this;
++ }
++
++ TestCaseInfo const& TestCase::getTestCaseInfo() const
++ {
++ return *this;
++ }
++
++} // end namespace Catch
++
++// #included from: catch_version.hpp
++#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
++
++namespace Catch {
++
++ // These numbers are maintained by a script
++ Version libraryVersion( 1, 1, 1, "master" );
++}
++
++// #included from: catch_message.hpp
++#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
++
++namespace Catch {
++
++ MessageInfo::MessageInfo( std::string const& _macroName,
++ SourceLineInfo const& _lineInfo,
++ ResultWas::OfType _type )
++ : macroName( _macroName ),
++ lineInfo( _lineInfo ),
++ type( _type ),
++ sequence( ++globalCount )
++ {}
++
++ // This may need protecting if threading support is added
++ unsigned int MessageInfo::globalCount = 0;
++
++ ////////////////////////////////////////////////////////////////////////////
++
++ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
++ : m_info( builder.m_info )
++ {
++ m_info.message = builder.m_stream.str();
++ getResultCapture().pushScopedMessage( m_info );
++ }
++ ScopedMessage::ScopedMessage( ScopedMessage const& other )
++ : m_info( other.m_info )
++ {}
++
++ ScopedMessage::~ScopedMessage() {
++ getResultCapture().popScopedMessage( m_info );
++ }
++
++} // end namespace Catch
++
++// #included from: catch_legacy_reporter_adapter.hpp
++#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
++
++// #included from: catch_legacy_reporter_adapter.h
++#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
++
++namespace Catch
++{
++ // Deprecated
++ struct IReporter : IShared {
++ virtual ~IReporter();
++
++ virtual bool shouldRedirectStdout() const = 0;
++
++ virtual void StartTesting() = 0;
++ virtual void EndTesting( Totals const& totals ) = 0;
++ virtual void StartGroup( std::string const& groupName ) = 0;
++ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
++ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
++ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
++ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
++ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
++ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
++ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
++ virtual void Aborted() = 0;
++ virtual void Result( AssertionResult const& result ) = 0;
++ };
++
++ class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
++ {
++ public:
++ LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
++ virtual ~LegacyReporterAdapter();
++
++ virtual ReporterPreferences getPreferences() const;
++ virtual void noMatchingTestCases( std::string const& );
++ virtual void testRunStarting( TestRunInfo const& );
++ virtual void testGroupStarting( GroupInfo const& groupInfo );
++ virtual void testCaseStarting( TestCaseInfo const& testInfo );
++ virtual void sectionStarting( SectionInfo const& sectionInfo );
++ virtual void assertionStarting( AssertionInfo const& );
++ virtual bool assertionEnded( AssertionStats const& assertionStats );
++ virtual void sectionEnded( SectionStats const& sectionStats );
++ virtual void testCaseEnded( TestCaseStats const& testCaseStats );
++ virtual void testGroupEnded( TestGroupStats const& testGroupStats );
++ virtual void testRunEnded( TestRunStats const& testRunStats );
++ virtual void skipTest( TestCaseInfo const& );
++
++ private:
++ Ptr<IReporter> m_legacyReporter;
++ };
++}
++
++namespace Catch
++{
++ LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
++ : m_legacyReporter( legacyReporter )
++ {}
++ LegacyReporterAdapter::~LegacyReporterAdapter() {}
++
++ ReporterPreferences LegacyReporterAdapter::getPreferences() const {
++ ReporterPreferences prefs;
++ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
++ return prefs;
++ }
++
++ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
++ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
++ m_legacyReporter->StartTesting();
++ }
++ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
++ m_legacyReporter->StartGroup( groupInfo.name );
++ }
++ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
++ m_legacyReporter->StartTestCase( testInfo );
++ }
++ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
++ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
++ }
++ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
++ // Not on legacy interface
++ }
++
++ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
++ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
++ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
++ it != itEnd;
++ ++it ) {
++ if( it->type == ResultWas::Info ) {
++ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
++ rb << it->message;
++ rb.setResultType( ResultWas::Info );
++ AssertionResult result = rb.build();
++ m_legacyReporter->Result( result );
++ }
++ }
++ }
++ m_legacyReporter->Result( assertionStats.assertionResult );
++ return true;
++ }
++ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
++ if( sectionStats.missingAssertions )
++ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
++ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
++ }
++ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
++ m_legacyReporter->EndTestCase
++ ( testCaseStats.testInfo,
++ testCaseStats.totals,
++ testCaseStats.stdOut,
++ testCaseStats.stdErr );
++ }
++ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
++ if( testGroupStats.aborting )
++ m_legacyReporter->Aborted();
++ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
++ }
++ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
++ m_legacyReporter->EndTesting( testRunStats.totals );
++ }
++ void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
++ }
++}
++
++// #included from: catch_timer.hpp
++
++#ifdef __clang__
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wc++11-long-long"
++#endif
++
++#ifdef CATCH_PLATFORM_WINDOWS
++#include <windows.h>
++#else
++#include <sys/time.h>
++#endif
++
++namespace Catch {
++
++ namespace {
++#ifdef CATCH_PLATFORM_WINDOWS
++ uint64_t getCurrentTicks() {
++ static uint64_t hz=0, hzo=0;
++ if (!hz) {
++ QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
++ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
++ }
++ uint64_t t;
++ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
++ return ((t-hzo)*1000000)/hz;
++ }
++#else
++ uint64_t getCurrentTicks() {
++ timeval t;
++ gettimeofday(&t,NULL);
++ return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
++ }
++#endif
++ }
++
++ void Timer::start() {
++ m_ticks = getCurrentTicks();
++ }
++ unsigned int Timer::getElapsedMicroseconds() const {
++ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
++ }
++ unsigned int Timer::getElapsedMilliseconds() const {
++ return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
++ }
++ double Timer::getElapsedSeconds() const {
++ return getElapsedMicroseconds()/1000000.0;
++ }
++
++} // namespace Catch
++
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++// #included from: catch_common.hpp
++#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
++
++namespace Catch {
++
++ bool startsWith( std::string const& s, std::string const& prefix ) {
++ return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
++ }
++ bool endsWith( std::string const& s, std::string const& suffix ) {
++ return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
++ }
++ bool contains( std::string const& s, std::string const& infix ) {
++ return s.find( infix ) != std::string::npos;
++ }
++ void toLowerInPlace( std::string& s ) {
++ std::transform( s.begin(), s.end(), s.begin(), ::tolower );
++ }
++ std::string toLower( std::string const& s ) {
++ std::string lc = s;
++ toLowerInPlace( lc );
++ return lc;
++ }
++ std::string trim( std::string const& str ) {
++ static char const* whitespaceChars = "\n\r\t ";
++ std::string::size_type start = str.find_first_not_of( whitespaceChars );
++ std::string::size_type end = str.find_last_not_of( whitespaceChars );
++
++ return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
++ }
++
++ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
++ bool replaced = false;
++ std::size_t i = str.find( replaceThis );
++ while( i != std::string::npos ) {
++ replaced = true;
++ str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
++ if( i < str.size()-withThis.size() )
++ i = str.find( replaceThis, i+withThis.size() );
++ else
++ i = std::string::npos;
++ }
++ return replaced;
++ }
++
++ pluralise::pluralise( std::size_t count, std::string const& label )
++ : m_count( count ),
++ m_label( label )
++ {}
++
++ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
++ os << pluraliser.m_count << " " << pluraliser.m_label;
++ if( pluraliser.m_count != 1 )
++ os << "s";
++ return os;
++ }
++
++ SourceLineInfo::SourceLineInfo() : line( 0 ){}
++ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
++ : file( _file ),
++ line( _line )
++ {}
++ SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
++ : file( other.file ),
++ line( other.line )
++ {}
++ bool SourceLineInfo::empty() const {
++ return file.empty();
++ }
++ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
++ return line == other.line && file == other.file;
++ }
++ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
++ return line < other.line || ( line == other.line && file < other.file );
++ }
++
++ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
++#ifndef __GNUG__
++ os << info.file << "(" << info.line << ")";
++#else
++ os << info.file << ":" << info.line;
++#endif
++ return os;
++ }
++
++ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
++ std::ostringstream oss;
++ oss << locationInfo << ": Internal Catch error: '" << message << "'";
++ if( alwaysTrue() )
++ throw std::logic_error( oss.str() );
++ }
++}
++
++// #included from: catch_section.hpp
++#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
++
++namespace Catch {
++
++ SectionInfo::SectionInfo
++ ( SourceLineInfo const& _lineInfo,
++ std::string const& _name,
++ std::string const& _description )
++ : name( _name ),
++ description( _description ),
++ lineInfo( _lineInfo )
++ {}
++
++ Section::Section( SectionInfo const& info )
++ : m_info( info ),
++ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
++ {
++ m_timer.start();
++ }
++
++ Section::~Section() {
++ if( m_sectionIncluded )
++ getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
++ }
++
++ // This indicates whether the section should be executed or not
++ Section::operator bool() const {
++ return m_sectionIncluded;
++ }
++
++} // end namespace Catch
++
++// #included from: catch_debugger.hpp
++#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
++
++#include <iostream>
++
++#ifdef CATCH_PLATFORM_MAC
++
++ #include <assert.h>
++ #include <stdbool.h>
++ #include <sys/types.h>
++ #include <unistd.h>
++ #include <sys/sysctl.h>
++
++ namespace Catch{
++
++ // The following function is taken directly from the following technical note:
++ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
++
++ // Returns true if the current process is being debugged (either
++ // running under the debugger or has a debugger attached post facto).
++ bool isDebuggerActive(){
++
++ int mib[4];
++ struct kinfo_proc info;
++ size_t size;
++
++ // Initialize the flags so that, if sysctl fails for some bizarre
++ // reason, we get a predictable result.
++
++ info.kp_proc.p_flag = 0;
++
++ // Initialize mib, which tells sysctl the info we want, in this case
++ // we're looking for information about a specific process ID.
++
++ mib[0] = CTL_KERN;
++ mib[1] = KERN_PROC;
++ mib[2] = KERN_PROC_PID;
++ mib[3] = getpid();
++
++ // Call sysctl.
++
++ size = sizeof(info);
++ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
++ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
++ return false;
++ }
++
++ // We're being debugged if the P_TRACED flag is set.
++
++ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
++ }
++ } // namespace Catch
++
++#elif defined(_MSC_VER)
++ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
++ namespace Catch {
++ bool isDebuggerActive() {
++ return IsDebuggerPresent() != 0;
++ }
++ }
++#elif defined(__MINGW32__)
++ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
++ namespace Catch {
++ bool isDebuggerActive() {
++ return IsDebuggerPresent() != 0;
++ }
++ }
++#else
++ namespace Catch {
++ inline bool isDebuggerActive() { return false; }
++ }
++#endif // Platform
++
++#ifdef CATCH_PLATFORM_WINDOWS
++ extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
++ namespace Catch {
++ void writeToDebugConsole( std::string const& text ) {
++ ::OutputDebugStringA( text.c_str() );
++ }
++ }
++#else
++ namespace Catch {
++ void writeToDebugConsole( std::string const& text ) {
++ // !TBD: Need a version for Mac/ XCode and other IDEs
++ Catch::cout() << text;
++ }
++ }
++#endif // Platform
++
++// #included from: catch_tostring.hpp
++#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
++
++namespace Catch {
++
++namespace Detail {
++
++ std::string unprintableString = "{?}";
++
++ namespace {
++ struct Endianness {
++ enum Arch { Big, Little };
++
++ static Arch which() {
++ union _{
++ int asInt;
++ char asChar[sizeof (int)];
++ } u;
++
++ u.asInt = 1;
++ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
++ }
++ };
++ }
++
++ std::string rawMemoryToString( const void *object, std::size_t size )
++ {
++ // Reverse order for little endian architectures
++ int i = 0, end = static_cast<int>( size ), inc = 1;
++ if( Endianness::which() == Endianness::Little ) {
++ i = end-1;
++ end = inc = -1;
++ }
++
++ unsigned char const *bytes = static_cast<unsigned char const *>(object);
++ std::ostringstream os;
++ os << "0x" << std::setfill('0') << std::hex;
++ for( ; i != end; i += inc )
++ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
++ return os.str();
++ }
++}
++
++std::string toString( std::string const& value ) {
++ std::string s = value;
++ if( getCurrentContext().getConfig()->showInvisibles() ) {
++ for(size_t i = 0; i < s.size(); ++i ) {
++ std::string subs;
++ switch( s[i] ) {
++ case '\n': subs = "\\n"; break;
++ case '\t': subs = "\\t"; break;
++ default: break;
++ }
++ if( !subs.empty() ) {
++ s = s.substr( 0, i ) + subs + s.substr( i+1 );
++ ++i;
++ }
++ }
++ }
++ return "\"" + s + "\"";
++}
++std::string toString( std::wstring const& value ) {
++
++ std::string s;
++ s.reserve( value.size() );
++ for(size_t i = 0; i < value.size(); ++i )
++ s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
++ return Catch::toString( s );
++}
++
++std::string toString( const char* const value ) {
++ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
++}
++
++std::string toString( char* const value ) {
++ return Catch::toString( static_cast<const char*>( value ) );
++}
++
++std::string toString( const wchar_t* const value )
++{
++ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
++}
++
++std::string toString( wchar_t* const value )
++{
++ return Catch::toString( static_cast<const wchar_t*>( value ) );
++}
++
++std::string toString( int value ) {
++ std::ostringstream oss;
++ if( value > 8192 )
++ oss << "0x" << std::hex << value;
++ else
++ oss << value;
++ return oss.str();
++}
++
++std::string toString( unsigned long value ) {
++ std::ostringstream oss;
++ if( value > 8192 )
++ oss << "0x" << std::hex << value;
++ else
++ oss << value;
++ return oss.str();
++}
++
++std::string toString( unsigned int value ) {
++ return Catch::toString( static_cast<unsigned long>( value ) );
++}
++
++template<typename T>
++std::string fpToString( T value, int precision ) {
++ std::ostringstream oss;
++ oss << std::setprecision( precision )
++ << std::fixed
++ << value;
++ std::string d = oss.str();
++ std::size_t i = d.find_last_not_of( '0' );
++ if( i != std::string::npos && i != d.size()-1 ) {
++ if( d[i] == '.' )
++ i++;
++ d = d.substr( 0, i+1 );
++ }
++ return d;
++}
++
++std::string toString( const double value ) {
++ return fpToString( value, 10 );
++}
++std::string toString( const float value ) {
++ return fpToString( value, 5 ) + "f";
++}
++
++std::string toString( bool value ) {
++ return value ? "true" : "false";
++}
++
++std::string toString( char value ) {
++ return value < ' '
++ ? toString( static_cast<unsigned int>( value ) )
++ : Detail::makeString( value );
++}
++
++std::string toString( signed char value ) {
++ return toString( static_cast<char>( value ) );
++}
++
++std::string toString( unsigned char value ) {
++ return toString( static_cast<char>( value ) );
++}
++
++#ifdef CATCH_CONFIG_CPP11_NULLPTR
++std::string toString( std::nullptr_t ) {
++ return "nullptr";
++}
++#endif
++
++#ifdef __OBJC__
++ std::string toString( NSString const * const& nsstring ) {
++ if( !nsstring )
++ return "nil";
++ return "@" + toString([nsstring UTF8String]);
++ }
++ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
++ if( !nsstring )
++ return "nil";
++ return "@" + toString([nsstring UTF8String]);
++ }
++ std::string toString( NSObject* const& nsObject ) {
++ return toString( [nsObject description] );
++ }
++#endif
++
++} // end namespace Catch
++
++// #included from: catch_result_builder.hpp
++#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
++
++namespace Catch {
++
++ ResultBuilder::ResultBuilder( char const* macroName,
++ SourceLineInfo const& lineInfo,
++ char const* capturedExpression,
++ ResultDisposition::Flags resultDisposition )
++ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
++ m_shouldDebugBreak( false ),
++ m_shouldThrow( false )
++ {}
++
++ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
++ m_data.resultType = result;
++ return *this;
++ }
++ ResultBuilder& ResultBuilder::setResultType( bool result ) {
++ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
++ return *this;
++ }
++ ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
++ m_exprComponents.lhs = lhs;
++ return *this;
++ }
++ ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
++ m_exprComponents.rhs = rhs;
++ return *this;
++ }
++ ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
++ m_exprComponents.op = op;
++ return *this;
++ }
++
++ void ResultBuilder::endExpression() {
++ m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
++ captureExpression();
++ }
++
++ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
++ m_assertionInfo.resultDisposition = resultDisposition;
++ m_stream.oss << Catch::translateActiveException();
++ captureResult( ResultWas::ThrewException );
++ }
++
++ void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
++ setResultType( resultType );
++ captureExpression();
++ }
++
++ void ResultBuilder::captureExpression() {
++ AssertionResult result = build();
++ getResultCapture().assertionEnded( result );
++
++ if( !result.isOk() ) {
++ if( getCurrentContext().getConfig()->shouldDebugBreak() )
++ m_shouldDebugBreak = true;
++ if( getCurrentContext().getRunner()->aborting() || m_assertionInfo.resultDisposition == ResultDisposition::Normal )
++ m_shouldThrow = true;
++ }
++ }
++ void ResultBuilder::react() {
++ if( m_shouldThrow )
++ throw Catch::TestFailureException();
++ }
++
++ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
++ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
++
++ AssertionResult ResultBuilder::build() const
++ {
++ assert( m_data.resultType != ResultWas::Unknown );
++
++ AssertionResultData data = m_data;
++
++ // Flip bool results if testFalse is set
++ if( m_exprComponents.testFalse ) {
++ if( data.resultType == ResultWas::Ok )
++ data.resultType = ResultWas::ExpressionFailed;
++ else if( data.resultType == ResultWas::ExpressionFailed )
++ data.resultType = ResultWas::Ok;
++ }
++
++ data.message = m_stream.oss.str();
++ data.reconstructedExpression = reconstructExpression();
++ if( m_exprComponents.testFalse ) {
++ if( m_exprComponents.op == "" )
++ data.reconstructedExpression = "!" + data.reconstructedExpression;
++ else
++ data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
++ }
++ return AssertionResult( m_assertionInfo, data );
++ }
++ std::string ResultBuilder::reconstructExpression() const {
++ if( m_exprComponents.op == "" )
++ return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
++ else if( m_exprComponents.op == "matches" )
++ return m_exprComponents.lhs + " " + m_exprComponents.rhs;
++ else if( m_exprComponents.op != "!" ) {
++ if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
++ m_exprComponents.lhs.find("\n") == std::string::npos &&
++ m_exprComponents.rhs.find("\n") == std::string::npos )
++ return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
++ else
++ return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
++ }
++ else
++ return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
++ }
++
++} // end namespace Catch
++
++// #included from: catch_tag_alias_registry.hpp
++#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
++
++// #included from: catch_tag_alias_registry.h
++#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
++
++#include <map>
++
++namespace Catch {
++
++ class TagAliasRegistry : public ITagAliasRegistry {
++ public:
++ virtual ~TagAliasRegistry();
++ virtual Option<TagAlias> find( std::string const& alias ) const;
++ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
++ void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
++ static TagAliasRegistry& get();
++
++ private:
++ std::map<std::string, TagAlias> m_registry;
++ };
++
++} // end namespace Catch
++
++#include <map>
++#include <iostream>
++
++namespace Catch {
++
++ TagAliasRegistry::~TagAliasRegistry() {}
++
++ Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
++ std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
++ if( it != m_registry.end() )
++ return it->second;
++ else
++ return Option<TagAlias>();
++ }
++
++ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
++ std::string expandedTestSpec = unexpandedTestSpec;
++ for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
++ it != itEnd;
++ ++it ) {
++ std::size_t pos = expandedTestSpec.find( it->first );
++ if( pos != std::string::npos ) {
++ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
++ it->second.tag +
++ expandedTestSpec.substr( pos + it->first.size() );
++ }
++ }
++ return expandedTestSpec;
++ }
++
++ void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
++
++ if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
++ std::ostringstream oss;
++ oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
++ throw std::domain_error( oss.str().c_str() );
++ }
++ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
++ std::ostringstream oss;
++ oss << "error: tag alias, \"" << alias << "\" already registered.\n"
++ << "\tFirst seen at " << find(alias)->lineInfo << "\n"
++ << "\tRedefined at " << lineInfo;
++ throw std::domain_error( oss.str().c_str() );
++ }
++ }
++
++ TagAliasRegistry& TagAliasRegistry::get() {
++ static TagAliasRegistry instance;
++ return instance;
++
++ }
++
++ ITagAliasRegistry::~ITagAliasRegistry() {}
++ ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
++
++ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
++ try {
++ TagAliasRegistry::get().add( alias, tag, lineInfo );
++ }
++ catch( std::exception& ex ) {
++ Colour colourGuard( Colour::Red );
++ Catch::cerr() << ex.what() << std::endl;
++ exit(1);
++ }
++ }
++
++} // end namespace Catch
++
++// #included from: ../reporters/catch_reporter_xml.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
++
++// #included from: catch_reporter_bases.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
++
++#include <cstring>
++
++namespace Catch {
++
++ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
++
++ StreamingReporterBase( ReporterConfig const& _config )
++ : m_config( _config.fullConfig() ),
++ stream( _config.stream() )
++ {}
++
++ virtual ~StreamingReporterBase();
++
++ virtual void noMatchingTestCases( std::string const& ) {}
++
++ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
++ currentTestRunInfo = _testRunInfo;
++ }
++ virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
++ currentGroupInfo = _groupInfo;
++ }
++
++ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
++ currentTestCaseInfo = _testInfo;
++ }
++ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
++ m_sectionStack.push_back( _sectionInfo );
++ }
++
++ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
++ m_sectionStack.pop_back();
++ }
++ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
++ currentTestCaseInfo.reset();
++ }
++ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
++ currentGroupInfo.reset();
++ }
++ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
++ currentTestCaseInfo.reset();
++ currentGroupInfo.reset();
++ currentTestRunInfo.reset();
++ }
++
++ virtual void skipTest( TestCaseInfo const& ) {
++ // Don't do anything with this by default.
++ // It can optionally be overridden in the derived class.
++ }
++
++ Ptr<IConfig> m_config;
++ std::ostream& stream;
++
++ LazyStat<TestRunInfo> currentTestRunInfo;
++ LazyStat<GroupInfo> currentGroupInfo;
++ LazyStat<TestCaseInfo> currentTestCaseInfo;
++
++ std::vector<SectionInfo> m_sectionStack;
++ };
++
++ struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
++ template<typename T, typename ChildNodeT>
++ struct Node : SharedImpl<> {
++ explicit Node( T const& _value ) : value( _value ) {}
++ virtual ~Node() {}
++
++ typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
++ T value;
++ ChildNodes children;
++ };
++ struct SectionNode : SharedImpl<> {
++ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
++ virtual ~SectionNode();
++
++ bool operator == ( SectionNode const& other ) const {
++ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
++ }
++ bool operator == ( Ptr<SectionNode> const& other ) const {
++ return operator==( *other );
++ }
++
++ SectionStats stats;
++ typedef std::vector<Ptr<SectionNode> > ChildSections;
++ typedef std::vector<AssertionStats> Assertions;
++ ChildSections childSections;
++ Assertions assertions;
++ std::string stdOut;
++ std::string stdErr;
++ };
++
++ struct BySectionInfo {
++ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
++ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
++ bool operator() ( Ptr<SectionNode> const& node ) const {
++ return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
++ }
++ private:
++ void operator=( BySectionInfo const& );
++ SectionInfo const& m_other;
++ };
++
++ typedef Node<TestCaseStats, SectionNode> TestCaseNode;
++ typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
++ typedef Node<TestRunStats, TestGroupNode> TestRunNode;
++
++ CumulativeReporterBase( ReporterConfig const& _config )
++ : m_config( _config.fullConfig() ),
++ stream( _config.stream() )
++ {}
++ ~CumulativeReporterBase();
++
++ virtual void testRunStarting( TestRunInfo const& ) {}
++ virtual void testGroupStarting( GroupInfo const& ) {}
++
++ virtual void testCaseStarting( TestCaseInfo const& ) {}
++
++ virtual void sectionStarting( SectionInfo const& sectionInfo ) {
++ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
++ Ptr<SectionNode> node;
++ if( m_sectionStack.empty() ) {
++ if( !m_rootSection )
++ m_rootSection = new SectionNode( incompleteStats );
++ node = m_rootSection;
++ }
++ else {
++ SectionNode& parentNode = *m_sectionStack.back();
++ SectionNode::ChildSections::const_iterator it =
++ std::find_if( parentNode.childSections.begin(),
++ parentNode.childSections.end(),
++ BySectionInfo( sectionInfo ) );
++ if( it == parentNode.childSections.end() ) {
++ node = new SectionNode( incompleteStats );
++ parentNode.childSections.push_back( node );
++ }
++ else
++ node = *it;
++ }
++ m_sectionStack.push_back( node );
++ m_deepestSection = node;
++ }
++
++ virtual void assertionStarting( AssertionInfo const& ) {}
++
++ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
++ assert( !m_sectionStack.empty() );
++ SectionNode& sectionNode = *m_sectionStack.back();
++ sectionNode.assertions.push_back( assertionStats );
++ return true;
++ }
++ virtual void sectionEnded( SectionStats const& sectionStats ) {
++ assert( !m_sectionStack.empty() );
++ SectionNode& node = *m_sectionStack.back();
++ node.stats = sectionStats;
++ m_sectionStack.pop_back();
++ }
++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
++ Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
++ assert( m_sectionStack.size() == 0 );
++ node->children.push_back( m_rootSection );
++ m_testCases.push_back( node );
++ m_rootSection.reset();
++
++ assert( m_deepestSection );
++ m_deepestSection->stdOut = testCaseStats.stdOut;
++ m_deepestSection->stdErr = testCaseStats.stdErr;
++ }
++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
++ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
++ node->children.swap( m_testCases );
++ m_testGroups.push_back( node );
++ }
++ virtual void testRunEnded( TestRunStats const& testRunStats ) {
++ Ptr<TestRunNode> node = new TestRunNode( testRunStats );
++ node->children.swap( m_testGroups );
++ m_testRuns.push_back( node );
++ testRunEndedCumulative();
++ }
++ virtual void testRunEndedCumulative() = 0;
++
++ virtual void skipTest( TestCaseInfo const& ) {}
++
++ Ptr<IConfig> m_config;
++ std::ostream& stream;
++ std::vector<AssertionStats> m_assertions;
++ std::vector<std::vector<Ptr<SectionNode> > > m_sections;
++ std::vector<Ptr<TestCaseNode> > m_testCases;
++ std::vector<Ptr<TestGroupNode> > m_testGroups;
++
++ std::vector<Ptr<TestRunNode> > m_testRuns;
++
++ Ptr<SectionNode> m_rootSection;
++ Ptr<SectionNode> m_deepestSection;
++ std::vector<Ptr<SectionNode> > m_sectionStack;
++
++ };
++
++ template<char C>
++ char const* getLineOfChars() {
++ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
++ if( !*line ) {
++ memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
++ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
++ }
++ return line;
++ }
++
++} // end namespace Catch
++
++// #included from: ../internal/catch_reporter_registrars.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
++
++namespace Catch {
++
++ template<typename T>
++ class LegacyReporterRegistrar {
++
++ class ReporterFactory : public IReporterFactory {
++ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
++ return new LegacyReporterAdapter( new T( config ) );
++ }
++
++ virtual std::string getDescription() const {
++ return T::getDescription();
++ }
++ };
++
++ public:
++
++ LegacyReporterRegistrar( std::string const& name ) {
++ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
++ }
++ };
++
++ template<typename T>
++ class ReporterRegistrar {
++
++ class ReporterFactory : public IReporterFactory {
++
++ // *** Please Note ***:
++ // - If you end up here looking at a compiler error because it's trying to register
++ // your custom reporter class be aware that the native reporter interface has changed
++ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
++ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
++ // However please consider updating to the new interface as the old one is now
++ // deprecated and will probably be removed quite soon!
++ // Please contact me via github if you have any questions at all about this.
++ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
++ // no idea who is actually using custom reporters at all (possibly no-one!).
++ // The new interface is designed to minimise exposure to interface changes in the future.
++ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
++ return new T( config );
++ }
++
++ virtual std::string getDescription() const {
++ return T::getDescription();
++ }
++ };
++
++ public:
++
++ ReporterRegistrar( std::string const& name ) {
++ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
++ }
++ };
++}
++
++#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
++ namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
++#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
++ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
++
++// #included from: ../internal/catch_xmlwriter.hpp
++#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
++
++#include <sstream>
++#include <string>
++#include <vector>
++
++namespace Catch {
++
++ class XmlWriter {
++ public:
++
++ class ScopedElement {
++ public:
++ ScopedElement( XmlWriter* writer )
++ : m_writer( writer )
++ {}
++
++ ScopedElement( ScopedElement const& other )
++ : m_writer( other.m_writer ){
++ other.m_writer = NULL;
++ }
++
++ ~ScopedElement() {
++ if( m_writer )
++ m_writer->endElement();
++ }
++
++ ScopedElement& writeText( std::string const& text, bool indent = true ) {
++ m_writer->writeText( text, indent );
++ return *this;
++ }
++
++ template<typename T>
++ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
++ m_writer->writeAttribute( name, attribute );
++ return *this;
++ }
++
++ private:
++ mutable XmlWriter* m_writer;
++ };
++
++ XmlWriter()
++ : m_tagIsOpen( false ),
++ m_needsNewline( false ),
++ m_os( &Catch::cout() )
++ {}
++
++ XmlWriter( std::ostream& os )
++ : m_tagIsOpen( false ),
++ m_needsNewline( false ),
++ m_os( &os )
++ {}
++
++ ~XmlWriter() {
++ while( !m_tags.empty() )
++ endElement();
++ }
++
++//# ifndef CATCH_CPP11_OR_GREATER
++// XmlWriter& operator = ( XmlWriter const& other ) {
++// XmlWriter temp( other );
++// swap( temp );
++// return *this;
++// }
++//# else
++// XmlWriter( XmlWriter const& ) = default;
++// XmlWriter( XmlWriter && ) = default;
++// XmlWriter& operator = ( XmlWriter const& ) = default;
++// XmlWriter& operator = ( XmlWriter && ) = default;
++//# endif
++//
++// void swap( XmlWriter& other ) {
++// std::swap( m_tagIsOpen, other.m_tagIsOpen );
++// std::swap( m_needsNewline, other.m_needsNewline );
++// std::swap( m_tags, other.m_tags );
++// std::swap( m_indent, other.m_indent );
++// std::swap( m_os, other.m_os );
++// }
++
++ XmlWriter& startElement( std::string const& name ) {
++ ensureTagClosed();
++ newlineIfNecessary();
++ stream() << m_indent << "<" << name;
++ m_tags.push_back( name );
++ m_indent += " ";
++ m_tagIsOpen = true;
++ return *this;
++ }
++
++ ScopedElement scopedElement( std::string const& name ) {
++ ScopedElement scoped( this );
++ startElement( name );
++ return scoped;
++ }
++
++ XmlWriter& endElement() {
++ newlineIfNecessary();
++ m_indent = m_indent.substr( 0, m_indent.size()-2 );
++ if( m_tagIsOpen ) {
++ stream() << "/>\n";
++ m_tagIsOpen = false;
++ }
++ else {
++ stream() << m_indent << "</" << m_tags.back() << ">\n";
++ }
++ m_tags.pop_back();
++ return *this;
++ }
++
++ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
++ if( !name.empty() && !attribute.empty() ) {
++ stream() << " " << name << "=\"";
++ writeEncodedText( attribute );
++ stream() << "\"";
++ }
++ return *this;
++ }
++
++ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
++ stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
++ return *this;
++ }
++
++ template<typename T>
++ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
++ if( !name.empty() )
++ stream() << " " << name << "=\"" << attribute << "\"";
++ return *this;
++ }
++
++ XmlWriter& writeText( std::string const& text, bool indent = true ) {
++ if( !text.empty() ){
++ bool tagWasOpen = m_tagIsOpen;
++ ensureTagClosed();
++ if( tagWasOpen && indent )
++ stream() << m_indent;
++ writeEncodedText( text );
++ m_needsNewline = true;
++ }
++ return *this;
++ }
++
++ XmlWriter& writeComment( std::string const& text ) {
++ ensureTagClosed();
++ stream() << m_indent << "<!--" << text << "-->";
++ m_needsNewline = true;
++ return *this;
++ }
++
++ XmlWriter& writeBlankLine() {
++ ensureTagClosed();
++ stream() << "\n";
++ return *this;
++ }
++
++ void setStream( std::ostream& os ) {
++ m_os = &os;
++ }
++
++ private:
++ XmlWriter( XmlWriter const& );
++ void operator=( XmlWriter const& );
++
++ std::ostream& stream() {
++ return *m_os;
++ }
++
++ void ensureTagClosed() {
++ if( m_tagIsOpen ) {
++ stream() << ">\n";
++ m_tagIsOpen = false;
++ }
++ }
++
++ void newlineIfNecessary() {
++ if( m_needsNewline ) {
++ stream() << "\n";
++ m_needsNewline = false;
++ }
++ }
++
++ void writeEncodedText( std::string const& text ) {
++ static const char* charsToEncode = "<&\"";
++ std::string mtext = text;
++ std::string::size_type pos = mtext.find_first_of( charsToEncode );
++ while( pos != std::string::npos ) {
++ stream() << mtext.substr( 0, pos );
++
++ switch( mtext[pos] ) {
++ case '<':
++ stream() << "&lt;";
++ break;
++ case '&':
++ stream() << "&amp;";
++ break;
++ case '\"':
++ stream() << "&quot;";
++ break;
++ }
++ mtext = mtext.substr( pos+1 );
++ pos = mtext.find_first_of( charsToEncode );
++ }
++ stream() << mtext;
++ }
++
++ bool m_tagIsOpen;
++ bool m_needsNewline;
++ std::vector<std::string> m_tags;
++ std::string m_indent;
++ std::ostream* m_os;
++ };
++
++}
++namespace Catch {
++ class XmlReporter : public StreamingReporterBase {
++ public:
++ XmlReporter( ReporterConfig const& _config )
++ : StreamingReporterBase( _config ),
++ m_sectionDepth( 0 )
++ {}
++
++ virtual ~XmlReporter();
++
++ static std::string getDescription() {
++ return "Reports test results as an XML document";
++ }
++
++ public: // StreamingReporterBase
++ virtual ReporterPreferences getPreferences() const {
++ ReporterPreferences prefs;
++ prefs.shouldRedirectStdOut = true;
++ return prefs;
++ }
++
++ virtual void noMatchingTestCases( std::string const& s ) {
++ StreamingReporterBase::noMatchingTestCases( s );
++ }
++
++ virtual void testRunStarting( TestRunInfo const& testInfo ) {
++ StreamingReporterBase::testRunStarting( testInfo );
++ m_xml.setStream( stream );
++ m_xml.startElement( "Catch" );
++ if( !m_config->name().empty() )
++ m_xml.writeAttribute( "name", m_config->name() );
++ }
++
++ virtual void testGroupStarting( GroupInfo const& groupInfo ) {
++ StreamingReporterBase::testGroupStarting( groupInfo );
++ m_xml.startElement( "Group" )
++ .writeAttribute( "name", groupInfo.name );
++ }
++
++ virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
++ StreamingReporterBase::testCaseStarting(testInfo);
++ m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
++
++ if ( m_config->showDurations() == ShowDurations::Always )
++ m_testCaseTimer.start();
++ }
++
++ virtual void sectionStarting( SectionInfo const& sectionInfo ) {
++ StreamingReporterBase::sectionStarting( sectionInfo );
++ if( m_sectionDepth++ > 0 ) {
++ m_xml.startElement( "Section" )
++ .writeAttribute( "name", trim( sectionInfo.name ) )
++ .writeAttribute( "description", sectionInfo.description );
++ }
++ }
++
++ virtual void assertionStarting( AssertionInfo const& ) { }
++
++ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
++ const AssertionResult& assertionResult = assertionStats.assertionResult;
++
++ // Print any info messages in <Info> tags.
++ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
++ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
++ it != itEnd;
++ ++it ) {
++ if( it->type == ResultWas::Info ) {
++ m_xml.scopedElement( "Info" )
++ .writeText( it->message );
++ } else if ( it->type == ResultWas::Warning ) {
++ m_xml.scopedElement( "Warning" )
++ .writeText( it->message );
++ }
++ }
++ }
++
++ // Drop out if result was successful but we're not printing them.
++ if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
++ return true;
++
++ // Print the expression if there is one.
++ if( assertionResult.hasExpression() ) {
++ m_xml.startElement( "Expression" )
++ .writeAttribute( "success", assertionResult.succeeded() )
++ .writeAttribute( "type", assertionResult.getTestMacroName() )
++ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
++ .writeAttribute( "line", assertionResult.getSourceInfo().line );
++
++ m_xml.scopedElement( "Original" )
++ .writeText( assertionResult.getExpression() );
++ m_xml.scopedElement( "Expanded" )
++ .writeText( assertionResult.getExpandedExpression() );
++ }
++
++ // And... Print a result applicable to each result type.
++ switch( assertionResult.getResultType() ) {
++ case ResultWas::ThrewException:
++ m_xml.scopedElement( "Exception" )
++ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
++ .writeAttribute( "line", assertionResult.getSourceInfo().line )
++ .writeText( assertionResult.getMessage() );
++ break;
++ case ResultWas::FatalErrorCondition:
++ m_xml.scopedElement( "Fatal Error Condition" )
++ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
++ .writeAttribute( "line", assertionResult.getSourceInfo().line )
++ .writeText( assertionResult.getMessage() );
++ break;
++ case ResultWas::Info:
++ m_xml.scopedElement( "Info" )
++ .writeText( assertionResult.getMessage() );
++ break;
++ case ResultWas::Warning:
++ // Warning will already have been written
++ break;
++ case ResultWas::ExplicitFailure:
++ m_xml.scopedElement( "Failure" )
++ .writeText( assertionResult.getMessage() );
++ break;
++ default:
++ break;
++ }
++
++ if( assertionResult.hasExpression() )
++ m_xml.endElement();
++
++ return true;
++ }
++
++ virtual void sectionEnded( SectionStats const& sectionStats ) {
++ StreamingReporterBase::sectionEnded( sectionStats );
++ if( --m_sectionDepth > 0 ) {
++ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
++ e.writeAttribute( "successes", sectionStats.assertions.passed );
++ e.writeAttribute( "failures", sectionStats.assertions.failed );
++ e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
++
++ if ( m_config->showDurations() == ShowDurations::Always )
++ e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
++
++ m_xml.endElement();
++ }
++ }
++
++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
++ StreamingReporterBase::testCaseEnded( testCaseStats );
++ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
++ e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
++
++ if ( m_config->showDurations() == ShowDurations::Always )
++ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
++
++ m_xml.endElement();
++ }
++
++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
++ StreamingReporterBase::testGroupEnded( testGroupStats );
++ // TODO: Check testGroupStats.aborting and act accordingly.
++ m_xml.scopedElement( "OverallResults" )
++ .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
++ .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
++ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
++ m_xml.endElement();
++ }
++
++ virtual void testRunEnded( TestRunStats const& testRunStats ) {
++ StreamingReporterBase::testRunEnded( testRunStats );
++ m_xml.scopedElement( "OverallResults" )
++ .writeAttribute( "successes", testRunStats.totals.assertions.passed )
++ .writeAttribute( "failures", testRunStats.totals.assertions.failed )
++ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
++ m_xml.endElement();
++ }
++
++ private:
++ Timer m_testCaseTimer;
++ XmlWriter m_xml;
++ int m_sectionDepth;
++ };
++
++ INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
++
++} // end namespace Catch
++
++// #included from: ../reporters/catch_reporter_junit.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
++
++#include <assert.h>
++
++namespace Catch {
++
++ class JunitReporter : public CumulativeReporterBase {
++ public:
++ JunitReporter( ReporterConfig const& _config )
++ : CumulativeReporterBase( _config ),
++ xml( _config.stream() )
++ {}
++
++ ~JunitReporter();
++
++ static std::string getDescription() {
++ return "Reports test results in an XML format that looks like Ant's junitreport target";
++ }
++
++ virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
++
++ virtual ReporterPreferences getPreferences() const {
++ ReporterPreferences prefs;
++ prefs.shouldRedirectStdOut = true;
++ return prefs;
++ }
++
++ virtual void testRunStarting( TestRunInfo const& runInfo ) {
++ CumulativeReporterBase::testRunStarting( runInfo );
++ xml.startElement( "testsuites" );
++ }
++
++ virtual void testGroupStarting( GroupInfo const& groupInfo ) {
++ suiteTimer.start();
++ stdOutForSuite.str("");
++ stdErrForSuite.str("");
++ unexpectedExceptions = 0;
++ CumulativeReporterBase::testGroupStarting( groupInfo );
++ }
++
++ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
++ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
++ unexpectedExceptions++;
++ return CumulativeReporterBase::assertionEnded( assertionStats );
++ }
++
++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
++ stdOutForSuite << testCaseStats.stdOut;
++ stdErrForSuite << testCaseStats.stdErr;
++ CumulativeReporterBase::testCaseEnded( testCaseStats );
++ }
++
++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
++ double suiteTime = suiteTimer.getElapsedSeconds();
++ CumulativeReporterBase::testGroupEnded( testGroupStats );
++ writeGroup( *m_testGroups.back(), suiteTime );
++ }
++
++ virtual void testRunEndedCumulative() {
++ xml.endElement();
++ }
++
++ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
++ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
++ TestGroupStats const& stats = groupNode.value;
++ xml.writeAttribute( "name", stats.groupInfo.name );
++ xml.writeAttribute( "errors", unexpectedExceptions );
++ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
++ xml.writeAttribute( "tests", stats.totals.assertions.total() );
++ xml.writeAttribute( "hostname", "tbd" ); // !TBD
++ if( m_config->showDurations() == ShowDurations::Never )
++ xml.writeAttribute( "time", "" );
++ else
++ xml.writeAttribute( "time", suiteTime );
++ xml.writeAttribute( "timestamp", "tbd" ); // !TBD
++
++ // Write test cases
++ for( TestGroupNode::ChildNodes::const_iterator
++ it = groupNode.children.begin(), itEnd = groupNode.children.end();
++ it != itEnd;
++ ++it )
++ writeTestCase( **it );
++
++ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
++ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
++ }
++
++ void writeTestCase( TestCaseNode const& testCaseNode ) {
++ TestCaseStats const& stats = testCaseNode.value;
++
++ // All test cases have exactly one section - which represents the
++ // test case itself. That section may have 0-n nested sections
++ assert( testCaseNode.children.size() == 1 );
++ SectionNode const& rootSection = *testCaseNode.children.front();
++
++ std::string className = stats.testInfo.className;
++
++ if( className.empty() ) {
++ if( rootSection.childSections.empty() )
++ className = "global";
++ }
++ writeSection( className, "", rootSection );
++ }
++
++ void writeSection( std::string const& className,
++ std::string const& rootName,
++ SectionNode const& sectionNode ) {
++ std::string name = trim( sectionNode.stats.sectionInfo.name );
++ if( !rootName.empty() )
++ name = rootName + "/" + name;
++
++ if( !sectionNode.assertions.empty() ||
++ !sectionNode.stdOut.empty() ||
++ !sectionNode.stdErr.empty() ) {
++ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
++ if( className.empty() ) {
++ xml.writeAttribute( "classname", name );
++ xml.writeAttribute( "name", "root" );
++ }
++ else {
++ xml.writeAttribute( "classname", className );
++ xml.writeAttribute( "name", name );
++ }
++ xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
++
++ writeAssertions( sectionNode );
++
++ if( !sectionNode.stdOut.empty() )
++ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
++ if( !sectionNode.stdErr.empty() )
++ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
++ }
++ for( SectionNode::ChildSections::const_iterator
++ it = sectionNode.childSections.begin(),
++ itEnd = sectionNode.childSections.end();
++ it != itEnd;
++ ++it )
++ if( className.empty() )
++ writeSection( name, "", **it );
++ else
++ writeSection( className, name, **it );
++ }
++
++ void writeAssertions( SectionNode const& sectionNode ) {
++ for( SectionNode::Assertions::const_iterator
++ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
++ it != itEnd;
++ ++it )
++ writeAssertion( *it );
++ }
++ void writeAssertion( AssertionStats const& stats ) {
++ AssertionResult const& result = stats.assertionResult;
++ if( !result.isOk() ) {
++ std::string elementName;
++ switch( result.getResultType() ) {
++ case ResultWas::ThrewException:
++ case ResultWas::FatalErrorCondition:
++ elementName = "error";
++ break;
++ case ResultWas::ExplicitFailure:
++ elementName = "failure";
++ break;
++ case ResultWas::ExpressionFailed:
++ elementName = "failure";
++ break;
++ case ResultWas::DidntThrowException:
++ elementName = "failure";
++ break;
++
++ // We should never see these here:
++ case ResultWas::Info:
++ case ResultWas::Warning:
++ case ResultWas::Ok:
++ case ResultWas::Unknown:
++ case ResultWas::FailureBit:
++ case ResultWas::Exception:
++ elementName = "internalError";
++ break;
++ }
++
++ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
++
++ xml.writeAttribute( "message", result.getExpandedExpression() );
++ xml.writeAttribute( "type", result.getTestMacroName() );
++
++ std::ostringstream oss;
++ if( !result.getMessage().empty() )
++ oss << result.getMessage() << "\n";
++ for( std::vector<MessageInfo>::const_iterator
++ it = stats.infoMessages.begin(),
++ itEnd = stats.infoMessages.end();
++ it != itEnd;
++ ++it )
++ if( it->type == ResultWas::Info )
++ oss << it->message << "\n";
++
++ oss << "at " << result.getSourceInfo();
++ xml.writeText( oss.str(), false );
++ }
++ }
++
++ XmlWriter xml;
++ Timer suiteTimer;
++ std::ostringstream stdOutForSuite;
++ std::ostringstream stdErrForSuite;
++ unsigned int unexpectedExceptions;
++ };
++
++ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
++
++} // end namespace Catch
++
++// #included from: ../reporters/catch_reporter_console.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
++
++namespace Catch {
++
++ struct ConsoleReporter : StreamingReporterBase {
++ ConsoleReporter( ReporterConfig const& _config )
++ : StreamingReporterBase( _config ),
++ m_headerPrinted( false )
++ {}
++
++ virtual ~ConsoleReporter();
++ static std::string getDescription() {
++ return "Reports test results as plain lines of text";
++ }
++ virtual ReporterPreferences getPreferences() const {
++ ReporterPreferences prefs;
++ prefs.shouldRedirectStdOut = false;
++ return prefs;
++ }
++
++ virtual void noMatchingTestCases( std::string const& spec ) {
++ stream << "No test cases matched '" << spec << "'" << std::endl;
++ }
++
++ virtual void assertionStarting( AssertionInfo const& ) {
++ }
++
++ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
++ AssertionResult const& result = _assertionStats.assertionResult;
++
++ bool printInfoMessages = true;
++
++ // Drop out if result was successful and we're not printing those
++ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
++ if( result.getResultType() != ResultWas::Warning )
++ return false;
++ printInfoMessages = false;
++ }
++
++ lazyPrint();
++
++ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
++ printer.print();
++ stream << std::endl;
++ return true;
++ }
++
++ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
++ m_headerPrinted = false;
++ StreamingReporterBase::sectionStarting( _sectionInfo );
++ }
++ virtual void sectionEnded( SectionStats const& _sectionStats ) {
++ if( _sectionStats.missingAssertions ) {
++ lazyPrint();
++ Colour colour( Colour::ResultError );
++ if( m_sectionStack.size() > 1 )
++ stream << "\nNo assertions in section";
++ else
++ stream << "\nNo assertions in test case";
++ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
++ }
++ if( m_headerPrinted ) {
++ if( m_config->showDurations() == ShowDurations::Always )
++ stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
++ m_headerPrinted = false;
++ }
++ else {
++ if( m_config->showDurations() == ShowDurations::Always )
++ stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
++ }
++ StreamingReporterBase::sectionEnded( _sectionStats );
++ }
++
++ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
++ StreamingReporterBase::testCaseEnded( _testCaseStats );
++ m_headerPrinted = false;
++ }
++ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
++ if( currentGroupInfo.used ) {
++ printSummaryDivider();
++ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
++ printTotals( _testGroupStats.totals );
++ stream << "\n" << std::endl;
++ }
++ StreamingReporterBase::testGroupEnded( _testGroupStats );
++ }
++ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
++ printTotalsDivider( _testRunStats.totals );
++ printTotals( _testRunStats.totals );
++ stream << std::endl;
++ StreamingReporterBase::testRunEnded( _testRunStats );
++ }
++
++ private:
++
++ class AssertionPrinter {
++ void operator= ( AssertionPrinter const& );
++ public:
++ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
++ : stream( _stream ),
++ stats( _stats ),
++ result( _stats.assertionResult ),
++ colour( Colour::None ),
++ message( result.getMessage() ),
++ messages( _stats.infoMessages ),
++ printInfoMessages( _printInfoMessages )
++ {
++ switch( result.getResultType() ) {
++ case ResultWas::Ok:
++ colour = Colour::Success;
++ passOrFail = "PASSED";
++ //if( result.hasMessage() )
++ if( _stats.infoMessages.size() == 1 )
++ messageLabel = "with message";
++ if( _stats.infoMessages.size() > 1 )
++ messageLabel = "with messages";
++ break;
++ case ResultWas::ExpressionFailed:
++ if( result.isOk() ) {
++ colour = Colour::Success;
++ passOrFail = "FAILED - but was ok";
++ }
++ else {
++ colour = Colour::Error;
++ passOrFail = "FAILED";
++ }
++ if( _stats.infoMessages.size() == 1 )
++ messageLabel = "with message";
++ if( _stats.infoMessages.size() > 1 )
++ messageLabel = "with messages";
++ break;
++ case ResultWas::ThrewException:
++ colour = Colour::Error;
++ passOrFail = "FAILED";
++ messageLabel = "due to unexpected exception with message";
++ break;
++ case ResultWas::FatalErrorCondition:
++ colour = Colour::Error;
++ passOrFail = "FAILED";
++ messageLabel = "due to a fatal error condition";
++ break;
++ case ResultWas::DidntThrowException:
++ colour = Colour::Error;
++ passOrFail = "FAILED";
++ messageLabel = "because no exception was thrown where one was expected";
++ break;
++ case ResultWas::Info:
++ messageLabel = "info";
++ break;
++ case ResultWas::Warning:
++ messageLabel = "warning";
++ break;
++ case ResultWas::ExplicitFailure:
++ passOrFail = "FAILED";
++ colour = Colour::Error;
++ if( _stats.infoMessages.size() == 1 )
++ messageLabel = "explicitly with message";
++ if( _stats.infoMessages.size() > 1 )
++ messageLabel = "explicitly with messages";
++ break;
++ // These cases are here to prevent compiler warnings
++ case ResultWas::Unknown:
++ case ResultWas::FailureBit:
++ case ResultWas::Exception:
++ passOrFail = "** internal error **";
++ colour = Colour::Error;
++ break;
++ }
++ }
++
++ void print() const {
++ printSourceInfo();
++ if( stats.totals.assertions.total() > 0 ) {
++ if( result.isOk() )
++ stream << "\n";
++ printResultType();
++ printOriginalExpression();
++ printReconstructedExpression();
++ }
++ else {
++ stream << "\n";
++ }
++ printMessage();
++ }
++
++ private:
++ void printResultType() const {
++ if( !passOrFail.empty() ) {
++ Colour colourGuard( colour );
++ stream << passOrFail << ":\n";
++ }
++ }
++ void printOriginalExpression() const {
++ if( result.hasExpression() ) {
++ Colour colourGuard( Colour::OriginalExpression );
++ stream << " ";
++ stream << result.getExpressionInMacro();
++ stream << "\n";
++ }
++ }
++ void printReconstructedExpression() const {
++ if( result.hasExpandedExpression() ) {
++ stream << "with expansion:\n";
++ Colour colourGuard( Colour::ReconstructedExpression );
++ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
++ }
++ }
++ void printMessage() const {
++ if( !messageLabel.empty() )
++ stream << messageLabel << ":" << "\n";
++ for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
++ it != itEnd;
++ ++it ) {
++ // If this assertion is a warning ignore any INFO messages
++ if( printInfoMessages || it->type != ResultWas::Info )
++ stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
++ }
++ }
++ void printSourceInfo() const {
++ Colour colourGuard( Colour::FileName );
++ stream << result.getSourceInfo() << ": ";
++ }
++
++ std::ostream& stream;
++ AssertionStats const& stats;
++ AssertionResult const& result;
++ Colour::Code colour;
++ std::string passOrFail;
++ std::string messageLabel;
++ std::string message;
++ std::vector<MessageInfo> messages;
++ bool printInfoMessages;
++ };
++
++ void lazyPrint() {
++
++ if( !currentTestRunInfo.used )
++ lazyPrintRunInfo();
++ if( !currentGroupInfo.used )
++ lazyPrintGroupInfo();
++
++ if( !m_headerPrinted ) {
++ printTestCaseAndSectionHeader();
++ m_headerPrinted = true;
++ }
++ }
++ void lazyPrintRunInfo() {
++ stream << "\n" << getLineOfChars<'~'>() << "\n";
++ Colour colour( Colour::SecondaryText );
++ stream << currentTestRunInfo->name
++ << " is a Catch v" << libraryVersion.majorVersion << "."
++ << libraryVersion.minorVersion << " b"
++ << libraryVersion.buildNumber;
++ if( libraryVersion.branchName != std::string( "master" ) )
++ stream << " (" << libraryVersion.branchName << ")";
++ stream << " host application.\n"
++ << "Run with -? for options\n\n";
++
++ if( m_config->rngSeed() != 0 )
++ stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
++
++ currentTestRunInfo.used = true;
++ }
++ void lazyPrintGroupInfo() {
++ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
++ printClosedHeader( "Group: " + currentGroupInfo->name );
++ currentGroupInfo.used = true;
++ }
++ }
++ void printTestCaseAndSectionHeader() {
++ assert( !m_sectionStack.empty() );
++ printOpenHeader( currentTestCaseInfo->name );
++
++ if( m_sectionStack.size() > 1 ) {
++ Colour colourGuard( Colour::Headers );
++
++ std::vector<SectionInfo>::const_iterator
++ it = m_sectionStack.begin()+1, // Skip first section (test case)
++ itEnd = m_sectionStack.end();
++ for( ; it != itEnd; ++it )
++ printHeaderString( it->name, 2 );
++ }
++
++ SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
++
++ if( !lineInfo.empty() ){
++ stream << getLineOfChars<'-'>() << "\n";
++ Colour colourGuard( Colour::FileName );
++ stream << lineInfo << "\n";
++ }
++ stream << getLineOfChars<'.'>() << "\n" << std::endl;
++ }
++
++ void printClosedHeader( std::string const& _name ) {
++ printOpenHeader( _name );
++ stream << getLineOfChars<'.'>() << "\n";
++ }
++ void printOpenHeader( std::string const& _name ) {
++ stream << getLineOfChars<'-'>() << "\n";
++ {
++ Colour colourGuard( Colour::Headers );
++ printHeaderString( _name );
++ }
++ }
++
++ // if string has a : in first line will set indent to follow it on
++ // subsequent lines
++ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
++ std::size_t i = _string.find( ": " );
++ if( i != std::string::npos )
++ i+=2;
++ else
++ i = 0;
++ stream << Text( _string, TextAttributes()
++ .setIndent( indent+i)
++ .setInitialIndent( indent ) ) << "\n";
++ }
++
++ struct SummaryColumn {
++
++ SummaryColumn( std::string const& _label, Colour::Code _colour )
++ : label( _label ),
++ colour( _colour )
++ {}
++ SummaryColumn addRow( std::size_t count ) {
++ std::ostringstream oss;
++ oss << count;
++ std::string row = oss.str();
++ for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
++ while( it->size() < row.size() )
++ *it = " " + *it;
++ while( it->size() > row.size() )
++ row = " " + row;
++ }
++ rows.push_back( row );
++ return *this;
++ }
++
++ std::string label;
++ Colour::Code colour;
++ std::vector<std::string> rows;
++
++ };
++
++ void printTotals( Totals const& totals ) {
++ if( totals.testCases.total() == 0 ) {
++ stream << Colour( Colour::Warning ) << "No tests ran\n";
++ }
++ else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
++ stream << Colour( Colour::ResultSuccess ) << "All tests passed";
++ stream << " ("
++ << pluralise( totals.assertions.passed, "assertion" ) << " in "
++ << pluralise( totals.testCases.passed, "test case" ) << ")"
++ << "\n";
++ }
++ else {
++
++ std::vector<SummaryColumn> columns;
++ columns.push_back( SummaryColumn( "", Colour::None )
++ .addRow( totals.testCases.total() )
++ .addRow( totals.assertions.total() ) );
++ columns.push_back( SummaryColumn( "passed", Colour::Success )
++ .addRow( totals.testCases.passed )
++ .addRow( totals.assertions.passed ) );
++ columns.push_back( SummaryColumn( "failed", Colour::ResultError )
++ .addRow( totals.testCases.failed )
++ .addRow( totals.assertions.failed ) );
++ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
++ .addRow( totals.testCases.failedButOk )
++ .addRow( totals.assertions.failedButOk ) );
++
++ printSummaryRow( "test cases", columns, 0 );
++ printSummaryRow( "assertions", columns, 1 );
++ }
++ }
++ void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
++ for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
++ std::string value = it->rows[row];
++ if( it->label.empty() ) {
++ stream << label << ": ";
++ if( value != "0" )
++ stream << value;
++ else
++ stream << Colour( Colour::Warning ) << "- none -";
++ }
++ else if( value != "0" ) {
++ stream << Colour( Colour::LightGrey ) << " | ";
++ stream << Colour( it->colour )
++ << value << " " << it->label;
++ }
++ }
++ stream << "\n";
++ }
++
++ static std::size_t makeRatio( std::size_t number, std::size_t total ) {
++ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
++ return ( ratio == 0 && number > 0 ) ? 1 : ratio;
++ }
++ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
++ if( i > j && i > k )
++ return i;
++ else if( j > k )
++ return j;
++ else
++ return k;
++ }
++
++ void printTotalsDivider( Totals const& totals ) {
++ if( totals.testCases.total() > 0 ) {
++ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
++ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
++ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
++ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
++ findMax( failedRatio, failedButOkRatio, passedRatio )++;
++ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
++ findMax( failedRatio, failedButOkRatio, passedRatio )--;
++
++ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
++ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
++ if( totals.testCases.allPassed() )
++ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
++ else
++ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
++ }
++ else {
++ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
++ }
++ stream << "\n";
++ }
++ void printSummaryDivider() {
++ stream << getLineOfChars<'-'>() << "\n";
++ }
++
++ private:
++ bool m_headerPrinted;
++ };
++
++ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
++
++} // end namespace Catch
++
++// #included from: ../reporters/catch_reporter_compact.hpp
++#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
++
++namespace Catch {
++
++ struct CompactReporter : StreamingReporterBase {
++
++ CompactReporter( ReporterConfig const& _config )
++ : StreamingReporterBase( _config )
++ {}
++
++ virtual ~CompactReporter();
++
++ static std::string getDescription() {
++ return "Reports test results on a single line, suitable for IDEs";
++ }
++
++ virtual ReporterPreferences getPreferences() const {
++ ReporterPreferences prefs;
++ prefs.shouldRedirectStdOut = false;
++ return prefs;
++ }
++
++ virtual void noMatchingTestCases( std::string const& spec ) {
++ stream << "No test cases matched '" << spec << "'" << std::endl;
++ }
++
++ virtual void assertionStarting( AssertionInfo const& ) {
++ }
++
++ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
++ AssertionResult const& result = _assertionStats.assertionResult;
++
++ bool printInfoMessages = true;
++
++ // Drop out if result was successful and we're not printing those
++ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
++ if( result.getResultType() != ResultWas::Warning )
++ return false;
++ printInfoMessages = false;
++ }
++
++ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
++ printer.print();
++
++ stream << std::endl;
++ return true;
++ }
++
++ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
++ printTotals( _testRunStats.totals );
++ stream << "\n" << std::endl;
++ StreamingReporterBase::testRunEnded( _testRunStats );
++ }
++
++ private:
++ class AssertionPrinter {
++ void operator= ( AssertionPrinter const& );
++ public:
++ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
++ : stream( _stream )
++ , stats( _stats )
++ , result( _stats.assertionResult )
++ , messages( _stats.infoMessages )
++ , itMessage( _stats.infoMessages.begin() )
++ , printInfoMessages( _printInfoMessages )
++ {}
++
++ void print() {
++ printSourceInfo();
++
++ itMessage = messages.begin();
++
++ switch( result.getResultType() ) {
++ case ResultWas::Ok:
++ printResultType( Colour::ResultSuccess, passedString() );
++ printOriginalExpression();
++ printReconstructedExpression();
++ if ( ! result.hasExpression() )
++ printRemainingMessages( Colour::None );
++ else
++ printRemainingMessages();
++ break;
++ case ResultWas::ExpressionFailed:
++ if( result.isOk() )
++ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
++ else
++ printResultType( Colour::Error, failedString() );
++ printOriginalExpression();
++ printReconstructedExpression();
++ printRemainingMessages();
++ break;
++ case ResultWas::ThrewException:
++ printResultType( Colour::Error, failedString() );
++ printIssue( "unexpected exception with message:" );
++ printMessage();
++ printExpressionWas();
++ printRemainingMessages();
++ break;
++ case ResultWas::FatalErrorCondition:
++ printResultType( Colour::Error, failedString() );
++ printIssue( "fatal error condition with message:" );
++ printMessage();
++ printExpressionWas();
++ printRemainingMessages();
++ break;
++ case ResultWas::DidntThrowException:
++ printResultType( Colour::Error, failedString() );
++ printIssue( "expected exception, got none" );
++ printExpressionWas();
++ printRemainingMessages();
++ break;
++ case ResultWas::Info:
++ printResultType( Colour::None, "info" );
++ printMessage();
++ printRemainingMessages();
++ break;
++ case ResultWas::Warning:
++ printResultType( Colour::None, "warning" );
++ printMessage();
++ printRemainingMessages();
++ break;
++ case ResultWas::ExplicitFailure:
++ printResultType( Colour::Error, failedString() );
++ printIssue( "explicitly" );
++ printRemainingMessages( Colour::None );
++ break;
++ // These cases are here to prevent compiler warnings
++ case ResultWas::Unknown:
++ case ResultWas::FailureBit:
++ case ResultWas::Exception:
++ printResultType( Colour::Error, "** internal error **" );
++ break;
++ }
++ }
++
++ private:
++ // Colour::LightGrey
++
++ static Colour::Code dimColour() { return Colour::FileName; }
++
++#ifdef CATCH_PLATFORM_MAC
++ static const char* failedString() { return "FAILED"; }
++ static const char* passedString() { return "PASSED"; }
++#else
++ static const char* failedString() { return "failed"; }
++ static const char* passedString() { return "passed"; }
++#endif
++
++ void printSourceInfo() const {
++ Colour colourGuard( Colour::FileName );
++ stream << result.getSourceInfo() << ":";
++ }
++
++ void printResultType( Colour::Code colour, std::string passOrFail ) const {
++ if( !passOrFail.empty() ) {
++ {
++ Colour colourGuard( colour );
++ stream << " " << passOrFail;
++ }
++ stream << ":";
++ }
++ }
++
++ void printIssue( std::string issue ) const {
++ stream << " " << issue;
++ }
++
++ void printExpressionWas() {
++ if( result.hasExpression() ) {
++ stream << ";";
++ {
++ Colour colour( dimColour() );
++ stream << " expression was:";
++ }
++ printOriginalExpression();
++ }
++ }
++
++ void printOriginalExpression() const {
++ if( result.hasExpression() ) {
++ stream << " " << result.getExpression();
++ }
++ }
++
++ void printReconstructedExpression() const {
++ if( result.hasExpandedExpression() ) {
++ {
++ Colour colour( dimColour() );
++ stream << " for: ";
++ }
++ stream << result.getExpandedExpression();
++ }
++ }
++
++ void printMessage() {
++ if ( itMessage != messages.end() ) {
++ stream << " '" << itMessage->message << "'";
++ ++itMessage;
++ }
++ }
++
++ void printRemainingMessages( Colour::Code colour = dimColour() ) {
++ if ( itMessage == messages.end() )
++ return;
++
++ // using messages.end() directly yields compilation error:
++ std::vector<MessageInfo>::const_iterator itEnd = messages.end();
++ const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
++
++ {
++ Colour colourGuard( colour );
++ stream << " with " << pluralise( N, "message" ) << ":";
++ }
++
++ for(; itMessage != itEnd; ) {
++ // If this assertion is a warning ignore any INFO messages
++ if( printInfoMessages || itMessage->type != ResultWas::Info ) {
++ stream << " '" << itMessage->message << "'";
++ if ( ++itMessage != itEnd ) {
++ Colour colourGuard( dimColour() );
++ stream << " and";
++ }
++ }
++ }
++ }
++
++ private:
++ std::ostream& stream;
++ AssertionStats const& stats;
++ AssertionResult const& result;
++ std::vector<MessageInfo> messages;
++ std::vector<MessageInfo>::const_iterator itMessage;
++ bool printInfoMessages;
++ };
++
++ // Colour, message variants:
++ // - white: No tests ran.
++ // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
++ // - white: Passed [both/all] N test cases (no assertions).
++ // - red: Failed N tests cases, failed M assertions.
++ // - green: Passed [both/all] N tests cases with M assertions.
++
++ std::string bothOrAll( std::size_t count ) const {
++ return count == 1 ? "" : count == 2 ? "both " : "all " ;
++ }
++
++ void printTotals( const Totals& totals ) const {
++ if( totals.testCases.total() == 0 ) {
++ stream << "No tests ran.";
++ }
++ else if( totals.testCases.failed == totals.testCases.total() ) {
++ Colour colour( Colour::ResultError );
++ const std::string qualify_assertions_failed =
++ totals.assertions.failed == totals.assertions.total() ?
++ bothOrAll( totals.assertions.failed ) : "";
++ stream <<
++ "Failed " << bothOrAll( totals.testCases.failed )
++ << pluralise( totals.testCases.failed, "test case" ) << ", "
++ "failed " << qualify_assertions_failed <<
++ pluralise( totals.assertions.failed, "assertion" ) << ".";
++ }
++ else if( totals.assertions.total() == 0 ) {
++ stream <<
++ "Passed " << bothOrAll( totals.testCases.total() )
++ << pluralise( totals.testCases.total(), "test case" )
++ << " (no assertions).";
++ }
++ else if( totals.assertions.failed ) {
++ Colour colour( Colour::ResultError );
++ stream <<
++ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
++ "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
++ }
++ else {
++ Colour colour( Colour::ResultSuccess );
++ stream <<
++ "Passed " << bothOrAll( totals.testCases.passed )
++ << pluralise( totals.testCases.passed, "test case" ) <<
++ " with " << pluralise( totals.assertions.passed, "assertion" ) << ".";
++ }
++ }
++ };
++
++ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
++
++} // end namespace Catch
++
++namespace Catch {
++ NonCopyable::~NonCopyable() {}
++ IShared::~IShared() {}
++ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
++ IContext::~IContext() {}
++ IResultCapture::~IResultCapture() {}
++ ITestCase::~ITestCase() {}
++ ITestCaseRegistry::~ITestCaseRegistry() {}
++ IRegistryHub::~IRegistryHub() {}
++ IMutableRegistryHub::~IMutableRegistryHub() {}
++ IExceptionTranslator::~IExceptionTranslator() {}
++ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
++ IReporter::~IReporter() {}
++ IReporterFactory::~IReporterFactory() {}
++ IReporterRegistry::~IReporterRegistry() {}
++ IStreamingReporter::~IStreamingReporter() {}
++ AssertionStats::~AssertionStats() {}
++ SectionStats::~SectionStats() {}
++ TestCaseStats::~TestCaseStats() {}
++ TestGroupStats::~TestGroupStats() {}
++ TestRunStats::~TestRunStats() {}
++ CumulativeReporterBase::SectionNode::~SectionNode() {}
++ CumulativeReporterBase::~CumulativeReporterBase() {}
++
++ StreamingReporterBase::~StreamingReporterBase() {}
++ ConsoleReporter::~ConsoleReporter() {}
++ CompactReporter::~CompactReporter() {}
++ IRunner::~IRunner() {}
++ IMutableContext::~IMutableContext() {}
++ IConfig::~IConfig() {}
++ XmlReporter::~XmlReporter() {}
++ JunitReporter::~JunitReporter() {}
++ TestRegistry::~TestRegistry() {}
++ FreeFunctionTestCase::~FreeFunctionTestCase() {}
++ IGeneratorInfo::~IGeneratorInfo() {}
++ IGeneratorsForTest::~IGeneratorsForTest() {}
++ TestSpec::Pattern::~Pattern() {}
++ TestSpec::NamePattern::~NamePattern() {}
++ TestSpec::TagPattern::~TagPattern() {}
++ TestSpec::ExcludedPattern::~ExcludedPattern() {}
++
++ Matchers::Impl::StdString::Equals::~Equals() {}
++ Matchers::Impl::StdString::Contains::~Contains() {}
++ Matchers::Impl::StdString::StartsWith::~StartsWith() {}
++ Matchers::Impl::StdString::EndsWith::~EndsWith() {}
++
++ void Config::dummy() {}
++}
++
++#ifdef __clang__
++#pragma clang diagnostic pop
++#endif
++
++#endif
++
++#ifdef CATCH_CONFIG_MAIN
++// #included from: internal/catch_default_main.hpp
++#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
++
++#ifndef __OBJC__
++
++// Standard C/C++ main entry point
++int main (int argc, char * const argv[]) {
++ return Catch::Session().run( argc, argv );
++}
++
++#else // __OBJC__
++
++// Objective-C entry point
++int main (int argc, char * const argv[]) {
++#if !CATCH_ARC_ENABLED
++ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
++#endif
++
++ Catch::registerTestMethods();
++ int result = Catch::Session().run( argc, (char* const*)argv );
++
++#if !CATCH_ARC_ENABLED
++ [pool drain];
++#endif
++
++ return result;
++}
++
++#endif // __OBJC__
++
++#endif
++
++#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
++# undef CLARA_CONFIG_MAIN
++#endif
++
++//////
++
++// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
++#ifdef CATCH_CONFIG_PREFIX_ALL
++
++#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
++#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
++
++#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
++#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
++#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
++
++#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
++#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
++#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
++#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
++#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
++
++#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
++#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
++#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
++
++#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
++#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
++
++#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
++#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
++#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
++#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
++#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
++
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
++ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
++ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
++ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
++ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
++ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
++#else
++ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
++ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
++ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
++ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
++ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
++ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
++#endif
++#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
++
++#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
++#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
++
++#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
++
++// "BDD-style" convenience wrappers
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
++#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
++#else
++#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
++#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
++#endif
++#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" )
++#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" )
++#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" )
++#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" )
++#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" )
++
++// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
++#else
++
++#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
++#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
++
++#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
++#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
++#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
++
++#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
++#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
++#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
++#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
++#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
++
++#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
++#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
++#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
++
++#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
++#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
++
++#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
++#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
++#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
++#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
++#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
++
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
++ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
++ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
++ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
++ #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
++ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
++#else
++ #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
++ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
++ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
++ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
++ #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
++ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
++#endif
++#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
++
++#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
++#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
++
++#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
++
++#endif
++
++#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
++
++// "BDD-style" convenience wrappers
++#ifdef CATCH_CONFIG_VARIADIC_MACROS
++#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
++#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
++#else
++#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
++#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
++#endif
++#define GIVEN( desc ) SECTION( " Given: " desc, "" )
++#define WHEN( desc ) SECTION( " When: " desc, "" )
++#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
++#define THEN( desc ) SECTION( " Then: " desc, "" )
++#define AND_THEN( desc ) SECTION( " And: " desc, "" )
++
++using Catch::Detail::Approx;
++
++// #included from: internal/catch_reenable_warnings.h
++
++#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
++
++#ifdef __clang__
++# ifdef __ICC // icpc defines the __clang__ macro
++# pragma warning(pop)
++# else
++# pragma clang diagnostic pop
++# endif
++#elif defined __GNUC__
++# pragma GCC diagnostic pop
++#endif
++
++#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
++
+diff --git a/external/spdlog-0.14.0/tests/cond_logging.cpp b/external/spdlog-0.14.0/tests/cond_logging.cpp
+new file mode 100644
+index 00000000..dd5a6ced
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/cond_logging.cpp
+@@ -0,0 +1,154 @@
++
++#include "includes.h"
++
++template<class T>
++std::string conditional_log(const bool flag, const T& what, spdlog::level::level_enum logger_level)
++{
++ std::ostringstream oss;
++ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
++
++ spdlog::logger oss_logger("oss", oss_sink);
++ oss_logger.set_level(logger_level);
++ oss_logger.set_pattern("%v");
++
++ switch (logger_level)
++ {
++ case spdlog::level::trace:
++ oss_logger.trace_if(flag, what);
++ break;
++ case spdlog::level::debug:
++ oss_logger.debug_if(flag, what);
++ break;
++ case spdlog::level::info:
++ oss_logger.info_if(flag, what);
++ break;
++ case spdlog::level::warn:
++ oss_logger.warn_if(flag, what);
++ break;
++ case spdlog::level::err:
++ oss_logger.error_if(flag, what);
++ break;
++ case spdlog::level::critical:
++ oss_logger.critical_if(flag, what);
++ break;
++ default:
++ break;
++ }
++
++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
++}
++
++template <typename Arg1, typename... Args>
++std::string conditional_log_varags(spdlog::level::level_enum logger_level, const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
++{
++ std::ostringstream oss;
++ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
++
++ spdlog::logger oss_logger("oss", oss_sink);
++ oss_logger.set_level(logger_level);
++ oss_logger.set_pattern("%v");
++
++ switch (logger_level)
++ {
++ case spdlog::level::trace:
++ oss_logger.trace_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::debug:
++ oss_logger.debug_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::info:
++ oss_logger.info_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::warn:
++ oss_logger.warn_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::err:
++ oss_logger.error_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::critical:
++ oss_logger.critical_if(flag, fmt, arg1, args...);
++ break;
++ default:
++ break;
++ }
++
++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
++}
++
++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
++
++template <typename Arg1, typename... Args>
++std::wstring conditional_log_varags(spdlog::level::level_enum logger_level, const bool flag, const wchar_t* fmt, const Arg1& arg1, const Args&... args)
++{
++ std::wstringstream oss;
++ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
++
++ spdlog::logger oss_logger("oss", oss_sink);
++ oss_logger.set_level(logger_level);
++ oss_logger.set_pattern("%v");
++
++ switch (logger_level)
++ {
++ case spdlog::level::trace:
++ oss_logger.trace_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::debug:
++ oss_logger.debug_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::info:
++ oss_logger.info_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::warn:
++ oss_logger.warn_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::err:
++ oss_logger.error_if(flag, fmt, arg1, args...);
++ break;
++ case spdlog::level::critical:
++ oss_logger.critical_if(flag, fmt, arg1, args...);
++ break;
++ default:
++ break;
++ }
++
++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
++}
++
++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
++
++TEST_CASE("conditional_trace_simple", "[conditional_trace_simple]")
++{
++ //const char
++ for (auto i = 0; i < 2; i++)
++ {
++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::trace) == ( i % 2 == 0 ? "Hello" : ""));
++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::debug) == (i % 2 == 0 ? "Hello" : ""));
++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::info) == (i % 2 == 0 ? "Hello" : ""));
++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::warn) == (i % 2 == 0 ? "Hello" : ""));
++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::err) == (i % 2 == 0 ? "Hello" : ""));
++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::critical) == (i % 2 == 0 ? "Hello" : ""));
++ }
++}
++
++TEST_CASE("conditional_trace_varargs", "[conditional_trace_varargs]")
++{
++ //const char
++ for (auto i = 0; i < 2; i++)
++ {
++ REQUIRE(conditional_log_varags(spdlog::level::trace, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
++ REQUIRE(conditional_log_varags(spdlog::level::debug, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
++ REQUIRE(conditional_log_varags(spdlog::level::info, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
++ REQUIRE(conditional_log_varags(spdlog::level::warn, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
++ REQUIRE(conditional_log_varags(spdlog::level::err, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
++ REQUIRE(conditional_log_varags(spdlog::level::critical, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : ""));
++
++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
++ REQUIRE(conditional_log_varags(spdlog::level::trace, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
++ REQUIRE(conditional_log_varags(spdlog::level::debug, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
++ REQUIRE(conditional_log_varags(spdlog::level::info, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
++ REQUIRE(conditional_log_varags(spdlog::level::warn, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
++ REQUIRE(conditional_log_varags(spdlog::level::err, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
++ REQUIRE(conditional_log_varags(spdlog::level::critical, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L""));
++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
++ }
++}
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/tests/errors.cpp b/external/spdlog-0.14.0/tests/errors.cpp
+new file mode 100644
+index 00000000..75de900a
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/errors.cpp
+@@ -0,0 +1,113 @@
++/*
++* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
++*/
++#include "includes.h"
++
++#include<iostream>
++
++
++
++
++class failing_sink: public spdlog::sinks::sink
++{
++ void log(const spdlog::details::log_msg& msg) override
++ {
++ throw std::runtime_error("some error happened during log");
++ }
++
++ void flush()
++ {}
++};
++
++TEST_CASE("default_error_handler", "[errors]]")
++{
++ prepare_logdir();
++ std::string filename = "logs/simple_log.txt";
++
++ auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
++ logger->set_pattern("%v");
++ logger->info("Test message {} {}", 1);
++ logger->info("Test message {}", 2);
++ logger->flush();
++
++ REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
++ REQUIRE(count_lines(filename) == 1);
++}
++
++
++
++
++struct custom_ex
++{};
++TEST_CASE("custom_error_handler", "[errors]]")
++{
++ prepare_logdir();
++ std::string filename = "logs/simple_log.txt";
++ auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
++ logger->flush_on(spdlog::level::info);
++ logger->set_error_handler([=](const std::string& msg)
++ {
++ throw custom_ex();
++ });
++ logger->info("Good message #1");
++ REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
++ logger->info("Good message #2");
++ REQUIRE(count_lines(filename) == 2);
++}
++
++TEST_CASE("default_error_handler2", "[errors]]")
++{
++
++ auto logger = spdlog::create<failing_sink>("failed_logger");
++ logger->set_error_handler([=](const std::string& msg)
++ {
++ throw custom_ex();
++ });
++ REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
++}
++
++TEST_CASE("async_error_handler", "[errors]]")
++{
++ prepare_logdir();
++ std::string err_msg("log failed with some msg");
++ spdlog::set_async_mode(128);
++ std::string filename = "logs/simple_async_log.txt";
++ {
++ auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
++ logger->set_error_handler([=](const std::string& msg)
++ {
++ std::ofstream ofs("logs/custom_err.txt");
++ if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt");
++ ofs << err_msg;
++ });
++ logger->info("Good message #1");
++ logger->info("Bad format msg {} {}", "xxx");
++ logger->info("Good message #2");
++ spdlog::drop("logger"); //force logger to drain the queue and shutdown
++ spdlog::set_sync_mode();
++ }
++ REQUIRE(count_lines(filename) == 2);
++ REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
++}
++
++// Make sure async error handler is executed
++TEST_CASE("async_error_handler2", "[errors]]")
++{
++ prepare_logdir();
++ std::string err_msg("This is async handler error message");
++ spdlog::set_async_mode(128);
++ {
++ auto logger = spdlog::create<failing_sink>("failed_logger");
++ logger->set_error_handler([=](const std::string& msg)
++ {
++ std::ofstream ofs("logs/custom_err2.txt");
++ if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
++ ofs << err_msg;
++ });
++ logger->info("Hello failure");
++ spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
++ spdlog::set_sync_mode();
++ }
++
++ REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
++}
+diff --git a/external/spdlog-0.14.0/tests/file_helper.cpp b/external/spdlog-0.14.0/tests/file_helper.cpp
+new file mode 100644
+index 00000000..9a4ad603
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/file_helper.cpp
+@@ -0,0 +1,78 @@
++/*
++* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
++*/
++#include "includes.h"
++
++using namespace spdlog::details;
++
++static const std::string target_filename = "logs/file_helper_test.txt";
++
++static void write_with_helper(file_helper &helper, size_t howmany)
++{
++ log_msg msg;
++ msg.formatted << std::string(howmany, '1');
++ helper.write(msg);
++ helper.flush();
++}
++
++
++TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
++{
++ prepare_logdir();
++
++ file_helper helper;
++ helper.open(target_filename);
++ REQUIRE(helper.filename() == target_filename);
++}
++
++
++
++TEST_CASE("file_helper_size", "[file_helper::size()]]")
++{
++ prepare_logdir();
++ size_t expected_size = 123;
++ {
++ file_helper helper;
++ helper.open(target_filename);
++ write_with_helper(helper, expected_size);
++ REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
++ }
++ REQUIRE(get_filesize(target_filename) == expected_size);
++}
++
++
++TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
++{
++ prepare_logdir();
++ REQUIRE(!file_helper::file_exists(target_filename));
++ file_helper helper;
++ helper.open(target_filename);
++ REQUIRE(file_helper::file_exists(target_filename));
++}
++
++TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
++{
++ prepare_logdir();
++ file_helper helper;
++ helper.open(target_filename);
++ write_with_helper(helper, 12);
++ REQUIRE(helper.size() == 12);
++ helper.reopen(true);
++ REQUIRE(helper.size() == 0);
++}
++
++TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
++{
++ prepare_logdir();
++ size_t expected_size = 14;
++ file_helper helper;
++ helper.open(target_filename);
++ write_with_helper(helper, expected_size);
++ REQUIRE(helper.size() == expected_size);
++ helper.reopen(false);
++ REQUIRE(helper.size() == expected_size);
++}
++
++
++
++
+diff --git a/external/spdlog-0.14.0/tests/file_log.cpp b/external/spdlog-0.14.0/tests/file_log.cpp
+new file mode 100644
+index 00000000..45f6e8c1
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/file_log.cpp
+@@ -0,0 +1,151 @@
++/*
++ * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
++ */
++#include "includes.h"
++
++
++TEST_CASE("simple_file_logger", "[simple_logger]]")
++{
++ prepare_logdir();
++ std::string filename = "logs/simple_log";
++
++ auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
++ logger->set_pattern("%v");
++
++
++ logger->info("Test message {}", 1);
++ logger->info("Test message {}", 2);
++ logger->flush();
++ REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
++ REQUIRE(count_lines(filename) == 2);
++}
++
++
++TEST_CASE("flush_on", "[flush_on]]")
++{
++ prepare_logdir();
++ std::string filename = "logs/simple_log";
++
++ auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
++ logger->set_pattern("%v");
++ logger->set_level(spdlog::level::trace);
++ logger->flush_on(spdlog::level::info);
++ logger->trace("Should not be flushed");
++ REQUIRE(count_lines(filename) == 0);
++
++ logger->info("Test message {}", 1);
++ logger->info("Test message {}", 2);
++ logger->flush();
++ REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
++ REQUIRE(count_lines(filename) == 3);
++}
++
++TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
++{
++ prepare_logdir();
++ std::string basename = "logs/rotating_log";
++ auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
++
++ for (int i = 0; i < 10; ++i)
++ logger->info("Test message {}", i);
++
++ logger->flush();
++ auto filename = basename;
++ REQUIRE(count_lines(filename) == 10);
++}
++
++
++TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
++{
++ prepare_logdir();
++ std::string basename = "logs/rotating_log";
++ auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1);
++ for (int i = 0; i < 10; ++i)
++ logger->info("Test message {}", i);
++
++ logger->flush();
++ auto filename = basename;
++ REQUIRE(count_lines(filename) == 10);
++ for (int i = 0; i < 1000; i++)
++ logger->info("Test message {}", i);
++
++ logger->flush();
++ REQUIRE(get_filesize(filename) <= 1024);
++ auto filename1 = basename + ".1";
++ REQUIRE(get_filesize(filename1) <= 1024);
++}
++
++
++TEST_CASE("daily_logger", "[daily_logger]]")
++{
++ prepare_logdir();
++ //calculate filename (time based)
++ std::string basename = "logs/daily_log";
++ std::tm tm = spdlog::details::os::localtime();
++ fmt::MemoryWriter w;
++ w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
++
++ auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
++ logger->flush_on(spdlog::level::info);
++ for (int i = 0; i < 10; ++i)
++ logger->info("Test message {}", i);
++
++ auto filename = w.str();
++ REQUIRE(count_lines(filename) == 10);
++}
++
++
++TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
++{
++ using sink_type = spdlog::sinks::daily_file_sink<
++ std::mutex,
++ spdlog::sinks::dateonly_daily_file_name_calculator>;
++
++ prepare_logdir();
++ //calculate filename (time based)
++ std::string basename = "logs/daily_dateonly";
++ std::tm tm = spdlog::details::os::localtime();
++ fmt::MemoryWriter w;
++ w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++
++ auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
++ for (int i = 0; i < 10; ++i)
++ logger->info("Test message {}", i);
++ logger->flush();
++ auto filename = w.str();
++ REQUIRE(count_lines(filename) == 10);
++}
++
++struct custom_daily_file_name_calculator
++{
++ static spdlog::filename_t calc_filename(const spdlog::filename_t& basename)
++ {
++ std::tm tm = spdlog::details::os::localtime();
++ fmt::MemoryWriter w;
++ w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++ return w.str();
++ }
++};
++
++TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
++{
++ using sink_type = spdlog::sinks::daily_file_sink<
++ std::mutex,
++ custom_daily_file_name_calculator>;
++
++ prepare_logdir();
++ //calculate filename (time based)
++ std::string basename = "logs/daily_dateonly";
++ std::tm tm = spdlog::details::os::localtime();
++ fmt::MemoryWriter w;
++ w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++
++ auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
++ for (int i = 0; i < 10; ++i)
++ logger->info("Test message {}", i);
++
++ logger->flush();
++ auto filename = w.str();
++ REQUIRE(count_lines(filename) == 10);
++}
++
+diff --git a/external/spdlog-0.14.0/tests/format.cpp b/external/spdlog-0.14.0/tests/format.cpp
+new file mode 100644
+index 00000000..adb8ba8b
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/format.cpp
+@@ -0,0 +1,56 @@
++
++#include "includes.h"
++
++template<class T>
++std::string log_info(const T& what, spdlog::level::level_enum logger_level = spdlog::level::info)
++{
++
++ std::ostringstream oss;
++ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
++
++ spdlog::logger oss_logger("oss", oss_sink);
++ oss_logger.set_level(logger_level);
++ oss_logger.set_pattern("%v");
++ oss_logger.info(what);
++
++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
++}
++
++
++
++
++
++
++TEST_CASE("basic_logging ", "[basic_logging]")
++{
++ //const char
++ REQUIRE(log_info("Hello") == "Hello");
++ REQUIRE(log_info("") == "");
++
++ //std::string
++ REQUIRE(log_info(std::string("Hello")) == "Hello");
++ REQUIRE(log_info(std::string()) == std::string());
++
++ //Numbers
++ REQUIRE(log_info(5) == "5");
++ REQUIRE(log_info(5.6) == "5.6");
++
++ //User defined class
++ //REQUIRE(log_info(some_logged_class("some_val")) == "some_val");
++}
++
++
++TEST_CASE("log_levels", "[log_levels]")
++{
++ REQUIRE(log_info("Hello", spdlog::level::err) == "");
++ REQUIRE(log_info("Hello", spdlog::level::critical) == "");
++ REQUIRE(log_info("Hello", spdlog::level::info) == "Hello");
++ REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello");
++ REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
++}
++
++
++
++
++
++
+diff --git a/external/spdlog-0.14.0/tests/includes.h b/external/spdlog-0.14.0/tests/includes.h
+new file mode 100644
+index 00000000..0590fc60
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/includes.h
+@@ -0,0 +1,16 @@
++#pragma once
++
++#include <cstdio>
++#include <fstream>
++#include <string>
++#include <ostream>
++#include <chrono>
++#include <exception>
++
++#include "catch.hpp"
++#include "utils.h"
++
++#include "../include/spdlog/spdlog.h"
++#include "../include/spdlog/sinks/null_sink.h"
++#include "../include/spdlog/sinks/ostream_sink.h"
++
+diff --git a/external/spdlog-0.14.0/tests/install_libcxx.sh b/external/spdlog-0.14.0/tests/install_libcxx.sh
+new file mode 100755
+index 00000000..cee97692
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/install_libcxx.sh
+@@ -0,0 +1,12 @@
++#!/bin/bash
++#
++# Install libc++ under travis
++
++svn --quiet co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx
++mkdir libcxx/build
++(cd libcxx/build && cmake .. -DLIBCXX_CXX_ABI=libstdc++ -DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/4.6;/usr/include/c++/4.6/x86_64-linux-gnu")
++make -C libcxx/build cxx -j2
++sudo cp libcxx/build/lib/libc++.so.1.0 /usr/lib/
++sudo cp -r libcxx/build/include/c++/v1 /usr/include/c++/v1/
++sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so
++sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so.1
+diff --git a/external/spdlog-0.14.0/tests/main.cpp b/external/spdlog-0.14.0/tests/main.cpp
+new file mode 100644
+index 00000000..063e8787
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/main.cpp
+@@ -0,0 +1,2 @@
++#define CATCH_CONFIG_MAIN
++#include "catch.hpp"
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/tests/registry.cpp b/external/spdlog-0.14.0/tests/registry.cpp
+new file mode 100644
+index 00000000..1936932a
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/registry.cpp
+@@ -0,0 +1,84 @@
++#include "includes.h"
++
++static const char *tested_logger_name = "null_logger";
++static const char *tested_logger_name2 = "null_logger2";
++
++TEST_CASE("register_drop", "[registry]")
++{
++ spdlog::drop_all();
++ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
++ REQUIRE(spdlog::get(tested_logger_name)!=nullptr);
++ //Throw if registring existing name
++ REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), spdlog::spdlog_ex);
++}
++
++
++TEST_CASE("explicit register" "[registry]")
++{
++ spdlog::drop_all();
++ auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
++ spdlog::register_logger(logger);
++ REQUIRE(spdlog::get(tested_logger_name) != nullptr);
++ //Throw if registring existing name
++ REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), spdlog::spdlog_ex);
++}
++
++TEST_CASE("apply_all" "[registry]")
++{
++ spdlog::drop_all();
++ auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
++ spdlog::register_logger(logger);
++ auto logger2 = std::make_shared<spdlog::logger>(tested_logger_name2, std::make_shared<spdlog::sinks::null_sink_st>());
++ spdlog::register_logger(logger2);
++
++ int counter = 0;
++ spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l)
++ {
++ counter++;
++ });
++ REQUIRE(counter == 2);
++
++ counter = 0;
++ spdlog::drop(tested_logger_name2);
++ spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l)
++ {
++ REQUIRE(l->name() == tested_logger_name);
++ counter++;
++ }
++ );
++ REQUIRE(counter == 1);
++}
++
++
++
++TEST_CASE("drop" "[registry]")
++{
++ spdlog::drop_all();
++ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
++ spdlog::drop(tested_logger_name);
++ REQUIRE_FALSE(spdlog::get(tested_logger_name));
++}
++
++TEST_CASE("drop_all" "[registry]")
++{
++ spdlog::drop_all();
++ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
++ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name2);
++ spdlog::drop_all();
++ REQUIRE_FALSE(spdlog::get(tested_logger_name));
++ REQUIRE_FALSE(spdlog::get(tested_logger_name));
++}
++
++
++TEST_CASE("drop non existing" "[registry]")
++{
++ spdlog::drop_all();
++ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
++ spdlog::drop("some_name");
++ REQUIRE_FALSE(spdlog::get("some_name"));
++ REQUIRE(spdlog::get(tested_logger_name));
++ spdlog::drop_all();
++}
++
++
++
+diff --git a/external/spdlog-0.14.0/tests/tests.sln b/external/spdlog-0.14.0/tests/tests.sln
+new file mode 100644
+index 00000000..d224d204
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/tests.sln
+@@ -0,0 +1,28 @@
++
++Microsoft Visual Studio Solution File, Format Version 12.00
++# Visual Studio 2015
++VisualStudioVersion = 14.0
++MinimumVisualStudioVersion = 10.0.40219.1
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcxproj", "{59A07559-5F38-4DD6-A7FA-DB4153690B42}"
++EndProject
++Global
++ GlobalSection(SolutionConfigurationPlatforms) = preSolution
++ Debug|Win32 = Debug|Win32
++ Debug|x64 = Debug|x64
++ Release|Win32 = Release|Win32
++ Release|x64 = Release|x64
++ EndGlobalSection
++ GlobalSection(ProjectConfigurationPlatforms) = postSolution
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.ActiveCfg = Debug|Win32
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.Build.0 = Debug|Win32
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.ActiveCfg = Debug|x64
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.Build.0 = Debug|x64
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.ActiveCfg = Release|Win32
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.Build.0 = Release|Win32
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.ActiveCfg = Release|x64
++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.Build.0 = Release|x64
++ EndGlobalSection
++ GlobalSection(SolutionProperties) = preSolution
++ HideSolutionNode = FALSE
++ EndGlobalSection
++EndGlobal
+diff --git a/external/spdlog-0.14.0/tests/tests.vcxproj b/external/spdlog-0.14.0/tests/tests.vcxproj
+new file mode 100644
+index 00000000..f5c854db
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/tests.vcxproj
+@@ -0,0 +1,145 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Debug|x64">
++ <Configuration>Debug</Configuration>
++ <Platform>x64</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|x64">
++ <Configuration>Release</Configuration>
++ <Platform>x64</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{59A07559-5F38-4DD6-A7FA-DB4153690B42}</ProjectGuid>
++ <RootNamespace>tests</RootNamespace>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>Application</ConfigurationType>
++ <UseDebugLibraries>true</UseDebugLibraries>
++ <PlatformToolset>v140</PlatformToolset>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
++ <ConfigurationType>Application</ConfigurationType>
++ <UseDebugLibraries>true</UseDebugLibraries>
++ <PlatformToolset>v140</PlatformToolset>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>Application</ConfigurationType>
++ <UseDebugLibraries>false</UseDebugLibraries>
++ <PlatformToolset>v140</PlatformToolset>
++ <WholeProgramOptimization>true</WholeProgramOptimization>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
++ <ConfigurationType>Application</ConfigurationType>
++ <UseDebugLibraries>false</UseDebugLibraries>
++ <PlatformToolset>v140</PlatformToolset>
++ <WholeProgramOptimization>true</WholeProgramOptimization>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup />
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>Disabled</Optimization>
++ <SDLCheck>true</SDLCheck>
++ <AdditionalIncludeDirectories>$(SolutionDir)\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <SubSystem>Console</SubSystem>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>Disabled</Optimization>
++ <SDLCheck>true</SDLCheck>
++ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <SubSystem>Console</SubSystem>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <WarningLevel>Level4</WarningLevel>
++ <Optimization>MaxSpeed</Optimization>
++ <FunctionLevelLinking>true</FunctionLevelLinking>
++ <IntrinsicFunctions>true</IntrinsicFunctions>
++ <SDLCheck>true</SDLCheck>
++ <AdditionalIncludeDirectories>$(SolutionDir)\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <EnableCOMDATFolding>true</EnableCOMDATFolding>
++ <OptimizeReferences>true</OptimizeReferences>
++ <SubSystem>Console</SubSystem>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
++ <ClCompile>
++ <WarningLevel>Level4</WarningLevel>
++ <Optimization>MaxSpeed</Optimization>
++ <FunctionLevelLinking>true</FunctionLevelLinking>
++ <IntrinsicFunctions>true</IntrinsicFunctions>
++ <SDLCheck>true</SDLCheck>
++ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <EnableCOMDATFolding>true</EnableCOMDATFolding>
++ <OptimizeReferences>true</OptimizeReferences>
++ <SubSystem>Console</SubSystem>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemGroup>
++ <ClCompile Include="cond_logging.cpp" />
++ <ClCompile Include="errors.cpp" />
++ <ClCompile Include="file_helper.cpp" />
++ <ClCompile Include="file_log.cpp" />
++ <ClCompile Include="format.cpp" />
++ <ClCompile Include="main.cpp" />
++ <ClCompile Include="registry.cpp" />
++ <ClCompile Include="utils.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="catch.hpp" />
++ <ClInclude Include="includes.h" />
++ <ClInclude Include="utils.h" />
++ </ItemGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/tests/tests.vcxproj.filters b/external/spdlog-0.14.0/tests/tests.vcxproj.filters
+new file mode 100644
+index 00000000..b5612d0c
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/tests.vcxproj.filters
+@@ -0,0 +1,54 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ <Filter Include="Header Files">
++ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
++ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
++ </Filter>
++ <Filter Include="Resource Files">
++ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
++ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="file_log.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="format.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="main.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="registry.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="file_helper.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="utils.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="errors.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="cond_logging.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="includes.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="catch.hpp">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="utils.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ </ItemGroup>
++</Project>
+\ No newline at end of file
+diff --git a/external/spdlog-0.14.0/tests/utils.cpp b/external/spdlog-0.14.0/tests/utils.cpp
+new file mode 100644
+index 00000000..e0785352
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/utils.cpp
+@@ -0,0 +1,48 @@
++#include "includes.h"
++
++
++void prepare_logdir()
++{
++ spdlog::drop_all();
++#ifdef _WIN32
++ system("if not exist logs mkdir logs");
++ system("del /F /Q logs\\*");
++#else
++ auto rv = system("mkdir -p logs");
++ rv = system("rm -f logs/*");
++ (void)rv;
++#endif
++}
++
++
++std::string file_contents(const std::string& filename)
++{
++ std::ifstream ifs(filename);
++ if (!ifs)
++ throw std::runtime_error("Failed open file ");
++ return std::string((std::istreambuf_iterator<char>(ifs)),
++ (std::istreambuf_iterator<char>()));
++
++}
++
++std::size_t count_lines(const std::string& filename)
++{
++ std::ifstream ifs(filename);
++ if (!ifs)
++ throw std::runtime_error("Failed open file ");
++
++ std::string line;
++ size_t counter = 0;
++ while(std::getline(ifs, line))
++ counter++;
++ return counter;
++}
++
++std::size_t get_filesize(const std::string& filename)
++{
++ std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary);
++ if (!ifs)
++ throw std::runtime_error("Failed open file ");
++
++ return static_cast<std::size_t>(ifs.tellg());
++}
+diff --git a/external/spdlog-0.14.0/tests/utils.h b/external/spdlog-0.14.0/tests/utils.h
+new file mode 100644
+index 00000000..1d9b6213
+--- /dev/null
++++ b/external/spdlog-0.14.0/tests/utils.h
+@@ -0,0 +1,15 @@
++#pragma once
++
++#include <string>
++#include<cstddef>
++
++std::size_t count_lines(const std::string& filename);
++
++void prepare_logdir();
++
++std::string file_contents(const std::string& filename);
++
++std::size_t count_lines(const std::string& filename);
++
++std::size_t get_filesize(const std::string& filename);
++
+--
+2.14.3
+