[med-svn] [spdlog] 01/01: Imported Upstream version 1.8
Michael Crusoe
misterc-guest at moszumanska.debian.org
Tue May 17 09:52:42 UTC 2016
This is an automated email from the git hooks/post-receive script.
misterc-guest pushed a commit to annotated tag upstream/1.8
in repository spdlog.
commit 5feb04af06094073890ffbc802a0319b76e557db
Author: Michael R. Crusoe <crusoe at ucdavis.edu>
Date: Tue May 17 02:46:41 2016 -0700
Imported Upstream version 1.8
---
.gitignore | 20 +-
.travis.yml | 216 +--
CMakeLists.txt | 135 +-
LICENSE | 2 +-
README.md | 315 ++--
bench/Makefile | 130 +-
bench/run_all.sh | 152 +-
bench/zf_log-bench-mt.cpp | 22 +-
bench/zf_log-bench.cpp | 21 +-
cmake/spdlog.pc.in | 6 +
example/example.cpp | 76 +-
example/example.vcxproj | 178 +-
example/logs/.gitignore | 6 +-
include/spdlog/async_logger.h | 12 +-
include/spdlog/common.h | 45 +-
include/spdlog/details/async_log_helper.h | 48 +-
include/spdlog/details/async_logger_impl.h | 15 +-
include/spdlog/details/file_helper.h | 22 +-
include/spdlog/details/format.cc | 2013 ++++++++++++-----------
include/spdlog/details/format.h | 827 ++++++----
include/spdlog/details/logger_impl.h | 11 +-
include/spdlog/details/null_mutex.h | 23 +-
include/spdlog/details/os.h | 47 +-
include/spdlog/details/pattern_formatter_impl.h | 4 +
include/spdlog/details/registry.h | 34 +-
include/spdlog/details/spdlog_impl.h | 47 +-
include/spdlog/logger.h | 8 +-
include/spdlog/sinks/ansicolor_sink.h | 115 ++
include/spdlog/sinks/file_sinks.h | 81 +-
include/spdlog/sinks/msvc_sink.h | 4 +-
include/spdlog/sinks/stdout_sinks.h | 32 +-
include/spdlog/spdlog.h | 22 +-
include/spdlog/tweakme.h | 17 +
tests/Makefile | 44 +-
tests/file_helper.cpp | 20 +-
tests/file_log.cpp | 49 +-
tests/includes.h | 1 +
tests/tests.vcxproj | 282 ++--
tests/tests.vcxproj.filters | 94 +-
tests/utils.cpp | 6 +-
40 files changed, 2916 insertions(+), 2286 deletions(-)
diff --git a/.gitignore b/.gitignore
index 6c04147..d0dd396 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,8 +29,20 @@
# example files
example/*
!example/example.cpp
+!example/bench.cpp
!example/utils.h
-!example/makefile
-!example/makefile.clang
-
-
+!example/Makefile*
+!example/example.sln
+!example/example.vcxproj
+!example/CMakeLists.txt
+
+# generated files
+generated
+
+# Cmake
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Makefile
+cmake_install.cmake
+install_manifest.txt
diff --git a/.travis.yml b/.travis.yml
index 6364b6a..3cbb527 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,108 +1,108 @@
-# 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="-I${CHECKOUT_PATH}/include"
- - export CXX_LINKER_FLAGS=""
- - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
- - if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
- - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
- - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
- - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
- - CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
-
- # Build examples
- - cd example
- - if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
- - if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
-
-
-script:
- - ./"${BIN}"
- - valgrind --trace-children=yes --leak-check=full ./"${BIN}"
- - cd $CHECKOUT_PATH/tests; make rebuild; ./tests
-
-notifications:
- email: false
+# 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="-I${CHECKOUT_PATH}/include"
+ - export CXX_LINKER_FLAGS=""
+ - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
+ - if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
+ - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
+ - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
+ - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
+ - CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
+
+ # Build examples
+ - cd example
+ - if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
+ - if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
+
+
+script:
+ - ./"${BIN}"
+ - valgrind --trace-children=yes --leak-check=full ./"${BIN}"
+ - cd $CHECKOUT_PATH/tests; make rebuild; ./tests
+
+notifications:
+ email: false
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 15553b1..f11d732 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,62 +1,73 @@
-#
-# Copyright(c) 2015 Ruslan Baratov.
-# Distributed under the MIT License (http://opensource.org/licenses/MIT)
-#
-
-cmake_minimum_required(VERSION 3.0)
-project(spdlog VERSION 1.0.0)
-
-add_library(spdlog INTERFACE)
-
-option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
-
-target_include_directories(
- spdlog
- INTERFACE
- "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
- "$<INSTALL_INTERFACE:include>"
-)
-
-if(SPDLOG_BUILD_EXAMPLES)
- enable_testing()
- add_subdirectory(example)
-endif()
-
-### Install ###
-# * https://github.com/forexample/package-example
-set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
-
-set(config_install_dir "lib/cmake/${PROJECT_NAME}")
-set(include_install_dir "include")
-
-set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
-set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
-set(targets_export_name "${PROJECT_NAME}Targets")
-set(namespace "${PROJECT_NAME}::")
-
-include(CMakePackageConfigHelpers)
-write_basic_package_version_file(
- "${version_config}" COMPATIBILITY SameMajorVersion
-)
-
-# Note: use 'targets_export_name'
-configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
-
-install(
- TARGETS spdlog
- EXPORT "${targets_export_name}"
- INCLUDES DESTINATION "${include_install_dir}"
-)
-
-install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
-
-install(
- FILES "${project_config}" "${version_config}"
- DESTINATION "${config_install_dir}"
-)
-
-install(
- EXPORT "${targets_export_name}"
- NAMESPACE "${namespace}"
- DESTINATION "${config_install_dir}"
-)
+#
+# Copyright(c) 2015 Ruslan Baratov.
+# Distributed under the MIT License (http://opensource.org/licenses/MIT)
+#
+
+cmake_minimum_required(VERSION 3.0)
+project(spdlog VERSION 1.0.0)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_library(spdlog INTERFACE)
+
+option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
+
+target_include_directories(
+ spdlog
+ INTERFACE
+ "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>"
+)
+
+if(SPDLOG_BUILD_EXAMPLES)
+ enable_testing()
+ add_subdirectory(example)
+endif()
+
+### Install ###
+# * https://github.com/forexample/package-example
+set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
+
+set(config_install_dir "lib/cmake/${PROJECT_NAME}")
+set(include_install_dir "include")
+set(pkgconfig_install_dir "lib/pkgconfig")
+
+set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
+set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
+set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
+set(targets_export_name "${PROJECT_NAME}Targets")
+set(namespace "${PROJECT_NAME}::")
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ "${version_config}" COMPATIBILITY SameMajorVersion
+)
+
+# Note: use 'targets_export_name'
+configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
+configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
+
+install(
+ TARGETS spdlog
+ EXPORT "${targets_export_name}"
+ INCLUDES DESTINATION "${include_install_dir}"
+)
+
+install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
+
+install(
+ FILES "${project_config}" "${version_config}"
+ DESTINATION "${config_install_dir}"
+)
+
+install(
+ FILES "${pkg_config}"
+ DESTINATION "${pkgconfig_install_dir}"
+)
+
+install(
+ EXPORT "${targets_export_name}"
+ NAMESPACE "${namespace}"
+ DESTINATION "${config_install_dir}"
+)
diff --git a/LICENSE b/LICENSE
index a199709..806124d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2015 Gabi Melman.
+Copyright (c) 2016 Gabi Melman.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index bc55968..6416e45 100644
--- a/README.md
+++ b/README.md
@@ -1,157 +1,158 @@
-# 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 source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler
-
-## Platforms
- * Linux (gcc 4.8.1+, clang 3.5+)
- * Windows (visual studio 2013+, cygwin/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 1.54|glog |easylogging |spdlog|
-|-------|:-------:|:-----:|----------:|------:|
-|1| 4.169s |1.066s |0.975s |0.302s|
-|10| 6.180s |3.032s |2.857s |0.968s|
-|100| 5.981s |1.139s |4.512s |0.497s|
-
-
-#### Asynchronous mode
-Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
-
-|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
-|:-------|:-----:|-------------------------:|
-|1| 1.850s |0.216s |
-|10| 0.943s |0.173s|
-|100| 0.959s |0.202s|
-
-
-
-
-## Usage Example
-```c++
-#include <iostream>
-#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
+
+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 source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler
+
+## Platforms
+ * Linux (gcc 4.8.1+, clang 3.5+)
+ * Windows (visual studio 2013+, cygwin/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 (colors supported).
+ * 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 1.54|glog |easylogging |spdlog|
+|-------|:-------:|:-----:|----------:|------:|
+|1| 4.169s |1.066s |0.975s |0.302s|
+|10| 6.180s |3.032s |2.857s |0.968s|
+|100| 5.981s |1.139s |4.512s |0.497s|
+
+
+#### Asynchronous mode
+Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
+
+|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
+|:-------|:-----:|-------------------------:|
+|1| 1.850s |0.216s |
+|10| 0.943s |0.173s|
+|100| 0.959s |0.202s|
+
+
+
+
+## Usage Example
+```c++
+#include <iostream>
+#include "spdlog/spdlog.h"
+
+int main(int, char* [])
+{
+ namespace spd = spdlog;
+ try
+ {
+ // console logger (multithreaded and with color)
+ auto console = spd::stdout_logger_mt("console", true);
+ 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.
diff --git a/bench/Makefile b/bench/Makefile
index 6331f21..8416d04 100644
--- a/bench/Makefile
+++ b/bench/Makefile
@@ -1,65 +1,65 @@
-CXX ?= g++
-CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
-CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
-
-
-binaries=spdlog-bench spdlog-bench-mt spdlog-async zf_log-bench zf_log-bench-mt 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)
-
-
-ZF_LOG_FLAGS = -I../../zf_log.git/zf_log/
-zf_log-bench: zf_log-bench.cpp
- $(CXX) zf_log-bench.cpp -o zf_log-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
-
-zf_log-bench-mt: zf_log-bench-mt.cpp
- $(CXX) zf_log-bench-mt.cpp -o zf_log-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
-
-
-BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -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
-
-
-
+CXX ?= g++
+CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
+CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
+
+
+binaries=spdlog-bench spdlog-bench-mt spdlog-async zf_log-bench zf_log-bench-mt 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)
+
+
+ZF_LOG_FLAGS = -I../../zf_log.git/zf_log/
+zf_log-bench: zf_log-bench.cpp
+ $(CXX) zf_log-bench.cpp -o zf_log-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
+
+zf_log-bench-mt: zf_log-bench-mt.cpp
+ $(CXX) zf_log-bench-mt.cpp -o zf_log-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
+
+
+BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
+
+boost-bench: boost-bench.cpp
+ $(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
+
+boost-bench-mt: boost-bench-mt.cpp
+ $(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
+
+
+GLOG_FLAGS = -lglog
+glog-bench: glog-bench.cpp
+ $(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
+
+glog-bench-mt: glog-bench-mt.cpp
+ $(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
+
+
+G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
+g2log-async: g2log-async.cpp
+ $(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
+
+
+EASYL_FLAGS = -I../../easylogging/src/
+easylogging-bench: easylogging-bench.cpp
+ $(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
+easylogging-bench-mt: easylogging-bench-mt.cpp
+ $(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -f *.o logs/* $(binaries)
+
+
+rebuild: clean all
+
+
+
diff --git a/bench/run_all.sh b/bench/run_all.sh
index 92fcd9e..fcecc37 100755
--- a/bench/run_all.sh
+++ b/bench/run_all.sh
@@ -1,76 +1,76 @@
-#~/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 zf_log-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 zf_log-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 zf_log-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;
+#~/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 zf_log-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 zf_log-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 zf_log-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;
diff --git a/bench/zf_log-bench-mt.cpp b/bench/zf_log-bench-mt.cpp
index 3463d7e..aace277 100644
--- a/bench/zf_log-bench-mt.cpp
+++ b/bench/zf_log-bench-mt.cpp
@@ -11,21 +11,21 @@ int g_fd;
static void output_callback(zf_log_message *msg)
{
- *msg->p = '\n';
- write(g_fd, msg->buf, msg->p - msg->buf + 1);
+ *msg->p = '\n';
+ write(g_fd, msg->buf, msg->p - msg->buf + 1);
}
using namespace std;
int main(int argc, char* argv[])
{
- g_fd = open(g_path, O_APPEND|O_CREAT|O_WRONLY);
- if (0 > g_fd)
- {
- ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
- return -1;
- }
- zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
+ g_fd = open(g_path, O_APPEND|O_CREAT|O_WRONLY);
+ if (0 > g_fd)
+ {
+ ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
+ return -1;
+ }
+ zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
int thread_count = 10;
if(argc > 1)
@@ -42,7 +42,7 @@ int main(int argc, char* argv[])
{
int counter = ++msg_counter;
if (counter > howmany) break;
- ZF_LOGI("zf_log message #%i: This is some text for your pleasure", counter);
+ ZF_LOGI("zf_log message #%i: This is some text for your pleasure", counter);
}
}));
}
@@ -51,6 +51,6 @@ int main(int argc, char* argv[])
{
t.join();
};
- close(g_fd);
+ close(g_fd);
return 0;
}
diff --git a/bench/zf_log-bench.cpp b/bench/zf_log-bench.cpp
index dfa2892..a6e3e1f 100644
--- a/bench/zf_log-bench.cpp
+++ b/bench/zf_log-bench.cpp
@@ -6,22 +6,23 @@ static FILE *g_f;
static void output_callback(zf_log_message *msg)
{
- *msg->p = '\n';
- fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_f);
+ *msg->p = '\n';
+ fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_f);
}
int main(int, char* [])
{
- g_f = fopen(g_path, "wb");
- if (!g_f) {
- ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
- return -1;
- }
- zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
+ g_f = fopen(g_path, "wb");
+ if (!g_f)
+ {
+ ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
+ return -1;
+ }
+ zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
const int howmany = 1000000;
for(int i = 0 ; i < howmany; ++i)
- ZF_LOGI("zf_log message #%i: This is some text for your pleasure", i);
- fclose(g_f);
+ ZF_LOGI("zf_log message #%i: This is some text for your pleasure", i);
+ fclose(g_f);
return 0;
}
diff --git a/cmake/spdlog.pc.in b/cmake/spdlog.pc.in
new file mode 100644
index 0000000..2c94a0a
--- /dev/null
+++ b/cmake/spdlog.pc.in
@@ -0,0 +1,6 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+includedir=${prefix}/include
+
+Name: @PROJECT_NAME@
+Description: Super fast C++ logging library.
+Version: @PROJECT_VERSION@
diff --git a/example/example.cpp b/example/example.cpp
index ac45bdd..b3d6c43 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -5,22 +5,27 @@
//
// spdlog usage example
//
+#include "spdlog/spdlog.h"
+
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
-#include "spdlog/spdlog.h"
+#include <memory>
+void async_example();
+void syslog_example();
+
+namespace spd = spdlog;
int main(int, char*[])
{
- namespace spd = spdlog;
try
{
- //Create console, multithreaded logger
- auto console = spd::stdout_logger_mt("console");
+ // Multithreaded color console
+ auto console = spd::stdout_logger_mt("console", true);
console->info("Welcome to spdlog!");
console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1;
- //Formatting examples
+ // 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);
@@ -30,64 +35,41 @@ int main(int, char*[])
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
- //
+ spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
+
// 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..");
+ console->debug("This message shold be displayed..");
- //
// 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");
- for (int i = 0; i < 100; ++i)
- async_file->info("Async message #{}", i);
-
- //
- // 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
+ async_example();
+
+ // syslog example. linux/osx only..
+ syslog_example();
- //
- //Release and close all loggers
- //
+
+ // Release and close all loggers
spdlog::drop_all();
}
@@ -100,6 +82,26 @@ int main(int, char*[])
}
+void async_example()
+{
+ size_t q_size = 4096; //queue size must be power of 2
+ spdlog::set_async_mode(q_size);
+ auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
+ for (int i = 0; i < 100; ++i)
+ async_file->info("Async message #{}", i);
+}
+
+//syslog example (linux/osx only)
+void syslog_example()
+{
+#if defined (__linux__) || defined(__APPLE__)
+ 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
+}
+
+
// Example of user defined class with operator<<
class some_class {};
std::ostream& operator<<(std::ostream& os, const some_class&)
diff --git a/example/example.vcxproj b/example/example.vcxproj
index a8218f4..b7988fc 100644
--- a/example/example.vcxproj
+++ b/example/example.vcxproj
@@ -1,90 +1,90 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="example.cpp" />
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
- <Keyword>Win32Proj</Keyword>
- <RootNamespace>.</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <LinkIncremental>true</LinkIncremental>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <LinkIncremental>false</LinkIncremental>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PrecompiledHeaderFile />
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PrecompiledHeaderFile />
- <PrecompiledHeaderOutputFile />
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- </Link>
- </ItemDefinitionGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="example.cpp" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>.</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PrecompiledHeaderFile />
+ <PrecompiledHeaderOutputFile />
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PrecompiledHeaderFile />
+ <PrecompiledHeaderOutputFile />
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
</Project>
\ No newline at end of file
diff --git a/example/logs/.gitignore b/example/logs/.gitignore
index 5e7d273..960fe79 100644
--- a/example/logs/.gitignore
+++ b/example/logs/.gitignore
@@ -1,4 +1,2 @@
-# Ignore everything in this directory
-*
-# Except this file
-!.gitignore
+*.txt
+
diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h
index be21501..786eb02 100644
--- a/include/spdlog/async_logger.h
+++ b/include/spdlog/async_logger.h
@@ -13,7 +13,7 @@
// 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..
+// Upon destruction, logs all remaining messages in the queue before destructing..
#include <spdlog/common.h>
#include <spdlog/logger.h>
@@ -41,21 +41,24 @@ public:
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
- const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
- const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
- const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
void flush() override;
@@ -71,4 +74,3 @@ private:
#include <spdlog/details/async_logger_impl.h>
-
diff --git a/include/spdlog/common.h b/include/spdlog/common.h
index 269c881..2156505 100644
--- a/include/spdlog/common.h
+++ b/include/spdlog/common.h
@@ -5,20 +5,30 @@
#pragma once
+
#include <string>
#include <initializer_list>
#include <chrono>
#include <memory>
+#include <atomic>
#include <exception>
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+#include <codecvt>
+#include <locale>
+#endif
-//visual studio does not support noexcept yet
-#ifndef _MSC_VER
-#define SPDLOG_NOEXCEPT noexcept
-#else
+#include <spdlog/details/null_mutex.h>
+
+
+//visual studio upto 2013 does not support noexcept
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT throw()
+#else
+#define SPDLOG_NOEXCEPT noexcept
#endif
+
namespace spdlog
{
@@ -34,7 +44,11 @@ using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr < sinks::sink >;
using sinks_init_list = std::initializer_list < sink_ptr >;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
-
+#if defined(SPDLOG_NO_ATOMIC_LEVELS)
+using level_t = details::null_atomic_int;
+#else
+using level_t = std::atomic_int;
+#endif
//Log level enum
namespace level
@@ -95,4 +109,25 @@ private:
};
+//
+// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
+//
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+#define SPDLOG_FILENAME_T(s) L ## s
+using filename_t = std::wstring;
+inline std::string filename_to_str(const filename_t& filename)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
+ return c.to_bytes(filename);
+}
+#else
+#define SPDLOG_FILENAME_T(s) s
+using filename_t = std::string;
+
+inline std::string filename_to_str(const filename_t& filename)
+{
+ return filename;
+}
+#endif
+
} //spdlog
diff --git a/include/spdlog/details/async_log_helper.h b/include/spdlog/details/async_log_helper.h
index 8555ef0..eb7f476 100644
--- a/include/spdlog/details/async_log_helper.h
+++ b/include/spdlog/details/async_log_helper.h
@@ -121,7 +121,8 @@ public:
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
- const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
void log(const details::log_msg& msg);
@@ -157,12 +158,15 @@ private:
// auto periodic sink flush parameter
const std::chrono::milliseconds _flush_interval_ms;
+ // worker thread teardown callback
+ const std::function<void()> _worker_teardown_cb;
+
// worker thread
std::thread _worker_thread;
void push_msg(async_msg&& new_msg);
- // throw last worker thread exception or if worker thread is not active
+ // throw last worker thread exception or if worker thread is not active
void throw_if_bad_worker();
// worker thread main loop
@@ -190,7 +194,8 @@ inline spdlog::details::async_log_helper::async_log_helper(
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
- const std::chrono::milliseconds& flush_interval_ms):
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb):
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
@@ -199,6 +204,7 @@ inline spdlog::details::async_log_helper::async_log_helper(
_overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb),
_flush_interval_ms(flush_interval_ms),
+ _worker_teardown_cb(worker_teardown_cb),
_worker_thread(&async_log_helper::worker_loop, this)
{}
@@ -216,7 +222,7 @@ inline spdlog::details::async_log_helper::~async_log_helper()
}
-//Try to push and block until succeeded
+//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{
push_msg(async_msg(msg));
@@ -224,7 +230,6 @@ inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
}
-//Try to push and block until succeeded
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
{
throw_if_bad_worker();
@@ -255,6 +260,7 @@ inline void spdlog::details::async_log_helper::worker_loop()
auto last_pop = details::os::now();
auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush));
+ if (_worker_teardown_cb) _worker_teardown_cb();
}
catch (const std::exception& ex)
{
@@ -267,7 +273,7 @@ inline void spdlog::details::async_log_helper::worker_loop()
}
// process next message in the queue
-// return true if this thread should still be active (no msg with level::off was received)
+// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{
@@ -309,6 +315,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
}
}
+// flush all sinks if _flush_interval_ms has expired
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
{
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
@@ -320,34 +327,37 @@ inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::
_flush_requested = false;
}
}
+
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
+// spin, yield or sleep. use the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
{
- using std::chrono::milliseconds;
using namespace std::this_thread;
-
+ using std::chrono::milliseconds;
+ using std::chrono::microseconds;
+
auto time_since_op = now - last_op_time;
-
- // spin upto 1 ms
- if (time_since_op <= milliseconds(1))
+
+ // spin upto 50 micros
+ if (time_since_op <= microseconds(50))
return;
-
- // yield upto 10ms
- if (time_since_op <= milliseconds(10))
+
+ // yield upto 150 micros
+ if (time_since_op <= microseconds(100))
return yield();
- // sleep for half of duration since last op
- if (time_since_op <= milliseconds(100))
- return sleep_for(time_since_op / 2);
+ // sleep for 20 ms upto 200 ms
+ if (time_since_op <= milliseconds(200))
+ return sleep_for(milliseconds(20));
- return sleep_for(milliseconds(100));
+ // sleep for 200 ms
+ return sleep_for(milliseconds(200));
}
// throw if the worker thread threw an exception or not active
diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h
index 140d45f..ebacdb5 100644
--- a/include/spdlog/details/async_logger_impl.h
+++ b/include/spdlog/details/async_logger_impl.h
@@ -23,9 +23,10 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
- const std::chrono::milliseconds& flush_interval_ms) :
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb) :
logger(logger_name, begin, end),
- _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms))
+ _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
{
}
@@ -34,19 +35,21 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
- const std::chrono::milliseconds& flush_interval_ms) :
- async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {}
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb) :
+ async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
- const std::chrono::milliseconds& flush_interval_ms) :
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name,
{
single_sink
-}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {}
+ }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline void spdlog::async_logger::flush()
diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h
index e563d00..024a21b 100644
--- a/include/spdlog/details/file_helper.h
+++ b/include/spdlog/details/file_helper.h
@@ -43,11 +43,11 @@ public:
}
- void open(const std::string& fname, bool truncate = false)
+ void open(const filename_t& fname, bool truncate = false)
{
close();
- const char* mode = truncate ? "wb" : "ab";
+ auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries)
{
@@ -57,7 +57,7 @@ public:
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
}
- throw spdlog_ex("Failed opening file " + fname + " for writing");
+ throw spdlog_ex("Failed opening file " + filename_to_str(_filename) + " for writing");
}
void reopen(bool truncate)
@@ -88,7 +88,7 @@ public:
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
- throw spdlog_ex("Failed writing to file " + _filename);
+ throw spdlog_ex("Failed writing to file " + filename_to_str(_filename));
if (_force_flush)
std::fflush(_fd);
@@ -98,19 +98,19 @@ public:
long size()
{
if (!_fd)
- throw spdlog_ex("Cannot use size() on closed file " + _filename);
+ throw spdlog_ex("Cannot use size() on closed file " + filename_to_str(_filename));
auto pos = ftell(_fd);
if (fseek(_fd, 0, SEEK_END) != 0)
- throw spdlog_ex("fseek failed on file " + _filename);
+ throw spdlog_ex("fseek failed on file " + filename_to_str(_filename));
auto file_size = ftell(_fd);
if(fseek(_fd, pos, SEEK_SET) !=0)
- throw spdlog_ex("fseek failed on file " + _filename);
+ throw spdlog_ex("fseek failed on file " + filename_to_str(_filename));
if (file_size == -1)
- throw spdlog_ex("ftell failed on file " + _filename);
+ throw spdlog_ex("ftell failed on file " + filename_to_str(_filename));
return file_size;
@@ -118,12 +118,12 @@ public:
}
- const std::string& filename() const
+ const filename_t& filename() const
{
return _filename;
}
- static bool file_exists(const std::string& name)
+ static bool file_exists(const filename_t& name)
{
return os::file_exists(name);
@@ -133,7 +133,7 @@ public:
private:
FILE* _fd;
- std::string _filename;
+ filename_t _filename;
bool _force_flush;
diff --git a/include/spdlog/details/format.cc b/include/spdlog/details/format.cc
index c77e1ef..cd246e4 100644
--- a/include/spdlog/details/format.cc
+++ b/include/spdlog/details/format.cc
@@ -1,949 +1,1066 @@
-/*
-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 <string.h>
-
-#include <cctype>
-#include <cerrno>
-#include <climits>
-#include <cmath>
-#include <cstdarg>
-#include <cstddef> // for std::ptrdiff_t
-
-#if defined(_WIN32) && defined(__MINGW32__)
-# include <cstring>
-#endif
-
-#if FMT_USE_WINDOWS_H
-# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
-# include <windows.h>
-# else
-# define NOMINMAX
-# include <windows.h>
-# undef NOMINMAX
-# endif
-#endif
-
-using fmt::internal::Arg;
-
-#if FMT_EXCEPTIONS
-# define FMT_TRY try
-# define FMT_CATCH(x) catch (x)
-#else
-# define FMT_TRY if (true)
-# define FMT_CATCH(x) if (false)
-#endif
-
-#ifdef FMT_HEADER_ONLY
-# define FMT_FUNC inline
-#else
-# define FMT_FUNC
-#endif
-
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable: 4127) // conditional expression is constant
-# pragma warning(disable: 4702) // unreachable code
-// Disable deprecation warning for strerror. The latter is not called but
-// MSVC fails to detect it.
-# pragma warning(disable: 4996)
-#endif
-
-// Dummy implementations of strerror_r and strerror_s called if corresponding
-// system functions are not available.
-static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
- return fmt::internal::Null<>();
-}
-static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
- return fmt::internal::Null<>();
-}
-
-namespace fmt {
-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 <bool IsSigned>
-struct IntChecker {
- template <typename T>
- 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<true> {
- template <typename T>
- static bool fits_in_int(T value) {
- return value >= INT_MIN && value <= INT_MAX;
- }
- static bool fits_in_int(int) {
- return true;
- }
-};
-
-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<int>::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<IsZeroInt, bool> {
-public:
- template <typename T>
- bool visit_any_int(T value) {
- return value == 0;
- }
-};
-
-// Checks if an argument is a valid printf width specifier and sets
-// left alignment if it is negative.
-class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
-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 <typename T>
- unsigned visit_any_int(T value) {
- typedef typename fmt::internal::IntTraits<T>::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<unsigned>(width);
- }
-};
-
-class PrecisionHandler :
- public fmt::internal::ArgVisitor<PrecisionHandler, int> {
-public:
- void report_unhandled_arg() {
- FMT_THROW(fmt::FormatError("precision is not integer"));
- }
-
- template <typename T>
- int visit_any_int(T value) {
- if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
- FMT_THROW(fmt::FormatError("number is too big"));
- return static_cast<int>(value);
- }
-};
-
-// Converts an integer argument to an integral type T for printf.
-template <typename T>
-class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, 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) {}
-
- void visit_bool(bool value) {
- if (type_ != 's')
- visit_any_int(value);
- }
-
- template <typename U>
- 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<int>(static_cast<T>(value));
- }
- else {
- arg_.type = Arg::UINT;
- arg_.uint_value = static_cast<unsigned>(
- static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value));
- }
- }
- else {
- if (is_signed) {
- arg_.type = Arg::LONG_LONG;
- arg_.long_long_value =
- static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
- }
- else {
- arg_.type = Arg::ULONG_LONG;
- arg_.ulong_long_value =
- static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
- }
- }
- }
-};
-
-// Converts an integer argument to char for printf.
-class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {
-private:
- fmt::internal::Arg &arg_;
-
- FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
-
-public:
- explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {}
-
- template <typename T>
- void visit_any_int(T value) {
- arg_.type = Arg::CHAR;
- arg_.int_value = static_cast<char>(value);
- }
-};
-} // namespace
-
-namespace internal {
-
-template <typename Char>
-class PrintfArgFormatter :
- public ArgFormatterBase<PrintfArgFormatter<Char>, Char> {
-
- void write_null_pointer() {
- this->spec().type_ = 0;
- this->write("(nil)");
- }
-
- typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
-
-public:
- PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
- : ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s) {}
-
- void visit_bool(bool value) {
- FormatSpec &fmt_spec = this->spec();
- if (fmt_spec.type_ != 's')
- return this->visit_any_int(value);
- fmt_spec.type_ = 0;
- this->write(value);
- }
-
- void visit_char(int value) {
- const FormatSpec &fmt_spec = this->spec();
- BasicWriter<Char> &w = this->writer();
- if (fmt_spec.type_ && fmt_spec.type_ != 'c')
- w.write_int(value, fmt_spec);
- typedef typename BasicWriter<Char>::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<Char>(value);
- }
-
- void visit_cstring(const char *value) {
- if (value)
- Base::visit_cstring(value);
- else if (this->spec().type_ == 'p')
- write_null_pointer();
- else
- this->write("(null)");
- }
-
- void visit_pointer(const void *value) {
- if (value)
- return Base::visit_pointer(value);
- this->spec().type_ = 0;
- write_null_pointer();
- }
-
- void visit_custom(Arg::CustomValue c) {
- BasicFormatter<Char> formatter(ArgList(), this->writer());
- const Char format_str[] = { '}', 0 };
- const Char *format = format_str;
- c.format(&formatter, c.value, &format);
- }
-};
-} // 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 <typename T>
-int fmt::internal::CharTraits<char>::format_float(
- char *buffer, std::size_t size, const char *format,
- unsigned width, int precision, T value) {
- if (width == 0) {
- return precision < 0 ?
- FMT_SNPRINTF(buffer, size, format, value) :
- FMT_SNPRINTF(buffer, size, format, precision, value);
- }
- return precision < 0 ?
- FMT_SNPRINTF(buffer, size, format, width, value) :
- FMT_SNPRINTF(buffer, size, format, width, precision, value);
-}
-
-template <typename T>
-int fmt::internal::CharTraits<wchar_t>::format_float(
- wchar_t *buffer, std::size_t size, const wchar_t *format,
- unsigned width, int precision, T value) {
- if (width == 0) {
- return precision < 0 ?
- FMT_SWPRINTF(buffer, size, format, value) :
- FMT_SWPRINTF(buffer, size, format, precision, value);
- }
- return precision < 0 ?
- FMT_SWPRINTF(buffer, size, format, width, value) :
- FMT_SWPRINTF(buffer, size, format, width, precision, value);
-}
-
-template <typename T>
-const char fmt::internal::BasicData<T>::DIGITS[] =
- "0001020304050607080910111213141516171819"
- "2021222324252627282930313233343536373839"
- "4041424344454647484950515253545556575859"
- "6061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-
-#define FMT_POWERS_OF_10(factor) \
- factor * 10, \
- factor * 100, \
- factor * 1000, \
- factor * 10000, \
- factor * 100000, \
- factor * 1000000, \
- factor * 10000000, \
- factor * 100000000, \
- factor * 1000000000
-
-template <typename T>
-const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
- 0, FMT_POWERS_OF_10(1)
-};
-
-template <typename T>
-const uint64_t fmt::internal::BasicData<T>::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<unsigned char>(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<unsigned>(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<int>(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<int>(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<LPWSTR>(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(...) {}
- fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
-}
-
-#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<char, INLINE_BUFFER_SIZE> 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(...) {}
- fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
-}
-
-template <typename Char>
-void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
- if (!map_.empty())
- return;
- typedef internal::NamedArg<Char> 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<const NamedArg*>(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<const NamedArg*>(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<const NamedArg*>(args.args_[i].pointer);
- map_.insert(Pair(named_arg->name, *named_arg));
- break;
- default:
- /*nothing*/
- ;
- }
- }
-}
-
-template <typename Char>
-void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
- FMT_THROW(std::runtime_error("buffer overflow"));
-}
-
-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<const internal::Arg*>(arg.pointer);
- default:
- /*nothing*/
- ;
- }
- return arg;
-}
-
-template <typename Char>
-void fmt::internal::PrintfFormatter<Char>::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 <typename Char>
-Arg fmt::internal::PrintfFormatter<Char>::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 <typename Char>
-unsigned fmt::internal::PrintfFormatter<Char>::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 <typename Char>
-void fmt::internal::PrintfFormatter<Char>::format(
- BasicWriter<Char> &writer, BasicCStringRef<Char> 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<signed char>(arg, *++s).visit(arg);
- else
- ArgConverter<short>(arg, *s).visit(arg);
- break;
- case 'l':
- if (*s == 'l')
- ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
- else
- ArgConverter<long>(arg, *s).visit(arg);
- break;
- case 'j':
- ArgConverter<intmax_t>(arg, *s).visit(arg);
- break;
- case 'z':
- ArgConverter<std::size_t>(arg, *s).visit(arg);
- break;
- case 't':
- ArgConverter<std::ptrdiff_t>(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<int>(arg, *s).visit(arg);
- }
-
- // Parse type.
- if (!*s)
- FMT_THROW(FormatError("invalid format string"));
- spec.type_ = static_cast<char>(*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<Char>(writer, spec).visit(arg);
- }
- write(writer, start, s);
-}
-
-FMT_FUNC void fmt::report_system_error(
- int error_code, fmt::StringRef message) FMT_NOEXCEPT{
- // 'fmt::' is for bcc32.
- fmt::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{
- // 'fmt::' is for bcc32.
- fmt::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] = static_cast<char>('0' + 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<int>(size);
-}
-
-#ifndef FMT_HEADER_ONLY
-
-template struct fmt::internal::BasicData<void>;
-
-// Explicit instantiations for char.
-
-template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
-
-template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
-
-template void fmt::internal::PrintfFormatter<char>::format(
- BasicWriter<char> &writer, CStringRef format);
-
-template int fmt::internal::CharTraits<char>::format_float(
- char *buffer, std::size_t size, const char *format,
- unsigned width, int precision, double value);
-
-template int fmt::internal::CharTraits<char>::format_float(
- char *buffer, std::size_t size, const char *format,
- unsigned width, int precision, long double value);
-
-// Explicit instantiations for wchar_t.
-
-template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
-
-template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
-
-template void fmt::internal::PrintfFormatter<wchar_t>::format(
- BasicWriter<wchar_t> &writer, WCStringRef format);
-
-template int fmt::internal::CharTraits<wchar_t>::format_float(
- wchar_t *buffer, std::size_t size, const wchar_t *format,
- unsigned width, int precision, double value);
-
-template int fmt::internal::CharTraits<wchar_t>::format_float(
- wchar_t *buffer, std::size_t size, const wchar_t *format,
- unsigned width, int precision, long double value);
-
-#endif // FMT_HEADER_ONLY
-
-#ifdef _MSC_VER
-# pragma warning(pop)
+/*
+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 <string.h>
+
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+#include <cstddef> // for std::ptrdiff_t
+
+#if defined(_WIN32) && defined(__MINGW32__)
+# include <cstring>
+#endif
+
+#if FMT_USE_WINDOWS_H
+# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
+# include <windows.h>
+# else
+# define NOMINMAX
+# include <windows.h>
+# undef NOMINMAX
+# endif
+#endif
+
+using fmt::internal::Arg;
+
+#if FMT_EXCEPTIONS
+# define FMT_TRY try
+# define FMT_CATCH(x) catch (x)
+#else
+# define FMT_TRY if (true)
+# define FMT_CATCH(x) if (false)
+#endif
+
+#ifdef FMT_HEADER_ONLY
+# define FMT_FUNC inline
+#else
+# define FMT_FUNC
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4127) // conditional expression is constant
+# pragma warning(disable: 4702) // unreachable code
+// Disable deprecation warning for strerror. The latter is not called but
+// MSVC fails to detect it.
+# pragma warning(disable: 4996)
+#endif
+
+// Dummy implementations of strerror_r and strerror_s called if corresponding
+// system functions are not available.
+static inline fmt::internal::Null<> strerror_r(int, char *, ...)
+{
+ return fmt::internal::Null<>();
+}
+static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...)
+{
+ return fmt::internal::Null<>();
+}
+
+namespace fmt {
+ 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 <bool IsSigned>
+ struct IntChecker
+ {
+ template <typename T>
+ 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<true>
+ {
+ template <typename T>
+ static bool fits_in_int(T value)
+ {
+ return value >= INT_MIN && value <= INT_MAX;
+ }
+ static bool fits_in_int(int)
+ {
+ return true;
+ }
+ };
+
+ 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 ";
+ // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+ std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+ typedef fmt::internal::IntTraits<int>::MainType MainType;
+ MainType abs_value = static_cast<MainType>(error_code);
+ if (internal::is_negative(error_code)) {
+ abs_value = 0 - abs_value;
+ ++error_code_size;
+ }
+ error_code_size += fmt::internal::count_digits(abs_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<IsZeroInt, bool>
+ {
+ public:
+ template <typename T>
+ bool visit_any_int(T value)
+ {
+ return value == 0;
+ }
+ };
+
+ // Checks if an argument is a valid printf width specifier and sets
+ // left alignment if it is negative.
+ class WidthHandler: public fmt::internal::ArgVisitor<WidthHandler, unsigned>
+ {
+ 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 <typename T>
+ unsigned visit_any_int(T value)
+ {
+ typedef typename fmt::internal::IntTraits<T>::MainType UnsignedType;
+ UnsignedType width = static_cast<UnsignedType>(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<unsigned>(width);
+ }
+ };
+
+ class PrecisionHandler:
+ public fmt::internal::ArgVisitor<PrecisionHandler, int>
+ {
+ public:
+ void report_unhandled_arg()
+ {
+ FMT_THROW(fmt::FormatError("precision is not integer"));
+ }
+
+ template <typename T>
+ int visit_any_int(T value)
+ {
+ if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
+ FMT_THROW(fmt::FormatError("number is too big"));
+ return static_cast<int>(value);
+ }
+ };
+
+ template <typename T, typename U>
+ struct is_same
+ {
+ enum
+ {
+ value = 0
+ };
+ };
+
+ template <typename T>
+ struct is_same<T, T>
+ {
+ enum
+ {
+ value = 1
+ };
+ };
+
+ // An argument visitor that converts an integer argument to T for printf,
+ // if T is an integral type. If T is void, the argument is converted to
+ // corresponding signed or unsigned type depending on the type specifier:
+ // 'd' and 'i' - signed, other - unsigned)
+ template <typename T = void>
+ class ArgConverter: public fmt::internal::ArgVisitor<ArgConverter<T>, 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)
+ {}
+
+ void visit_bool(bool value)
+ {
+ if (type_ != 's')
+ visit_any_int(value);
+ }
+
+ template <typename U>
+ void visit_any_int(U value)
+ {
+ bool is_signed = type_ == 'd' || type_ == 'i';
+ using fmt::internal::Arg;
+ typedef typename fmt::internal::Conditional<
+ is_same<T, void>::value, U, T>::type TargetType;
+ if (sizeof(TargetType) <= sizeof(int)) {
+ // Extra casts are used to silence warnings.
+ if (is_signed) {
+ arg_.type = Arg::INT;
+ arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
+ }
+ else {
+ arg_.type = Arg::UINT;
+ typedef typename fmt::internal::MakeUnsigned<TargetType>::Type Unsigned;
+ arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
+ }
+ }
+ else {
+ if (is_signed) {
+ arg_.type = Arg::LONG_LONG;
+ // glibc's printf doesn't sign extend arguments of smaller types:
+ // std::printf("%lld", -42); // prints "4294967254"
+ // but we don't have to do the same because it's a UB.
+ arg_.long_long_value = static_cast<fmt::LongLong>(value);
+ }
+ else {
+ arg_.type = Arg::ULONG_LONG;
+ arg_.ulong_long_value =
+ static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
+ }
+ }
+ }
+ };
+
+ // Converts an integer argument to char for printf.
+ class CharConverter: public fmt::internal::ArgVisitor<CharConverter, void>
+ {
+ private:
+ fmt::internal::Arg &arg_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
+
+ public:
+ explicit CharConverter(fmt::internal::Arg &arg): arg_(arg)
+ {}
+
+ template <typename T>
+ void visit_any_int(T value)
+ {
+ arg_.type = Arg::CHAR;
+ arg_.int_value = static_cast<char>(value);
+ }
+ };
+
+ // Write the content of w to os.
+ void write(std::ostream &os, fmt::Writer &w)
+ {
+ const char *data = w.data();
+ typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
+ UnsignedStreamSize size = w.size();
+ UnsignedStreamSize max_size =
+ internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
+ do {
+ UnsignedStreamSize n = size <= max_size ? size : max_size;
+ os.write(data, static_cast<std::streamsize>(n));
+ data += n;
+ size -= n;
+ } while (size != 0);
+ }
+ } // namespace
+
+ namespace internal {
+
+ template <typename Char>
+ class PrintfArgFormatter:
+ public ArgFormatterBase<PrintfArgFormatter<Char>, Char>
+ {
+
+ void write_null_pointer()
+ {
+ this->spec().type_ = 0;
+ this->write("(nil)");
+ }
+
+ typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
+
+ public:
+ PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
+ : ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s)
+ {}
+
+ void visit_bool(bool value)
+ {
+ FormatSpec &fmt_spec = this->spec();
+ if (fmt_spec.type_ != 's')
+ return this->visit_any_int(value);
+ fmt_spec.type_ = 0;
+ this->write(value);
+ }
+
+ void visit_char(int value)
+ {
+ const FormatSpec &fmt_spec = this->spec();
+ BasicWriter<Char> &w = this->writer();
+ if (fmt_spec.type_ && fmt_spec.type_ != 'c')
+ w.write_int(value, fmt_spec);
+ typedef typename BasicWriter<Char>::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<Char>(value);
+ }
+
+ void visit_cstring(const char *value)
+ {
+ if (value)
+ Base::visit_cstring(value);
+ else if (this->spec().type_ == 'p')
+ write_null_pointer();
+ else
+ this->write("(null)");
+ }
+
+ void visit_pointer(const void *value)
+ {
+ if (value)
+ return Base::visit_pointer(value);
+ this->spec().type_ = 0;
+ write_null_pointer();
+ }
+
+ void visit_custom(Arg::CustomValue c)
+ {
+ BasicFormatter<Char> formatter(ArgList(), this->writer());
+ const Char format_str[] = { '}', 0 };
+ const Char *format = format_str;
+ c.format(&formatter, c.value, &format);
+ }
+ };
+ } // 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 <typename T>
+int fmt::internal::CharTraits<char>::format_float(
+ char *buffer, std::size_t size, const char *format,
+ unsigned width, int precision, T value)
+{
+ if (width == 0) {
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, value) :
+ FMT_SNPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, width, value) :
+ FMT_SNPRINTF(buffer, size, format, width, precision, value);
+}
+
+template <typename T>
+int fmt::internal::CharTraits<wchar_t>::format_float(
+ wchar_t *buffer, std::size_t size, const wchar_t *format,
+ unsigned width, int precision, T value)
+{
+ if (width == 0) {
+ return precision < 0 ?
+ FMT_SWPRINTF(buffer, size, format, value) :
+ FMT_SWPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SWPRINTF(buffer, size, format, width, value) :
+ FMT_SWPRINTF(buffer, size, format, width, precision, value);
+}
+
+template <typename T>
+const char fmt::internal::BasicData<T>::DIGITS[] =
+"0001020304050607080910111213141516171819"
+"2021222324252627282930313233343536373839"
+"4041424344454647484950515253545556575859"
+"6061626364656667686970717273747576777879"
+"8081828384858687888990919293949596979899";
+
+#define FMT_POWERS_OF_10(factor) \
+ factor * 10, \
+ factor * 100, \
+ factor * 1000, \
+ factor * 10000, \
+ factor * 100000, \
+ factor * 1000000, \
+ factor * 10000000, \
+ factor * 100000000, \
+ factor * 1000000000
+
+template <typename T>
+const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
+ 0, FMT_POWERS_OF_10(1)
+};
+
+template <typename T>
+const uint64_t fmt::internal::BasicData<T>::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<unsigned char>(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<unsigned>(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<int>(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<int>(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
+{
+ FMT_TRY{
+ MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
+ buffer.resize(INLINE_BUFFER_SIZE);
+ for (;;) {
+ wchar_t *system_message = &buffer[0];
+ int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ system_message, static_cast<uint32_t>(buffer.size()), 0);
+ if (result != 0) {
+ UTF16ToUTF8 utf8_message;
+ if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
+ out << message << ": " << utf8_message;
+ return;
+ }
+ break;
+ }
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ break; // Can't get error message, report error code instead.
+ buffer.resize(buffer.size() * 2);
+ }
+ } FMT_CATCH(...)
+ {}
+ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
+}
+
+#endif // FMT_USE_WINDOWS_H
+
+FMT_FUNC void fmt::internal::format_system_error(
+ fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT
+{
+ FMT_TRY{
+ MemoryBuffer<char, INLINE_BUFFER_SIZE> 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(...)
+ {}
+ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
+}
+
+template <typename Char>
+void fmt::internal::ArgMap<Char>::init(const ArgList &args)
+{
+ if (!map_.empty())
+ return;
+ typedef internal::NamedArg<Char> 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<const NamedArg*>(args.values_[i].pointer);
+ map_.push_back(Pair(named_arg->name, *named_arg));
+ break;
+ default:
+ /*nothing*/;
+ }
+ }
+ return;
+ }
+ for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
+ internal::Arg::Type arg_type = args.type(i);
+ if (arg_type == internal::Arg::NAMED_ARG) {
+ named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
+ map_.push_back(Pair(named_arg->name, *named_arg));
+ }
+ }
+ for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
+ switch (args.args_[i].type) {
+ case internal::Arg::NONE:
+ return;
+ case internal::Arg::NAMED_ARG:
+ named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
+ map_.push_back(Pair(named_arg->name, *named_arg));
+ break;
+ default:
+ /*nothing*/;
+ }
+ }
+}
+
+template <typename Char>
+void fmt::internal::FixedBuffer<Char>::grow(std::size_t)
+{
+ FMT_THROW(std::runtime_error("buffer overflow"));
+}
+
+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<const internal::Arg*>(arg.pointer);
+ break;
+ default:
+ /*nothing*/;
+ }
+ return arg;
+}
+
+template <typename Char>
+void fmt::internal::PrintfFormatter<Char>::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 <typename Char>
+Arg fmt::internal::PrintfFormatter<Char>::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 <typename Char>
+unsigned fmt::internal::PrintfFormatter<Char>::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 <typename Char>
+void fmt::internal::PrintfFormatter<Char>::format(
+ BasicWriter<Char> &writer, BasicCStringRef<Char> 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_ = static_cast<int>(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_ &= ~to_unsigned<int>(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<signed char>(arg, *++s).visit(arg);
+ else
+ ArgConverter<short>(arg, *s).visit(arg);
+ break;
+ case 'l':
+ if (*s == 'l')
+ ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
+ else
+ ArgConverter<long>(arg, *s).visit(arg);
+ break;
+ case 'j':
+ ArgConverter<intmax_t>(arg, *s).visit(arg);
+ break;
+ case 'z':
+ ArgConverter<std::size_t>(arg, *s).visit(arg);
+ break;
+ case 't':
+ ArgConverter<std::ptrdiff_t>(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<void>(arg, *s).visit(arg);
+ }
+
+ // Parse type.
+ if (!*s)
+ FMT_THROW(FormatError("invalid format string"));
+ spec.type_ = static_cast<char>(*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<Char>(writer, spec).visit(arg);
+ }
+ write(writer, start, s);
+}
+
+FMT_FUNC void fmt::report_system_error(
+ int error_code, fmt::StringRef message) FMT_NOEXCEPT
+{
+ // 'fmt::' is for bcc32.
+ fmt::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
+{
+ // 'fmt::' is for bcc32.
+ fmt::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);
+ write(os, w);
+}
+
+FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args)
+{
+ char escape[] = "\x1b[30m";
+ escape[3] = static_cast<char>('0' + c);
+ std::fputs(escape, stdout);
+ print(format, args);
+ std::fputs(RESET_COLOR, stdout);
+}
+
+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<int>(size);
+}
+
+FMT_FUNC int fmt::fprintf(std::ostream &os, CStringRef format, ArgList args)
+{
+ MemoryWriter w;
+ printf(w, format, args);
+ write(os, w);
+ return static_cast<int>(w.size());
+}
+
+#ifndef FMT_HEADER_ONLY
+
+template struct fmt::internal::BasicData<void>;
+
+// Explicit instantiations for char.
+
+template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
+
+template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
+
+template void fmt::internal::PrintfFormatter<char>::format(
+ BasicWriter<char> &writer, CStringRef format);
+
+template int fmt::internal::CharTraits<char>::format_float(
+ char *buffer, std::size_t size, const char *format,
+ unsigned width, int precision, double value);
+
+template int fmt::internal::CharTraits<char>::format_float(
+ char *buffer, std::size_t size, const char *format,
+ unsigned width, int precision, long double value);
+
+// Explicit instantiations for wchar_t.
+
+template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
+
+template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
+
+template void fmt::internal::PrintfFormatter<wchar_t>::format(
+ BasicWriter<wchar_t> &writer, WCStringRef format);
+
+template int fmt::internal::CharTraits<wchar_t>::format_float(
+ wchar_t *buffer, std::size_t size, const wchar_t *format,
+ unsigned width, int precision, double value);
+
+template int fmt::internal::CharTraits<wchar_t>::format_float(
+ wchar_t *buffer, std::size_t size, const wchar_t *format,
+ unsigned width, int precision, long double value);
+
+#endif // FMT_HEADER_ONLY
+
+#ifdef _MSC_VER
+# pragma warning(pop)
#endif
\ No newline at end of file
diff --git a/include/spdlog/details/format.h b/include/spdlog/details/format.h
index 2e98d67..8046e61 100644
--- a/include/spdlog/details/format.h
+++ b/include/spdlog/details/format.h
@@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_
+
//Added to spdlog version for header only usage
#define FMT_HEADER_ONLY
@@ -36,14 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define FMT_USE_WINDOWS_H 0
#endif
-#if defined _MSC_VER && _MSC_VER <= 1500
-typedef unsigned int uint32_t;
-typedef unsigned long long uint64_t;
-typedef long long intmax_t;
-#else
-#include <stdint.h>
-#endif
-
#include <cassert>
#include <cmath>
#include <cstdio>
@@ -52,7 +45,8 @@ typedef long long intmax_t;
#include <memory>
#include <stdexcept>
#include <string>
-#include <map>
+#include <vector>
+#include <utility>
#ifndef FMT_USE_IOSTREAMS
# define FMT_USE_IOSTREAMS 1
@@ -72,6 +66,14 @@ typedef long long intmax_t;
# include <iterator>
#endif
+#if defined(_MSC_VER) && _MSC_VER <= 1500
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 intmax_t;
+#else
+#include <stdint.h>
+#endif
+
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
@@ -83,46 +85,6 @@ typedef long long intmax_t;
# define FMT_API
#endif
-#ifdef _MSC_VER
-# include <intrin.h> // _BitScanReverse, _BitScanReverse64
-
-namespace fmt
-{
-namespace internal
-{
-# pragma intrinsic(_BitScanReverse)
-inline uint32_t clz(uint32_t x)
-{
- unsigned long r = 0;
- _BitScanReverse(&r, x);
- return 31 - r;
-}
-# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
-
-# ifdef _WIN64
-# pragma intrinsic(_BitScanReverse64)
-# endif
-
-inline uint32_t clzll(uint64_t x)
-{
- unsigned long r = 0;
-# ifdef _WIN64
- _BitScanReverse64(&r, x);
-# else
- // Scan the high 32 bits.
- if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
- return 63 - (r + 32);
-
- // Scan the low 32 bits.
- _BitScanReverse(&r, static_cast<uint32_t>(x));
-# endif
- 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__
@@ -197,21 +159,6 @@ inline uint32_t clzll(uint64_t x)
# include <utility> // for std::move
#endif
-// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature).
-#ifndef FMT_USE_NOEXCEPT
-# define FMT_USE_NOEXCEPT 0
-#endif
-
-#ifndef FMT_NOEXCEPT
-# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
- (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
- _MSC_VER >= 1900
-# define FMT_NOEXCEPT noexcept
-# else
-# define FMT_NOEXCEPT throw()
-# endif
-#endif
-
// Check if exceptions are disabled.
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
# define FMT_EXCEPTIONS 0
@@ -231,6 +178,25 @@ inline uint32_t clzll(uint64_t x)
# endif
#endif
+// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature).
+#ifndef FMT_USE_NOEXCEPT
+# define FMT_USE_NOEXCEPT 0
+#endif
+
+#ifndef FMT_NOEXCEPT
+# if FMT_EXCEPTIONS
+# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
+ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
+ _MSC_VER >= 1900
+# define FMT_NOEXCEPT noexcept
+# else
+# define FMT_NOEXCEPT throw()
+# endif
+# else
+# define FMT_NOEXCEPT
+# endif
+#endif
+
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#ifndef FMT_USE_DELETED_FUNCTIONS
@@ -264,6 +230,71 @@ inline uint32_t clzll(uint64_t x)
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
+
+#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
+
+// Some compilers masquerade as both MSVC and GCC-likes or
+// otherwise support __builtin_clz and __builtin_clzll, so
+// only define FMT_BUILTIN_CLZ using the MSVC intrinsics
+// if the clz and clzll builtins are not available.
+#if defined(_MSC_VER) && !defined(FMT_BUILTIN_CLZLL)
+# include <intrin.h> // _BitScanReverse, _BitScanReverse64
+
+namespace fmt
+{
+namespace internal
+{
+# pragma intrinsic(_BitScanReverse)
+inline uint32_t clz(uint32_t x)
+{
+ unsigned long r = 0;
+ _BitScanReverse(&r, x);
+
+ assert(x != 0);
+ // Static analysis complains about using uninitialized data
+ // "r", but the only way that can happen is if "x" is 0,
+ // which the callers guarantee to not happen.
+# pragma warning(suppress: 6102)
+ return 31 - r;
+}
+# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
+
+# ifdef _WIN64
+# pragma intrinsic(_BitScanReverse64)
+# endif
+
+inline uint32_t clzll(uint64_t x)
+{
+ unsigned long r = 0;
+# ifdef _WIN64
+ _BitScanReverse64(&r, x);
+# else
+ // Scan the high 32 bits.
+ if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
+ return 63 - (r + 32);
+
+ // Scan the low 32 bits.
+ _BitScanReverse(&r, static_cast<uint32_t>(x));
+# endif
+
+ assert(x != 0);
+ // Static analysis complains about using uninitialized data
+ // "r", but the only way that can happen is if "x" is 0,
+ // which the callers guarantee to not happen.
+# pragma warning(suppress: 6102)
+ return 63 - r;
+}
+# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n)
+}
+}
+#endif
+
namespace fmt
{
namespace internal
@@ -322,7 +353,7 @@ namespace std
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891
// and the same for isnan and signbit.
template <>
-class numeric_limits<fmt::internal::DummyInt> :
+class numeric_limits<fmt::internal::DummyInt>:
public std::numeric_limits<int>
{
public:
@@ -388,7 +419,14 @@ class BasicWriter;
typedef BasicWriter<char> Writer;
typedef BasicWriter<wchar_t> WWriter;
+namespace internal
+{
template <typename Char>
+class BasicArgFormatter;
+}
+
+template <typename CharType,
+ typename ArgFormatter = internal::BasicArgFormatter<CharType> >
class BasicFormatter;
template <typename Char, typename T>
@@ -427,7 +465,8 @@ private:
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) {}
+ BasicStringRef(const Char *s, std::size_t size): data_(s), size_(size)
+ {}
/**
\rst
@@ -436,7 +475,8 @@ public:
\endrst
*/
BasicStringRef(const Char *s)
- : data_(s), size_(std::char_traits<Char>::length(s)) {}
+ : data_(s), size_(std::char_traits<Char>::length(s))
+ {}
/**
\rst
@@ -444,7 +484,8 @@ public:
\endrst
*/
BasicStringRef(const std::basic_string<Char> &s)
- : data_(s.c_str()), size_(s.size()) {}
+ : data_(s.c_str()), size_(s.size())
+ {}
/**
\rst
@@ -456,7 +497,7 @@ public:
return std::basic_string<Char>(data_, size_);
}
- /** Returns the pointer to a C string. */
+ /** Returns a pointer to the string data. */
const Char *data() const
{
return data_;
@@ -540,14 +581,16 @@ private:
public:
/** Constructs a string reference object from a C string. */
- BasicCStringRef(const Char *s) : data_(s) {}
+ BasicCStringRef(const Char *s): data_(s)
+ {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
- BasicCStringRef(const std::basic_string<Char> &s) : data_(s.c_str()) {}
+ BasicCStringRef(const std::basic_string<Char> &s): data_(s.c_str())
+ {}
/** Returns the pointer to a C string. */
const Char *c_str() const
@@ -562,18 +605,49 @@ typedef BasicCStringRef<wchar_t> WCStringRef;
/**
A formatting error such as invalid format string.
*/
-class FormatError : public std::runtime_error
+class FormatError: public std::runtime_error
{
public:
explicit FormatError(CStringRef message)
- : std::runtime_error(message.c_str()) {}
+ : std::runtime_error(message.c_str())
+ {}
};
namespace internal
{
+
+// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T.
+template <typename T>
+struct MakeUnsigned
+{
+ typedef T Type;
+};
+
+#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \
+ template <> \
+ struct MakeUnsigned<T> { typedef U Type; }
+
+FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char);
+FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char);
+FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short);
+FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned);
+FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long);
+FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong);
+
+// Casts nonnegative integer to unsigned.
+template <typename Int>
+inline typename MakeUnsigned<Int>::Type to_unsigned(Int value)
+{
+ FMT_ASSERT(value >= 0, "negative value");
+ return static_cast<typename MakeUnsigned<Int>::Type>(value);
+}
+
// The number of characters to store in the MemoryBuffer object itself
// to avoid dynamic memory allocation.
-enum { INLINE_BUFFER_SIZE = 500 };
+enum
+{
+ INLINE_BUFFER_SIZE = 500
+};
#if FMT_SECURE_SCL
// Use checked iterator to avoid warnings on MSVC.
@@ -608,7 +682,8 @@ protected:
std::size_t capacity_;
Buffer(T *ptr = 0, std::size_t capacity = 0)
- : ptr_(ptr), size_(0), capacity_(capacity) {}
+ : ptr_(ptr), size_(0), capacity_(capacity)
+ {}
/**
\rst
@@ -619,7 +694,8 @@ protected:
virtual void grow(std::size_t size) = 0;
public:
- virtual ~Buffer() {}
+ virtual ~Buffer()
+ {}
/** Returns the size of this buffer. */
std::size_t size() const
@@ -654,7 +730,10 @@ public:
grow(capacity);
}
- void clear() FMT_NOEXCEPT { size_ = 0; }
+ void clear() FMT_NOEXCEPT
+ {
+ size_ = 0;
+ }
void push_back(const T &value)
{
@@ -681,8 +760,7 @@ template <typename T>
template <typename U>
void Buffer<T>::append(const U *begin, const U *end)
{
- assert(begin <= end);
- std::size_t new_size = size_ + (end - begin);
+ std::size_t new_size = size_ + internal::to_unsigned(end - begin);
if (new_size > capacity_)
grow(new_size);
std::uninitialized_copy(begin, end,
@@ -693,10 +771,10 @@ void Buffer<T>::append(const U *begin, const U *end)
namespace internal
{
-// A memory buffer for POD types with the first SIZE elements stored in
-// the object itself.
+// A memory buffer for trivially copyable/constructible types with the first SIZE
+// elements stored in the object itself.
template <typename T, std::size_t SIZE, typename Allocator = std::allocator<T> >
-class MemoryBuffer : private Allocator, public Buffer<T>
+class MemoryBuffer: private Allocator, public Buffer<T>
{
private:
T data_[SIZE];
@@ -712,7 +790,8 @@ protected:
public:
explicit MemoryBuffer(const Allocator &alloc = Allocator())
- : Allocator(alloc), Buffer<T>(data_, SIZE) {}
+ : Allocator(alloc), Buffer<T>(data_, SIZE)
+ {}
~MemoryBuffer()
{
deallocate();
@@ -787,10 +866,11 @@ void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size)
// A fixed-size buffer.
template <typename Char>
-class FixedBuffer : public fmt::Buffer<Char>
+class FixedBuffer: public fmt::Buffer<Char>
{
public:
- FixedBuffer(Char *array, std::size_t size) : fmt::Buffer<Char>(array, size) {}
+ FixedBuffer(Char *array, std::size_t size): fmt::Buffer<Char>(array, size)
+ {}
protected:
FMT_API void grow(std::size_t size);
@@ -815,7 +895,7 @@ template <typename Char>
class CharTraits;
template <>
-class CharTraits<char> : public BasicCharTraits<char>
+class CharTraits<char>: public BasicCharTraits<char>
{
private:
// Conversion from wchar_t to char is not allowed.
@@ -834,7 +914,7 @@ public:
};
template <>
-class CharTraits<wchar_t> : public BasicCharTraits<wchar_t>
+class CharTraits<wchar_t>: public BasicCharTraits<wchar_t>
{
public:
static wchar_t convert(char value)
@@ -902,24 +982,6 @@ struct IntTraits
TypeSelector<std::numeric_limits<T>::digits <= 32>::Type MainType;
};
-// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T.
-template <typename T>
-struct MakeUnsigned
-{
- typedef T Type;
-};
-
-#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \
- template <> \
- struct MakeUnsigned<T> { typedef U Type; }
-
-FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char);
-FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char);
-FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short);
-FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned);
-FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long);
-FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong);
-
FMT_API void report_unknown_type(char code, const char *type);
// Static data is placed in this class template to allow header-only
@@ -934,14 +996,6 @@ struct FMT_API BasicData
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.
@@ -949,8 +1003,8 @@ 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;
+ int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
+ return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1;
}
#else
// Fallback version of count_digits used when __builtin_clz is not available.
@@ -976,8 +1030,8 @@ inline unsigned count_digits(uint64_t n)
// 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;
+ int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
+ return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1;
}
#endif
@@ -1050,7 +1104,8 @@ private:
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer_;
public:
- UTF16ToUTF8() {}
+ UTF16ToUTF8()
+ {}
FMT_API explicit UTF16ToUTF8(WStringRef s);
operator StringRef() const
{
@@ -1128,9 +1183,9 @@ struct Value
};
};
-// A formatting argument. It is a POD type to allow storage in
-// internal::MemoryBuffer.
-struct Arg : Value
+// A formatting argument. It is a trivially copyable/constructible type to
+// allow storage in internal::MemoryBuffer.
+struct Arg: Value
{
Type type;
};
@@ -1139,7 +1194,8 @@ template <typename Char>
struct NamedArg;
template <typename T = void>
-struct Null {};
+struct Null
+{};
// A helper class template to enable or disable overloads taking wide
// characters and strings in MakeValue.
@@ -1168,7 +1224,7 @@ No &convert(...);
template <typename T>
T &get();
-struct DummyStream : std::ostream
+struct DummyStream: std::ostream
{
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
@@ -1180,7 +1236,10 @@ No &operator<<(std::ostream &, int);
template<typename T, bool ENABLE_CONVERSION>
struct ConvertToIntImpl
{
- enum { value = false };
+ enum
+ {
+ value = false
+ };
};
template<typename T>
@@ -1196,7 +1255,10 @@ struct ConvertToIntImpl<T, true>
template<typename T, bool ENABLE_CONVERSION>
struct ConvertToIntImpl2
{
- enum { value = false };
+ enum
+ {
+ value = false
+ };
};
template<typename T>
@@ -1212,8 +1274,14 @@ struct ConvertToIntImpl2<T, true>
template<typename T>
struct ConvertToInt
{
- enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
- enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
+ enum
+ {
+ enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes)
+ };
+ enum
+ {
+ value = ConvertToIntImpl2<T, enable_conversion>::value
+ };
};
#define FMT_DISABLE_CONVERSION_TO_INT(Type) \
@@ -1226,7 +1294,8 @@ FMT_DISABLE_CONVERSION_TO_INT(double);
FMT_DISABLE_CONVERSION_TO_INT(long double);
template<bool B, class T = void>
-struct EnableIf {};
+struct EnableIf
+{};
template<class T>
struct EnableIf<true, T>
@@ -1250,18 +1319,24 @@ struct Conditional<false, T, F>
template<bool>
struct Not
{
- enum { value = 0 };
+ enum
+ {
+ value = 0
+ };
};
template<>
struct Not<false>
{
- enum { value = 1 };
+ enum
+ {
+ value = 1
+ };
};
// Makes an Arg object from any type.
template <typename Formatter>
-class MakeValue : public Arg
+class MakeValue: public Arg
{
public:
typedef typename Formatter::Char Char;
@@ -1312,7 +1387,8 @@ private:
}
public:
- MakeValue() {}
+ MakeValue()
+ {}
#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \
MakeValue(Type value) { field = rhs; } \
@@ -1389,7 +1465,7 @@ public:
#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \
MakeValue(typename WCharHelper<Type, Char>::Supported value) { \
set_string(value); \
- } \
+ } \
static uint64_t type(Type) { return Arg::TYPE; }
FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING)
@@ -1437,19 +1513,32 @@ public:
}
};
+template <typename Formatter>
+class MakeArg: public Arg
+{
+public:
+ MakeArg()
+ {
+ type = Arg::NONE;
+ }
+
+ template <typename T>
+ MakeArg(const T &value)
+ : Arg(MakeValue<Formatter>(value))
+ {
+ type = static_cast<Arg::Type>(MakeValue<Formatter>::type(value));
+ }
+};
+
template <typename Char>
-struct NamedArg : Arg
+struct NamedArg: Arg
{
BasicStringRef<Char> name;
- typedef internal::MakeValue< BasicFormatter<Char> > MakeValue;
-
template <typename T>
NamedArg(BasicStringRef<Char> argname, const T &value)
- : Arg(MakeValue(value)), name(argname)
- {
- type = static_cast<Arg::Type>(MakeValue::type(value));
- }
+ : Arg(MakeArg< BasicFormatter<Char> >(value)), name(argname)
+ {}
};
#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
@@ -1478,7 +1567,8 @@ template <typename Impl, typename Result>
class ArgVisitor
{
public:
- void report_unhandled_arg() {}
+ void report_unhandled_arg()
+ {}
Result visit_unhandled_arg()
{
@@ -1588,10 +1678,11 @@ public:
}
};
-class RuntimeError : public std::runtime_error
+class RuntimeError: public std::runtime_error
{
protected:
- RuntimeError() : std::runtime_error("") {}
+ RuntimeError(): std::runtime_error("")
+ {}
};
template <typename Char>
@@ -1632,14 +1723,20 @@ private:
public:
// Maximum number of arguments with packed types.
- enum { MAX_PACKED_ARGS = 16 };
+ enum
+ {
+ MAX_PACKED_ARGS = 16
+ };
- ArgList() : types_(0) {}
+ ArgList(): types_(0)
+ {}
ArgList(ULongLong types, const internal::Value *values)
- : types_(types), values_(values) {}
+ : types_(types), values_(values)
+ {}
ArgList(ULongLong types, const internal::Arg *args)
- : types_(types), args_(args) {}
+ : types_(types), args_(args)
+ {}
/** Returns the argument at specified index. */
internal::Arg operator[](unsigned index) const
@@ -1685,11 +1782,12 @@ enum
};
// An empty format specifier.
-struct EmptySpec {};
+struct EmptySpec
+{};
// A type specifier.
template <char TYPE>
-struct TypeSpec : EmptySpec
+struct TypeSpec: EmptySpec
{
Alignment align() const
{
@@ -1725,7 +1823,8 @@ struct WidthSpec
// two specialization of WidthSpec and its subclasses.
wchar_t fill_;
- WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {}
+ WidthSpec(unsigned width, wchar_t fill): width_(width), fill_(fill)
+ {}
unsigned width() const
{
@@ -1738,12 +1837,13 @@ struct WidthSpec
};
// An alignment specifier.
-struct AlignSpec : WidthSpec
+struct AlignSpec: WidthSpec
{
Alignment align_;
AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT)
- : WidthSpec(width, fill), align_(align) {}
+ : WidthSpec(width, fill), align_(align)
+ {}
Alignment align() const
{
@@ -1758,9 +1858,10 @@ struct AlignSpec : WidthSpec
// An alignment and type specifier.
template <char TYPE>
-struct AlignTypeSpec : AlignSpec
+struct AlignTypeSpec: AlignSpec
{
- AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {}
+ AlignTypeSpec(unsigned width, wchar_t fill): AlignSpec(width, fill)
+ {}
bool flag(unsigned) const
{
@@ -1773,7 +1874,7 @@ struct AlignTypeSpec : AlignSpec
};
// A full format specifier.
-struct FormatSpec : AlignSpec
+struct FormatSpec: AlignSpec
{
unsigned flags_;
int precision_;
@@ -1781,7 +1882,8 @@ struct FormatSpec : AlignSpec
FormatSpec(
unsigned width = 0, char type = 0, wchar_t fill = ' ')
- : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {}
+ : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type)
+ {}
bool flag(unsigned f) const
{
@@ -1799,14 +1901,15 @@ struct FormatSpec : AlignSpec
// An integer format specifier.
template <typename T, typename SpecT = TypeSpec<0>, typename Char = char>
-class IntFormatSpec : public SpecT
+class IntFormatSpec: public SpecT
{
private:
T value_;
public:
IntFormatSpec(T val, const SpecT &spec = SpecT())
- : SpecT(spec), value_(val) {}
+ : SpecT(spec), value_(val)
+ {}
T value() const
{
@@ -1816,7 +1919,7 @@ public:
// A string format specifier.
template <typename Char>
-class StrFormatSpec : public AlignSpec
+class StrFormatSpec: public AlignSpec
{
private:
const Char *str_;
@@ -1878,26 +1981,26 @@ IntFormatSpec<int, AlignTypeSpec<TYPE_CODE>, Char> pad(
#define FMT_DEFINE_INT_FORMATTERS(TYPE) \
inline IntFormatSpec<TYPE, TypeSpec<'b'> > bin(TYPE value) { \
return IntFormatSpec<TYPE, TypeSpec<'b'> >(value, TypeSpec<'b'>()); \
- } \
+} \
\
inline IntFormatSpec<TYPE, TypeSpec<'o'> > oct(TYPE value) { \
return IntFormatSpec<TYPE, TypeSpec<'o'> >(value, TypeSpec<'o'>()); \
- } \
+} \
\
inline IntFormatSpec<TYPE, TypeSpec<'x'> > hex(TYPE value) { \
return IntFormatSpec<TYPE, TypeSpec<'x'> >(value, TypeSpec<'x'>()); \
- } \
+} \
\
inline IntFormatSpec<TYPE, TypeSpec<'X'> > hexu(TYPE value) { \
return IntFormatSpec<TYPE, TypeSpec<'X'> >(value, TypeSpec<'X'>()); \
- } \
+} \
\
template <char TYPE_CODE> \
inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE> > pad( \
IntFormatSpec<TYPE, TypeSpec<TYPE_CODE> > f, unsigned width) { \
return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE> >( \
f.value(), AlignTypeSpec<TYPE_CODE>(width, ' ')); \
- } \
+} \
\
/* For compatibility with older compilers we provide two overloads for pad, */ \
/* one that takes a fill character and one that doesn't. In the future this */ \
@@ -1909,20 +2012,20 @@ inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char> pad( \
unsigned width, Char fill) { \
return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char>( \
f.value(), AlignTypeSpec<TYPE_CODE>(width, fill)); \
- } \
+} \
\
inline IntFormatSpec<TYPE, AlignTypeSpec<0> > pad( \
TYPE value, unsigned width) { \
return IntFormatSpec<TYPE, AlignTypeSpec<0> >( \
value, AlignTypeSpec<0>(width, ' ')); \
- } \
+} \
\
template <typename Char> \
inline IntFormatSpec<TYPE, AlignTypeSpec<0>, Char> pad( \
TYPE value, unsigned width, Char fill) { \
return IntFormatSpec<TYPE, AlignTypeSpec<0>, Char>( \
value, AlignTypeSpec<0>(width, fill)); \
- }
+}
FMT_DEFINE_INT_FORMATTERS(int)
FMT_DEFINE_INT_FORMATTERS(long)
@@ -1963,7 +2066,7 @@ template <typename Char>
class ArgMap
{
private:
- typedef std::map<fmt::BasicStringRef<Char>, internal::Arg> MapType;
+ typedef std::vector<std::pair<fmt::BasicStringRef<Char>, internal::Arg> > MapType;
typedef typename MapType::value_type Pair;
MapType map_;
@@ -1973,13 +2076,19 @@ public:
const internal::Arg* find(const fmt::BasicStringRef<Char> &name) const
{
- typename MapType::const_iterator it = map_.find(name);
- return it != map_.end() ? &it->second : 0;
+ // The list is unsorted, so just return the first matching name.
+ for (typename MapType::const_iterator it = map_.begin(), end = map_.end();
+ it != end; ++it)
+ {
+ if (it->first == name)
+ return &it->second;
+ }
+ return 0;
}
};
template <typename Impl, typename Char>
-class ArgFormatterBase : public ArgVisitor<Impl, void>
+class ArgFormatterBase: public ArgVisitor<Impl, void>
{
private:
BasicWriter<Char> &writer_;
@@ -2019,7 +2128,8 @@ protected:
public:
ArgFormatterBase(BasicWriter<Char> &w, FormatSpec &s)
- : writer_(w), spec_(s) {}
+ : writer_(w), spec_(s)
+ {}
template <typename T>
void visit_any_int(T value)
@@ -2109,7 +2219,7 @@ public:
// An argument formatter.
template <typename Char>
-class BasicArgFormatter :
+class BasicArgFormatter:
public ArgFormatterBase<BasicArgFormatter<Char>, Char>
{
private:
@@ -2119,7 +2229,8 @@ private:
public:
BasicArgFormatter(BasicFormatter<Char> &f, FormatSpec &s, const Char *fmt)
: ArgFormatterBase<BasicArgFormatter<Char>, Char>(f.writer(), s),
- formatter_(f), format_(fmt) {}
+ formatter_(f), format_(fmt)
+ {}
void visit_custom(Arg::CustomValue c)
{
@@ -2152,7 +2263,7 @@ protected:
Arg next_arg(const char *&error)
{
if (next_arg_index_ >= 0)
- return do_get_arg(next_arg_index_++, error);
+ return do_get_arg(internal::to_unsigned(next_arg_index_++), error);
error = "cannot switch from manual to automatic argument indexing";
return Arg();
}
@@ -2179,13 +2290,13 @@ protected:
void write(BasicWriter<Char> &w, const Char *start, const Char *end)
{
if (start != end)
- w << BasicStringRef<Char>(start, end - start);
+ w << BasicStringRef<Char>(start, internal::to_unsigned(end - start));
}
};
// A printf formatter.
template <typename Char>
-class PrintfFormatter : private FormatterBase
+class PrintfFormatter: private FormatterBase
{
private:
void parse_flags(FormatSpec &spec, const Char *&s);
@@ -2199,17 +2310,19 @@ private:
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
- explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {}
+ explicit PrintfFormatter(const ArgList &args): FormatterBase(args)
+ {}
FMT_API void format(BasicWriter<Char> &writer,
BasicCStringRef<Char> format_str);
};
} // namespace internal
-// A formatter.
-template <typename CharType>
-class BasicFormatter : private internal::FormatterBase
+/** This template formats data and writes the output to a writer. */
+template <typename CharType, typename ArgFormatter>
+class BasicFormatter: private internal::FormatterBase
{
public:
+ /** The character type for the output. */
typedef CharType Char;
private:
@@ -2231,16 +2344,27 @@ private:
internal::Arg parse_arg_name(const Char *&s);
public:
+ /**
+ \rst
+ Constructs a ``BasicFormatter`` object. References to the arguments and
+ the writer are stored in the formatter object so make sure they have
+ appropriate lifetimes.
+ \endrst
+ */
BasicFormatter(const ArgList &args, BasicWriter<Char> &w)
- : internal::FormatterBase(args), writer_(w) {}
+ : internal::FormatterBase(args), writer_(w)
+ {}
+ /** Returns a reference to the writer associated with this formatter. */
BasicWriter<Char> &writer()
{
return writer_;
}
+ /** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str);
+ // Formats a single argument and advances format_str, a format string pointer.
const Char *format(const Char *&format_str, const internal::Arg &arg);
};
@@ -2276,82 +2400,56 @@ inline uint64_t make_type(const T &arg)
return MakeValue< BasicFormatter<char> >::type(arg);
}
-template <unsigned N>
-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];
-};
+template <unsigned N, bool/*IsPacked*/ = (N < ArgList::MAX_PACKED_ARGS)>
+ struct ArgArray;
-#if FMT_USE_VARIADIC_TEMPLATES
-template <typename Arg, typename... Args>
-inline uint64_t make_type(const Arg &first, const Args & ... tail)
-{
- return make_type(first) | (make_type(tail...) << 4);
-}
-
-inline void do_set_types(Arg *) {}
-
-template <typename T, typename... Args>
-inline void do_set_types(Arg *args, const T &arg, const Args & ... tail)
+template <unsigned N>
+struct ArgArray<N, true/*IsPacked*/>
{
- args->type = static_cast<Arg::Type>(
- MakeValue< BasicFormatter<char> >::type(arg));
- do_set_types(args + 1, tail...);
-}
+ typedef Value Type[N > 0 ? N : 1];
-template <typename... Args>
-inline void set_types(Arg *array, const Args & ... args)
+template <typename Formatter, typename T>
+static Value make(const T &value)
{
- if (check(sizeof...(Args) > ArgList::MAX_PACKED_ARGS))
- do_set_types(array, args...);
- array[sizeof...(Args)].type = Arg::NONE;
+ Value result = MakeValue<Formatter>(value);
+ // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang:
+ // https://github.com/cppformat/cppformat/issues/276
+ (void)result.custom.format;
+ return result;
}
+ };
-template <typename... Args>
-inline void set_types(Value *, const Args & ...)
+template <unsigned N>
+struct ArgArray<N, false/*IsPacked*/>
{
- // Do nothing as types are passed separately from values.
-}
+ typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE
-template <typename Formatter, typename Value>
-inline void store_args(Value *) {}
+ template <typename Formatter, typename T>
+ static Arg make(const T &value)
+ {
+ return MakeArg<Formatter>(value);
+ }
+};
-template <typename Formatter, typename Arg, typename T, typename... Args>
-inline void store_args(Arg *args, const T &arg, const Args & ... tail)
+#if FMT_USE_VARIADIC_TEMPLATES
+template <typename Arg, typename... Args>
+inline uint64_t make_type(const Arg &first, 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<Formatter>(arg);
- store_args<Formatter>(args + 1, tail...);
+ return make_type(first) | (make_type(tail...) << 4);
}
-template <typename Formatter, typename... Args>
-ArgList make_arg_list(typename ArgArray<sizeof...(Args)>::Type array,
- const Args & ... args)
-{
- if (check(sizeof...(Args) >= ArgList::MAX_PACKED_ARGS))
- set_types(array, args...);
- store_args<Formatter>(array, args...);
- return ArgList(make_type(args...), array);
-}
#else
struct ArgType
{
uint64_t type;
- ArgType() : type(0) {}
+ ArgType(): type(0)
+ {}
template <typename T>
- ArgType(const T &arg) : type(make_type(arg)) {}
+ ArgType(const T &arg) : type(make_type(arg))
+ {}
};
# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType()
@@ -2366,7 +2464,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT))
#endif
template <class Char>
-class FormatBuf : public std::basic_streambuf<Char>
+class FormatBuf: public std::basic_streambuf<Char>
{
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
@@ -2376,7 +2474,7 @@ private:
Char *start_;
public:
- FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
+ FormatBuf(Buffer<Char> &buffer): buffer_(buffer), start_(&buffer[0])
{
this->setp(start_, start_ + buffer_.capacity());
}
@@ -2385,7 +2483,7 @@ public:
{
if (!traits_type::eq_int_type(ch, traits_type::eof()))
{
- size_t size = this->pptr() - start_;
+ size_t size = this->size();
buffer_.resize(size);
buffer_.reserve(size * 2);
@@ -2398,7 +2496,7 @@ public:
size_t size() const
{
- return this->pptr() - start_;
+ return to_unsigned(this->pptr() - start_);
}
};
} // namespace internal
@@ -2416,19 +2514,21 @@ public:
# define FMT_VARIADIC_VOID(func, arg_type) \
template <typename... Args> \
void func(arg_type arg0, const Args & ... args) { \
- typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
- func(arg0, fmt::internal::make_arg_list< \
- fmt::BasicFormatter<Char> >(array, args...)); \
- }
+ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
+ typename ArgArray::Type array{ \
+ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
+ func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \
+ }
// Defines a variadic constructor.
# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
template <typename... Args> \
ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \
- typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
- func(arg0, arg1, fmt::internal::make_arg_list< \
- fmt::BasicFormatter<Char> >(array, args...)); \
- }
+ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
+ typename ArgArray::Type array{ \
+ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
+ func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \
+ }
#else
@@ -2444,7 +2544,7 @@ public:
const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \
func(arg1, fmt::ArgList( \
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \
- }
+ }
// Emulates a variadic function returning void on a pre-C++11 compiler.
# define FMT_VARIADIC_VOID(func, arg_type) \
@@ -2461,7 +2561,7 @@ public:
const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \
func(arg0, arg1, fmt::ArgList( \
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \
- }
+ }
// Emulates a variadic constructor on a pre-C++11 compiler.
# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
@@ -2503,7 +2603,7 @@ public:
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
-class SystemError : public internal::RuntimeError
+class SystemError: public internal::RuntimeError
{
private:
void init(int err_code, CStringRef format_str, ArgList args);
@@ -2513,7 +2613,8 @@ protected:
typedef char Char; // For FMT_VARIADIC_CTOR.
- SystemError() {}
+ SystemError()
+ {}
public:
/**
@@ -2623,7 +2724,8 @@ private:
template <typename Int>
void write_decimal(Int value)
{
- typename internal::IntTraits<Int>::MainType abs_value = value;
+ typedef typename internal::IntTraits<Int>::MainType MainType;
+ MainType abs_value = static_cast<MainType>(value);
if (internal::is_negative(value))
{
abs_value = 0 - abs_value;
@@ -2681,7 +2783,8 @@ private:
}
template<typename T>
- void append_float_length(Char *&, T) {}
+ void append_float_length(Char *&, T)
+ {}
template <typename Impl, typename Char_>
friend class internal::ArgFormatterBase;
@@ -2692,7 +2795,8 @@ protected:
/**
Constructs a ``BasicWriter`` object.
*/
- explicit BasicWriter(Buffer<Char> &b) : buffer_(b) {}
+ explicit BasicWriter(Buffer<Char> &b): buffer_(b)
+ {}
public:
/**
@@ -2700,7 +2804,8 @@ public:
Destroys a ``BasicWriter`` object.
\endrst
*/
- virtual ~BasicWriter() {}
+ virtual ~BasicWriter()
+ {}
/**
Returns the total number of characters written.
@@ -2876,7 +2981,10 @@ public:
return *this;
}
- void clear() FMT_NOEXCEPT { buffer_.clear(); }
+ void clear() FMT_NOEXCEPT
+ {
+ buffer_.clear();
+ }
};
template <typename Char>
@@ -2930,9 +3038,9 @@ void BasicWriter<Char>::write_str(
return;
}
}
- std::size_t precision = spec.precision_;
+ std::size_t precision = static_cast<std::size_t>(spec.precision_);
if (spec.precision_ >= 0 && precision < str_size)
- str_size = spec.precision_;
+ str_size = precision;
write_str(str_value, str_size, spec);
}
@@ -2969,7 +3077,8 @@ BasicWriter<Char>::prepare_int_buffer(
// is specified.
if (prefix_size > 0 && prefix[prefix_size - 1] == '0')
--prefix_size;
- unsigned number_size = prefix_size + spec.precision();
+ unsigned number_size =
+ prefix_size + internal::to_unsigned(spec.precision());
AlignSpec subspec(number_size, '0', ALIGN_NUMERIC);
if (number_size >= width)
return prepare_int_buffer(num_digits, subspec, prefix, prefix_size);
@@ -3036,7 +3145,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
{
unsigned prefix_size = 0;
typedef typename internal::IntTraits<T>::MainType UnsignedType;
- UnsignedType abs_value = value;
+ UnsignedType abs_value = static_cast<UnsignedType>(value);
char prefix[4] = "";
if (internal::is_negative(value))
{
@@ -3140,8 +3249,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
template <typename Char>
template <typename T>
-void BasicWriter<Char>::write_double(
- T value, const FormatSpec &spec)
+void BasicWriter<Char>::write_double(T value, const FormatSpec &spec)
{
// Check type.
char type = spec.type();
@@ -3230,7 +3338,10 @@ void BasicWriter<Char>::write_double(
}
// Build format string.
- enum { MAX_FORMAT_SIZE = 10 }; // longest format: %#-*.*Lg
+ enum
+ {
+ MAX_FORMAT_SIZE = 10
+ }; // longest format: %#-*.*Lg
Char format[MAX_FORMAT_SIZE];
Char *format_ptr = format;
*format_ptr++ = '%';
@@ -3260,6 +3371,8 @@ void BasicWriter<Char>::write_double(
// Format using snprintf.
Char fill = internal::CharTraits<Char>::cast(spec.fill());
+ unsigned n = 0;
+ Char *start = 0;
for (;;)
{
std::size_t buffer_size = buffer_.capacity() - offset;
@@ -3273,48 +3386,53 @@ void BasicWriter<Char>::write_double(
buffer_size = buffer_.capacity() - offset;
}
#endif
- Char *start = &buffer_[offset];
- int n = internal::CharTraits<Char>::format_float(
- start, buffer_size, format, width_for_sprintf, spec.precision(), value);
- if (n >= 0 && offset + n < buffer_.capacity())
+ start = &buffer_[offset];
+ int result = internal::CharTraits<Char>::format_float(
+ start, buffer_size, format, width_for_sprintf, spec.precision(), value);
+ if (result >= 0)
{
- 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<unsigned>(n))
- {
- width = spec.width();
- CharPtr p = grow_buffer(width);
- std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char));
- fill_padding(p, spec.width(), n, fill);
- return;
- }
- if (spec.fill() != ' ' || sign)
- {
- while (*start == ' ')
- *start++ = fill;
- if (sign)
- *(start - 1) = sign;
- }
- grow_buffer(n);
- return;
+ n = internal::to_unsigned(result);
+ if (offset + n < buffer_.capacity())
+ break; // The buffer is large enough - continue with formatting.
+ buffer_.reserve(offset + n + 1);
+ }
+ else
+ {
+ // If result is negative we ask to increase the capacity by at least 1,
+ // but as std::vector, the buffer grows exponentially.
+ buffer_.reserve(buffer_.capacity() + 1);
}
- // If 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);
}
+ if (sign)
+ {
+ if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
+ *start != ' ')
+ {
+ *(start - 1) = sign;
+ sign = 0;
+ }
+ else
+ {
+ *(start - 1) = fill;
+ }
+ ++n;
+ }
+ if (spec.align() == ALIGN_CENTER && spec.width() > n)
+ {
+ width = spec.width();
+ CharPtr p = grow_buffer(width);
+ std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char));
+ fill_padding(p, spec.width(), n, fill);
+ return;
+ }
+ if (spec.fill() != ' ' || sign)
+ {
+ while (*start == ' ')
+ *start++ = fill;
+ if (sign)
+ *(start - 1) = sign;
+ }
+ grow_buffer(n);
}
/**
@@ -3352,14 +3470,15 @@ accessed as a C string with ``out.c_str()``.
\endrst
*/
template <typename Char, typename Allocator = std::allocator<Char> >
-class BasicMemoryWriter : public BasicWriter<Char>
+class BasicMemoryWriter: public BasicWriter<Char>
{
private:
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE, Allocator> buffer_;
public:
explicit BasicMemoryWriter(const Allocator& alloc = Allocator())
- : BasicWriter<Char>(buffer_), buffer_(alloc) {}
+ : BasicWriter<Char>(buffer_), buffer_(alloc)
+ {}
#if FMT_USE_RVALUE_REFERENCES
/**
@@ -3370,8 +3489,7 @@ public:
*/
BasicMemoryWriter(BasicMemoryWriter &&other)
: BasicWriter<Char>(buffer_), buffer_(std::move(other.buffer_))
- {
- }
+ {}
/**
\rst
@@ -3410,7 +3528,7 @@ You can use one of the following typedefs for common character types:
\endrst
*/
template <typename Char>
-class BasicArrayWriter : public BasicWriter<Char>
+class BasicArrayWriter: public BasicWriter<Char>
{
private:
internal::FixedBuffer<Char> buffer_;
@@ -3423,7 +3541,8 @@ public:
\endrst
*/
BasicArrayWriter(Char *array, std::size_t size)
- : BasicWriter<Char>(buffer_), buffer_(array, size) {}
+ : BasicWriter<Char>(buffer_), buffer_(array, size)
+ {}
/**
\rst
@@ -3433,7 +3552,8 @@ public:
*/
template <std::size_t SIZE>
explicit BasicArrayWriter(Char(&array)[SIZE])
- : BasicWriter<Char>(buffer_), buffer_(array, SIZE) {}
+ : BasicWriter<Char>(buffer_), buffer_(array, SIZE)
+ {}
};
typedef BasicArrayWriter<char> ArrayWriter;
@@ -3450,10 +3570,8 @@ void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value)
output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size());
- typedef internal::MakeValue< BasicFormatter<Char> > MakeValue;
- internal::Arg arg = MakeValue(str);
- arg.type = static_cast<internal::Arg::Type>(MakeValue::type(str));
- format_str = f.format(format_str, arg);
+ typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
+ format_str = f.format(format_str, MakeArg(str));
}
// Reports a system error without throwing an exception.
@@ -3464,7 +3582,7 @@ FMT_API void report_system_error(int error_code,
#if FMT_USE_WINDOWS_H
/** A Windows error. */
-class WindowsError : public SystemError
+class WindowsError: public SystemError
{
private:
FMT_API void init(int error_code, CStringRef format_str, ArgList args);
@@ -3512,7 +3630,10 @@ FMT_API void report_windows_error(int error_code,
#endif
-enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
+enum Color
+{
+ BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE
+};
/**
Formats a string and prints it to stdout using ANSI escape sequences
@@ -3629,7 +3750,10 @@ class FormatInt
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
- enum { BUFFER_SIZE = std::numeric_limits<ULongLong>::digits10 + 3 };
+ enum
+ {
+ BUFFER_SIZE = std::numeric_limits<ULongLong>::digits10 + 3
+ };
mutable char buffer_[BUFFER_SIZE];
char *str_;
@@ -3682,16 +3806,17 @@ public:
{
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)) {}
+ 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.
- */
+ /** Returns the number of characters written to the output buffer. */
std::size_t size() const
{
- return buffer_ - str_ + BUFFER_SIZE - 1;
+ return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1);
}
/**
@@ -3730,7 +3855,8 @@ public:
template <typename T>
inline void format_decimal(char *&buffer, T value)
{
- typename internal::IntTraits<T>::MainType abs_value = value;
+ typedef typename internal::IntTraits<T>::MainType MainType;
+ MainType abs_value = static_cast<MainType>(value);
if (internal::is_negative(value))
{
*buffer++ = '-';
@@ -3815,10 +3941,11 @@ void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
template <typename... Args> \
ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
const Args & ... args) { \
- typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
+ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
+ typename ArgArray::Type array{ \
+ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \
- fmt::internal::make_arg_list< \
- fmt::BasicFormatter<Char> >(array, args...)); \
+ fmt::ArgList(fmt::internal::make_type(args...), array)); \
}
#else
// Defines a wrapper for a function taking __VA_ARGS__ arguments
@@ -3934,6 +4061,18 @@ print(cerr, "Don't {}!", "panic");
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
+
+/**
+\rst
+Prints formatted data to the stream *os*.
+
+**Example**::
+
+fprintf(cerr, "Don't %s!", "panic");
+\endrst
+*/
+FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
+FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
#endif
namespace internal
@@ -3947,7 +4086,7 @@ inline bool is_name_start(Char c)
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
template <typename Char>
-int parse_nonnegative_int(const Char *&s)
+unsigned parse_nonnegative_int(const Char *&s)
{
assert('0' <= *s && *s <= '9');
unsigned value = 0;
@@ -3994,8 +4133,8 @@ void check_sign(const Char *&s, const Arg &arg)
}
} // namespace internal
-template <typename Char>
-inline internal::Arg BasicFormatter<Char>::get_arg(
+template <typename Char, typename AF>
+inline internal::Arg BasicFormatter<Char, AF>::get_arg(
BasicStringRef<Char> arg_name, const char *&error)
{
if (check_no_auto_index(error))
@@ -4009,8 +4148,8 @@ inline internal::Arg BasicFormatter<Char>::get_arg(
return internal::Arg();
}
-template <typename Char>
-inline internal::Arg BasicFormatter<Char>::parse_arg_index(const Char *&s)
+template <typename Char, typename AF>
+inline internal::Arg BasicFormatter<Char, AF>::parse_arg_index(const Char *&s)
{
const char *error = 0;
internal::Arg arg = *s < '0' || *s > '9' ?
@@ -4023,8 +4162,8 @@ inline internal::Arg BasicFormatter<Char>::parse_arg_index(const Char *&s)
return arg;
}
-template <typename Char>
-inline internal::Arg BasicFormatter<Char>::parse_arg_name(const Char *&s)
+template <typename Char, typename AF>
+inline internal::Arg BasicFormatter<Char, AF>::parse_arg_name(const Char *&s)
{
assert(internal::is_name_start(*s));
const Char *start = s;
@@ -4041,9 +4180,8 @@ inline internal::Arg BasicFormatter<Char>::parse_arg_name(const Char *&s)
return arg;
}
-// Should be after FormatSpec
-template <typename Char>
-const Char *BasicFormatter<Char>::format(
+template <typename Char, typename ArgFormatter>
+const Char *BasicFormatter<Char, ArgFormatter>::format(
const Char *&format_str, const internal::Arg &arg)
{
using internal::Arg;
@@ -4233,12 +4371,12 @@ const Char *BasicFormatter<Char>::format(
FMT_THROW(FormatError("missing '}' in format string"));
// Format argument.
- internal::BasicArgFormatter<Char>(*this, spec, s - 1).visit(arg);
+ ArgFormatter(*this, spec, s - 1).visit(arg);
return s;
}
-template <typename Char>
-void BasicFormatter<Char>::format(BasicCStringRef<Char> format_str)
+template <typename Char, typename AF>
+void BasicFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
{
const Char *s = format_str.c_str();
const Char *start = s;
@@ -4358,4 +4496,5 @@ operator"" _a(const wchar_t *s, std::size_t)
# include "format.cc"
#endif
-#endif // FMT_FORMAT_H_
\ No newline at end of file
+#endif // FMT_FORMAT_H_
+
diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h
index c096382..9f2f13d 100644
--- a/include/spdlog/details/logger_impl.h
+++ b/include/spdlog/details/logger_impl.h
@@ -7,7 +7,6 @@
#include <spdlog/logger.h>
-#include <atomic>
#include <memory>
#include <string>
@@ -22,6 +21,7 @@ inline spdlog::logger::logger(const std::string& logger_name, const It& begin, c
// no support under vs2013 for member initialization for std::atomic
_level = level::info;
+ _flush_level = level::off;
}
// ctor with sinks as init list
@@ -267,6 +267,11 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
_level.store(log_level);
}
+inline void spdlog::logger::flush_on(level::level_enum log_level)
+{
+ _flush_level.store(log_level);
+}
+
inline spdlog::level::level_enum spdlog::logger::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
@@ -285,6 +290,10 @@ inline void spdlog::logger::_log_msg(details::log_msg& msg)
_formatter->format(msg);
for (auto &sink : _sinks)
sink->log(msg);
+
+ const auto flush_level = _flush_level.load(std::memory_order_relaxed);
+ if (msg.level >= flush_level)
+ flush();
}
inline void spdlog::logger::_set_pattern(const std::string& pattern)
diff --git a/include/spdlog/details/null_mutex.h b/include/spdlog/details/null_mutex.h
index 19e90bf..67b0aee 100644
--- a/include/spdlog/details/null_mutex.h
+++ b/include/spdlog/details/null_mutex.h
@@ -5,7 +5,8 @@
#pragma once
-// null, no cost mutex
+#include <atomic>
+// null, no cost dummy "mutex" and dummy "atomic" int
namespace spdlog
{
@@ -20,5 +21,25 @@ struct null_mutex
return true;
}
};
+
+struct null_atomic_int
+{
+ int value;
+ null_atomic_int() = default;
+
+ null_atomic_int(int val):value(val)
+ {}
+
+ int load(std::memory_order) const
+ {
+ return value;
+ }
+
+ void store(int val)
+ {
+ value = val;
+ }
+};
+
}
}
diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h
index ac05a3b..4567fca 100644
--- a/include/spdlog/details/os.h
+++ b/include/spdlog/details/os.h
@@ -12,10 +12,15 @@
#include <string>
#ifdef _WIN32
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# include <windows.h>
+
+#ifndef NOMINMAX
+#define NOMINMAX //prevent windows redefining min/max
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
#ifdef __MINGW32__
#include <share.h>
@@ -132,24 +137,49 @@ constexpr inline unsigned short eol_size()
#endif
//fopen_s on non windows for writing
-inline int fopen_s(FILE** fp, const std::string& filename, const char* mode)
+inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
{
#ifdef _WIN32
- *fp = _fsopen((filename.c_str()), mode, _SH_DENYWR);
+#ifdef SPDLOG_WCHAR_FILENAMES
+ *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
+#else
+ *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
+#endif
return *fp == nullptr;
#else
- *fp = fopen((filename.c_str()), mode);
+ *fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr;
#endif
+}
+inline int remove(const filename_t &filename)
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return _wremove(filename.c_str());
+#else
+ return std::remove(filename.c_str());
+#endif
+}
+
+inline int rename(const filename_t& filename1, const filename_t& filename2)
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return _wrename(filename1.c_str(), filename2.c_str());
+#else
+ return std::rename(filename1.c_str(), filename2.c_str());
+#endif
}
//Return if file exists
-inline bool file_exists(const std::string& filename)
+inline bool file_exists(const filename_t& filename)
{
#ifdef _WIN32
+#ifdef SPDLOG_WCHAR_FILENAMES
+ auto attribs = GetFileAttributesW(filename.c_str());
+#else
auto attribs = GetFileAttributesA(filename.c_str());
+#endif
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#elif __linux__
struct stat buffer;
@@ -164,7 +194,6 @@ inline bool file_exists(const std::string& filename)
return false;
#endif
-
}
//Return utc offset in minutes or throw spdlog_ex on failure
diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h
index 3965b83..6a33754 100644
--- a/include/spdlog/details/pattern_formatter_impl.h
+++ b/include/spdlog/details/pattern_formatter_impl.h
@@ -619,7 +619,11 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg)
f->format(msg, tm_time);
}
//write eol
+#if defined(SPDLOG_EOL)
+ msg.formatted << SPDLOG_EOL;
+#else
msg.formatted << details::os::eol();
+#endif
}
catch(const fmt::FormatError& e)
{
diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h
index 78a47fe..7d744f8 100644
--- a/include/spdlog/details/registry.h
+++ b/include/spdlog/details/registry.h
@@ -33,9 +33,9 @@ public:
void register_logger(std::shared_ptr<logger> logger)
{
std::lock_guard<Mutex> lock(_mutex);
- auto logger_name = logger->name();
- throw_if_exists(logger_name);
- _loggers[logger_name] = logger;
+ auto logger_name = logger->name();
+ throw_if_exists(logger_name);
+ _loggers[logger_name] = logger;
}
@@ -48,12 +48,12 @@ public:
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
- {
+ {
std::lock_guard<Mutex> lock(_mutex);
- throw_if_exists(logger_name);
- std::shared_ptr<logger> new_logger;
+ throw_if_exists(logger_name);
+ std::shared_ptr<logger> new_logger;
if (_async_mode)
- new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms);
+ new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
@@ -61,8 +61,8 @@ public:
new_logger->set_formatter(_formatter);
new_logger->set_level(_level);
- //Add to registry
- _loggers[logger_name] = new_logger;
+ //Add to registry
+ _loggers[logger_name] = new_logger;
return new_logger;
}
@@ -112,7 +112,7 @@ public:
_level = log_level;
}
- void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms)
+ void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
std::lock_guard<Mutex> lock(_mutex);
_async_mode = true;
@@ -120,6 +120,7 @@ public:
_overflow_policy = overflow_policy;
_worker_warmup_cb = worker_warmup_cb;
_flush_interval_ms = flush_interval_ms;
+ _worker_teardown_cb = worker_teardown_cb;
}
void set_sync_mode()
@@ -134,16 +135,16 @@ public:
return s_instance;
}
-private:
+private:
registry_t<Mutex>() {}
registry_t<Mutex>(const registry_t<Mutex>&) = delete;
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
- void throw_if_exists(const std::string &logger_name)
- {
- if (_loggers.find(logger_name) != _loggers.end())
- throw spdlog_ex("logger with name '" + logger_name + "' already exists");
- }
+ void throw_if_exists(const std::string &logger_name)
+ {
+ if (_loggers.find(logger_name) != _loggers.end())
+ throw spdlog_ex("logger with name '" + logger_name + "' already exists");
+ }
Mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
@@ -153,6 +154,7 @@ private:
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
std::function<void()> _worker_warmup_cb = nullptr;
std::chrono::milliseconds _flush_interval_ms;
+ std::function<void()> _worker_teardown_cb = nullptr;
};
#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry;
diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h
index e3c966d..8337ab4 100644
--- a/include/spdlog/details/spdlog_impl.h
+++ b/include/spdlog/details/spdlog_impl.h
@@ -13,6 +13,7 @@
#include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/syslog_sink.h>
+#include <spdlog/sinks/ansicolor_sink.h>
#include <chrono>
#include <functional>
@@ -35,46 +36,53 @@ inline void spdlog::drop(const std::string &name)
}
// Create multi/single threaded rotating file logger
-inline std::shared_ptr<spdlog::logger> 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)
+inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush)
{
- return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, "txt", max_file_size, max_files, force_flush);
+ return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files, force_flush);
}
-inline std::shared_ptr<spdlog::logger> 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)
+inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush)
{
- return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, "txt", max_file_size, max_files, force_flush);
+ return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files, force_flush);
}
// Create file logger which creates new file at midnight):
-inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush)
+inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute, bool force_flush)
{
- return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, "txt", hour, minute, force_flush);
+ return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute, force_flush);
}
-inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush)
+
+inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute, bool force_flush)
{
- return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, "txt", hour, minute, force_flush);
+ return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute, force_flush);
}
+// Create stdout/stderr loggers (with optinal color support)
+inline std::shared_ptr<spdlog::logger> create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color)
+{
+ if (color) //use color wrapper sink
+ sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink);
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
-// Create stdout/stderr loggers
-inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name, bool color)
{
- return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
+ return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color);
}
-inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name, bool color)
{
- return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
+ return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color);
}
-inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name, bool color)
{
- return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
+ return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color);
}
-inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name, bool color)
{
- return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
+ return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color);
}
#if defined(__linux__) || defined(__APPLE__)
@@ -124,9 +132,9 @@ inline void spdlog::set_level(level::level_enum log_level)
}
-inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms)
+inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
- details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms);
+ details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
}
inline void spdlog::set_sync_mode()
@@ -138,4 +146,3 @@ inline void spdlog::drop_all()
{
details::registry::instance().drop_all();
}
-
diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h
index ebd2dd0..ca94e55 100644
--- a/include/spdlog/logger.h
+++ b/include/spdlog/logger.h
@@ -18,7 +18,6 @@
#include <vector>
#include <memory>
-#include <atomic>
#include <string>
namespace spdlog
@@ -42,6 +41,9 @@ public:
const std::string& name() const;
bool should_log(level::level_enum) const;
+ // automatically call flush() after a message of level log_level or higher is emitted
+ void flush_on(level::level_enum log_level);
+
// logger.info(cppformat_string, arg1, arg2, arg3, ...) call style
template <typename... Args> details::line_logger trace(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger debug(const char* fmt, const Args&... args);
@@ -104,8 +106,8 @@ protected:
std::string _name;
std::vector<sink_ptr> _sinks;
formatter_ptr _formatter;
- std::atomic_int _level;
-
+ spdlog::level_t _level;
+ spdlog::level_t _flush_level;
};
}
diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h
new file mode 100644
index 0000000..664b259
--- /dev/null
+++ b/include/spdlog/sinks/ansicolor_sink.h
@@ -0,0 +1,115 @@
+//
+// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog).
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/common.h>
+
+#include <string>
+#include <map>
+
+namespace spdlog
+{
+namespace sinks
+{
+
+/**
+ * @brief The ansi_color_sink is a decorator around another sink and prefixes
+ * the output with an ANSI escape sequence color code depending on the severity
+ * of the message.
+ */
+class ansicolor_sink : public sink
+{
+public:
+ ansicolor_sink(sink_ptr wrapped_sink);
+ virtual ~ansicolor_sink();
+
+ ansicolor_sink(const ansicolor_sink& other) = delete;
+ ansicolor_sink& operator=(const ansicolor_sink& other) = delete;
+
+ virtual void log(const details::log_msg& msg) override;
+ virtual void flush() override;
+
+ void set_color(level::level_enum level, const std::string& color);
+
+ /// Formatting codes
+ const std::string reset = "\033[00m";
+ const std::string bold = "\033[1m";
+ const std::string dark = "\033[2m";
+ const std::string underline = "\033[4m";
+ const std::string blink = "\033[5m";
+ const std::string reverse = "\033[7m";
+ const std::string concealed = "\033[8m";
+
+ // Foreground colors
+ const std::string grey = "\033[30m";
+ const std::string red = "\033[31m";
+ const std::string green = "\033[32m";
+ const std::string yellow = "\033[33m";
+ const std::string blue = "\033[34m";
+ const std::string magenta = "\033[35m";
+ const std::string cyan = "\033[36m";
+ const std::string white = "\033[37m";
+
+ /// Background colors
+ const std::string on_grey = "\033[40m";
+ const std::string on_red = "\033[41m";
+ const std::string on_green = "\033[42m";
+ const std::string on_yellow = "\033[43m";
+ const std::string on_blue = "\033[44m";
+ const std::string on_magenta = "\033[45m";
+ const std::string on_cyan = "\033[46m";
+ const std::string on_white = "\033[47m";
+
+
+protected:
+ sink_ptr sink_;
+ std::map<level::level_enum, std::string> colors_;
+};
+
+inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink)
+{
+ colors_[level::trace] = cyan;
+ colors_[level::debug] = cyan;
+ colors_[level::info] = white;
+ colors_[level::notice] = bold + white;
+ colors_[level::warn] = bold + yellow;
+ colors_[level::err] = red;
+ colors_[level::critical] = bold + red;
+ colors_[level::alert] = bold + white + on_red;
+ colors_[level::emerg] = bold + yellow + on_red;
+ colors_[level::off] = reset;
+}
+
+inline void ansicolor_sink::log(const details::log_msg& msg)
+{
+ // Wrap the originally formatted message in color codes
+ const std::string& prefix = colors_[msg.level];
+ const std::string& s = msg.formatted.str();
+ const std::string& suffix = reset;
+ details::log_msg m;
+ m.formatted << prefix << s << suffix;
+ sink_->log(m);
+}
+
+inline void ansicolor_sink::flush()
+{
+ sink_->flush();
+}
+
+inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color)
+{
+ colors_[level] = color;
+}
+
+inline ansicolor_sink::~ansicolor_sink()
+{
+ flush();
+}
+
+} // namespace sinks
+} // namespace spdlog
+
diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h
index c3d214f..e6fe44b 100644
--- a/include/spdlog/sinks/file_sinks.h
+++ b/include/spdlog/sinks/file_sinks.h
@@ -28,7 +28,7 @@ template<class Mutex>
class simple_file_sink : public base_sink < Mutex >
{
public:
- explicit simple_file_sink(const std::string &filename,
+ explicit simple_file_sink(const filename_t &filename,
bool force_flush = false) :
_file_helper(force_flush)
{
@@ -58,7 +58,7 @@ template<class Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
- rotating_file_sink(const std::string &base_filename, const std::string &extension,
+ rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files,
bool force_flush = false) :
_base_filename(base_filename),
@@ -90,13 +90,13 @@ protected:
}
private:
- static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension)
+ static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
{
- fmt::MemoryWriter w;
+ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
- w.write("{}.{}.{}", filename, index, extension);
+ w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
else
- w.write("{}.{}", filename, extension);
+ w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
return w.str();
}
@@ -111,25 +111,25 @@ private:
_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);
+ filename_t src = calc_filename(_base_filename, i - 1, _extension);
+ filename_t target = calc_filename(_base_filename, i, _extension);
if (details::file_helper::file_exists(target))
{
- if (std::remove(target.c_str()) != 0)
+ if (details::os::remove(target) != 0)
{
- throw spdlog_ex("rotating_file_sink: failed removing " + target);
+ throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target));
}
}
- if (details::file_helper::file_exists(src) && std::rename(src.c_str(), target.c_str()))
+ if (details::file_helper::file_exists(src) && details::os::rename(src, target))
{
- throw spdlog_ex("rotating_file_sink: failed renaming " + src + " to " + target);
+ throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target));
}
}
_file_helper.reopen(true);
}
- std::string _base_filename;
- std::string _extension;
+ filename_t _base_filename;
+ filename_t _extension;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
@@ -140,16 +140,46 @@ typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
+* Default generator of daily log file names.
+*/
+struct default_daily_file_name_calculator
+{
+ //Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
+ static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
+ {
+ std::tm tm = spdlog::details::os::localtime();
+ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
+ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
+ return w.str();
+ }
+};
+
+/*
+* Generator of daily log file names in format basename.YYYY-MM-DD.extension
+*/
+struct dateonly_daily_file_name_calculator
+{
+ //Create filename for the form basename.YYYY-MM-DD.extension
+ static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
+ {
+ std::tm tm = spdlog::details::os::localtime();
+ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
+ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
+ return w.str();
+ }
+};
+
+/*
* Rotating file sink based on date. rotates at midnight
*/
-template<class Mutex>
+template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
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,
+ const filename_t& base_filename,
+ const filename_t& extension,
int rotation_hour,
int rotation_minute,
bool force_flush = false) : _base_filename(base_filename),
@@ -161,7 +191,7 @@ public:
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));
+ _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
}
void flush() override
@@ -174,7 +204,7 @@ protected:
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
- _file_helper.open(calc_filename(_base_filename, _extension));
+ _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
@@ -197,17 +227,8 @@ private:
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;
+ filename_t _base_filename;
+ filename_t _extension;
int _rotation_h;
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;
diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h
index 9e101ad..16342ca 100644
--- a/include/spdlog/sinks/msvc_sink.h
+++ b/include/spdlog/sinks/msvc_sink.h
@@ -26,7 +26,7 @@ template<class Mutex>
class msvc_sink : public base_sink < Mutex >
{
public:
- explicit msvc_sink()
+ explicit msvc_sink()
{
}
@@ -37,7 +37,7 @@ public:
protected:
void _sink_it(const details::log_msg& msg) override
{
- OutputDebugStringA(msg.formatted.c_str());
+ OutputDebugStringA(msg.formatted.c_str());
}
};
diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h
index 85db334..ca4c55a 100644
--- a/include/spdlog/sinks/stdout_sinks.h
+++ b/include/spdlog/sinks/stdout_sinks.h
@@ -5,10 +5,9 @@
#pragma once
-#include <spdlog/sinks/ostream_sink.h>
#include <spdlog/details/null_mutex.h>
-#include <iostream>
+#include <cstdio>
#include <memory>
#include <mutex>
@@ -18,16 +17,27 @@ namespace sinks
{
template <class Mutex>
-class stdout_sink : public ostream_sink<Mutex>
+class stdout_sink : public base_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
- stdout_sink() : ostream_sink<Mutex>(std::cout, true) {}
+ stdout_sink() {}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
+
+ void _sink_it(const details::log_msg& msg) override
+ {
+ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
+ flush();
+ }
+
+ void flush() override
+ {
+ fflush(stdout);
+ }
};
typedef stdout_sink<details::null_mutex> stdout_sink_st;
@@ -35,17 +45,27 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
-class stderr_sink : public ostream_sink<Mutex>
+class stderr_sink : public base_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
- stderr_sink() : ostream_sink<Mutex>(std::cerr, true) {}
+ stderr_sink() {}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
+ void _sink_it(const details::log_msg& msg) override
+ {
+ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
+ flush();
+ }
+
+ void flush() override
+ {
+ fflush(stderr);
+ }
};
typedef stderr_sink<std::mutex> stderr_sink_mt;
diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h
index b92a239..2dc0209 100644
--- a/include/spdlog/spdlog.h
+++ b/include/spdlog/spdlog.h
@@ -53,7 +53,10 @@ void set_level(level::level_enum log_level);
// 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<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
+// worker_teardown_cb (optional):
+// callback function that will be called in worker thread upon exit
+//
+void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
// Turn off async mode
void set_sync_mode();
@@ -61,23 +64,22 @@ void set_sync_mode();
//
// Create and register multi/single threaded rotating file logger
//
-std::shared_ptr<logger> 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<logger> 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);
+std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
+std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
//
// Create file logger which creates new file on the given time (default in midnight):
//
-std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false);
-std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false);
-
+std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0, bool force_flush = false);
+std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0, bool force_flush = false);
//
// Create and register stdout/stderr loggers
//
-std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
-std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
-std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
-std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
+std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false);
+std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false);
+std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false);
+std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name, bool color = false);
//
diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h
index f136514..8c0ccfa 100644
--- a/include/spdlog/tweakme.h
+++ b/include/spdlog/tweakme.h
@@ -51,3 +51,20 @@
// Note that upon creating a logger the registry is modified by spdlog..
// #define SPDLOG_NO_REGISTRY_MUTEX
///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to avoid spdlog's usage of atomic log levels
+// Use only if your code never modifies a logger's log levels concurrently.
+// #define SPDLOG_NO_ATOMIC_LEVELS
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable usage of wchar_t for file names on Windows.
+// #define SPDLOG_WCHAR_FILENAMES
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// uncomment the below to override spdlog's default eol
+// #define SPDLOG_EOL "\n"
+///////////////////////////////////////////////////////////////////////////////
diff --git a/tests/Makefile b/tests/Makefile
index 0eec327..97871ba 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,22 +1,22 @@
-CXX ?= g++
-CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
-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
-
-
-
+CXX ?= g++
+CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
+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
+
+
+
diff --git a/tests/file_helper.cpp b/tests/file_helper.cpp
index f57ab70..5f3574d 100644
--- a/tests/file_helper.cpp
+++ b/tests/file_helper.cpp
@@ -5,7 +5,7 @@
using namespace spdlog::details;
-static const std::string filename = "logs/file_helper_test.txt";
+static const std::string target_filename = "logs/file_helper_test.txt";
static void write_with_helper(file_helper &helper, size_t howmany)
{
@@ -20,8 +20,8 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
prepare_logdir();
file_helper helper(false);
- helper.open(filename);
- REQUIRE(helper.filename() == filename);
+ helper.open(target_filename);
+ REQUIRE(helper.filename() == target_filename);
}
@@ -32,28 +32,28 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
auto expected_size = 123;
{
file_helper helper(true);
- helper.open(filename);
+ helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size);
}
- REQUIRE(get_filesize(filename) == expected_size);
+ REQUIRE(get_filesize(target_filename) == expected_size);
}
TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{
prepare_logdir();
- REQUIRE(!file_helper::file_exists(filename));
+ REQUIRE(!file_helper::file_exists(target_filename));
file_helper helper(false);
- helper.open(filename);
- REQUIRE(file_helper::file_exists(filename));
+ helper.open(target_filename);
+ REQUIRE(file_helper::file_exists(target_filename));
}
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
{
prepare_logdir();
file_helper helper(true);
- helper.open(filename);
+ helper.open(target_filename);
write_with_helper(helper, 12);
REQUIRE(helper.size() == 12);
helper.reopen(true);
@@ -65,7 +65,7 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
prepare_logdir();
auto expected_size = 14;
file_helper helper(true);
- helper.open(filename);
+ helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size);
helper.reopen(false);
diff --git a/tests/file_log.cpp b/tests/file_log.cpp
index 2abd021..3eedbee 100644
--- a/tests/file_log.cpp
+++ b/tests/file_log.cpp
@@ -77,15 +77,56 @@ TEST_CASE("daily_logger", "[daily_logger]]")
}
+TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
+{
+ using sink_type = spdlog::sinks::daily_file_sink<
+ std::mutex,
+ spdlog::sinks::dateonly_daily_file_name_calculator>;
+ prepare_logdir();
+ //calculate filename (time based)
+ std::string basename = "logs/daily_dateonly";
+ std::tm tm = spdlog::details::os::localtime();
+ fmt::MemoryWriter w;
+ w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true);
+ for (int i = 0; i < 10; ++i)
+ logger->info("Test message {}", i);
+ auto filename = w.str();
+ REQUIRE(count_lines(filename) == 10);
+}
+struct custom_daily_file_name_calculator
+{
+ static spdlog::filename_t calc_filename(const spdlog::filename_t& basename, const spdlog::filename_t& extension)
+ {
+ std::tm tm = spdlog::details::os::localtime();
+ fmt::MemoryWriter w;
+ w.write("{}{:04d}{:02d}{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
+ return w.str();
+ }
+};
+
+TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
+{
+ using sink_type = spdlog::sinks::daily_file_sink<
+ std::mutex,
+ custom_daily_file_name_calculator>;
+ prepare_logdir();
+ //calculate filename (time based)
+ std::string basename = "logs/daily_dateonly";
+ std::tm tm = spdlog::details::os::localtime();
+ fmt::MemoryWriter w;
+ w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true);
+ for (int i = 0; i < 10; ++i)
+ logger->info("Test message {}", i);
-
-
-
-
+ auto filename = w.str();
+ REQUIRE(count_lines(filename) == 10);
+}
diff --git a/tests/includes.h b/tests/includes.h
index 9cdee27..0590fc6 100644
--- a/tests/includes.h
+++ b/tests/includes.h
@@ -12,4 +12,5 @@
#include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h"
+#include "../include/spdlog/sinks/ostream_sink.h"
diff --git a/tests/tests.vcxproj b/tests/tests.vcxproj
index 56787c7..f05c162 100644
--- a/tests/tests.vcxproj
+++ b/tests/tests.vcxproj
@@ -1,141 +1,143 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{59A07559-5F38-4DD6-A7FA-DB4153690B42}</ProjectGuid>
- <RootNamespace>tests</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup />
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <SubSystem>Console</SubSystem>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <SubSystem>Console</SubSystem>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level4</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <SubSystem>Console</SubSystem>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
- <WarningLevel>Level4</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <SubSystem>Console</SubSystem>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="file_helper.cpp" />
- <ClCompile Include="file_log.cpp" />
- <ClCompile Include="format.cpp" />
- <ClCompile Include="main.cpp" />
- <ClCompile Include="registry.cpp" />
- <ClCompile Include="utils.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="catch.hpp" />
- <ClInclude Include="includes.h" />
- <ClInclude Include="utils.h" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{59A07559-5F38-4DD6-A7FA-DB4153690B42}</ProjectGuid>
+ <RootNamespace>tests</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="file_helper.cpp" />
+ <ClCompile Include="file_log.cpp" />
+ <ClCompile Include="format.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="registry.cpp" />
+ <ClCompile Include="utils.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="catch.hpp" />
+ <ClInclude Include="includes.h" />
+ <ClInclude Include="utils.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
</Project>
\ No newline at end of file
diff --git a/tests/tests.vcxproj.filters b/tests/tests.vcxproj.filters
index 8a1a5d6..72ca548 100644
--- a/tests/tests.vcxproj.filters
+++ b/tests/tests.vcxproj.filters
@@ -1,48 +1,48 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="file_log.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="format.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="main.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="registry.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="file_helper.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="utils.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="includes.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="catch.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="utils.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="file_log.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="format.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="registry.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="file_helper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="includes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="catch.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="utils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
</Project>
\ No newline at end of file
diff --git a/tests/utils.cpp b/tests/utils.cpp
index 83fdf84..751ff82 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -4,10 +4,11 @@ void prepare_logdir()
{
spdlog::drop_all();
#ifdef _WIN32
- system("del /F /Q logs\\*");
+ auto rv = system("del /F /Q logs\\*");
#else
- system("rm -f logs/*");
+ auto rv = system("rm -f logs/*");
#endif
+ (void)rv;
}
@@ -42,4 +43,3 @@ std::size_t get_filesize(const std::string& filename)
return ifs.tellg();
}
-
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/spdlog.git
More information about the debian-med-commit
mailing list