pax_global_header00006660000000000000000000000064125677605360014533gustar00rootroot0000000000000052 comment=eade79f798244e08f8c703a7ee82301f9cc83671 spdlog-1.6/000077500000000000000000000000001256776053600126715ustar00rootroot00000000000000spdlog-1.6/.gitignore000066400000000000000000000005131256776053600146600ustar00rootroot00000000000000# Compiled Object files *.slo *.lo *.o *.obj # 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/utils.h !example/makefile !example/makefile.clang spdlog-1.6/.travis.yml000066400000000000000000000074451256776053600150140ustar00rootroot00000000000000# 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 clang-3.5: C++11/C++14, Buidd=Debug/Release, ASAN=On/Off - env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 ASAN=On LIBCXX=On os: linux addons: &clang35 apt: packages: - clang-3.5 - valgrind sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.5 - env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=On os: linux addons: *clang35 # 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="" - 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 spdlog-1.6/INSTALL000066400000000000000000000004661256776053600137300ustar00rootroot00000000000000spdlog 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 spdlog-1.6/LICENSE000066400000000000000000000033241256776053600137000ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ spdlog-1.6/README.md000066400000000000000000000133431256776053600141540ustar00rootroot00000000000000# spdlog Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog) ## Install Just copy the files to your build tree and use a C++11 compiler ## Platforms * Linux (gcc 4.8.1+, clang 3.5+) * Windows (visual studio 2013+, mingw with g++ 4.9.1+) * Mac OSX (clang 3.5+) ##Features * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Headers only. * No dependencies - just copy and use. * Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library. * ostream call style is supported too. * 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. * Multi/Single threaded loggers. * Various log targets: * Rotating log files. * Daily log files. * Console logging. * Linux syslog. * 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|glog |easylogging |spdlog| |-------|:-------:|:-----:|----------:|------:| |1| 4.169s |1.066s |0.975s |0.302s| |10| 16.029 |3.032s |2.857s |0.968s| |100| 15.008 |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 async logger |spdlog async mode| |:-------|:-----:|-------------------------:| |1| 1.850s |0.216s | |10| 0.943s |0.173s| |100| 0.959s |0.202s| ## Usage Example ```c++ #include #include "spdlog/spdlog.h" int main(int, char* []) { namespace spd = spdlog; try { //Create console, multithreaded logger auto console = spd::stdout_logger_mt("console"); console->info("Welcome to spdlog!") ; console->info("An info message example {}..", 1); console->info() << "Streams are supported too " << 1; //Formatting examples console->info("Easy padding in numbers like {:08d}", 12); console->info("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"); console->info("{:>30}", "right aligned"); console->info("{:^30}", "centered"); // // 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("Now it should.."); // // Create a file rotating logger with 5mb size max and 3 rotated files // auto file_logger = spd::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3); for(int i = 0; i < 10; ++i) file_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); // // Customize msg format for all messages // spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); file_logger->info("This is another message with custom format"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); // // Compile time debug or trace macros. // Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef 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.. // size_t q_size = 1048576; //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.txt"); async_file->info() << "This is async log.." << "Should be very fast!"; // // syslog example. linux only.. // #ifdef __linux__ 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. This is Linux only!"); #endif } catch (const spd::spdlog_ex& ex) { std::cout << "Log failed: " << ex.what() << std::endl; } } // Example of user defined class with operator<< class some_class {}; std::ostream& operator<<(std::ostream& os, const some_class& c) { return os << "some_class"; } void custom_class_example() { some_class c; spdlog::get("console")->info("custom class with operator<<: {}..", c); spdlog::get("console")->info() << "custom class with operator<<: " << c << ".."; } ``` ## Documentation Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. spdlog-1.6/astyle.sh000077500000000000000000000002011256776053600145220ustar00rootroot00000000000000#!/bin/bash find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -c -A1 spdlog-1.6/bench/000077500000000000000000000000001256776053600137505ustar00rootroot00000000000000spdlog-1.6/bench/Makefile000066400000000000000000000037541256776053600154210ustar00rootroot00000000000000CXX ?= g++ CXXFLAGS = -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 spdlog-1.6/bench/Makefile.mingw000066400000000000000000000040011256776053600165230ustar00rootroot00000000000000CXX ?= 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 spdlog-1.6/bench/boost-bench-mt.cpp000066400000000000000000000033451256776053600173020ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include 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 msg_counter {0}; vector 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; } spdlog-1.6/bench/boost-bench.cpp000066400000000000000000000023141256776053600166570ustar00rootroot00000000000000 #include #include #include #include #include #include #include #include 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; } spdlog-1.6/bench/easyl.conf000066400000000000000000000005731256776053600157410ustar00rootroot00000000000000* 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 spdlog-1.6/bench/easylogging-bench-mt.cpp000066400000000000000000000016661256776053600204700ustar00rootroot00000000000000#include #include #include #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 msg_counter {0}; vector 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; } spdlog-1.6/bench/easylogging-bench.cpp000066400000000000000000000006031256776053600200400ustar00rootroot00000000000000#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; } spdlog-1.6/bench/g2log-async.cpp000066400000000000000000000025351256776053600166060ustar00rootroot00000000000000#include #include #include #include #include #include "g2logworker.h" #include "g2log.h" using namespace std; template 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 msg_counter {0}; vector 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 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; } spdlog-1.6/bench/glog-bench-mt.cpp000066400000000000000000000015311256776053600170770ustar00rootroot00000000000000#include #include #include #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 msg_counter {0}; vector 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; } spdlog-1.6/bench/glog-bench.cpp000066400000000000000000000005071256776053600164630ustar00rootroot00000000000000 #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; } spdlog-1.6/bench/logs/000077500000000000000000000000001256776053600147145ustar00rootroot00000000000000spdlog-1.6/bench/logs/.gitignore000066400000000000000000000001071256776053600167020ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignore spdlog-1.6/bench/run_all.sh000077500000000000000000000042361256776053600157500ustar00rootroot00000000000000#~/bin/bash #execute each bench 3 times and print the timing exec 2>&1 #execute and time given exe 3 times bench_exe () { echo "**************** $1 ****************" for i in {1..3}; do time ./$1 $2; rm -f logs/* sleep 3 done; } #execute given async tests 3 times (timing is already builtin) bench_async () { echo "**************** $1 ****************" for i in {1..3}; do ./$1 $2; echo rm -f logs/* sleep 3 done; } echo "----------------------------------------------------------" echo "Single threaded benchmarks.. (1 thread, 1,000,000 lines)" echo "----------------------------------------------------------" for exe in boost-bench glog-bench easylogging-bench spdlog-bench; do bench_exe $exe 1 done; echo "----------------------------------------------------------" echo "Multi threaded benchmarks.. (10 threads, 1,000,000 lines)" echo "----------------------------------------------------------" for exe in boost-bench-mt glog-bench-mt easylogging-bench-mt spdlog-bench-mt; do bench_exe $exe 10 done; echo "----------------------------------------------------------" echo "Multi threaded benchmarks.. (100 threads, 1,000,000 lines)" echo "----------------------------------------------------------" for exe in boost-bench-mt glog-bench-mt easylogging-bench-mt spdlog-bench-mt; do bench_exe $exe 100 done; echo "---------------------------------------------------------------" echo "Async, single threaded benchmark.. (1 thread, 1,000,000 lines)" echo "---------------------------------------------------------------" for exe in spdlog-async g2log-async do bench_async $exe 1 done; echo "---------------------------------------------------------------" echo "Async, multi threaded benchmark.. (10 threads, 1,000,000 lines)" echo "---------------------------------------------------------------" for exe in spdlog-async g2log-async do bench_async $exe 10 done; echo "---------------------------------------------------------------" echo "Async, multi threaded benchmark.. (100 threads, 1,000,000 lines)" echo "---------------------------------------------------------------" for exe in spdlog-async g2log-async do bench_async $exe 100 done; spdlog-1.6/bench/spdlog-async.cpp000066400000000000000000000026631256776053600170660ustar00rootroot00000000000000 #include #include #include #include #include #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("file_logger", "logs/spd-bench-async.txt", false); logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); std::atomic msg_counter {0}; vector 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 #" << counter << ": This is some text for your pleasure"; } })); } for(auto &t:threads) { t.join(); }; duration 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; } spdlog-1.6/bench/spdlog-bench-mt.cpp000066400000000000000000000017061256776053600174430ustar00rootroot00000000000000#include #include #include #include "spdlog/spdlog.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; namespace spd = spdlog; auto logger = spdlog::create("file_logger", "logs/spd-bench-mt.txt", false); logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); std::atomic msg_counter {0}; vector 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 #" << counter << ": This is some text for your pleasure"; } })); } for(auto &t:threads) { t.join(); }; return 0; } spdlog-1.6/bench/spdlog-bench.cpp000066400000000000000000000007631256776053600170270ustar00rootroot00000000000000 #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("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 #" << i << ": This is some text for your pleasure"; return 0; } spdlog-1.6/example/000077500000000000000000000000001256776053600143245ustar00rootroot00000000000000spdlog-1.6/example/Makefile000066400000000000000000000013331256776053600157640ustar00rootroot00000000000000CXX ?= g++ CXXFLAGS = CXX_FLAGS = -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -I../include CXX_RELEASE_FLAGS = -O3 -march=native CXX_DEBUG_FLAGS= -g all: example bench debug: example-debug bench-debug example: example.cpp $(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) bench: bench.cpp $(CXX) bench.cpp -o bench $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) example-debug: example.cpp $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) bench-debug: bench.cpp $(CXX) bench.cpp -o bench-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) clean: rm -f *.o logs/*.txt example example-debug bench bench-debug rebuild: clean all rebuild-debug: clean debug spdlog-1.6/example/Makefile.clang000066400000000000000000000013301256776053600170440ustar00rootroot00000000000000CXX ?= 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 spdlog-1.6/example/Makefile.mingw000066400000000000000000000013201256776053600171000ustar00rootroot00000000000000CXX ?= 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 spdlog-1.6/example/bench.cpp000066400000000000000000000137461256776053600161220ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ // // bench.cpp : spdlog benchmarks // #include #include #include #include #include #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 log); void bench_mt(int howmany, std::shared_ptr log, int thread_count); int main(int argc, char* argv[]) { int queue_size = 1048576; int howmany = 1000000; int threads = 10; bool auto_flush = false; 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, auto flush=" << auto_flush << endl; cout << "*******************************************************************************\n"; auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files, auto_flush); bench(howmany, rotating_st); auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st", auto_flush); bench(howmany, daily_st); bench(howmany, spdlog::create("null_st")); cout << "\n*******************************************************************************\n"; cout << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << endl; cout << "*******************************************************************************\n"; auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files, auto_flush); bench_mt(howmany, rotating_mt, threads); auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt", auto_flush); bench_mt(howmany, daily_mt, threads); bench(howmany, spdlog::create("null_mt")); cout << "\n*******************************************************************************\n"; cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << 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", auto_flush); bench_mt(howmany, as, threads); spdlog::drop("as"); } } catch (std::exception &ex) { std::cerr << "Error: " << ex.what() << std::endl; perror("Last error"); } return 0; } void bench(int howmany, std::shared_ptr 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> (delta).count(); cout << format(int(howmany / delta_d)) << "/sec" << endl; } void bench_mt(int howmany, std::shared_ptr log, int thread_count) { cout << log->name() << "...\t\t" << flush; std::atomic msg_counter {0}; vector 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> (delta).count(); cout << format(int(howmany / delta_d)) << "/sec" << endl; } spdlog-1.6/example/example.cpp000066400000000000000000000124411256776053600164650ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ // // spdlog usage example // #include #include "spdlog/spdlog.h" int main(int, char*[]) { namespace spd = spdlog; try { //Create console, multithreaded logger auto console = spd::stdout_logger_mt("console"); console->info("Welcome to spdlog!"); console->info("An info message example {}..", 1); console->info() << "Streams are supported too " << 1; //Formatting examples console->info("Easy padding in numbers like {:08d}", 12); console->info("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"); console->info("{:>30}", "right aligned"); console->info("{:^30}", "centered"); // // 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("Now it should.."); // // Create a file rotating logger with 5mb size max and 3 rotated files // auto file_logger = spd::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3); for (int i = 0; i < 10; ++i) file_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); // // Customize msg format for all messages // spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); file_logger->info("This is another message with custom format"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); // // Compile time debug or trace macros. // Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef 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.. // size_t q_size = 1048576; //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.txt"); async_file->info() << "This is async log.." << "Should be very fast!"; spdlog::drop_all(); //Close all loggers // // syslog example. linux only.. // #ifdef __linux__ 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. This is Linux only!"); #endif } catch (const spd::spdlog_ex& ex) { std::cout << "Log failed: " << ex.what() << std::endl; } } // Example of user defined class with operator<< class some_class {}; std::ostream& operator<<(std::ostream& os, const some_class&) { return os << "some_class"; } void custom_class_example() { some_class c; spdlog::get("console")->info("custom class with operator<<: {}..", c); spdlog::get("console")->info() << "custom class with operator<<: " << c << ".."; } spdlog-1.6/example/logs/000077500000000000000000000000001256776053600152705ustar00rootroot00000000000000spdlog-1.6/example/logs/.gitignore000066400000000000000000000001071256776053600172560ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignore spdlog-1.6/example/utils.h000066400000000000000000000043011256776053600156330ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include #include namespace utils { template 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(); } } spdlog-1.6/include/000077500000000000000000000000001256776053600143145ustar00rootroot00000000000000spdlog-1.6/include/spdlog/000077500000000000000000000000001256776053600156045ustar00rootroot00000000000000spdlog-1.6/include/spdlog/async_logger.h000066400000000000000000000100741256776053600204330ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #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 // Upong destruction, logs all remaining messages in the queue before destructing.. #include #include #include "common.h" #include "logger.h" #include "spdlog.h" namespace spdlog { namespace details { class async_log_helper; } class async_logger :public logger { public: template 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& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); 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& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); 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& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); protected: void _log_msg(details::log_msg& msg) override; void _set_formatter(spdlog::formatter_ptr msg_formatter) override; void _set_pattern(const std::string& pattern) override; private: std::unique_ptr _async_log_helper; }; } #include "./details/async_logger_impl.h" spdlog-1.6/include/spdlog/common.h000066400000000000000000000065431256776053600172550ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include #include #include //visual studio does not support noexcept yet #ifndef _MSC_VER #define SPDLOG_NOEXCEPT noexcept #else #define SPDLOG_NOEXCEPT throw() #endif namespace spdlog { class formatter; namespace sinks { class sink; } // Common types across the lib 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; //Log level enum namespace level { typedef enum { trace = 0, debug = 1, info = 2, notice = 3, warn = 4, err = 5, critical = 6, alert = 7, emerg = 8, off = 9 } level_enum; static const char* level_names[] { "trace", "debug", "info", "notice", "warning", "error", "critical", "alert", "emerg", "off"}; static const char* short_level_names[] { "T", "D", "I", "N", "W", "E", "C", "A", "M", "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 }; // // Log exception // class spdlog_ex : public std::exception { public: spdlog_ex(const std::string& msg) :_msg(msg) {} const char* what() const SPDLOG_NOEXCEPT override { return _msg.c_str(); } private: std::string _msg; }; } //spdlogspdlog-1.6/include/spdlog/details/000077500000000000000000000000001256776053600172315ustar00rootroot00000000000000spdlog-1.6/include/spdlog/details/async_log_helper.h000066400000000000000000000245431256776053600227270ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ // 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. // // If the back thread throws during logging, a spdlog::spdlog_ex exception // will be thrown in client's thread when tries to log the next message #pragma once #include #include #include #include #include "../common.h" #include "../sinks/sink.h" #include "./mpmc_bounded_q.h" #include "./log_msg.h" #include "./format.h" #include "os.h" namespace spdlog { namespace details { class async_log_helper { // Async msg to move to/from the queue // Movable only. should never be copied 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() = 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)), txt(std::move(other.txt)) {} 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); return *this; } // never copy or assign. should only be moved.. async_msg(const async_msg&) = delete; async_msg& operator=(async_msg& other) = delete; // construct from log_msg async_msg(const details::log_msg& m) : logger_name(m.logger_name), level(m.level), time(m.time), thread_id(m.thread_id), txt(m.raw.data(), m.raw.size()) {} // copy into log_msg void fill_log_msg(log_msg &msg) { msg.clear(); msg.logger_name = logger_name; msg.level = level; msg.time = time; msg.thread_id = thread_id; msg.raw << txt; } }; public: using item_type = async_msg; using q_type = details::mpmc_bounded_queue; using clock = std::chrono::steady_clock; async_log_helper(formatter_ptr formatter, const std::vector& sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); void log(const details::log_msg& msg); // stop logging and join the back thread ~async_log_helper(); void set_formatter(formatter_ptr); private: formatter_ptr _formatter; std::vector> _sinks; // queue of messages to log q_type _q; // last exception thrown from the worker thread std::shared_ptr _last_workerthread_ex; // overflow policy const async_overflow_policy _overflow_policy; // worker thread warmup callback - one can set thread priority, affinity, etc const std::function _worker_warmup_cb; // auto periodic sink flush parameter const std::chrono::milliseconds _flush_interval_ms; // worker thread std::thread _worker_thread; // throw last worker thread exception or if worker thread is not active void throw_if_bad_worker(); // worker thread main loop void worker_loop(); // pop next message from the queue and process it // return true if a message was available (queue was not empty), will set the last_pop to the pop time 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 immediatly 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); }; } } /////////////////////////////////////////////////////////////////////////////// // async_sink class implementation /////////////////////////////////////////////////////////////////////////////// inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, const std::vector& sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms): _formatter(formatter), _sinks(sinks), _q(queue_size), _overflow_policy(overflow_policy), _worker_warmup_cb(worker_warmup_cb), _flush_interval_ms(flush_interval_ms), _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 { log(log_msg(level::off)); _worker_thread.join(); } catch (...) //Dont crash if thread not joinable {} } //Try to push and block until succeeded inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) { throw_if_bad_worker(); async_msg new_msg(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))); } } inline void spdlog::details::async_log_helper::worker_loop() { try { if (_worker_warmup_cb) _worker_warmup_cb(); auto last_pop = details::os::now(); auto last_flush = last_pop; while(process_next_msg(last_pop, last_flush)); } catch (const std::exception& ex) { _last_workerthread_ex = std::make_shared(std::string("async_logger worker thread exception: ") + ex.what()); } catch (...) { _last_workerthread_ex = std::make_shared("async_logger worker thread exception"); } } // process next message in the queue // return true if this thread should still be active (no msg with level::off 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; log_msg incoming_log_msg; if (_q.dequeue(incoming_async_msg)) { last_pop = details::os::now(); if(incoming_async_msg.level == level::off) return false; incoming_async_msg.fill_log_msg(incoming_log_msg); _formatter->format(incoming_log_msg); for (auto &s : _sinks) s->log(incoming_log_msg); } else //empty queue { auto now = details::os::now(); handle_flush_interval(now, last_flush); sleep_or_yield(now, last_pop); } return true; } inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) { if (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms) { for (auto &s : _sinks) s->flush(); now = last_flush = details::os::now(); } } inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) { _formatter = msg_formatter; } // sleep,yield or return immediatly using 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 std::chrono::milliseconds; using namespace std::this_thread; auto time_since_op = now - last_op_time; // spin upto 1 ms if (time_since_op <= milliseconds(1)) return; // yield upto 10ms if (time_since_op <= milliseconds(10)) return yield(); // sleep for half of duration since last op if (time_since_op <= milliseconds(100)) return sleep_for(time_since_op / 2); return sleep_for(milliseconds(100)); } // throw if the worker thread threw an exception or not active inline void spdlog::details::async_log_helper::throw_if_bad_worker() { if (_last_workerthread_ex) { auto ex = std::move(_last_workerthread_ex); throw *ex; } } spdlog-1.6/include/spdlog/details/async_logger_impl.h000066400000000000000000000072501256776053600231030ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include "./async_log_helper.h" // // Async Logger implementation // Use single async_sink (queue) to perform the logging in a worker thread // template 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& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) : logger(logger_name, begin, end), _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms)) { } inline spdlog::async_logger::async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) : async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} 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& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) : async_logger(logger_name, { single_sink }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} 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) { _formatter = std::make_shared(pattern); _async_log_helper->set_formatter(_formatter); } inline void spdlog::async_logger::_log_msg(details::log_msg& msg) { _async_log_helper->log(msg); } spdlog-1.6/include/spdlog/details/file_helper.h000066400000000000000000000107161256776053600216650ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #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) // Can be set to auto flush on every line // Throw spdlog_ex exception on errors #include #include #include #include "os.h" namespace spdlog { namespace details { class file_helper { public: const int open_tries = 5; const int open_interval = 10; explicit file_helper(bool force_flush) : _fd(nullptr), _force_flush(force_flush) {} file_helper(const file_helper&) = delete; file_helper& operator=(const file_helper&) = delete; ~file_helper() { close(); } void open(const std::string& fname, bool truncate = false) { close(); const char* mode = truncate ? "wb" : "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 " + fname + " for writing"); } 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 size = msg.formatted.size(); auto data = msg.formatted.data(); if (std::fwrite(data, 1, size, _fd) != size) throw spdlog_ex("Failed writing to file " + _filename); if (_force_flush) std::fflush(_fd); } long size() { if (!_fd) throw spdlog_ex("Cannot use size() on closed file " + _filename); auto pos = ftell(_fd); if (fseek(_fd, 0, SEEK_END) != 0) throw spdlog_ex("fseek failed on file " + _filename); auto size = ftell(_fd); if(fseek(_fd, pos, SEEK_SET) !=0) throw spdlog_ex("fseek failed on file " + _filename); if (size == -1) throw spdlog_ex("ftell failed on file " + _filename); return size; } const std::string& filename() const { return _filename; } static bool file_exists(const std::string& name) { FILE* file; if (!os::fopen_s(&file, name.c_str(), "r")) { fclose(file); return true; } else { return false; } } private: FILE* _fd; std::string _filename; bool _force_flush; }; } } spdlog-1.6/include/spdlog/details/format.cc000066400000000000000000001144221256776053600210340ustar00rootroot00000000000000/* Formatting library for C++ Copyright (c) 2012 - 2015, 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 #include #include #include #include #include #if defined(_WIN32) && defined(__MINGW32__) # include #endif #if FMT_USE_WINDOWS_H # if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) # include # else # define NOMINMAX # include # undef NOMINMAX # endif #endif using fmt::internal::Arg; // Check if exceptions are disabled. #if __GNUC__ && !__EXCEPTIONS # define FMT_EXCEPTIONS 0 #endif #if _MSC_VER && !_HAS_EXCEPTIONS # define FMT_EXCEPTIONS 0 #endif #ifndef FMT_EXCEPTIONS # define FMT_EXCEPTIONS 1 #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 #ifndef FMT_THROW # if FMT_EXCEPTIONS # define FMT_THROW(x) throw x # else # define FMT_THROW(x) assert(false) # endif #endif #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline #else # define FMT_FUNC #endif #if _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 { 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) // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct IntChecker { template static bool fits_in_int(T value) { unsigned max = INT_MAX; return value <= max; } static bool fits_in_int(bool) { return true; } }; template <> struct IntChecker { template static bool fits_in_int(T value) { return value >= INT_MIN && value <= INT_MAX; } }; const char RESET_COLOR[] = "\x1b[0m"; typedef void (*FormatFunc)(fmt::Writer &, int, fmt::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(fmt::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(fmt::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() { strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r. return handle(strerror_r(error_code_, buffer_, buffer_size_)); } }; return StrError(error_code, buffer, buffer_size).run(); } void format_error_code(fmt::Writer &out, int error_code, fmt::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 "; fmt::internal::IntTraits::MainType ec_value = error_code; // 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; error_code_size += fmt::internal::count_digits(ec_value); if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) out << message << SEP; out << ERROR_STR << error_code; assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); } void report_error(FormatFunc func, int error_code, fmt::StringRef message) FMT_NOEXCEPT { fmt::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); } // IsZeroInt::visit(arg) returns true iff arg is a zero integer. class IsZeroInt : public fmt::internal::ArgVisitor { public: template bool visit_any_int(T value) { return value == 0; } }; // 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 int 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 = UINT_MAX; break; } value = new_value; } while ('0' <= *s && *s <= '9'); if (value > INT_MAX) FMT_THROW(fmt::FormatError("number is too big")); return value; } template inline bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } 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 void check_sign(const Char *&s, const Arg &arg) { char sign = static_cast(*s); require_numeric_argument(arg, sign); if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { FMT_THROW(fmt::FormatError(fmt::format( "format specifier '{}' requires signed argument", sign))); } ++s; } // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. class WidthHandler : public fmt::internal::ArgVisitor { private: fmt::FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); public: explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} void report_unhandled_arg() { FMT_THROW(fmt::FormatError("width is not integer")); } template unsigned visit_any_int(T value) { typedef typename fmt::internal::IntTraits::MainType UnsignedType; UnsignedType width = value; if (fmt::internal::is_negative(value)) { spec_.align_ = fmt::ALIGN_LEFT; width = 0 - width; } if (width > INT_MAX) FMT_THROW(fmt::FormatError("number is too big")); return static_cast(width); } }; class PrecisionHandler : public fmt::internal::ArgVisitor { public: void report_unhandled_arg() { FMT_THROW(fmt::FormatError("precision is not integer")); } template int visit_any_int(T value) { if (!IntChecker::is_signed>::fits_in_int(value)) FMT_THROW(fmt::FormatError("number is too big")); return static_cast(value); } }; // Converts an integer argument to an integral type T for printf. template class ArgConverter : public fmt::internal::ArgVisitor, void> { private: fmt::internal::Arg &arg_; wchar_t type_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); public: ArgConverter(fmt::internal::Arg &arg, wchar_t type) : arg_(arg), type_(type) {} template void visit_any_int(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; using fmt::internal::Arg; if (sizeof(T) <= sizeof(int)) { // Extra casts are used to silence warnings. if (is_signed) { arg_.type = Arg::INT; arg_.int_value = static_cast(static_cast(value)); } else { arg_.type = Arg::UINT; arg_.uint_value = static_cast( static_cast::Type>(value)); } } else { if (is_signed) { arg_.type = Arg::LONG_LONG; arg_.long_long_value = static_cast::Type>(value); } else { arg_.type = Arg::ULONG_LONG; arg_.ulong_long_value = static_cast::Type>(value); } } } }; // Converts an integer argument to char for printf. class CharConverter : public fmt::internal::ArgVisitor { private: fmt::internal::Arg &arg_; FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); public: explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} template void visit_any_int(T value) { arg_.type = Arg::CHAR; arg_.int_value = static_cast(value); } }; } // namespace namespace internal { template class BasicArgFormatter : public ArgVisitor { private: BasicWriter &writer_; FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicArgFormatter); protected: BasicWriter &writer() { return writer_; } const FormatSpec &spec() const { return spec_; } public: BasicArgFormatter(BasicWriter &w, FormatSpec &s) : writer_(w), spec_(s) {} template void visit_any_int(T value) { writer_.write_int(value, spec_); } template void visit_any_double(T value) { writer_.write_double(value, spec_); } void visit_bool(bool value) { if (spec_.type_) { writer_.write_int(value, spec_); return; } const char *str_value = value ? "true" : "false"; Arg::StringValue str = { str_value, strlen(str_value) }; writer_.write_str(str, spec_); } 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::CharPtr CharPtr; Char fill = internal::CharTraits::cast(spec_.fill()); CharPtr out = CharPtr(); if (spec_.width_ > 1) { out = writer_.grow_buffer(spec_.width_); if (spec_.align_ == ALIGN_RIGHT) { std::fill_n(out, spec_.width_ - 1, fill); out += spec_.width_ - 1; } else if (spec_.align_ == ALIGN_CENTER) { out = writer_.fill_padding(out, spec_.width_, 1, fill); } else { std::fill_n(out + 1, spec_.width_ - 1, fill); } } else { out = writer_.grow_buffer(1); } *out = internal::CharTraits::cast(value); } void visit_string(Arg::StringValue value) { writer_.write_str(value, spec_); } using ArgVisitor::visit_wstring; void visit_wstring(Arg::StringValue value) { writer_.write_str(value, spec_); } void visit_pointer(const void *value) { if (spec_.type_ && spec_.type_ != 'p') report_unknown_type(spec_.type_, "pointer"); spec_.flags_ = HASH_FLAG; spec_.type_ = 'x'; writer_.write_int(reinterpret_cast(value), spec_); } }; // An argument formatter. template class ArgFormatter : public BasicArgFormatter, Char> { private: BasicFormatter &formatter_; const Char *format_; public: ArgFormatter(BasicFormatter &f, FormatSpec &s, const Char *fmt) : BasicArgFormatter, Char>(f.writer(), s), formatter_(f), format_(fmt) {} void visit_custom(Arg::CustomValue c) { c.format(&formatter_, c.value, &format_); } }; template class PrintfArgFormatter : public BasicArgFormatter, Char> { public: PrintfArgFormatter(BasicWriter &w, FormatSpec &s) : BasicArgFormatter, Char>(w, s) {} void visit_char(int value) { const FormatSpec &fmt_spec = this->spec(); BasicWriter &w = this->writer(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') w.write_int(value, fmt_spec); typedef typename BasicWriter::CharPtr CharPtr; CharPtr out = CharPtr(); if (fmt_spec.width_ > 1) { Char fill = ' '; out = w.grow_buffer(fmt_spec.width_); if (fmt_spec.align_ != ALIGN_LEFT) { std::fill_n(out, fmt_spec.width_ - 1, fill); out += fmt_spec.width_ - 1; } else { std::fill_n(out + 1, fmt_spec.width_ - 1, fill); } } else { out = w.grow_buffer(1); } *out = static_cast(value); } }; } // namespace internal } // namespace fmt FMT_FUNC void fmt::SystemError::init( int err_code, CStringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; internal::format_system_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } template int fmt::internal::CharTraits::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 int fmt::internal::CharTraits::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 const char fmt::internal::BasicData::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 const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { 0, FMT_POWERS_OF_10(1) }; template const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), // Multiply several constants instead of using a single long long constant // to avoid warnings about C++98 not supporting long long. fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 }; FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { (void)type; if (std::isprint(static_cast(code))) { FMT_THROW(fmt::FormatError( fmt::format("unknown format code '{}' for {}", code, type))); } FMT_THROW(fmt::FormatError( fmt::format("unknown format code '\\x{:02x}' for {}", static_cast(code), type))); } #if FMT_USE_WINDOWS_H FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::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(s.size()); int length = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 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 fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::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 fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; int s_size = static_cast(s.size()); int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0); if (length == 0) return GetLastError(); buffer_.resize(length + 1); length = WideCharToMultiByte( CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0); if (length == 0) return GetLastError(); buffer_[length] = 0; return 0; } FMT_FUNC void fmt::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 fmt::internal::format_windows_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT { class String { private: LPWSTR str_; public: String() : str_() {} ~String() { LocalFree(str_); } LPWSTR *ptr() { return &str_; } LPCWSTR c_str() const { return str_; } }; FMT_TRY { String system_message; if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(system_message.ptr()), 0, 0)) { UTF16ToUTF8 utf8_message; if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { out << message << ": " << utf8_message; return; } } } FMT_CATCH(...) {} format_error_code(out, error_code, message); } #endif // FMT_USE_WINDOWS_H FMT_FUNC void fmt::internal::format_system_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT { FMT_TRY { MemoryBuffer buffer; buffer.resize(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(...) {} format_error_code(out, error_code, message); } template void fmt::internal::ArgMap::init(const ArgList &args) { if (!map_.empty()) return; typedef internal::NamedArg NamedArg; const NamedArg *named_arg = 0; 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(args.values_[i].pointer); map_.insert(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(args.args_[i].pointer); map_.insert(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(args.args_[i].pointer); map_.insert(Pair(named_arg->name, *named_arg)); break; default: /*nothing*/; } } } template void fmt::internal::FixedBuffer::grow(std::size_t) { FMT_THROW(std::runtime_error("buffer overflow")); } template template void fmt::BasicWriter::write_str( const Arg::StringValue &s, const FormatSpec &spec) { // Check if StrChar is convertible to Char. internal::CharTraits::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")); if (*str_value) str_size = std::char_traits::length(str_value); } std::size_t precision = spec.precision_; if (spec.precision_ >= 0 && precision < str_size) str_size = spec.precision_; write_str(str_value, str_size, spec); } template inline Arg fmt::BasicFormatter::get_arg( BasicStringRef arg_name, const char *&error) { if (check_no_auto_index(error)) { map_.init(args()); const Arg *arg = map_.find(arg_name); if (arg) return *arg; error = "argument not found"; } return Arg(); } template inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { const char *error = 0; Arg arg = *s < '0' || *s > '9' ? next_arg(error) : get_arg(parse_nonnegative_int(s), error); if (error) { FMT_THROW(FormatError( *s != '}' && *s != ':' ? "invalid format string" : error)); } return arg; } template inline Arg fmt::BasicFormatter::parse_arg_name(const Char *&s) { assert(is_name_start(*s)); const Char *start = s; Char c; do { c = *++s; } while (is_name_start(c) || ('0' <= c && c <= '9')); const char *error = 0; Arg arg = get_arg(fmt::BasicStringRef(start, s - start), error); if (error) FMT_THROW(fmt::FormatError(error)); return arg; } FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( unsigned arg_index, const char *&error) { Arg arg = args_[arg_index]; switch (arg.type) { case Arg::NONE: error = "argument index out of range"; break; case Arg::NAMED_ARG: arg = *static_cast(arg.pointer); default: /*nothing*/; } return arg; } inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { if (next_arg_index_ >= 0) return do_get_arg(next_arg_index_++, error); error = "cannot switch from manual to automatic argument indexing"; return Arg(); } inline bool fmt::internal::FormatterBase::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; } inline Arg fmt::internal::FormatterBase::get_arg( unsigned arg_index, const char *&error) { return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); } template void fmt::internal::PrintfFormatter::parse_flags( FormatSpec &spec, const Char *&s) { for (;;) { switch (*s++) { case '-': spec.align_ = ALIGN_LEFT; break; case '+': spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '0': spec.fill_ = '0'; break; case ' ': spec.flags_ |= SIGN_FLAG; break; case '#': spec.flags_ |= HASH_FLAG; break; default: --s; return; } } } template Arg fmt::internal::PrintfFormatter::get_arg( const Char *s, unsigned arg_index) { (void)s; const char *error = 0; Arg arg = arg_index == UINT_MAX ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); if (error) FMT_THROW(FormatError(!*s ? "invalid format string" : error)); return arg; } template unsigned fmt::internal::PrintfFormatter::parse_header( const Char *&s, FormatSpec &spec) { unsigned arg_index = UINT_MAX; Char c = *s; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). unsigned value = parse_nonnegative_int(s); if (*s == '$') { // value is an argument index ++s; arg_index = value; } else { if (c == '0') spec.fill_ = '0'; if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. spec.width_ = value; return arg_index; } } } parse_flags(spec, s); // Parse width. if (*s >= '0' && *s <= '9') { spec.width_ = parse_nonnegative_int(s); } else if (*s == '*') { ++s; spec.width_ = WidthHandler(spec).visit(get_arg(s)); } return arg_index; } template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicCStringRef format_str) { const Char *start = format_str.c_str(); const Char *s = start; while (*s) { Char c = *s++; if (c != '%') continue; if (*s == c) { write(writer, start, s); start = ++s; continue; } write(writer, start, s - 1); FormatSpec spec; spec.align_ = ALIGN_RIGHT; // Parse argument index, flags and width. unsigned arg_index = parse_header(s, spec); // Parse precision. if (*s == '.') { ++s; if ('0' <= *s && *s <= '9') { spec.precision_ = parse_nonnegative_int(s); } else if (*s == '*') { ++s; spec.precision_ = PrecisionHandler().visit(get_arg(s)); } } Arg arg = get_arg(s, arg_index); if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) spec.flags_ &= ~HASH_FLAG; if (spec.fill_ == '0') { if (arg.type <= Arg::LAST_NUMERIC_TYPE) spec.align_ = ALIGN_NUMERIC; else spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. } // Parse length and convert the argument to the required type. switch (*s++) { case 'h': if (*s == 'h') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'l': if (*s == 'l') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'j': ArgConverter(arg, *s).visit(arg); break; case 'z': ArgConverter(arg, *s).visit(arg); break; case 't': ArgConverter(arg, *s).visit(arg); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; default: --s; ArgConverter(arg, *s).visit(arg); } // Parse type. if (!*s) FMT_THROW(FormatError("invalid format string")); spec.type_ = static_cast(*s++); if (arg.type <= Arg::LAST_INTEGER_TYPE) { // Normalize type. switch (spec.type_) { case 'i': case 'u': spec.type_ = 'd'; break; case 'c': // TODO: handle wchar_t CharConverter(arg).visit(arg); break; } } start = s; // Format argument. internal::PrintfArgFormatter(writer, spec).visit(arg); } write(writer, start, s); } template const Char *fmt::BasicFormatter::format( const Char *&format_str, const Arg &arg) { const Char *s = format_str; FormatSpec 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_ = parse_nonnegative_int(s); } else if (*s == '{') { ++s; Arg width_arg = 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 > INT_MAX) FMT_THROW(FormatError("number is too big")); spec.width_ = static_cast(value); } // Parse precision. if (*s == '.') { ++s; spec.precision_ = 0; if ('0' <= *s && *s <= '9') { spec.precision_ = parse_nonnegative_int(s); } else if (*s == '{') { ++s; Arg precision_arg = 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 > INT_MAX) FMT_THROW(FormatError("number is too big")); spec.precision_ = static_cast(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(*s++); } if (*s++ != '}') FMT_THROW(FormatError("missing '}' in format string")); // Format argument. internal::ArgFormatter(*this, spec, s - 1).visit(arg); return s; } template void fmt::BasicFormatter::format(BasicCStringRef 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); Arg arg = is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); start = s = format(s, arg); } write(writer_, start, s); } FMT_FUNC void fmt::report_system_error( int error_code, fmt::StringRef message) FMT_NOEXCEPT { report_error(internal::format_system_error, error_code, message); } #if FMT_USE_WINDOWS_H FMT_FUNC void fmt::report_windows_error( int error_code, fmt::StringRef message) FMT_NOEXCEPT { report_error(internal::format_windows_error, error_code, message); } #endif FMT_FUNC void fmt::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 fmt::print(CStringRef format_str, ArgList args) { print(stdout, format_str, args); } FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) { MemoryWriter w; w.write(format_str, args); os.write(w.data(), w.size()); } FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) { char escape[] = "\x1b[30m"; escape[3] = '0' + static_cast(c); std::fputs(escape, stdout); print(format, args); std::fputs(RESET_COLOR, stdout); } FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); std::size_t size = w.size(); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); } #ifndef FMT_HEADER_ONLY template struct fmt::internal::BasicData; // Explicit instantiations for char. template void fmt::internal::FixedBuffer::grow(std::size_t); template const char *fmt::BasicFormatter::format( const char *&format_str, const fmt::internal::Arg &arg); template void fmt::BasicFormatter::format(CStringRef format); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, CStringRef format); template int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); template int fmt::internal::CharTraits::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 fmt::internal::FixedBuffer::grow(std::size_t); template const wchar_t *fmt::BasicFormatter::format( const wchar_t *&format_str, const fmt::internal::Arg &arg); template void fmt::BasicFormatter::format( BasicCStringRef format); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, WCStringRef format); template int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); template int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, long double value); #endif // FMT_HEADER_ONLY #if _MSC_VER # pragma warning(pop) #endifspdlog-1.6/include/spdlog/details/format.h000066400000000000000000002612241256776053600207010ustar00rootroot00000000000000/* Formatting library for C++ Copyright (c) 2012 - 2015, 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_ #define FMT_HEADER_ONLY #include #include #include #include // for std::ptrdiff_t #include #include #include #include #include #include #include #if _SECURE_SCL # include #endif #ifdef _MSC_VER # include // _BitScanReverse, _BitScanReverse64 namespace fmt { namespace internal { # pragma intrinsic(_BitScanReverse) inline uint32_t clz(uint32_t x) { unsigned long r = 0; _BitScanReverse(&r, x); 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(x >> 32))) return 63 - (r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif return 63 - r; } # define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) } } #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" # endif # if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ # define FMT_HAS_GXX_CXX11 1 # endif #else # define FMT_GCC_EXTENSION #endif #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdocumentation" #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) || _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) || _MSC_VER >= 1600) # endif #endif #if FMT_USE_RVALUE_REFERENCES # include // for std::move #endif // Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). #ifndef FMT_NOEXCEPT # if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) # define FMT_NOEXCEPT noexcept # else # define FMT_NOEXCEPT throw() # endif #endif // A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for a class #if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _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_ASSERT # define FMT_ASSERT(condition, message) assert((condition) && message) #endif 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 class BasicWriter; typedef BasicWriter Writer; typedef BasicWriter WWriter; template class BasicFormatter; template void format(BasicFormatter &f, const Char *&format_str, const T &value); /** \rst A string reference. It can be constructed from a C string or ``std::string``. You can use one of the following typedefs for common character types: +------------+-------------------------+ | Type | Definition | +============+=========================+ | StringRef | BasicStringRef | +------------+-------------------------+ | WStringRef | BasicStringRef | +------------+-------------------------+ This class is most useful as a parameter type to allow passing different types of strings to a function, for example:: template std::string format(StringRef format_str, const Args & ... args); format("{}", 42); format(std::string("{}"), 42); \endrst */ template 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::length``. \endrst */ BasicStringRef(const Char *s) : data_(s), size_(std::char_traits::length(s)) {} /** \rst Constructs a string reference from an ``std::string`` object. \endrst */ BasicStringRef(const std::basic_string &s) : data_(s.c_str()), size_(s.size()) {} /** \rst Converts a string reference to an ``std::string`` object. \endrst */ std::basic_string to_string() const { return std::basic_string(data_, size_); } /** Returns the pointer to a C string. */ const Char *data() const { return data_; } /** Returns the string size. */ std::size_t size() const { return size_; } friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { return lhs.data_ == rhs.data_; } friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.data_ != rhs.data_; } friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { return std::lexicographical_compare( lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_); } }; typedef BasicStringRef StringRef; typedef BasicStringRef WStringRef; /** \rst A reference to a null terminated string. It can be constructed from a C string or ``std::string``. You can use one of the following typedefs for common character types: +-------------+--------------------------+ | Type | Definition | +=============+==========================+ | CStringRef | BasicCStringRef | +-------------+--------------------------+ | WCStringRef | BasicCStringRef | +-------------+--------------------------+ This class is most useful as a parameter type to allow passing different types of strings to a function, for example:: template std::string format(CStringRef format_str, const Args & ... args); format("{}", 42); format(std::string("{}"), 42); \endrst */ template 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 an ``std::string`` object. \endrst */ BasicCStringRef(const std::basic_string &s) : data_(s.c_str()) {} /** Returns the pointer to a C string. */ const Char *c_str() const { return data_; } }; typedef BasicCStringRef CStringRef; typedef BasicCStringRef 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()) {} }; namespace internal { // The number of characters to store in the MemoryBuffer object itself // to avoid dynamic memory allocation. enum { INLINE_BUFFER_SIZE = 500 }; #if _SECURE_SCL // Use checked iterator to avoid warnings on MSVC. template inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { return stdext::checked_array_iterator(ptr, size); } #else template 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 class Buffer { private: FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); protected: T *ptr_; std::size_t size_; std::size_t capacity_; Buffer(T *ptr = 0, 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 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 template void Buffer::append(const U *begin, const U *end) { std::ptrdiff_t num_elements = end - begin; if (size_ + num_elements > capacity_) grow(size_ + num_elements); std::copy(begin, end, internal::make_ptr(ptr_, capacity_) + size_); size_ += num_elements; } namespace internal { // A memory buffer for POD types with the first SIZE elements stored in // the object itself. template > class MemoryBuffer : private Allocator, public Buffer { private: T data_[SIZE]; // Free memory allocated by the buffer. void free() { if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); } protected: void grow(std::size_t size); public: explicit MemoryBuffer(const Allocator &alloc = Allocator()) : Allocator(alloc), Buffer(data_, SIZE) {} ~MemoryBuffer() { free(); } #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::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 freeing. other.ptr_ = other.data_; } } public: MemoryBuffer(MemoryBuffer &&other) { move(other); } MemoryBuffer &operator=(MemoryBuffer &&other) { assert(this != &other); free(); move(other); return *this; } #endif // Returns a copy of the allocator associated with this buffer. Allocator get_allocator() const { return *this; } }; template void MemoryBuffer::grow(std::size_t size) { std::size_t new_capacity = (std::max)(size, this->capacity_ + this->capacity_ / 2); T *new_ptr = this->allocate(new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. std::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_) this->deallocate(old_ptr, old_capacity); } // A fixed-size buffer. template class FixedBuffer : public fmt::Buffer { public: FixedBuffer(Char *array, std::size_t size) : fmt::Buffer(array, size) {} protected: void grow(std::size_t size); }; #ifndef _MSC_VER // Portable version of signbit. inline int getsign(double x) { // When compiled in C++11 mode signbit is no longer a macro but a function // defined in namespace std and the macro is undefined. # ifdef signbit return signbit(x); # else return std::signbit(x); # endif } // Portable version of isinf. # ifdef isinf inline int isinfinity(double x) { return isinf(x); } inline int isinfinity(long double x) { return isinf(x); } # else inline int isinfinity(double x) { return std::isinf(x); } inline int isinfinity(long double x) { return std::isinf(x); } # endif #else inline int getsign(double value) { if (value < 0) return 1; if (value == value) return 0; int dec = 0, sign = 0; char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); return sign; } inline int isinfinity(double x) { return !_finite(x); } inline int isinfinity(long double x) { return !_finite(static_cast(x)); } #endif template class BasicCharTraits { public: #if _SECURE_SCL typedef stdext::checked_array_iterator CharPtr; #else typedef Char *CharPtr; #endif static Char cast(wchar_t value) { return static_cast(value); } }; template class CharTraits; template <> class CharTraits : public BasicCharTraits { 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 static int format_float(char *buffer, std::size_t size, const char *format, unsigned width, int precision, T value); }; template <> class CharTraits : public BasicCharTraits { public: static wchar_t convert(char value) { return value; } static wchar_t convert(wchar_t value) { return value; } template static int format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value); }; // Checks if a number is negative - used to avoid warnings. template struct SignChecker { template static bool is_negative(T value) { return value < 0; } }; template <> struct SignChecker { template 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 inline bool is_negative(T value) { return SignChecker::is_signed>::is_negative(value); } // Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. template struct TypeSelector { typedef uint32_t Type; }; template <> struct TypeSelector { typedef uint64_t Type; }; template struct IntTraits { // Smallest of uint32_t and uint64_t that is large enough to represent // all values of T. typedef typename TypeSelector::digits <= 32>::Type MainType; }; // MakeUnsigned::Type gives an unsigned type corresponding to integer type T. template struct MakeUnsigned { typedef T Type; }; #define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ template <> \ struct MakeUnsigned { 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); void report_unknown_type(char code, const char *type); // Static data is placed in this class template to allow header-only // configuration. template struct BasicData { static const uint32_t POWERS_OF_10_32[]; static const uint64_t POWERS_OF_10_64[]; static const char DIGITS[]; }; typedef BasicData<> Data; #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 #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. unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; return 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) { uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; return t - (n < Data::POWERS_OF_10_32[t]) + 1; } #endif // Formats a decimal unsigned integer value writing into buffer. template inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { 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 = (value % 100) * 2; value /= 100; *--buffer = Data::DIGITS[index + 1]; *--buffer = Data::DIGITS[index]; } if (value < 10) { *--buffer = static_cast('0' + value); return; } unsigned index = static_cast(value * 2); *--buffer = Data::DIGITS[index + 1]; *--buffer = Data::DIGITS[index]; } #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 buffer_; public: 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 buffer_; public: UTF16ToUTF8() {} 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. int convert(WStringRef s); }; void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; #endif void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; // A formatting argument value. struct Value { template 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 string; StringValue sstring; StringValue ustring; StringValue 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 POD type to allow storage in // internal::MemoryBuffer. struct Arg : Value { Type type; }; template struct NamedArg; template struct Null {}; // A helper class template to enable or disable overloads taking wide // characters and strings in MakeValue. template struct WCharHelper { typedef Null Supported; typedef T Unsupported; }; template struct WCharHelper { typedef T Supported; typedef Null Unsupported; }; template class IsConvertibleToInt { private: typedef char yes[1]; typedef char no[2]; static const T &get(); static yes &convert(fmt::ULongLong); static no &convert(...); public: enum { value = (sizeof(convert(get())) == sizeof(yes)) }; }; #define FMT_CONVERTIBLE_TO_INT(Type) \ template <> \ class IsConvertibleToInt { \ public: \ enum { value = 1 }; \ } // Silence warnings about convering float to int. FMT_CONVERTIBLE_TO_INT(float); FMT_CONVERTIBLE_TO_INT(double); FMT_CONVERTIBLE_TO_INT(long double); template struct EnableIf {}; template struct EnableIf { typedef T type; }; template struct Conditional { typedef T type; }; template struct Conditional { typedef F type; }; // A helper function to suppress bogus "conditional expression is constant" // warnings. inline bool check(bool value) { return value; } // Makes an Arg object from any type. template class MakeValue : public Arg { 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 MakeValue(const T *value); template 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"). MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::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 static void format_custom_arg( void *formatter, const void *arg, void *format_str_ptr) { format(*static_cast*>(formatter), *static_cast(format_str_ptr), *static_cast(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 (check(sizeof(long) == sizeof(int))) int_value = static_cast(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 (check(sizeof(unsigned long) == sizeof(unsigned))) uint_value = static_cast(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, CHAR) FMT_MAKE_VALUE(unsigned char, int_value, CHAR) FMT_MAKE_VALUE(char, int_value, CHAR) MakeValue(typename WCharHelper::Supported value) { int_value = value; } static uint64_t type(wchar_t) { return Arg::CHAR; } #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(const signed char *, sstring.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::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 MakeValue(const T &value, typename EnableIf::value, int>::type = 0) { custom.value = &value; custom.format = &format_custom_arg; } template MakeValue(const T &value, typename EnableIf::value, int>::type = 0) { int_value = value; } template static uint64_t type(const T &) { return IsConvertibleToInt::value ? Arg::INT : Arg::CUSTOM; } // Additional template param `Char_` is needed here because make_type always // uses MakeValue. template MakeValue(const NamedArg &value) { pointer = &value; } template static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } }; template struct NamedArg : Arg { BasicStringRef name; template NamedArg(BasicStringRef argname, const T &value) : name(argname), Arg(MakeValue(value)) { type = static_cast(MakeValue::type(value)); } }; #define FMT_DISPATCH(call) static_cast(this)->call // An argument visitor. // To use ArgVisitor define a subclass that implements some or all of the // visit methods with the same signatures as the methods in ArgVisitor, // for example, visit_int(int). // Specify the subclass name as the Impl template parameter. Then calling // 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 visit_double(double) method of a subclass will be called. // If the subclass doesn't contain a method with this signature, then // a corresponding method of ArgVisitor will be called. // // Example: // class MyArgVisitor : public ArgVisitor { // public: // void visit_int(int value) { print("{}", value); } // void visit_double(double value) { print("{}", value ); } // }; // // ArgVisitor uses the curiously recurring template pattern: // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern template class ArgVisitor { public: void report_unhandled_arg() {} Result visit_unhandled_arg() { FMT_DISPATCH(report_unhandled_arg()); return Result(); } Result visit_int(int value) { return FMT_DISPATCH(visit_any_int(value)); } Result visit_long_long(LongLong value) { return FMT_DISPATCH(visit_any_int(value)); } Result visit_uint(unsigned value) { return FMT_DISPATCH(visit_any_int(value)); } Result visit_ulong_long(ULongLong value) { return FMT_DISPATCH(visit_any_int(value)); } Result visit_bool(bool value) { return FMT_DISPATCH(visit_any_int(value)); } Result visit_char(int value) { return FMT_DISPATCH(visit_any_int(value)); } template Result visit_any_int(T) { return FMT_DISPATCH(visit_unhandled_arg()); } Result visit_double(double value) { return FMT_DISPATCH(visit_any_double(value)); } Result visit_long_double(long double value) { return FMT_DISPATCH(visit_any_double(value)); } template Result visit_any_double(T) { return FMT_DISPATCH(visit_unhandled_arg()); } Result visit_string(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } Result visit_wstring(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } Result visit_pointer(const void *) { return FMT_DISPATCH(visit_unhandled_arg()); } Result visit_custom(Arg::CustomValue) { return FMT_DISPATCH(visit_unhandled_arg()); } Result visit(const Arg &arg) { switch (arg.type) { default: FMT_ASSERT(false, "invalid argument type"); return Result(); 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: { Arg::StringValue str = arg.string; str.size = 0; return FMT_DISPATCH(visit_string(str)); } 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)); } } }; class RuntimeError : public std::runtime_error { protected: RuntimeError() : std::runtime_error("") {} }; template class BasicArgFormatter; template class PrintfArgFormatter; template 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 { unsigned shift = index * 4; uint64_t mask = 0xf; return static_cast( (types_ & (mask << shift)) >> shift); } template 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) {} /** 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]; } }; struct FormatSpec; namespace internal { template class ArgMap { private: typedef std::map, internal::Arg> MapType; typedef typename MapType::value_type Pair; MapType map_; public: void init(const ArgList &args); const internal::Arg* find(const fmt::BasicStringRef &name) const { typename MapType::const_iterator it = map_.find(name); return it != map_.end() ? &it->second : 0; } }; class FormatterBase { private: ArgList args_; int next_arg_index_; // Returns the argument with specified index. 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); // Checks if manual indexing is used and returns the argument with // specified index. Arg get_arg(unsigned arg_index, const char *&error); bool check_no_auto_index(const char *&error); template void write(BasicWriter &w, const Char *start, const Char *end) { if (start != end) w << BasicStringRef(start, end - start); } }; // A printf formatter. template class PrintfFormatter : private FormatterBase { private: void parse_flags(FormatSpec &spec, const Char *&s); // Returns the argument with specified index or, if arg_index is equal // to the maximum unsigned value, the next argument. Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits::max)()); // Parses argument index, flags and width and returns the argument index. unsigned parse_header(const Char *&s, FormatSpec &spec); public: explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {} void format(BasicWriter &writer, BasicCStringRef format_str); }; } // namespace internal // A formatter. template class BasicFormatter : private internal::FormatterBase { private: BasicWriter &writer_; internal::ArgMap map_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); using FormatterBase::get_arg; // Checks if manual indexing is used and returns the argument with // specified name. internal::Arg get_arg(BasicStringRef 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: BasicFormatter(const ArgList &args, BasicWriter &w) : FormatterBase(args), writer_(w) {} BasicWriter &writer() { return writer_; } void format(BasicCStringRef format_str); const Char *format(const Char *&format_str, const internal::Arg &arg); }; 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 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 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 struct AlignTypeSpec : AlignSpec { AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} bool flag(unsigned) const { return false; } char type() 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_; } }; // An integer format specifier. template , 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 class StrFormatSpec : public AlignSpec { private: const Char *str_; public: template StrFormatSpec(const Char *str, unsigned width, FillChar fill) : AlignSpec(width, fill), str_(str) { internal::CharTraits::convert(FillChar()); } const Char *str() const { return str_; } }; /** Returns an integer format specifier to format the value in base 2. */ IntFormatSpec > bin(int value); /** Returns an integer format specifier to format the value in base 8. */ IntFormatSpec > 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 > hex(int value); /** Returns an integer formatter format specifier to format in base 16 using upper-case letters for the digits above 9. */ IntFormatSpec > 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 IntFormatSpec, Char> pad( int value, unsigned width, Char fill = ' '); #define FMT_DEFINE_INT_FORMATTERS(TYPE) \ inline IntFormatSpec > bin(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'b'>()); \ } \ \ inline IntFormatSpec > oct(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'o'>()); \ } \ \ inline IntFormatSpec > hex(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'x'>()); \ } \ \ inline IntFormatSpec > hexu(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'X'>()); \ } \ \ template \ inline IntFormatSpec > pad( \ IntFormatSpec > f, unsigned width) { \ return IntFormatSpec >( \ f.value(), AlignTypeSpec(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 \ inline IntFormatSpec, Char> pad( \ IntFormatSpec, Char> f, \ unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ f.value(), AlignTypeSpec(width, fill)); \ } \ \ inline IntFormatSpec > pad( \ TYPE value, unsigned width) { \ return IntFormatSpec >( \ value, AlignTypeSpec<0>(width, ' ')); \ } \ \ template \ inline IntFormatSpec, Char> pad( \ TYPE value, unsigned width, Char fill) { \ return IntFormatSpec, 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 inline StrFormatSpec pad( const Char *str, unsigned width, Char fill = ' ') { return StrFormatSpec(str, width, fill); } inline StrFormatSpec pad( const wchar_t *str, unsigned width, char fill = ' ') { return StrFormatSpec(str, width, fill); } // 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 inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } template struct ArgArray { // Computes the argument array size by adding 1 to N, which is the number of // arguments, if N is zero, because array of zero size is invalid, or if N // is greater than ArgList::MAX_PACKED_ARGS to accommodate for an extra // argument that marks the end of the list. enum { SIZE = N + (N == 0 || N >= ArgList::MAX_PACKED_ARGS ? 1 : 0) }; typedef typename Conditional< (N < ArgList::MAX_PACKED_ARGS), Value, Arg>::type Type[SIZE]; }; #if FMT_USE_VARIADIC_TEMPLATES template inline uint64_t make_type(const Arg &first, const Args & ... tail) { return make_type(first) | (make_type(tail...) << 4); } inline void do_set_types(Arg *) {} template inline void do_set_types(Arg *args, const T &arg, const Args & ... tail) { args->type = static_cast(MakeValue::type(arg)); do_set_types(args + 1, tail...); } template inline void set_types(Arg *array, const Args & ... args) { if (check(sizeof...(Args) > ArgList::MAX_PACKED_ARGS)) do_set_types(array, args...); array[sizeof...(Args)].type = Arg::NONE; } template inline void set_types(Value *, const Args & ...) { // Do nothing as types are passed separately from values. } template inline void store_args(Value *) {} template inline void store_args(Arg *args, const T &arg, const Args & ... tail) { // Assign only the Value subobject of Arg and don't overwrite type (if any) // that is assigned by set_types. Value &value = *args; value = MakeValue(arg); store_args(args + 1, tail...); } template ArgList make_arg_list(typename ArgArray::Type array, const Args & ... args) { if (check(sizeof...(Args) >= ArgList::MAX_PACKED_ARGS)) set_types(array, args...); store_args(array, args...); return ArgList(make_type(args...), array); } #else struct ArgType { uint64_t type; ArgType() : type(0) {} template 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_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) # define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) #if FMT_USE_VARIADIC_TEMPLATES // Defines a variadic function returning void. # define FMT_VARIADIC_VOID(func, arg_type) \ template \ void func(arg_type arg0, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ func(arg0, fmt::internal::make_arg_list(array, args...)); \ } // Defines a variadic constructor. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ template \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ func(arg0, arg1, fmt::internal::make_arg_list(array, args...)); \ } #else # define FMT_MAKE_REF(n) fmt::internal::MakeValue(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 \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::ArgArray::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 \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::ArgArray::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: 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 the description of the form .. parsed-literal:: **: ** where ** is the formatted message and ** 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. **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_VARIADIC_CTOR(SystemError, init, int, CStringRef) int error_code() const { return error_code_; } }; /** \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 | +---------+----------------------+ | WWriter | BasicWriter | +---------+----------------------+ \endrst */ template class BasicWriter { private: // Output buffer. Buffer &buffer_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); typedef typename internal::CharTraits::CharPtr CharPtr; #if _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 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 void write_decimal(Int value) { typename internal::IntTraits::MainType abs_value = 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::copy(prefix, prefix + prefix_size, p); return p + size - 1; } template CharPtr prepare_int_buffer(unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size); // Formats an integer. template void write_int(T value, Spec spec); // Formats a floating-point number (double or long double). template void write_double(T value, const FormatSpec &spec); // Writes a formatted string. template CharPtr write_str( const StrChar *s, std::size_t size, const AlignSpec &spec); template void write_str( const internal::Arg::StringValue &str, const FormatSpec &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::Unsupported); void operator<<( typename internal::WCharHelper::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 void append_float_length(Char *&, T) {} template friend class internal::BasicArgFormatter; friend class internal::PrintfArgFormatter; protected: /** Constructs a ``BasicWriter`` object. */ explicit BasicWriter(Buffer &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 str() const { return std::basic_string(&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 format, ArgList args) { BasicFormatter(args, *this).format(format); } FMT_VARIADIC_VOID(write, BasicCStringRef) BasicWriter &operator<<(int value) { write_decimal(value); return *this; } BasicWriter &operator<<(unsigned value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(long value) { write_decimal(value); return *this; } BasicWriter &operator<<(unsigned long value) { return *this << IntFormatSpec(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(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::Supported value) { buffer_.push_back(value); return *this; } /** \rst Writes *value* to the stream. \endrst */ BasicWriter &operator<<(fmt::BasicStringRef value) { const Char *str = value.data(); buffer_.append(str, str + value.size()); return *this; } BasicWriter &operator<<( typename internal::WCharHelper::Supported value) { const char *str = value.data(); buffer_.append(str, str + value.size()); return *this; } template BasicWriter &operator<<(IntFormatSpec spec) { internal::CharTraits::convert(FillChar()); write_int(spec.value(), spec); return *this; } template BasicWriter &operator<<(const StrFormatSpec &spec) { const StrChar *s = spec.str(); write_str(s, std::char_traits::length(s), spec); return *this; } void clear() FMT_NOEXCEPT { buffer_.clear(); } }; template template typename BasicWriter::CharPtr BasicWriter::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::cast(spec.fill()); if (spec.align() == ALIGN_RIGHT) { std::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::fill_n(out + size, spec.width() - size, fill); } } else { out = grow_buffer(size); } std::copy(s, s + size, out); return out; } template typename BasicWriter::CharPtr BasicWriter::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::cast(fill); std::fill_n(buffer, left_padding, fill_char); buffer += left_padding; CharPtr content = buffer; std::fill_n(buffer + content_size, padding - left_padding, fill_char); return content; } template template typename BasicWriter::CharPtr BasicWriter::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::cast(spec.fill()); if (spec.precision() > static_cast(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 + 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::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::fill(p, p + fill_size, fill); } return result; } unsigned size = prefix_size + num_digits; if (width <= size) { CharPtr p = grow_buffer(size); std::copy(prefix, prefix + prefix_size, p); return p + size - 1; } CharPtr p = grow_buffer(width); CharPtr end = p + width; if (align == ALIGN_LEFT) { std::copy(prefix, prefix + prefix_size, p); p += size; std::fill(p, end, fill); } else if (align == ALIGN_CENTER) { p = fill_padding(p, width, size, fill); std::copy(prefix, prefix + prefix_size, p); p += size; } else { if (align == ALIGN_NUMERIC) { if (prefix_size != 0) { p = std::copy(prefix, prefix + prefix_size, p); size -= prefix_size; } } else { std::copy(prefix, prefix + prefix_size, end - size); } std::fill(p, end - size, fill); p = end; } return p - 1; } template template void BasicWriter::write_int(T value, Spec spec) { unsigned prefix_size = 0; typedef typename internal::IntTraits::MainType UnsignedType; UnsignedType abs_value = 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 - num_digits; internal::format_decimal(get(p), abs_value, num_digits); break; } case 'x': case 'X': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = spec.type(); } 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(); } 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-- = '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-- = '0' + (n & 7); } while ((n >>= 3) != 0); break; } default: internal::report_unknown_type( spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); break; } } template template void BasicWriter::write_double( T value, const FormatSpec &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': #ifdef _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 getsign instead of value < 0 because the latter is always // false for NaN. if (internal::getsign(static_cast(value))) { sign = '-'; value = -value; } else if (spec.flag(SIGN_FLAG)) { sign = spec.flag(PLUS_FLAG) ? '+' : ' '; } if (value != 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::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() + (std::max)(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::cast(spec.fill()); for (;;) { std::size_t buffer_size = buffer_.capacity() - offset; #if _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 Char *start = &buffer_[offset]; int n = internal::CharTraits::format_float( start, buffer_size, format, width_for_sprintf, spec.precision(), value); if (n >= 0 && offset + n < buffer_.capacity()) { 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() > static_cast(n)) { width = spec.width(); CharPtr p = grow_buffer(width); std::copy(p, p + n, p + (width - n) / 2); fill_padding(p, spec.width(), n, fill); return; } if (spec.fill() != ' ' || sign) { while (*start == ' ') *start++ = fill; if (sign) *(start - 1) = sign; } grow_buffer(n); return; } // If n is negative we ask to increase the capacity by at least 1, // but as std::vector, the buffer grows exponentially. buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); } } /** \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> | +---------------+-----------------------------------------------------+ | WMemoryWriter | BasicMemoryWriter> | +---------------+-----------------------------------------------------+ **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 > class BasicMemoryWriter : public BasicWriter { private: internal::MemoryBuffer buffer_; public: explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) : BasicWriter(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(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 MemoryWriter; typedef BasicMemoryWriter 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 | +--------------+---------------------------+ | WArrayWriter | BasicArrayWriter | +--------------+---------------------------+ \endrst */ template class BasicArrayWriter : public BasicWriter { private: internal::FixedBuffer buffer_; public: /** \rst Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the given size. \endrst */ BasicArrayWriter(Char *array, std::size_t size) : BasicWriter(buffer_), buffer_(array, size) {} /** \rst Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the size known at compile time. \endrst */ template explicit BasicArrayWriter(Char (&array)[SIZE]) : BasicWriter(buffer_), buffer_(array, SIZE) {} }; typedef BasicArrayWriter ArrayWriter; typedef BasicArrayWriter WArrayWriter; // Formats a value. template void format(BasicFormatter &f, const Char *&format_str, const T &value) { std::basic_ostringstream os; os << value; std::basic_string str = os.str(); internal::Arg arg = internal::MakeValue(str); arg.type = static_cast( internal::MakeValue::type(str)); format_str = f.format(format_str, arg); } // Reports a system error without throwing an exception. // Can be used to report errors from destructors. void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; #if FMT_USE_WINDOWS_H /** A Windows error. */ class WindowsError : public SystemError { private: 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:: **: ** where ** is the formatted message and ** 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. 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: PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; */ 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 */ 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 */ void print(CStringRef format_str, ArgList args); /** \rst Prints formatted data to the stream *os*. **Example**:: print(cerr, "Don't {}!", "panic"); \endrst */ void print(std::ostream &os, CStringRef format_str, ArgList args); template void printf(BasicWriter &w, BasicCStringRef format, ArgList args) { internal::PrintfFormatter(args).format(w, format); } /** \rst Formats arguments and returns the result as a string. **Example**:: std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ inline std::string sprintf(CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); return w.str(); } /** \rst Prints formatted data to the file *f*. **Example**:: fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ int fprintf(std::FILE *f, CStringRef format, ArgList args); /** \rst Prints formatted data to ``stdout``. **Example**:: fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ inline int printf(CStringRef format, ArgList args) { return fprintf(stdout, format, 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::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 = (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('0' + value); return buffer_end; } unsigned index = static_cast(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(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 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 inline void format_decimal(char *&buffer, T value) { typename internal::IntTraits::MainType abs_value = value; if (internal::is_negative(value)) { *buffer++ = '-'; abs_value = 0 - abs_value; } if (abs_value < 100) { if (abs_value < 10) { *buffer++ = static_cast('0' + abs_value); return; } unsigned index = static_cast(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 inline internal::NamedArg arg(StringRef name, const T &arg) { return internal::NamedArg(name, arg); } template inline internal::NamedArg arg(WStringRef name, const T &arg) { return internal::NamedArg(name, arg); } // The following two functions are deleted intentionally to disable // nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. template void arg(StringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; template void arg(WStringRef, const internal::NamedArg&) 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_CONCAT(a, b) a##b #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 \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ fmt::internal::make_arg_list(array, args...)); \ } #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 \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ FMT_GEN(n, FMT_MAKE_ARG)) { \ fmt::internal::ArgArray::Type arr = {FMT_GEN(n, FMT_MAKE_REF_##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 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, std::ostream &, CStringRef) FMT_VARIADIC(void, print_colored, Color, CStringRef) FMT_VARIADIC(std::string, sprintf, CStringRef) FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) } // Restore warnings. #if FMT_GCC_VERSION >= 406 # pragma GCC diagnostic pop #endif #ifdef __clang__ # pragma clang diagnostic pop #endif #ifdef FMT_HEADER_ONLY # include "format.cc" #endif #endif // FMT_FORMAT_H_spdlog-1.6/include/spdlog/details/line_logger.h000066400000000000000000000131431256776053600216720ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include "../common.h" #include "../logger.h" // Line logger class - aggregates operator<< calls to fast ostream // and logs upon destruction namespace spdlog { namespace details { class line_logger { public: line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled): _callback_logger(callback_logger), _log_msg(msg_level), _enabled(enabled) {} // No copy intended. Only move line_logger(const line_logger& other) = delete; line_logger& operator=(const line_logger&) = delete; line_logger& operator=(line_logger&&) = delete; line_logger(line_logger&& other) : _callback_logger(other._callback_logger), _log_msg(std::move(other._log_msg)), _enabled(other._enabled) { other.disable(); } //Log the log message using the callback logger ~line_logger() { if (_enabled) { #ifndef SPDLOG_NO_NAME _log_msg.logger_name = _callback_logger->name(); #endif #ifndef SPDLOG_NO_DATETIME _log_msg.time = os::now(); #endif #ifndef SPDLOG_NO_THREAD_ID _log_msg.thread_id = os::thread_id(); #endif _callback_logger->_log_msg(_log_msg); } } // // Support for format string with variadic args // void write(const char* what) { if (_enabled) _log_msg.raw << what; } template void write(const char* fmt, const Args&... args) { if (!_enabled) return; try { _log_msg.raw.write(fmt, args...); } catch (const fmt::FormatError& e) { throw spdlog_ex(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what())); } } // // Support for operator<< // line_logger& operator<<(const char* what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(const std::string& what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(int what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(unsigned int what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(long what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(unsigned long what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(long long what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(unsigned long long what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(double what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(long double what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(float what) { if (_enabled) _log_msg.raw << what; return *this; } line_logger& operator<<(char what) { if (_enabled) _log_msg.raw << what; return *this; } //Support user types which implements operator<< template line_logger& operator<<(const T& what) { if (_enabled) _log_msg.raw.write("{}", what); return *this; } void disable() { _enabled = false; } bool is_enabled() const { return _enabled; } private: logger* _callback_logger; log_msg _log_msg; bool _enabled; }; } //Namespace details } // Namespace spdlog spdlog-1.6/include/spdlog/details/log_msg.h000066400000000000000000000065431256776053600210410ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include "../common.h" #include "./format.h" namespace spdlog { namespace details { struct log_msg { log_msg() = default; log_msg(level::level_enum l): logger_name(), level(l), raw(), formatted() {} log_msg(const log_msg& other) : logger_name(other.logger_name), level(other.level), time(other.time), thread_id(other.thread_id) { if (other.raw.size()) raw << fmt::BasicStringRef(other.raw.data(), other.raw.size()); if (other.formatted.size()) formatted << fmt::BasicStringRef(other.formatted.data(), other.formatted.size()); } log_msg(log_msg&& other) : logger_name(std::move(other.logger_name)), level(other.level), time(std::move(other.time)), thread_id(other.thread_id), raw(std::move(other.raw)), formatted(std::move(other.formatted)) { other.clear(); } log_msg& operator=(log_msg&& other) { if (this == &other) return *this; logger_name = std::move(other.logger_name); level = other.level; time = std::move(other.time); thread_id = other.thread_id; raw = std::move(other.raw); formatted = std::move(other.formatted); other.clear(); return *this; } void clear() { level = level::off; raw.clear(); formatted.clear(); } std::string logger_name; level::level_enum level; log_clock::time_point time; size_t thread_id; fmt::MemoryWriter raw; fmt::MemoryWriter formatted; }; } } spdlog-1.6/include/spdlog/details/logger_impl.h000066400000000000000000000217031256776053600217050ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once // // Logger implementation // #include "./line_logger.h" // create logger with given name, sinks and the default pattern formatter // all other ctors will call this one template 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("%+")) { // no support under vs2013 for member initialization for std::atomic _level = level::info; } // 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) { _set_pattern(pattern); } // // log only if given level>=logger's log level // template inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args) { bool msg_enabled = should_log(lvl); details::line_logger l(this, lvl, msg_enabled); l.write(fmt, args...); return l; } inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl) { return details::line_logger(this, lvl, should_log(lvl)); } template inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const T& msg) { bool msg_enabled = should_log(lvl); details::line_logger l(this, lvl, msg_enabled); l << msg; return l; } // // logger.info(cppformat_string, arg1, arg2, arg3, ...) call style // template inline spdlog::details::line_logger spdlog::logger::trace(const char* fmt, const Args&... args) { return _log_if_enabled(level::trace, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::debug(const char* fmt, const Args&... args) { return _log_if_enabled(level::debug, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::info(const char* fmt, const Args&... args) { return _log_if_enabled(level::info, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::notice(const char* fmt, const Args&... args) { return _log_if_enabled(level::notice, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::warn(const char* fmt, const Args&... args) { return _log_if_enabled(level::warn, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::error(const char* fmt, const Args&... args) { return _log_if_enabled(level::err, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::critical(const char* fmt, const Args&... args) { return _log_if_enabled(level::critical, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::alert(const char* fmt, const Args&... args) { return _log_if_enabled(level::alert, fmt, args...); } template inline spdlog::details::line_logger spdlog::logger::emerg(const char* fmt, const Args&... args) { return _log_if_enabled(level::emerg, fmt, args...); } // // logger.info(msg) << ".." call style // template inline spdlog::details::line_logger spdlog::logger::trace(const T& msg) { return _log_if_enabled(level::trace, msg); } template inline spdlog::details::line_logger spdlog::logger::debug(const T& msg) { return _log_if_enabled(level::debug, msg); } template inline spdlog::details::line_logger spdlog::logger::info(const T& msg) { return _log_if_enabled(level::info, msg); } template inline spdlog::details::line_logger spdlog::logger::notice(const T& msg) { return _log_if_enabled(level::notice, msg); } template inline spdlog::details::line_logger spdlog::logger::warn(const T& msg) { return _log_if_enabled(level::warn, msg); } template inline spdlog::details::line_logger spdlog::logger::error(const T& msg) { return _log_if_enabled(level::err, msg); } template inline spdlog::details::line_logger spdlog::logger::critical(const T& msg) { return _log_if_enabled(level::critical, msg); } template inline spdlog::details::line_logger spdlog::logger::alert(const T& msg) { return _log_if_enabled(level::alert, msg); } template inline spdlog::details::line_logger spdlog::logger::emerg(const T& msg) { return _log_if_enabled(level::emerg, msg); } // // logger.info() << ".." call style // inline spdlog::details::line_logger spdlog::logger::trace() { return _log_if_enabled(level::trace); } inline spdlog::details::line_logger spdlog::logger::debug() { return _log_if_enabled(level::debug); } inline spdlog::details::line_logger spdlog::logger::info() { return _log_if_enabled(level::info); } inline spdlog::details::line_logger spdlog::logger::notice() { return _log_if_enabled(level::notice); } inline spdlog::details::line_logger spdlog::logger::warn() { return _log_if_enabled(level::warn); } inline spdlog::details::line_logger spdlog::logger::error() { return _log_if_enabled(level::err); } inline spdlog::details::line_logger spdlog::logger::critical() { return _log_if_enabled(level::critical); } inline spdlog::details::line_logger spdlog::logger::alert() { return _log_if_enabled(level::alert); } inline spdlog::details::line_logger spdlog::logger::emerg() { return _log_if_enabled(level::emerg); } // always log, no matter what is the actual logger's log level template inline spdlog::details::line_logger spdlog::logger::force_log(level::level_enum lvl, const char* fmt, const Args&... args) { details::line_logger l(this, lvl, true); l.write(fmt, args...); return l; } // // 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 spdlog::level::level_enum spdlog::logger::level() const { return static_cast(_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::_log_msg(details::log_msg& msg) { _formatter->format(msg); for (auto &sink : _sinks) sink->log(msg); } inline void spdlog::logger::_set_pattern(const std::string& pattern) { _formatter = std::make_shared(pattern); } inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) { _formatter = msg_formatter; } inline void spdlog::logger::flush() { for (auto& sink : _sinks) sink->flush(); }spdlog-1.6/include/spdlog/details/mpmc_bounded_q.h000066400000000000000000000136701256776053600223650ustar00rootroot00000000000000/* 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: spdlog - an extremely fast and easy to use c++11 logging library. Copyright (c) 2014 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. */ #pragma once #include #include "../common.h" namespace spdlog { namespace details { template class mpmc_bounded_queue { public: using item_type = T; mpmc_bounded_queue(size_t 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; } private: struct cell_t { std::atomic sequence_; T data_; }; 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 enqueue_pos_; cacheline_pad_t pad2_; std::atomic dequeue_pos_; cacheline_pad_t pad3_; mpmc_bounded_queue(mpmc_bounded_queue const&); void operator = (mpmc_bounded_queue const&); }; } // ns details } // ns spdlog spdlog-1.6/include/spdlog/details/null_mutex.h000066400000000000000000000036321256776053600216020ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once // null, no cost mutex namespace spdlog { namespace details { struct null_mutex { void lock() {} void unlock() {} bool try_lock() { return true; } }; } } spdlog-1.6/include/spdlog/details/os.h000066400000000000000000000120501256776053600200210ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include #include #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include #ifdef __MINGW32__ #include #endif #elif __linux__ #include //Use gettid() syscall under linux to get thread id #include #else #include #endif #include "../common.h" 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( std::chrono::duration_cast( 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); } #ifdef _WIN32 inline const char* eol() { return "\r\n"; } #else constexpr inline const char* eol() { return "\n"; } #endif #ifdef _WIN32 inline unsigned short eol_size() { return 2; } #else constexpr inline unsigned short eol_size() { return 1; } #endif //fopen_s on non windows for writing inline int fopen_s(FILE** fp, const std::string& filename, const char* mode) { #ifdef _WIN32 *fp = _fsopen((filename.c_str()), mode, _SH_DENYWR); return *fp == nullptr; #else *fp = fopen((filename.c_str()), mode); return *fp == nullptr; #endif } //Return utc offset in minutes or -1 on failure inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) { #ifdef _WIN32 (void)tm; // avoid unused param warning DYNAMIC_TIME_ZONE_INFORMATION tzinfo; auto rv = GetDynamicTimeZoneInformation(&tzinfo); if (!rv) return -1; return -1 * (tzinfo.Bias + tzinfo.DaylightBias); #else return static_cast(tm.tm_gmtoff / 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(::GetCurrentThreadId()); #elif __linux__ return static_cast(syscall(SYS_gettid)); #else //Default to standard C++11 (OSX and other Unix) return static_cast(std::hash()(std::this_thread::get_id())); #endif } } //os } //details } //spdlog spdlog-1.6/include/spdlog/details/pattern_formatter_impl.h000066400000000000000000000460661256776053600241770ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include #include #include #include #include "../formatter.h" #include "./log_msg.h" #include "./os.h" 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 :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 :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 :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 :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 :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 :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 :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 :public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); } }; // ninutes 0-59 class M_formatter :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 :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 :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(duration).count() % 1000; msg.formatted << fmt::pad(static_cast(millis), 3, '0'); } }; // microseconds class f_formatter :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(duration).count() % 1000000; msg.formatted << fmt::pad(static_cast(micros), 6, '0'); } }; // nanoseconds class F_formatter :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(duration).count() % 1000000000; msg.formatted << fmt::pad(static_cast(ns), 9, '0'); } }; // AM/PM class p_formatter :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 :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 :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 :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 :public flag_formatter { public: const std::chrono::seconds cache_refresh = std::chrono::seconds(5); z_formatter() :_last_update(std::chrono::seconds(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 int h = total_minutes / 60; int m = total_minutes % 60; char sign = h >= 0 ? '+' : '-'; 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 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 :public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { msg.formatted << msg.thread_id; } }; class v_formatter :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 :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 :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 :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(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(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(tm_time.tm_year + 1900) << '-' << fmt::pad(static_cast(tm_time.tm_mon + 1), 2, '0') << '-' << fmt::pad(static_cast(tm_time.tm_mday), 2, '0') << ' ' << fmt::pad(static_cast(tm_time.tm_hour), 2, '0') << ':' << fmt::pad(static_cast(tm_time.tm_min), 2, '0') << ':' << fmt::pad(static_cast(tm_time.tm_sec), 2, '0') << '.' << fmt::pad(static_cast(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) { compile_pattern(pattern); } inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern) { auto end = pattern.end(); std::unique_ptr 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(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(new details::name_formatter())); break; case 'l': _formatters.push_back(std::unique_ptr(new details::level_formatter())); break; case 'L': _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); break; case('t') : _formatters.push_back(std::unique_ptr(new details::t_formatter())); break; case('v') : _formatters.push_back(std::unique_ptr(new details::v_formatter())); break; case('a') : _formatters.push_back(std::unique_ptr(new details::a_formatter())); break; case('A') : _formatters.push_back(std::unique_ptr(new details::A_formatter())); break; case('b') : case('h') : _formatters.push_back(std::unique_ptr(new details::b_formatter())); break; case('B') : _formatters.push_back(std::unique_ptr(new details::B_formatter())); break; case('c') : _formatters.push_back(std::unique_ptr(new details::c_formatter())); break; case('C') : _formatters.push_back(std::unique_ptr(new details::C_formatter())); break; case('Y') : _formatters.push_back(std::unique_ptr(new details::Y_formatter())); break; case('D') : case('x') : _formatters.push_back(std::unique_ptr(new details::D_formatter())); break; case('m') : _formatters.push_back(std::unique_ptr(new details::m_formatter())); break; case('d') : _formatters.push_back(std::unique_ptr(new details::d_formatter())); break; case('H') : _formatters.push_back(std::unique_ptr(new details::H_formatter())); break; case('I') : _formatters.push_back(std::unique_ptr(new details::I_formatter())); break; case('M') : _formatters.push_back(std::unique_ptr(new details::M_formatter())); break; case('S') : _formatters.push_back(std::unique_ptr(new details::S_formatter())); break; case('e') : _formatters.push_back(std::unique_ptr(new details::e_formatter())); break; case('f') : _formatters.push_back(std::unique_ptr(new details::f_formatter())); break; case('F') : _formatters.push_back(std::unique_ptr(new details::F_formatter())); break; case('p') : _formatters.push_back(std::unique_ptr(new details::p_formatter())); break; case('r') : _formatters.push_back(std::unique_ptr(new details::r_formatter())); break; case('R') : _formatters.push_back(std::unique_ptr(new details::R_formatter())); break; case('T') : case('X') : _formatters.push_back(std::unique_ptr(new details::T_formatter())); break; case('z') : _formatters.push_back(std::unique_ptr(new details::z_formatter())); break; case ('+'): _formatters.push_back(std::unique_ptr(new details::full_formatter())); break; default: //Unkown flag appears as is _formatters.push_back(std::unique_ptr(new details::ch_formatter('%'))); _formatters.push_back(std::unique_ptr(new details::ch_formatter(flag))); break; } } inline void spdlog::pattern_formatter::format(details::log_msg& msg) { try { auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); for (auto &f : _formatters) { f->format(msg, tm_time); } //write eol msg.formatted << details::os::eol(); } catch(const fmt::FormatError& e) { throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what())); } } spdlog-1.6/include/spdlog/details/registry.h000066400000000000000000000142321256776053600212540ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once // Loggers registy of unique name->logger pointer // An attempt to create a logger with an alreasy existing name will be ignored // If user requests a non existing logger, nullptr will be returned // This class is thread safe #include #include #include #include #include "./null_mutex.h" #include "../logger.h" #include "../async_logger.h" #include "../common.h" namespace spdlog { namespace details { template class registry_t { public: void register_logger(std::shared_ptr logger) { std::lock_guard lock(_mutex); register_logger_impl(logger); } std::shared_ptr get(const std::string& logger_name) { std::lock_guard lock(_mutex); auto found = _loggers.find(logger_name); return found == _loggers.end() ? nullptr : found->second; } template std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) { std::shared_ptr new_logger; std::lock_guard lock(_mutex); if (_async_mode) new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms); else new_logger = std::make_shared(logger_name, sinks_begin, sinks_end); if (_formatter) new_logger->set_formatter(_formatter); new_logger->set_level(_level); register_logger_impl(new_logger); return new_logger; } void drop(const std::string& logger_name) { std::lock_guard lock(_mutex); _loggers.erase(logger_name); } void drop_all() { std::lock_guard lock(_mutex); _loggers.clear(); } std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks) { return create(logger_name, sinks.begin(), sinks.end()); } std::shared_ptr create(const std::string& logger_name, sink_ptr sink) { return create(logger_name, { sink }); } void formatter(formatter_ptr f) { std::lock_guard lock(_mutex); _formatter = f; for (auto& l : _loggers) l.second->set_formatter(_formatter); } void set_pattern(const std::string& pattern) { std::lock_guard lock(_mutex); _formatter = std::make_shared(pattern); for (auto& l : _loggers) l.second->set_formatter(_formatter); } void set_level(level::level_enum log_level) { std::lock_guard lock(_mutex); for (auto& l : _loggers) l.second->set_level(log_level); _level = log_level; } void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) { std::lock_guard 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; } void set_sync_mode() { std::lock_guard lock(_mutex); _async_mode = false; } static registry_t& instance() { static registry_t s_instance; return s_instance; } private: void register_logger_impl(std::shared_ptr logger) { auto logger_name = logger->name(); if (_loggers.find(logger_name) != std::end(_loggers)) throw spdlog_ex("logger with name " + logger_name + " already exists"); _loggers[logger->name()] = logger; } registry_t(){} registry_t(const registry_t&) = delete; registry_t& operator=(const registry_t&) = delete; Mutex _mutex; std::unordered_map > _loggers; formatter_ptr _formatter; level::level_enum _level = level::info; bool _async_mode = false; size_t _async_q_size = 0; async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; std::function _worker_warmup_cb = nullptr; std::chrono::milliseconds _flush_interval_ms; }; #ifdef SPDLOG_NO_REGISTRY_MUTEX typedef registry_t registry; #else typedef registry_t registry; #endif } } spdlog-1.6/include/spdlog/details/spdlog_impl.h000066400000000000000000000143371256776053600217230ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once // // Global registry functions // #include "registry.h" #include "../sinks/file_sinks.h" #include "../sinks/stdout_sinks.h" #include "../sinks/syslog_sink.h" inline void spdlog::register_logger(std::shared_ptr logger) { return details::registry::instance().register_logger(logger); } inline std::shared_ptr 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 rotating file logger inline std::shared_ptr spdlog::rotating_logger_mt(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush) { return create(logger_name, filename, "txt", max_file_size, max_files, force_flush); } inline std::shared_ptr spdlog::rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush) { return create(logger_name, filename, "txt", max_file_size, max_files, force_flush); } // Create file logger which creates new file at midnight): inline std::shared_ptr spdlog::daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush) { return create(logger_name, filename, "txt", hour, minute, force_flush); } inline std::shared_ptr spdlog::daily_logger_st(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush) { return create(logger_name, filename, "txt", hour, minute, force_flush); } // Create stdout/stderr loggers inline std::shared_ptr spdlog::stdout_logger_mt(const std::string& logger_name) { return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance()); } inline std::shared_ptr spdlog::stdout_logger_st(const std::string& logger_name) { return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance()); } inline std::shared_ptr spdlog::stderr_logger_mt(const std::string& logger_name) { return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance()); } inline std::shared_ptr spdlog::stderr_logger_st(const std::string& logger_name) { return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance()); } #ifdef __linux__ // Create syslog logger inline std::shared_ptr spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option) { return create(logger_name, syslog_ident, syslog_option); } #endif //Create logger with multiple sinks inline std::shared_ptr spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks) { return details::registry::instance().create(logger_name, sinks); } template inline std::shared_ptr spdlog::create(const std::string& logger_name, const Args&... args) { sink_ptr sink = std::make_shared(args...); return details::registry::instance().create(logger_name, { sink }); } template inline std::shared_ptr 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); } 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_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) { details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms); } inline void spdlog::set_sync_mode() { details::registry::instance().set_sync_mode(); } inline void spdlog::drop_all() { details::registry::instance().drop_all(); } spdlog-1.6/include/spdlog/formatter.h000066400000000000000000000047111256776053600177630ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include "details/log_msg.h" namespace spdlog { namespace details { class flag_formatter; } class formatter { public: virtual ~formatter() {} virtual void format(details::log_msg& msg) = 0; }; class pattern_formatter : public formatter { public: explicit pattern_formatter(const std::string& pattern); 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; std::vector> _formatters; void handle_flag(char flag); void compile_pattern(const std::string& pattern); }; } #include "details/pattern_formatter_impl.h" spdlog-1.6/include/spdlog/logger.h000066400000000000000000000131141256776053600172340ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once // Thread safe logger // 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 #include #include "sinks/base_sink.h" #include "common.h" namespace spdlog { namespace details { class line_logger; } class logger { public: logger(const std::string& logger_name, sink_ptr single_sink); logger(const std::string& name, sinks_init_list); template logger(const std::string& name, const It& begin, const It& end); virtual ~logger(); logger(const logger&) = delete; logger& operator=(const logger&) = delete; void set_level(level::level_enum); level::level_enum level() const; const std::string& name() const; bool should_log(level::level_enum) const; // logger.info(cppformat_string, arg1, arg2, arg3, ...) call style template details::line_logger trace(const char* fmt, const Args&... args); template details::line_logger debug(const char* fmt, const Args&... args); template details::line_logger info(const char* fmt, const Args&... args); template details::line_logger notice(const char* fmt, const Args&... args); template details::line_logger warn(const char* fmt, const Args&... args); template details::line_logger error(const char* fmt, const Args&... args); template details::line_logger critical(const char* fmt, const Args&... args); template details::line_logger alert(const char* fmt, const Args&... args); template details::line_logger emerg(const char* fmt, const Args&... args); // logger.info(msg) << ".." call style template details::line_logger trace(const T&); template details::line_logger debug(const T&); template details::line_logger info(const T&); template details::line_logger notice(const T&); template details::line_logger warn(const T&); template details::line_logger error(const T&); template details::line_logger critical(const T&); template details::line_logger alert(const T&); template details::line_logger emerg(const T&); // logger.info() << ".." call style details::line_logger trace(); details::line_logger debug(); details::line_logger info(); details::line_logger notice(); details::line_logger warn(); details::line_logger error(); details::line_logger critical(); details::line_logger alert(); details::line_logger emerg(); // Create log message with the given level, no matter what is the actual logger's level template details::line_logger force_log(level::level_enum lvl, const char* fmt, const Args&... args); // Set the format of the log messages from this logger void set_pattern(const std::string&); void set_formatter(formatter_ptr); void flush(); protected: virtual void _log_msg(details::log_msg&); virtual void _set_pattern(const std::string&); virtual void _set_formatter(formatter_ptr); details::line_logger _log_if_enabled(level::level_enum lvl); template details::line_logger _log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args); template inline details::line_logger _log_if_enabled(level::level_enum lvl, const T& msg); friend details::line_logger; std::string _name; std::vector _sinks; formatter_ptr _formatter; std::atomic_int _level; }; } #include "./details/logger_impl.h" spdlog-1.6/include/spdlog/sinks/000077500000000000000000000000001256776053600167335ustar00rootroot00000000000000spdlog-1.6/include/spdlog/sinks/base_sink.h000066400000000000000000000050701256776053600210440ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once // // base sink templated over a mutex (either dummy or realy) // concrete implementation should only overrid the _sink_it method. // all locking is taken care of here so no locking needed by the implementors.. // #include #include #include #include "./sink.h" #include "../formatter.h" #include "../common.h" #include "../details/log_msg.h" namespace spdlog { namespace sinks { template 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) override { std::lock_guard lock(_mutex); _sink_it(msg); } protected: virtual void _sink_it(const details::log_msg& msg) = 0; Mutex _mutex; }; } } spdlog-1.6/include/spdlog/sinks/file_sinks.h000066400000000000000000000173561256776053600212460ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include "base_sink.h" #include "../details/null_mutex.h" #include "../details/file_helper.h" #include "../details/format.h" namespace spdlog { namespace sinks { /* * Trivial file sink with single file as target */ template class simple_file_sink : public base_sink < Mutex > { public: explicit simple_file_sink(const std::string &filename, bool force_flush = false) : _file_helper(force_flush) { _file_helper.open(filename); } void flush() override { _file_helper.flush(); } protected: void _sink_it(const details::log_msg& msg) override { _file_helper.write(msg); } private: details::file_helper _file_helper; }; typedef simple_file_sink simple_file_sink_mt; typedef simple_file_sink simple_file_sink_st; /* * Rotating file sink based on size */ template class rotating_file_sink : public base_sink < Mutex > { public: rotating_file_sink(const std::string &base_filename, const std::string &extension, std::size_t max_size, std::size_t max_files, bool force_flush = false) : _base_filename(base_filename), _extension(extension), _max_size(max_size), _max_files(max_files), _current_size(0), _file_helper(force_flush) { _file_helper.open(calc_filename(_base_filename, 0, _extension)); _current_size = _file_helper.size(); //expensive. called only once } void flush() override { _file_helper.flush(); } 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); } private: static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension) { fmt::MemoryWriter w; if (index) w.write("{}.{}.{}", filename, index, extension); else w.write("{}.{}", filename, extension); return w.str(); } // Rotate files: // log.txt -> log.1.txt // log.1.txt -> log2.txt // log.2.txt -> log3.txt // log.3.txt -> delete void _rotate() { _file_helper.close(); for (auto i = _max_files; i > 0; --i) { std::string src = calc_filename(_base_filename, i - 1, _extension); std::string target = calc_filename(_base_filename, i, _extension); if (details::file_helper::file_exists(target)) { if (std::remove(target.c_str()) != 0) { throw spdlog_ex("rotating_file_sink: failed removing " + target); } } if (details::file_helper::file_exists(src) && std::rename(src.c_str(), target.c_str())) { throw spdlog_ex("rotating_file_sink: failed renaming " + src + " to " + target); } } _file_helper.reopen(true); } std::string _base_filename; std::string _extension; std::size_t _max_size; std::size_t _max_files; std::size_t _current_size; details::file_helper _file_helper; }; typedef rotating_file_sink rotating_file_sink_mt; typedef rotating_file_sinkrotating_file_sink_st; /* * Rotating file sink based on date. rotates at midnight */ template class daily_file_sink :public base_sink < Mutex > { public: //create daily file sink which rotates on given time daily_file_sink( const std::string& base_filename, const std::string& extension, int rotation_hour, int rotation_minute, bool force_flush = false) : _base_filename(base_filename), _extension(extension), _rotation_h(rotation_hour), _rotation_m(rotation_minute), _file_helper(force_flush) { 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(calc_filename(_base_filename, _extension)); } void flush() override { _file_helper.flush(); } protected: void _sink_it(const details::log_msg& msg) override { if (std::chrono::system_clock::now() >= _rotation_tp) { _file_helper.open(calc_filename(_base_filename, _extension)); _rotation_tp = _next_rotation_tp(); } _file_helper.write(msg); } private: std::chrono::system_clock::time_point _next_rotation_tp() { using namespace std::chrono; auto now = 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 system_clock::time_point(rotation_time + hours(24)); } //Create filename for the form basename.YYYY-MM-DD.extension static std::string calc_filename(const std::string& basename, const std::string& extension) { 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, extension); return w.str(); } std::string _base_filename; std::string _extension; int _rotation_h; int _rotation_m; std::chrono::system_clock::time_point _rotation_tp; details::file_helper _file_helper; }; typedef daily_file_sink daily_file_sink_mt; typedef daily_file_sink daily_file_sink_st; } }spdlog-1.6/include/spdlog/sinks/null_sink.h000066400000000000000000000041601256776053600211030ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include "./base_sink.h" #include "../details/null_mutex.h" namespace spdlog { namespace sinks { template class null_sink : public base_sink < Mutex > { protected: void _sink_it(const details::log_msg&) override {} void flush() override {} }; typedef null_sink null_sink_st; typedef null_sink null_sink_mt; } } spdlog-1.6/include/spdlog/sinks/ostream_sink.h000066400000000000000000000052001256776053600215770ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include #include #include "../details/null_mutex.h" #include "./base_sink.h" namespace spdlog { namespace sinks { template class ostream_sink: public base_sink { 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 ostream_sink_mt; typedef ostream_sink ostream_sink_st; } } spdlog-1.6/include/spdlog/sinks/sink.h000066400000000000000000000036621256776053600200570ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include "../details/log_msg.h" namespace spdlog { namespace sinks { class sink { public: virtual ~sink() {} virtual void log(const details::log_msg& msg) = 0; virtual void flush() = 0; }; } } spdlog-1.6/include/spdlog/sinks/stdout_sinks.h000066400000000000000000000053671256776053600216500ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #include #include #include "./ostream_sink.h" #include "../details/null_mutex.h" namespace spdlog { namespace sinks { template class stdout_sink : public ostream_sink { using MyType = stdout_sink; public: stdout_sink() : ostream_sink(std::cout, true) {} static std::shared_ptr instance() { static std::shared_ptr instance = std::make_shared(); return instance; } }; typedef stdout_sink stdout_sink_st; typedef stdout_sink stdout_sink_mt; template class stderr_sink : public ostream_sink { using MyType = stderr_sink; public: stderr_sink() : ostream_sink(std::cerr, true) {} static std::shared_ptr instance() { static std::shared_ptr instance = std::make_shared(); return instance; } }; typedef stderr_sink stderr_sink_mt; typedef stderr_sink stderr_sink_st; } } spdlog-1.6/include/spdlog/sinks/syslog_sink.h000066400000000000000000000073451256776053600214610ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once #ifdef __linux__ #include #include #include #include "./sink.h" #include "../common.h" #include "../details/log_msg.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(level::trace)] = LOG_DEBUG; _priorities[static_cast(level::debug)] = LOG_DEBUG; _priorities[static_cast(level::info)] = LOG_INFO; _priorities[static_cast(level::notice)] = LOG_NOTICE; _priorities[static_cast(level::warn)] = LOG_WARNING; _priorities[static_cast(level::err)] = LOG_ERR; _priorities[static_cast(level::critical)] = LOG_CRIT; _priorities[static_cast(level::alert)] = LOG_ALERT; _priorities[static_cast(level::emerg)] = LOG_EMERG; _priorities[static_cast(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.formatted.str().c_str()); } void flush() override { } private: std::array _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(msg.level)]; } }; } } #endif spdlog-1.6/include/spdlog/spdlog.h000066400000000000000000000147011256776053600172500ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ // spdlog main header file. //see example.cpp for usage example #pragma once #include "tweakme.h" #include "common.h" #include "logger.h" namespace spdlog { // Return an existing logger or nullptr if a logger with such name doesn't exist. // Examples: // // spdlog::get("mylog")->info("Hello"); // auto logger = spdlog::get("mylog"); // logger.info("This is another message" , x, y, z); // logger.info() << "This is another message" << x << y << z; std::shared_ptr 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); // // 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) // void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); // Turn off async mode void set_sync_mode(); // // Create and register multi/single threaded rotating file logger // std::shared_ptr rotating_logger_mt(const std::string& logger_name, const std::string& filenameB, size_t max_file_size, size_t max_files, bool force_flush = false); std::shared_ptr rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false); // // Create file logger which creates new file on the given time (default in midnight): // std::shared_ptr daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false); std::shared_ptr daily_logger_st(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false); // // Create and register stdout/stderr loggers // std::shared_ptr stdout_logger_mt(const std::string& logger_name); std::shared_ptr stdout_logger_st(const std::string& logger_name); std::shared_ptr stderr_logger_mt(const std::string& logger_name); std::shared_ptr stderr_logger_st(const std::string& logger_name); // // Create and register a syslog logger // #ifdef __linux__ std::shared_ptr syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0); #endif // Create and register a logger with multiple sinks std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks); template std::shared_ptr 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("mylog", "dailylog_filename", "txt"); template std::shared_ptr create(const std::string& logger_name, const Args&...); // Register the given logger with the given name void register_logger(std::shared_ptr logger); // Drop the reference to the given logger void drop(const std::string &name); // Drop all references void drop_all(); /////////////////////////////////////////////////////////////////////////////// // // Macros to be display source file & line // 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. // // Example: // spdlog::set_level(spdlog::level::debug); // SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2); /////////////////////////////////////////////////////////////////////////////// #ifdef SPDLOG_TRACE_ON #define SPDLOG_TRACE(logger, ...) logger->trace(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")"; #else #define SPDLOG_TRACE(logger, ...) #endif #ifdef SPDLOG_DEBUG_ON #define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")"; #else #define SPDLOG_DEBUG(logger, ...) #endif } #include "details/spdlog_impl.h" spdlog-1.6/include/spdlog/tweakme.h000066400000000000000000000100301256776053600174040ustar00rootroot00000000000000/*************************************************************************/ /* spdlog - an extremely fast and easy to use c++11 logging library. */ /* Copyright (c) 2014 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. */ /*************************************************************************/ #pragma once /////////////////////////////////////////////////////////////////////////////// // Edit this file to squeeze every last drop of performance out of spdlog. /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // 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 (but slower) clock. // #define SPDLOG_CLOCK_COARSE /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Uncomment if date/time logging is not needed. // This will prevent spdlog from quering the clock on each log call. // #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. // #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 /////////////////////////////////////////////////////////////////////////////// spdlog-1.6/tests/000077500000000000000000000000001256776053600140335ustar00rootroot00000000000000spdlog-1.6/tests/Makefile000066400000000000000000000005651256776053600155010ustar00rootroot00000000000000CXX ?= g++ CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 LDPFALGS = -pthread CPP_FILES := $(wildcard *.cpp) OBJ_FILES := $(addprefix ./,$(notdir $(CPP_FILES:.cpp=.o))) tests: $(OBJ_FILES) $(CXX) $(CXXFLAGS) $(LDPFALGS) -o $@ $^ mkdir -p logs %.o: %.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< clean: rm -f tests *.o logs/*.txt rebuild: clean tests spdlog-1.6/tests/catch.hpp000066400000000000000000012203021256776053600156260ustar00rootroot00000000000000/* * 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 #include #include // #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 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 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 T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); #include 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 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 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 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 #include #include 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 getConfig() const = 0; }; struct IMutableContext : IContext { virtual ~IMutableContext(); virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setRunner( IRunner* runner ) = 0; virtual void setConfig( Ptr 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 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 const& getAllTests() const = 0; virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const = 0; }; } namespace Catch { template class MethodTestCase : public SharedImpl { 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 AutoReg( void (C::*method)(), char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { registerTestCase( new MethodTestCase( 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( static_cast( lhs ) | static_cast( 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 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 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 ExpressionLhs operator->* ( T const& operand ); ExpressionLhs operator->* ( bool value ); template ResultBuilder& operator << ( T const& value ) { m_stream.oss << value; return *this; } template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); template 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 namespace Catch { namespace Internal { enum Operator { IsEqualTo, IsNotEqualTo, IsLessThan, IsGreaterThan, IsLessThanOrEqualTo, IsGreaterThanOrEqualTo }; template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template inline T& opCast(T const& t) { return const_cast(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 class Evaluator{}; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs) { return opCast( lhs ) == opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) != opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) < opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) > opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) >= opCast( rhs ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return opCast( lhs ) <= opCast( rhs ); } }; template bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // This level of indirection allows us to specialise for integer types // to avoid signed/ unsigned warnings // "base" overload template bool compare( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // unsigned X to int template bool compare( unsigned int lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // unsigned X to long template bool compare( unsigned int lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // int to unsigned X template bool compare( int lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // long to unsigned X template bool compare( long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long (when comparing against NULL) template bool compare( long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } // pointer to int (when comparing against NULL) template bool compare( int lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, int rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #ifdef CATCH_CONFIG_CPP11_NULLPTR // pointer to nullptr_t (when comparing against nullptr) template bool compare( std::nullptr_t, T* rhs ) { return Evaluator::evaluate( NULL, rhs ); } template bool compare( T* lhs, std::nullptr_t ) { return Evaluator::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 struct NotABooleanExpression; template struct If : NotABooleanExpression {}; template<> struct If : TrueType {}; template<> struct If : FalseType {}; template struct SizedIf; template<> struct SizedIf : TrueType {}; template<> struct SizedIf : FalseType {}; #endif // CATCH_CONFIG_SFINAE } // end namespace Catch #include #include #include #include #include #ifdef __OBJC__ // #included from: catch_objc_arc.hpp #define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED #import #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 #include #endif namespace Catch { // Why we're here. template 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 class IsStreamInsertableHelper { template struct TrueIfSizeable : TrueType {}; template static TrueIfSizeable dummy(T2*); static FalseType dummy(...); public: typedef SizedIf type; }; template struct IsStreamInsertable : IsStreamInsertableHelper::type {}; #else struct BorgType { template BorgType( T const& ); }; TrueType& testStreamable( std::ostream& ); FalseType testStreamable( FalseType ); FalseType operator<<( std::ostream const&, BorgType const& ); template 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::value > struct EnumStringMaker { static std::string convert( T const& ) { return unprintableString; } }; template struct EnumStringMaker { static std::string convert( T const& v ) { return ::Catch::toString( static_cast::type>(v) ); } }; #endif template struct StringMakerBase { #if defined(CATCH_CPP11_OR_GREATER) template static std::string convert( T const& v ) { return EnumStringMaker::convert( v ); } #else template static std::string convert( T const& ) { return unprintableString; } #endif }; template<> struct StringMakerBase { template 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 inline std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } } // end namespace Detail template struct StringMaker : Detail::StringMakerBase::value> {}; template struct StringMaker { template static std::string convert( U* p ) { if( !p ) return INTERNAL_CATCH_STRINGIFY( NULL ); else return Detail::rawMemoryToString( p ); } }; template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) return INTERNAL_CATCH_STRINGIFY( NULL ); else return Detail::rawMemoryToString( p ); } }; namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ); } //template //struct StringMaker > { // static std::string convert( std::vector const& v ) { // return Detail::rangeToString( v.begin(), v.end() ); // } //}; template std::string toString( std::vector 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::value) > struct ElementPrinter { static void print( const Tuple& tuple, std::ostream& os ) { os << ( N ? ", " : " " ) << Catch::toString(std::get(tuple)); ElementPrinter::print(tuple,os); } }; template< typename Tuple, std::size_t N > struct ElementPrinter { static void print( const Tuple&, std::ostream& ) {} }; } template struct StringMaker> { static std::string convert( const std::tuple& tuple ) { std::ostringstream os; os << '{'; TupleDetail::ElementPrinter>::print( tuple, os ); os << " }"; return os.str(); } }; #endif namespace Detail { template std::string makeString( T const& value ) { return StringMaker::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 std::string toString( T const& value ) { return StringMaker::convert( value ); } namespace Detail { template 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 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 ResultBuilder& operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } ResultBuilder& operator == ( bool rhs ) { return captureExpression( rhs ); } ResultBuilder& operator != ( bool rhs ) { return captureExpression( 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 STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); private: template ResultBuilder& captureExpression( RhsT const& rhs ) { return m_rb .setResultType( Internal::compare( m_lhs, rhs ) ) .setLhs( Catch::toString( m_lhs ) ) .setRhs( Catch::toString( rhs ) ) .setOp( Internal::OperatorTraits::getName() ); } private: ResultBuilder& m_rb; T m_lhs; }; } // end namespace Catch namespace Catch { template inline ExpressionLhs ResultBuilder::operator->* ( T const& operand ) { return ExpressionLhs( *this, operand ); } inline ExpressionLhs ResultBuilder::operator->* ( bool value ) { return ExpressionLhs( *this, value ); } } // namespace Catch // #included from: catch_message.h #define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED #include 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 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 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 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 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 #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 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 #include #include #include namespace Catch { template struct IGenerator { virtual ~IGenerator() {} virtual T getValue( std::size_t index ) const = 0; virtual std::size_t size () const = 0; }; template class BetweenGenerator : public IGenerator { 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( index ); } virtual std::size_t size() const { return static_cast( 1+m_to-m_from ); } private: T m_from; T m_to; }; template class ValuesGenerator : public IGenerator { 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 m_values; }; template 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_iterator it = m_composed.begin(); typename std::vector*>::const_iterator itEnd = m_composed.end(); for( size_t index = 0; it != itEnd; ++it ) { const IGenerator* 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* generator ) { m_totalSize += generator->size(); m_composed.push_back( generator ); } CompositeGenerator& then( CompositeGenerator& other ) { move( other ); return *this; } CompositeGenerator& then( T value ) { ValuesGenerator* valuesGen = new ValuesGenerator(); 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*> m_composed; std::string m_fileInfo; size_t m_totalSize; }; namespace Generators { template CompositeGenerator between( T from, T to ) { CompositeGenerator generators; generators.add( new BetweenGenerator( from, to ) ); return generators; } template CompositeGenerator values( T val1, T val2 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3 ){ CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3, T val4 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); 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 // #included from: catch_interfaces_registry_hub.h #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED #include 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 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 ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { getMutableRegistryHub().registerTranslator ( new ExceptionTranslator( 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 #include namespace Catch { namespace Detail { class Approx { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::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 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 struct Matcher : SharedImpl { typedef ExpressionT ExpressionType; virtual ~Matcher() {} virtual Ptr clone() const = 0; virtual bool match( ExpressionT const& expr ) const = 0; virtual std::string toString() const = 0; }; template struct MatcherImpl : Matcher { virtual Ptr > clone() const { return Ptr >( new DerivedT( static_cast( *this ) ) ); } }; namespace Generic { template class AllOf : public MatcherImpl, ExpressionT> { public: AllOf() {} AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} AllOf& add( Matcher 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 > > m_matchers; }; template class AnyOf : public MatcherImpl, ExpressionT> { public: AnyOf() {} AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} AnyOf& add( Matcher 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 > > 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 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 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 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 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 inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, Impl::Matcher const& m2 ) { return Impl::Generic::AllOf().add( m1 ).add( m2 ); } template inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, Impl::Matcher const& m2, Impl::Matcher const& m3 ) { return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); } template inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, Impl::Matcher const& m2 ) { return Impl::Generic::AnyOf().add( m1 ).add( m2 ); } template inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, Impl::Matcher const& m2, Impl::Matcher const& m3 ) { return Impl::Generic::AnyOf().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 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 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 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 #include #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 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 tags; std::set 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 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 #include // 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 { 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 struct StringHolder : MatcherImpl{ 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( 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( 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( 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( 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 #include 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( 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 const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} virtual ~ExcludedPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } private: Ptr m_underlyingPattern; }; struct Filter { std::vector > 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 >::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::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) if( it->matches( testCase ) ) return true; return false; } private: std::vector 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(); 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(); addFilter(); } else if( c == '[' ) { if( subString() == "exclude:" ) m_exclusion = true; else addPattern(); startNewMode( Tag, ++m_pos ); } } else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) addPattern(); } 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 void addPattern() { std::string token = subString(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { Ptr 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 #include #include 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 #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 #include #include #include #include #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 testsOrTags; }; class Config : public SharedImpl { 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 #include #include // 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::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 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 #include #include #include // 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 struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct IsBool { static const bool value = false; }; template<> struct IsBool { static const bool value = true; }; template 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 inline void convertInto( bool, T& ) { throw std::runtime_error( "Invalid conversion" ); } template 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 class BoundArgFunction { public: BoundArgFunction() : functionObj( NULL ) {} BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { IArgFunction* 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* functionObj; }; template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} virtual void setFlag( C& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; template struct BoundDataMember : IArgFunction{ 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::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; }; template struct BoundUnaryMethod : IArgFunction{ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); (p.*member)( value ); } virtual void setFlag( C& p ) const { typename RemoveConstRef::type value; convertInto( true, value ); (p.*member)( value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); }; template struct BoundNullaryMethod : IArgFunction{ 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* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); }; template struct BoundUnaryFunction : IArgFunction{ 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* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); }; template struct BoundBinaryFunction : IArgFunction{ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); function( obj, value ); } virtual void setFlag( C& obj ) const { typename RemoveConstRef::type value; convertInto( true, value ); function( obj, value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* 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& 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& 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 struct CommonArgProperties { CommonArgProperties() {} CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} Detail::BoundArgFunction 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 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 class CommandLine { struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { Arg() {} Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} using CommonArgProperties::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::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 ArgAutoPtr; #else typedef std::auto_ptr 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 void bind( M C::* field, std::string const& placeholder ) { m_arg->boundField = new Detail::BoundDataMember( field ); m_arg->placeholder = placeholder; } // Bind a boolean data member (no placeholder required) template void bind( bool C::* field ) { m_arg->boundField = new Detail::BoundDataMember( field ); } // Bind a method taking a single, non-boolean argument (requires a placeholder string) template void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); m_arg->placeholder = placeholder; } // Bind a method taking a single, boolean argument (no placeholder string required) template void bind( void (C::* unaryMethod)( bool ) ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); } // Bind a method that takes no arguments (will be called if opt is present) template void bind( void (C::* nullaryMethod)() ) { m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); } // Bind a free function taking a single argument - the object to operate on (no placeholder string required) template void bind( void (* unaryFunction)( C& ) ) { m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); } // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) template void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundBinaryFunction( 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() ), 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 void bindProcessName( M C::* field ) { m_boundProcessName = new Detail::BoundDataMember( field ); } template void bindProcessName( void (C::*_unaryMethod)( M ) ) { m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); } void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { typename std::vector::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::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 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 tokens; Parser parser; parser.parseIntoTokens( argc, argv, tokens ); return populate( tokens, config ); } std::vector populate( std::vector const& tokens, ConfigT& config ) const { validate(); std::vector unusedTokens = populateOptions( tokens, config ); unusedTokens = populateFixedArgs( unusedTokens, config ); unusedTokens = populateFloatingArgs( unusedTokens, config ); return unusedTokens; } std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; std::vector errors; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::vector::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::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 populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; int position = 1; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::map::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 populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { if( !m_floatingArg.get() ) return tokens; std::vector 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::const_iterator it = m_options.begin(), itEnd = m_options.end(); it != itEnd; ++it ) it->validate(); } private: Detail::BoundArgFunction m_boundProcessName; std::vector m_options; std::map 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 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( 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( 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( 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 makeCommandLineParser() { using namespace Clara; CommandLine 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 #include #include // 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::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 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 #include #include #include namespace Catch { struct ReporterConfig { explicit ReporterConfig( Ptr const& _fullConfig ) : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} std::ostream& stream() const { return *m_stream; } Ptr fullConfig() const { return m_fullConfig; } private: std::ostream* m_stream; Ptr m_fullConfig; }; struct ReporterPreferences { ReporterPreferences() : shouldRedirectStdOut( false ) {} bool shouldRedirectStdOut; }; template struct LazyStat : Option { LazyStat() : used( false ) {} LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; return *this; } void reset() { Option::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 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 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 FactoryMap; virtual ~IReporterRegistry(); virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; }; } #include #include 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 matchedTestCases; getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); for( std::vector::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 matchedTestCases; getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); for( std::vector::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::const_iterator it = spellings.begin(), itEnd = spellings.end(); it != itEnd; ++it ) out += "[" + *it + "]"; return out; } std::set 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 tagCounts; std::vector matchedTestCases; getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { for( std::set::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::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::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 list( Config const& config ) { Option 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 #include #include namespace Catch { namespace SectionTracking { class TrackedSection { typedef std::map 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 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( "", -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 #include 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 const& config, Ptr 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 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( 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::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 m_testCaseTracker; AssertionResult m_lastResult; Ptr m_config; Totals m_totals; Ptr m_reporter; std::vector m_messages; IRunner* m_prevRunner; IResultCapture* m_prevResultCapture; Ptr m_prevConfig; AssertionInfo m_lastAssertionInfo; std::vector 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 #include #include namespace Catch { class Runner { public: Runner( Ptr 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 testCases; getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases ); int testsRunForGroup = 0; for( std::vector::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 skippedTestCases; getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true ); for( std::vector::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 m_config; std::ofstream m_ofs; Ptr m_reporter; std::set 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::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 listed = list( config() ) ) return static_cast( *listed ); return static_cast( runner.runTests().assertions.failed ); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; return (std::numeric_limits::max)(); } } Clara::CommandLine const& cli() const { return m_cli; } std::vector 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 m_cli; std::vector m_unusedTokens; ConfigData m_configData; Ptr 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 #include #include #include #include namespace Catch { class TestRegistry : public ITestCaseRegistry { struct LexSort { bool operator() (TestCase i,TestCase j) const { return (i const& getAllTests() const { return m_functionsInOrder; } virtual std::vector const& getAllNonHiddenTests() const { return m_nonHiddenFunctions; } virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const { for( std::vector::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& 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 m_functions; std::vector m_functionsInOrder; std::vector m_nonHiddenFunctions; size_t m_unnamedCount; }; /////////////////////////////////////////////////////////////////////////// class FreeFunctionTestCase : public SharedImpl { 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 namespace Catch { class ReporterRegistry : public IReporterRegistry { public: virtual ~ReporterRegistry() { deleteAllValues( m_factories ); } virtual IStreamingReporter* create( std::string const& name, Ptr 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_iterator it ) const { if( it == m_translators.end() ) return "Unknown exception"; try { return (*it)->translate(); } catch(...) { return tryTranslators( it+1 ); } } private: std::vector 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 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 namespace Catch { class StreamBufBase : public std::streambuf { public: virtual ~StreamBufBase() CATCH_NOEXCEPT; }; } #include #include #include namespace Catch { template 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( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( 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 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 const& config ) { m_config = config; } friend IMutableContext& getCurrentMutableContext(); private: IGeneratorsForTest* findGeneratorsForCurrentTest() { std::string testName = getResultCapture()->getCurrentTestName(); std::map::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 m_config; IRunner* m_runner; IResultCapture* m_resultCapture; std::map 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, 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 #else #include #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 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 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( _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 #include #include 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::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::const_iterator it = m_generatorsInOrder.begin(); std::vector::const_iterator itEnd = m_generatorsInOrder.end(); for(; it != itEnd; ++it ) { if( (*it)->moveNext() ) return true; } return false; } private: std::map m_generatorsByName; std::vector 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 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 const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), className( _className ), description( _description ), tags( _tags ), lineInfo( _lineInfo ), properties( None ) { std::ostringstream oss; for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { oss << "[" << *it << "]"; std::string lcaseTag = toLower( *it ); properties = static_cast( 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( 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 { public: LegacyReporterAdapter( Ptr 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 m_legacyReporter; }; } namespace Catch { LegacyReporterAdapter::LegacyReporterAdapter( Ptr 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::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 #else #include #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS uint64_t getCurrentTicks() { static uint64_t hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } uint64_t t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else uint64_t getCurrentTicks() { timeval t; gettimeofday(&t,NULL); return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } void Timer::start() { m_ticks = getCurrentTicks(); } unsigned int Timer::getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } unsigned int Timer::getElapsedMilliseconds() const { return static_cast(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 #ifdef CATCH_PLATFORM_MAC #include #include #include #include #include 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( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { i = end-1; end = inc = -1; } unsigned char const *bytes = static_cast(object); std::ostringstream os; os << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) os << std::setw(2) << static_cast(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( 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( 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( 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( value ) ); } template 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( value ) ) : Detail::makeString( value ); } std::string toString( signed char value ) { return toString( static_cast( value ) ); } std::string toString( unsigned char value ) { return toString( static_cast( 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 namespace Catch { class TagAliasRegistry : public ITagAliasRegistry { public: virtual ~TagAliasRegistry(); virtual Option 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 m_registry; }; } // end namespace Catch #include #include namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} Option TagAliasRegistry::find( std::string const& alias ) const { std::map::const_iterator it = m_registry.find( alias ); if( it != m_registry.end() ) return it->second; else return Option(); } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { std::string expandedTestSpec = unexpandedTestSpec; for( std::map::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 namespace Catch { struct StreamingReporterBase : SharedImpl { 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 m_config; std::ostream& stream; LazyStat currentTestRunInfo; LazyStat currentGroupInfo; LazyStat currentTestCaseInfo; std::vector m_sectionStack; }; struct CumulativeReporterBase : SharedImpl { template struct Node : SharedImpl<> { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} typedef std::vector > 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 const& other ) const { return operator==( *other ); } SectionStats stats; typedef std::vector > ChildSections; typedef std::vector 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 const& node ) const { return node->stats.sectionInfo.lineInfo == m_other.lineInfo; } private: void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; typedef Node TestCaseNode; typedef Node TestGroupNode; typedef Node 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 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 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 node = new TestGroupNode( testGroupStats ); node->children.swap( m_testCases ); m_testGroups.push_back( node ); } virtual void testRunEnded( TestRunStats const& testRunStats ) { Ptr 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 m_config; std::ostream& stream; std::vector m_assertions; std::vector > > m_sections; std::vector > m_testCases; std::vector > m_testGroups; std::vector > m_testRuns; Ptr m_rootSection; Ptr m_deepestSection; std::vector > m_sectionStack; }; template 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 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 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 catch_internal_RegistrarFor##reporterType( name ); } #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED #include #include #include 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 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 << "\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 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 << ""; 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 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 tags. if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { for( std::vector::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 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::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::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 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::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::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 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 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 const& cols, std::size_t row ) { for( std::vector::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::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( 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 messages; std::vector::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 spdlog-1.6/tests/file_log.cpp000066400000000000000000000061541256776053600163250ustar00rootroot00000000000000#include "includes.h" static 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(ifs)), (std::istreambuf_iterator())); } static 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::ifstream::pos_type 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 ifs.tellg(); } static void prepare_logdir() { spdlog::drop_all(); #ifdef _WIN32 auto rv = system("del /F /Q logs\\*"); #else auto rv = system("rm -f logs/*"); #endif } TEST_CASE("simple_file_logger", "[simple_logger]]") { prepare_logdir(); std::string filename = "logs/simple_log.txt"; auto logger = spdlog::create("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("rotating_file_logger1", "[rotating_logger]]") { prepare_logdir(); std::string basename = "logs/rotating_log"; auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0, true); for (int i = 0; i < 10; ++i) logger->info("Test message {}", i); auto filename = basename + ".txt"; REQUIRE(count_lines(filename) == 10); for (int i = 0; i < 1000; i++) logger->info("Test message {}", i); } 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, false); for (int i = 0; i < 10; ++i) logger->info("Test message {}", i); logger->flush(); auto filename = basename + ".txt"; REQUIRE(count_lines(filename) == 10); for (int i = 0; i < 1000; i++) logger->info("Test message {}", i); logger->flush(); REQUIRE(filesize(filename) <= 1024); auto filename1 = basename + ".1.txt"; REQUIRE(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}.txt", 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, true); for (int i = 0; i < 10; ++i) logger->info("Test message {}", i); auto filename = w.str(); REQUIRE(count_lines(filename) == 10); } spdlog-1.6/tests/format.cpp000066400000000000000000000042511256776053600160310ustar00rootroot00000000000000 #include "includes.h" template 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(oss); spdlog::logger oss_logger("oss", oss_sink); oss_logger.set_level(logger_level); oss_logger.set_pattern("%v"); oss_logger.info() << what; //strip last eol and return the logged string auto eol_size = strlen(spdlog::details::os::eol()); return oss.str().substr(0, oss.str().length() - eol_size); } //User defined class with operator<< struct some_logged_class { some_logged_class(const std::string val) :value(val) {}; std::string value; }; std::ostream& operator<<(std::ostream& os, const some_logged_class& c) { return os << c.value; } 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::emerg) == ""); REQUIRE(log_info("Hello", spdlog::level::alert) == ""); REQUIRE(log_info("Hello", spdlog::level::info) == "Hello"); REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello"); REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello"); } TEST_CASE("invalid_format", "[format]") { using namespace spdlog::sinks; spdlog::logger null_logger("null_logger", std::make_shared()); REQUIRE_THROWS_AS( null_logger.info("{} {}", "first"), spdlog::spdlog_ex); REQUIRE_THROWS_AS( null_logger.info("{0:f}", "aads"), spdlog::spdlog_ex); REQUIRE_THROWS_AS( null_logger.info("{0:kk}", 123), spdlog::spdlog_ex); } spdlog-1.6/tests/includes.h000066400000000000000000000003521256776053600160120ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "catch.hpp" #include "../include/spdlog/spdlog.h" #include "../include/spdlog/sinks/null_sink.h" spdlog-1.6/tests/install_libcxx.sh000077500000000000000000000010211256776053600174030ustar00rootroot00000000000000#!/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 spdlog-1.6/tests/main.cpp000066400000000000000000000000561256776053600154640ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include "catch.hpp"spdlog-1.6/tests/registry.cpp000066400000000000000000000030461256776053600164120ustar00rootroot00000000000000#include "includes.h" static const char *logger_name = "null_logger"; TEST_CASE("register_drop", "[registry]") { spdlog::drop_all(); spdlog::create(logger_name); REQUIRE(spdlog::get(logger_name)!=nullptr); //Throw if registring existing name REQUIRE_THROWS_AS(spdlog::create(logger_name), spdlog::spdlog_ex); } TEST_CASE("explicit register" "[registry]") { spdlog::drop_all(); auto logger = std::make_shared(logger_name, std::make_shared()); spdlog::register_logger(logger); REQUIRE(spdlog::get(logger_name) != nullptr); //Throw if registring existing name REQUIRE_THROWS_AS(spdlog::create(logger_name), spdlog::spdlog_ex); } TEST_CASE("drop" "[registry]") { spdlog::drop_all(); spdlog::create(logger_name); spdlog::drop(logger_name); REQUIRE_FALSE(spdlog::get(logger_name)); } TEST_CASE("drop_all" "[registry]") { spdlog::drop_all(); spdlog::create(logger_name); spdlog::create("name2"); spdlog::drop_all(); REQUIRE_FALSE(spdlog::get(logger_name)); REQUIRE_FALSE(spdlog::get("name2")); } TEST_CASE("drop non existing" "[registry]") { spdlog::drop_all(); spdlog::create(logger_name); spdlog::drop("some_name"); REQUIRE_FALSE(spdlog::get("some_name")); REQUIRE(spdlog::get(logger_name)); spdlog::drop_all(); } spdlog-1.6/tests/tests.sln000066400000000000000000000024011256776053600157100ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.31101.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 spdlog-1.6/tests/tests.vcxproj000066400000000000000000000140011256776053600166060ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {59A07559-5F38-4DD6-A7FA-DB4153690B42} tests Application true v120 MultiByte Application true v120 MultiByte Application false v120 true MultiByte Application false v120 true MultiByte Level3 Disabled true true Console Level3 Disabled true true Console Level4 MaxSpeed true true true true true true Console Level4 MaxSpeed true true true true true true Console spdlog-1.6/tests/tests.vcxproj.filters000066400000000000000000000026111256776053600202610ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Header Files Header Files