diff options
author | CoprDistGit <infra@openeuler.org> | 2023-04-06 14:02:10 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-04-06 14:02:10 +0000 |
commit | 1b5f52e22f377cd7fa2bffab2ae1cefdc1c43fe2 (patch) | |
tree | cd455860dfaed53912bf8e4a99a084b84b94ac8f /0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch | |
parent | aac450e024c595ebf20cee258c114444cabacaf0 (diff) |
automatic import of lizardfsopeneuler20.03
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.patch | 23784 |
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. [](https://travis-ci.org/gabime/spdlog) [](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() << "<";
++ break;
++ case '&':
++ stream() << "&";
++ break;
++ case '\"':
++ stream() << """;
++ 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 + |