Compare commits

..

85 Commits

Author SHA1 Message Date
gabime
de0dbfa359 version 1.8.2 2020-12-11 16:40:52 +02:00
gabime
f93459579f version 1.9.0 2020-12-11 16:37:37 +02:00
gabime
2b81c40b90 Bump fmt to version 7.1.3 2020-12-11 16:32:39 +02:00
Gabi Melman
233e97c5e4 Merge pull request #1749 from bluescarni/pr/async_test_fix
Increase sleep time on a test case to avoid spurious failures
2020-11-26 11:03:46 +02:00
Francesco Biscani
fc1ce48dc7 Increase sleep time on a test case to avoid spurious failures. 2020-11-26 09:45:39 +01:00
Gabi Melman
fd5562eebe Merge pull request #1744 from ArnaudBienner/ArnaudBienner-readme-patch
📝 README example: Add {:a} format flag to bin_to_hex example
2020-11-23 02:01:34 +02:00
ArnaudBienner
0695d9cb5f 📝 README example: Add {:a} format flag to bin_to_hex example
Add {:a} format flag to bin_to_hex example
2020-11-22 20:54:28 +01:00
Gabi Melman
456b24134d Merge pull request #1742 from jwittbrodt/v1.x
Add missing include
2020-11-20 18:13:49 +02:00
Jonas Wittbrodt
f8ba24afee add missing <algorithm> include 2020-11-19 14:09:33 +01:00
Gabi Melman
eebb921c9f Merge pull request #1735 from o2gy84/v1.x
Ability to get size of messages queue of async thread pool
2020-11-13 12:59:52 +02:00
Могилин Виктор
e17ee87f38 Ability to get size of messages queue of async thread pool 2020-11-13 13:12:32 +03:00
Gabi Melman
18e3f07f7d Fix #1710 2020-11-05 18:27:31 +02:00
Gabi Melman
9ce39a470f Merge pull request #1726 from dkavolis/v1.x
Perfect forwarding for arguments
2020-11-02 04:02:38 +02:00
dkavolis
23572369fc Perfect forwarding for arguments 2020-11-02 00:37:03 +00:00
Gabi Melman
01b350de96 Merge pull request #1712 from ChristofKaufmann/deb-package
Add CPack debian package settings
2020-10-20 22:23:45 +03:00
Christof Kaufmann
365e470a32 Add CPack debian package settings 2020-10-20 19:57:56 +02:00
Gabi Melman
a42b40656c Merge pull request #1711 from ChristofKaufmann/fix-typo
Fix typo in comment
2020-10-20 10:00:43 +03:00
Christof Kaufmann
40160f2a57 Fix typo in comment 2020-10-20 02:49:09 +02:00
Gabi Melman
90b33b1552 Merge pull request #1709 from kitattyor/v1.x
Download googlebenchmark if not found
2020-10-17 17:56:59 +03:00
kitattyor
5567ed01e5 Download googlebenchmark if not found; requires cmake 3.11 2020-10-17 21:08:36 +08:00
gabime
cbe9448650 version 1.8.1 2020-09-30 17:30:06 +03:00
gabime
5b345534dc Minor change in ifdef for clarity 2020-09-30 17:29:44 +03:00
Gabi Melman
c8dc318fb3 Update logger-inl.h 2020-09-29 02:23:03 +03:00
Gabi Melman
23cb1a1080 Update logger-inl.h 2020-09-29 02:20:43 +03:00
Gabi Melman
21cf8d7d3c Merge pull request #1685 from eddelbuettel/feature/r_use_of_reprintf
let R header defines switch to REprintf over fprintf(stdderr)
2020-09-29 02:18:35 +03:00
Dirk Eddelbuettel
3cf4d34094 let R header defines switch to REprintf over fprintf(stdderr) 2020-09-28 17:13:09 -05:00
Gabi Melman
16d78ae5db Update stdout_sinks-inl.h 2020-09-28 17:08:13 +03:00
gabime
62b4b7af83 Fix #1667 2020-09-28 13:39:31 +03:00
Gabi Melman
9799ecac6a Remove redundant size check before calling WriteFile 2020-09-27 21:27:58 +03:00
Gabi Melman
dccb766095 Fix warning about enum usage 2020-09-27 19:08:24 +03:00
gabime
c97983a91c Fix linux build 2020-09-27 18:42:27 +03:00
gabime
680fb07fd5 Updatd WriteFile usage 2020-09-27 18:34:01 +03:00
gabime
cfd0ea197c Simplify WriteFile() usage under windows 2020-09-27 18:32:08 +03:00
gabime
48d4ed9bc0 Fix #1675 2020-09-27 18:27:41 +03:00
gabime
3bed78356e Added cfg tests 2020-09-27 02:10:52 +03:00
gabime
8923922f30 Cleaned level loading from env var 2020-09-27 02:08:24 +03:00
gabime
7542e42e4f removed extra parentheses 2020-09-27 01:05:55 +03:00
Gabi Melman
7a9b23e4f4 Update registry-inl.h 2020-09-26 16:09:43 +03:00
gabime
47253ba2a1 Updated comment 2020-09-26 15:56:57 +03:00
gabime
69b54dd9e4 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2020-09-26 15:49:21 +03:00
gabime
36138617fc small readablilty update in registry 2020-09-26 15:36:57 +03:00
gabime
231ca50700 clang-format 2020-09-26 15:34:05 +03:00
gabime
c7613f3e91 Fixed #1680 2020-09-26 15:32:44 +03:00
gabime
05d5546eb1 Restore example 2020-09-26 15:30:45 +03:00
gabime
cefe67726e wip fix #1680 again 2020-09-26 15:06:53 +03:00
gabime
1ac2dcc537 wip fix #1680 again 2020-09-26 14:41:33 +03:00
gabime
3a68eecb28 Fix issue #1680 2020-09-25 18:19:50 +03:00
gabime
54a8259b42 Fix #1680 2020-09-25 15:03:13 +03:00
gabime
32b6f1619f Added tests for issue #1680 2020-09-25 14:44:01 +03:00
Gabi Melman
99b8c5d379 Update bin_to_hex.h 2020-09-24 10:04:07 +03:00
Gabi Melman
5deb7c55e1 Fix #1676 2020-09-21 03:31:19 +03:00
gabime
9cd25dd216 Remove un needed hasher declaration 2020-09-01 23:49:54 +03:00
gabime
4a9ccf7e38 Fixed chrono wrapper 2020-09-01 12:34:59 +03:00
gabime
2963b9f07f Updated comment 2020-09-01 12:24:11 +03:00
gabime
a4a9bc4d8e version 1.8.0 2020-09-01 12:05:23 +03:00
gabime
a16a029790 Added bundled fmt os.h file 2020-09-01 12:04:36 +03:00
Gabi Melman
97fea81599 Update README.md 2020-08-31 03:51:17 +03:00
gabime
ccffb6ecd6 Fix #1581 2020-08-30 20:44:42 +03:00
gabime
63b5a1a4d8 Fix #1581 2020-08-30 20:44:04 +03:00
gabime
dfc777803a enable the 'n' flag in for backward compatibility with fmt 6.x 2020-08-30 15:58:22 +03:00
gabime
934a9bb23e Fix issue #1617 2020-08-30 15:18:33 +03:00
gabime
7097f7a894 Updated stopwatch example to README.md 2020-08-29 03:50:48 +03:00
gabime
537fd7c4ba Added stopwatch example to README.md 2020-08-29 03:49:18 +03:00
gabime
d5048b8b0c Added stopwatch example to README.md 2020-08-29 03:48:24 +03:00
gabime
0348556aac Added stopwatch example to README.md 2020-08-29 03:46:29 +03:00
gabime
d6329b9dce Added some stopwatch tests 2020-08-29 03:25:16 +03:00
gabime
34f3d29d93 Added stopwatch support 2020-08-29 02:48:43 +03:00
gabime
cd701761f9 clang-format 2020-08-26 00:10:05 +03:00
gabime
23c2c00d69 Bumb fmt version to 7.0.3 2020-08-26 00:02:09 +03:00
Gabi Melman
fa501b46cf Merge pull request #1653 from gk6k6k/patch-1
Update README.md
2020-08-20 12:26:03 +03:00
gk6k6k
38dc0a5c5d Update README.md
Hi,
fedora yum is obsolite log time ago...
https://www.2daygeek.com/comparison-difference-between-dnf-vs-yum/

Works for fedora 31:

[root@Galleon ~]# uname -a
Linux Galleon 5.6.13-200.fc31.x86_64 #1 SMP Thu May 14 23:26:14 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@Galleon ~]#
2020-08-20 11:01:49 +02:00
Gabi Melman
685cc4edbc Revert last commit 2020-08-20 00:00:54 +03:00
Gabi Melman
78369375e3 Update helpers-inl.h 2020-08-19 23:57:47 +03:00
Gabi Melman
6587058f74 Update argv.h 2020-08-19 23:56:56 +03:00
Gabi Melman
30f738e49a Merge pull request #1651 from bareya/make_load_env_levels_inline
Make load_env_levels inline function
2020-08-19 23:55:40 +03:00
Piotr Barejko
726ca01e5c Make load_env_levels inline function 2020-08-19 12:43:41 -07:00
Gabi Melman
83b40b8cda Merge pull request #1640 from dkruempe/v1.x
tcp_client.h bugfix for macOS and add tcp example
2020-08-06 13:06:08 +03:00
Dominik Krümpelmann
db0d0438ff tcp_client.h bugfix for macOS and add tcp example
-> fix the issue that the head is not compilable bc. of missing declaration
-> optimize if/else block in connection function. Else not needed bc. of break in if
2020-08-06 11:40:17 +02:00
Gabi Melman
3c527488e7 Update spdlog-inl.h 2020-07-24 13:06:29 +03:00
Gabi Melman
99abcf6ded Merge pull request #1628 from eyalroz/issue-1627
Fix for issue #1627: Global get_level() and should_log()
2020-07-24 00:31:51 +03:00
Eyal Rozenberg
7009727559 Fix for issue #1627:
* Added: `spdlog::get_level()` API function - like `logger::level()`, except for the name change
* Added: `spdlog::should_log()` API function - like `logger: should_log()`
2020-07-23 23:05:12 +03:00
Gabi Melman
ae02fba141 Merge pull request #1624 from tambry/cmake_range
Bump CMake requirement to 3.10, don't enable C language
2020-07-21 14:28:42 +03:00
Raul Tambre
76cdeb62e3 Don't enable C language in CMake
spdlog doesn't seem to actually require the C language.
Not enabling it results in a significant initial configure time speedup.
2020-07-21 11:39:40 +03:00
Raul Tambre
ae9627c64c Raise CMake requirement to 3.13
* Reduces the range of possible version we'd need to test with.
* Enables newer policies reducing possible deprecation warnings from new policies.
* Allows removing some code for compatibility with older versions.
* Coincides with LLVM's bump to requiring CMake 3.13.
2020-07-20 22:03:50 +03:00
Gabi Melman
58875bdcd7 Update tcp_client-windows.h 2020-07-14 23:15:49 +03:00
58 changed files with 6060 additions and 3125 deletions

View File

@@ -1,14 +1,6 @@
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.10)
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
enable_language(C)
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
# Start spdlog project # Start spdlog project
@@ -40,7 +32,7 @@ endif()
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN") if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
set(CMAKE_CXX_EXTENSIONS ON) set(CMAKE_CXX_EXTENSIONS ON)
endif() endif()
@@ -200,7 +192,8 @@ endif()
# Misc definitions according to tweak options # Misc definitions according to tweak options
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT}) set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
foreach(SPDLOG_OPTION foreach(
SPDLOG_OPTION
SPDLOG_WCHAR_TO_UTF8_SUPPORT SPDLOG_WCHAR_TO_UTF8_SUPPORT
SPDLOG_WCHAR_FILENAMES SPDLOG_WCHAR_FILENAMES
SPDLOG_NO_EXCEPTIONS SPDLOG_NO_EXCEPTIONS
@@ -208,8 +201,7 @@ foreach(SPDLOG_OPTION
SPDLOG_PREVENT_CHILD_FD SPDLOG_PREVENT_CHILD_FD
SPDLOG_NO_THREAD_ID SPDLOG_NO_THREAD_ID
SPDLOG_NO_TLS SPDLOG_NO_TLS
SPDLOG_NO_ATOMIC_LEVELS SPDLOG_NO_ATOMIC_LEVELS)
)
if(${SPDLOG_OPTION}) if(${SPDLOG_OPTION})
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION}) target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION}) target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})

View File

@@ -25,7 +25,7 @@ $ cmake .. && make -j
* Homebrew: `brew install spdlog` * Homebrew: `brew install spdlog`
* MacPorts: `sudo port install spdlog` * MacPorts: `sudo port install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean` * FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog` * Fedora: `dnf install spdlog`
* Gentoo: `emerge dev-libs/spdlog` * Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog` * Arch Linux: `pacman -S spdlog`
* vcpkg: `vcpkg install spdlog` * vcpkg: `vcpkg install spdlog`
@@ -56,7 +56,6 @@ $ cmake .. && make -j
#### Basic usage #### Basic usage
```c++ ```c++
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() int main()
{ {
@@ -79,10 +78,6 @@ int main()
// define SPDLOG_ACTIVE_LEVEL to desired level // define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42); SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message"); SPDLOG_DEBUG("Some debug message");
// Set the default logger to file logger
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
spdlog::set_default_logger(file_logger);
} }
``` ```
@@ -169,6 +164,20 @@ spdlog::flush_every(std::chrono::seconds(3));
``` ```
---
#### Stopwatch
```c++
// Stopwatch support for spdlog
#include "spdlog/stopwatch.h"
void stopwatch_example()
{
spdlog::stopwatch sw;
spdlog::debug("Elapsed {}", sw);
spdlog::debug("Elapsed {:.3}", sw);
}
```
--- ---
#### Log binary data in hex #### Log binary data in hex
```c++ ```c++
@@ -179,6 +188,7 @@ spdlog::flush_every(std::chrono::seconds(3));
// {:s} - don't separate each byte with space. // {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start. // {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines. // {:n} - don't split the output to lines.
// {:a} - show ASCII if :n is not set.
#include "spdlog/fmt/bin_to_hex.h" #include "spdlog/fmt/bin_to_hex.h"

View File

@@ -1,6 +1,6 @@
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.10)
project(spdlog_bench CXX) project(spdlog_bench CXX)
if(NOT TARGET spdlog) if(NOT TARGET spdlog)
@@ -9,7 +9,24 @@ if(NOT TARGET spdlog)
endif() endif()
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(benchmark CONFIG REQUIRED) find_package(benchmark CONFIG)
if (NOT benchmark_FOUND)
message(STATUS "Using CMake Version ${CMAKE_VERSION}")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
# User can fetch googlebenchmark
message(STATUS "Downloading GoogleBenchmark")
include(FetchContent)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
# Do not build and run googlebenchmark tests
FetchContent_Declare(googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.5.2)
FetchContent_MakeAvailable(googlebenchmark)
else()
message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
endif()
endif()
add_executable(bench bench.cpp) add_executable(bench bench.cpp)
spdlog_enable_warnings(bench) spdlog_enable_warnings(bench)

View File

@@ -9,7 +9,7 @@
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/async.h" #include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/fmt/bundled/locale.h"
#include "utils.h" #include "utils.h"
#include <atomic> #include <atomic>
@@ -29,7 +29,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4996) // disable fopen warning under msvc #pragma warning(disable : 4996) // disable fopen warning under msvc
#endif // _MSC_VER #endif // _MSC_VER
int count_lines(const char *filename) int count_lines(const char *filename)
{ {
@@ -48,14 +48,14 @@ int count_lines(const char *filename)
void verify_file(const char *filename, int expected_count) void verify_file(const char *filename, int expected_count)
{ {
spdlog::info("Verifying {} to contain {:n} line..", filename, expected_count); spdlog::info("Verifying {} to contain {} line..", filename, expected_count);
auto count = count_lines(filename); auto count = count_lines(filename);
if (count != expected_count) if (count != expected_count)
{ {
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, expected_count); spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, expected_count);
exit(1); exit(1);
} }
spdlog::info("Line count OK ({:n})\n", count); spdlog::info("Line count OK ({})\n", count);
} }
#ifdef _MSC_VER #ifdef _MSC_VER
@@ -98,11 +98,12 @@ int main(int argc, char *argv[])
auto slot_size = sizeof(spdlog::details::async_msg); auto slot_size = sizeof(spdlog::details::async_msg);
spdlog::info("-------------------------------------------------"); spdlog::info("-------------------------------------------------");
spdlog::info("Messages : {:n}", howmany); spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Messages : {:L}", howmany));
spdlog::info("Threads : {:n}", threads); spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads : {:L}", threads));
spdlog::info("Queue : {:n} slots", queue_size); spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue : {:L} slots", queue_size));
spdlog::info("Queue memory : {:n} x {} = {:n} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024); spdlog::info(fmt::format(
spdlog::info("Total iters : {:n}", iters); std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters : {:L}", iters));
spdlog::info("-------------------------------------------------"); spdlog::info("-------------------------------------------------");
const char *filename = "logs/basic_async.log"; const char *filename = "logs/basic_async.log";
@@ -175,5 +176,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
auto delta = high_resolution_clock::now() - start; auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d)); spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)));
} }

View File

@@ -11,6 +11,7 @@
#include "spdlog/sinks/daily_file_sink.h" #include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/null_sink.h" #include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/fmt/bundled/locale.h"
#include "utils.h" #include "utils.h"
#include <atomic> #include <atomic>
@@ -32,7 +33,7 @@ static const int max_threads = 1000;
void bench_threaded_logging(size_t threads, int iters) void bench_threaded_logging(size_t threads, int iters)
{ {
spdlog::info("**************************************************************"); spdlog::info("**************************************************************");
spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters); spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
spdlog::info("**************************************************************"); spdlog::info("**************************************************************");
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true); auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
@@ -68,7 +69,7 @@ void bench_threaded_logging(size_t threads, int iters)
void bench_single_threaded(int iters) void bench_single_threaded(int iters)
{ {
spdlog::info("**************************************************************"); spdlog::info("**************************************************************");
spdlog::info("Single threaded: {:n} messages", iters); spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
spdlog::info("**************************************************************"); spdlog::info("**************************************************************");
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true); auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
@@ -152,7 +153,8 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta = high_resolution_clock::now() - start; auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); spdlog::info(
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
spdlog::drop(log->name()); spdlog::drop(log->name());
} }
@@ -182,7 +184,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_co
auto delta = high_resolution_clock::now() - start; auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); spdlog::info(
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
spdlog::drop(log->name()); spdlog::drop(log->name());
} }
@@ -205,7 +208,7 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name()); spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default)); spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
} }
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log) void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
@@ -232,7 +235,7 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name()); spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default)); spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
} }
*/ */

View File

@@ -18,23 +18,37 @@ set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
set(CPACK_RPM_PACKAGE_LICENSE "MIT") set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL}) set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL})
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.") set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
if(CPACK_PACKAGE_NAME) if(CPACK_PACKAGE_NAME)
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
else() else()
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
endif() endif()
if(CPACK_RPM_PACKAGE_RELEASE) if(CPACK_RPM_PACKAGE_RELEASE)
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}") set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
endif() endif()
if(CPACK_DEBIAN_PACKAGE_RELEASE)
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}")
endif()
if(CPACK_RPM_PACKAGE_ARCHITECTURE) if(CPACK_RPM_PACKAGE_ARCHITECTURE)
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}") set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
endif() endif()
if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
endif()
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm") set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
if(NOT CPACK_PACKAGE_RELOCATABLE) if(NOT CPACK_PACKAGE_RELOCATABLE)
# Depend on pkgconfig rpm to create the system pkgconfig folder # Depend on pkgconfig rpm to create the system pkgconfig folder

View File

@@ -1,6 +1,6 @@
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.10)
project(spdlog_examples CXX) project(spdlog_examples CXX)
if(NOT TARGET spdlog) if(NOT TARGET spdlog)

View File

@@ -13,6 +13,7 @@ void rotating_example();
void daily_example(); void daily_example();
void async_example(); void async_example();
void binary_example(); void binary_example();
void stopwatch_example();
void trace_example(); void trace_example();
void multi_sink_example(); void multi_sink_example();
void user_defined_example(); void user_defined_example();
@@ -71,6 +72,7 @@ int main(int, char *[])
user_defined_example(); user_defined_example();
err_handler_example(); err_handler_example();
trace_example(); trace_example();
stopwatch_example();
custom_flags_example(); custom_flags_example();
// Flush all *registered* loggers using a worker thread every 3 seconds. // Flush all *registered* loggers using a worker thread every 3 seconds.
@@ -192,6 +194,16 @@ void trace_example()
SPDLOG_LOGGER_TRACE(logger, "another trace message"); SPDLOG_LOGGER_TRACE(logger, "another trace message");
} }
// stopwatch example
#include "spdlog/stopwatch.h"
#include <thread>
void stopwatch_example()
{
spdlog::stopwatch sw;
std::this_thread::sleep_for(std::chrono::milliseconds(123));
spdlog::info("Stopwatch: {} seconds", sw);
}
// A logger with multiple sinks (stdout and file) - each with a different format and log level. // A logger with multiple sinks (stdout and file) - each with a different format and log level.
void multi_sink_example() void multi_sink_example()
{ {

View File

@@ -35,7 +35,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl struct async_factory_impl
{ {
template<typename Sink, typename... SinkArgs> template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args) static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
{ {
auto &registry_inst = details::registry::instance(); auto &registry_inst = details::registry::instance();
@@ -61,13 +61,13 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>; using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
template<typename Sink, typename... SinkArgs> template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args) inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args)
{ {
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...); return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
} }
template<typename Sink, typename... SinkArgs> template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args) inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
{ {
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...); return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
} }

View File

@@ -21,7 +21,7 @@ namespace spdlog {
namespace cfg { namespace cfg {
// search for SPDLOG_LEVEL= in the args and use it to init the levels // search for SPDLOG_LEVEL= in the args and use it to init the levels
void load_argv_levels(int argc, const char **argv) inline void load_argv_levels(int argc, const char **argv)
{ {
const std::string spdlog_level_prefix = "SPDLOG_LEVEL="; const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++)
@@ -30,13 +30,12 @@ void load_argv_levels(int argc, const char **argv)
if (arg.find(spdlog_level_prefix) == 0) if (arg.find(spdlog_level_prefix) == 0)
{ {
auto levels_string = arg.substr(spdlog_level_prefix.size()); auto levels_string = arg.substr(spdlog_level_prefix.size());
auto levels = helpers::extract_levels(levels_string); helpers::load_levels(levels_string);
details::registry::instance().update_levels(std::move(levels));
} }
} }
} }
void load_argv_levels(int argc, char **argv) inline void load_argv_levels(int argc, char **argv)
{ {
load_argv_levels(argc, const_cast<const char **>(argv)); load_argv_levels(argc, const_cast<const char **>(argv));
} }

View File

@@ -17,7 +17,7 @@
// export SPDLOG_LEVEL=debug // export SPDLOG_LEVEL=debug
// //
// turn off all logging except for logger1: // turn off all logging except for logger1:
// export SPDLOG_LEVEL="off,logger1=debug" // export SPDLOG_LEVEL="*=off,logger1=debug"
// //
// turn off all logging except for logger1 and logger2: // turn off all logging except for logger1 and logger2:
@@ -25,11 +25,13 @@
namespace spdlog { namespace spdlog {
namespace cfg { namespace cfg {
void load_env_levels() inline void load_env_levels()
{ {
auto env_val = details::os::getenv("SPDLOG_LEVEL"); auto env_val = details::os::getenv("SPDLOG_LEVEL");
auto levels = helpers::extract_levels(env_val); if (!env_val.empty())
details::registry::instance().update_levels(std::move(levels)); {
helpers::load_levels(env_val);
}
} }
} // namespace cfg } // namespace cfg

View File

@@ -11,6 +11,7 @@
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#include <spdlog/details/registry.h> #include <spdlog/details/registry.h>
#include <algorithm>
#include <string> #include <string>
#include <utility> #include <utility>
#include <sstream> #include <sstream>
@@ -78,24 +79,40 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
return rv; return rv;
} }
SPDLOG_INLINE log_levels extract_levels(const std::string &input) SPDLOG_INLINE void load_levels(const std::string &input)
{ {
if (input.empty() || input.size() > 512)
{
return;
}
auto key_vals = extract_key_vals_(input); auto key_vals = extract_key_vals_(input);
log_levels rv; std::unordered_map<std::string, level::level_enum> levels;
level::level_enum global_level = level::info;
bool global_level_found = false;
for (auto &name_level : key_vals) for (auto &name_level : key_vals)
{ {
auto &logger_name = name_level.first; auto &logger_name = name_level.first;
auto level_name = to_lower_(name_level.second); auto level_name = to_lower_(name_level.second);
auto level = level::from_str(level_name); auto level = level::from_str(level_name);
// fallback to "info" if unrecognized level name // ignore unrecognized level names
if (level == level::off && level_name != "off") if (level == level::off && level_name != "off")
{ {
level = level::info; continue;
}
if (logger_name.empty()) // no logger name indicate global level
{
global_level_found = true;
global_level = level;
}
else
{
levels[logger_name] = level;
} }
rv.set(logger_name, level);
} }
return rv;
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
} }
} // namespace helpers } // namespace helpers

View File

@@ -3,7 +3,8 @@
#pragma once #pragma once
#include <spdlog/cfg/log_levels.h> #include <spdlog/common.h>
#include <unordered_map>
namespace spdlog { namespace spdlog {
namespace cfg { namespace cfg {
@@ -17,7 +18,7 @@ namespace helpers {
// turn off all logging except for logger1: "off,logger1=debug" // turn off all logging except for logger1: "off,logger1=debug"
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info" // turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
// //
SPDLOG_API log_levels extract_levels(const std::string &txt); SPDLOG_API void load_levels(const std::string &txt);
} // namespace helpers } // namespace helpers
} // namespace cfg } // namespace cfg

View File

@@ -1,47 +0,0 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/common.h>
#include <string>
#include <unordered_map>
namespace spdlog {
namespace cfg {
class log_levels
{
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
spdlog::level::level_enum default_level_ = level::info;
public:
void set(const std::string &logger_name, level::level_enum lvl)
{
if (logger_name.empty())
{
default_level_ = lvl;
}
else
{
levels_[logger_name] = lvl;
}
}
void set_default(level::level_enum lvl)
{
default_level_ = lvl;
}
level::level_enum get(const std::string &logger_name)
{
auto it = levels_.find(logger_name);
return it != levels_.end() ? it->second : default_level_;
}
level::level_enum default_level()
{
return default_level_;
}
};
} // namespace cfg
} // namespace spdlog

View File

@@ -167,7 +167,6 @@ SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOE
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT; SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
using level_hasher = std::hash<int>;
} // namespace level } // namespace level
// //
@@ -232,7 +231,7 @@ namespace details {
using std::make_unique; using std::make_unique;
#else #else
template<typename T, typename... Args> template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args) std::unique_ptr<T> make_unique(Args &&...args)
{ {
static_assert(!std::is_array<T>::value, "arrays not supported"); static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); return std::unique_ptr<T>(new T(std::forward<Args>(args)...));

View File

@@ -38,11 +38,11 @@ inline unsigned int count_digits(T n)
// fmt 7.0.0 renamed the internal namespace to detail. // fmt 7.0.0 renamed the internal namespace to detail.
// See: https://github.com/fmtlib/fmt/issues/1538 // See: https://github.com/fmtlib/fmt/issues/1538
#if FMT_VERSION < 70000 #if FMT_VERSION < 70000
internal internal
#else #else
detail detail
#endif #endif
::count_digits(static_cast<count_type>(n))); ::count_digits(static_cast<count_type>(n)));
} }
inline void pad2(int n, memory_buf_t &dest) inline void pad2(int n, memory_buf_t &dest)

View File

@@ -110,6 +110,12 @@ public:
return q_.overrun_counter(); return q_.overrun_counter();
} }
size_t size()
{
std::unique_lock<std::mutex> lock(queue_mutex_);
return q_.size();
}
private: private:
std::mutex queue_mutex_; std::mutex queue_mutex_;
std::condition_variable push_cv_; std::condition_variable push_cv_;

View File

@@ -67,7 +67,11 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
new_logger->set_error_handler(err_handler_); new_logger->set_error_handler(err_handler_);
} }
new_logger->set_level(levels_.get(new_logger->name())); // set new level according to previously configured level or default level
auto it = log_levels_.find(new_logger->name());
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
new_logger->set_level(new_level);
new_logger->flush_on(flush_level_); new_logger->flush_on(flush_level_);
if (backtrace_n_messages_ > 0) if (backtrace_n_messages_ > 0)
@@ -171,7 +175,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
{ {
l.second->set_level(log_level); l.second->set_level(log_level);
} }
levels_.set_default(log_level); global_log_level_ = log_level;
} }
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
@@ -263,14 +267,24 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
automatic_registration_ = automatic_registration; automatic_registration_ = automatic_registration;
} }
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels) SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
{ {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<std::mutex> lock(logger_map_mutex_);
levels_ = std::move(levels); log_levels_ = std::move(levels);
for (auto &l : loggers_) auto global_level_requested = global_level != nullptr;
global_log_level_ = global_level_requested ? *global_level : global_log_level_;
for (auto &logger : loggers_)
{ {
auto &logger = l.second; auto logger_entry = log_levels_.find(logger.first);
logger->set_level(levels_.get(logger->name())); if (logger_entry != log_levels_.end())
{
logger.second->set_level(logger_entry->second);
}
else if (global_level_requested)
{
logger.second->set_level(*global_level);
}
} }
} }

View File

@@ -9,7 +9,6 @@
// This class is thread safe // This class is thread safe
#include <spdlog/common.h> #include <spdlog/common.h>
#include <spdlog/cfg/log_levels.h>
#include <chrono> #include <chrono>
#include <functional> #include <functional>
@@ -28,6 +27,7 @@ class periodic_worker;
class SPDLOG_API registry class SPDLOG_API registry
{ {
public: public:
using log_levels = std::unordered_map<std::string, level::level_enum>;
registry(const registry &) = delete; registry(const registry &) = delete;
registry &operator=(const registry &) = delete; registry &operator=(const registry &) = delete;
@@ -80,7 +80,8 @@ public:
void set_automatic_registration(bool automatic_registration); void set_automatic_registration(bool automatic_registration);
void update_levels(cfg::log_levels levels); // set levels for all existing/future loggers. global_level can be null if should not set.
void set_levels(log_levels levels, level::level_enum *global_level);
static registry &instance(); static registry &instance();
@@ -90,11 +91,13 @@ private:
void throw_if_exists_(const std::string &logger_name); void throw_if_exists_(const std::string &logger_name);
void register_logger_(std::shared_ptr<logger> new_logger); void register_logger_(std::shared_ptr<logger> new_logger);
bool set_level_from_cfg_(logger *logger);
std::mutex logger_map_mutex_, flusher_mutex_; std::mutex logger_map_mutex_, flusher_mutex_;
std::recursive_mutex tp_mutex_; std::recursive_mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_; std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
cfg::log_levels levels_; log_levels log_levels_;
std::unique_ptr<formatter> formatter_; std::unique_ptr<formatter> formatter_;
spdlog::level::level_enum global_log_level_ = level::info;
level::level_enum flush_level_ = level::off; level::level_enum flush_level_ = level::off;
void (*err_handler_)(const std::string &msg); void (*err_handler_)(const std::string &msg);
std::shared_ptr<thread_pool> tp_; std::shared_ptr<thread_pool> tp_;

View File

@@ -13,7 +13,7 @@ class logger;
struct synchronous_factory struct synchronous_factory
{ {
template<typename Sink, typename... SinkArgs> template<typename Sink, typename... SinkArgs>
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args) static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args)
{ {
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...); auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink)); auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));

View File

@@ -8,8 +8,8 @@
#include <spdlog/common.h> #include <spdlog/common.h>
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#include <windows.h>
#include <winsock2.h> #include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>

View File

@@ -75,7 +75,11 @@ public:
int last_errno = 0; int last_errno = 0;
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{ {
int const flags = SOCK_CLOEXEC; #if defined(SOCK_CLOEXEC)
const int flags = SOCK_CLOEXEC;
#else
const int flags = 0;
#endif
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol); socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
if (socket_ == -1) if (socket_ == -1)
{ {
@@ -87,12 +91,9 @@ public:
{ {
break; break;
} }
else last_errno = errno;
{ ::close(socket_);
last_errno = errno; socket_ = -1;
::close(socket_);
socket_ = -1;
}
} }
::freeaddrinfo(addrinfo_result); ::freeaddrinfo(addrinfo_result);
if (socket_ == -1) if (socket_ == -1)

View File

@@ -68,6 +68,11 @@ size_t SPDLOG_INLINE thread_pool::overrun_counter()
return q_.overrun_counter(); return q_.overrun_counter();
} }
size_t SPDLOG_INLINE thread_pool::queue_size()
{
return q_.size();
}
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
{ {
if (overflow_policy == async_overflow_policy::block) if (overflow_policy == async_overflow_policy::block)

View File

@@ -97,6 +97,7 @@ public:
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy); void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy); void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
size_t overrun_counter(); size_t overrun_counter();
size_t queue_size();
private: private:
q_type q_; q_type q_;

View File

@@ -9,7 +9,7 @@
// //
// Support for logging binary data as hex // Support for logging binary data as hex
// format flags: // format flags, any combination of the followng:
// {:X} - print in uppercase. // {:X} - print in uppercase.
// {:s} - don't separate each byte with space. // {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start. // {:p} - don't print the position on each line start.

View File

@@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// From fits in To without any problem. // From fits in To without any problem.
} else { } else {
// From does not always fit in To, resort to a dynamic check. // From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) { if (from < (T::min)() || from > (T::max)()) {
// outside range. // outside range.
ec = 1; ec = 1;
return {}; return {};
@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
static_assert(F::is_integer, "From must be integral"); static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral"); static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) { if (detail::const_check(F::is_signed && !T::is_signed)) {
// From may be negative, not allowed! // From may be negative, not allowed!
if (fmt::internal::is_negative(from)) { if (fmt::detail::is_negative(from)) {
ec = 1; ec = 1;
return {}; return {};
} }
// From is positive. Can it always fit in To? // From is positive. Can it always fit in To?
if (F::digits <= T::digits) { if (F::digits > T::digits &&
// yes, From always fits in To. from > static_cast<From>(detail::max_value<To>())) {
} else { ec = 1;
// from may not fit in To, we have to do a dynamic check return {};
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
} }
} }
if (!F::is_signed && T::is_signed) { if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
// can from be held in To? from > static_cast<From>(detail::max_value<To>())) {
if (F::digits < T::digits) { ec = 1;
// yes, From always fits in To. return {};
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
} }
return static_cast<To>(from); // Lossless conversion.
// reaching here means all is ok for lossless conversion. }
return static_cast<To>(from);
} // function
template <typename To, typename From, template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)> FMT_ENABLE_IF(std::is_same<From, To>::value)>
@@ -141,7 +125,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
// catch the only happy case // catch the only happy case
if (std::isfinite(from)) { if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) { if (from >= T::lowest() && from <= (T::max)()) {
return static_cast<To>(from); return static_cast<To>(from);
} }
// not within range. // not within range.
@@ -190,17 +174,16 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// safe conversion to IntermediateRep // safe conversion to IntermediateRep
IntermediateRep count = IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec); lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) { if (ec) return {};
return {};
}
// multiply with Factor::num without overflow or underflow // multiply with Factor::num without overflow or underflow
if (Factor::num != 1) { if (detail::const_check(Factor::num != 1)) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num; const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) { if (count > max1) {
ec = 1; ec = 1;
return {}; return {};
} }
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num; const auto min1 =
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
if (count < min1) { if (count < min1) {
ec = 1; ec = 1;
return {}; return {};
@@ -208,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
count *= Factor::num; count *= Factor::num;
} }
// this can't go wrong, right? den>0 is checked earlier. if (detail::const_check(Factor::den != 1)) count /= Factor::den;
if (Factor::den != 1) { auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
count /= Factor::den; return ec ? To() : To(tocount);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
} }
/** /**
@@ -269,7 +244,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// multiply with Factor::num without overflow or underflow // multiply with Factor::num without overflow or underflow
if (Factor::num != 1) { if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() / constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num); static_cast<IntermediateRep>(Factor::num);
if (count > max1) { if (count > max1) {
ec = 1; ec = 1;
@@ -306,12 +281,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// Usage: f FMT_NOMACRO() // Usage: f FMT_NOMACRO()
#define FMT_NOMACRO #define FMT_NOMACRO
namespace internal { namespace detail {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal } // namespace detail
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) { inline std::tm localtime(std::time_t time) {
@@ -322,22 +297,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {} dispatcher(std::time_t t) : time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_)); return handle(localtime_r(&time_, &tm_));
} }
bool handle(std::tm* tm) { return tm != nullptr; } bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) { bool handle(detail::null<>) {
using namespace fmt::internal; using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_)); return fallback(localtime_s(&tm_, &time_));
} }
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(detail::null<>) {
using namespace fmt::internal; using namespace fmt::detail;
std::tm* tm = std::localtime(&time_); std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != nullptr; return tm != nullptr;
@@ -350,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
return lt.tm_; return lt.tm_;
} }
inline std::tm localtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return localtime(std::chrono::system_clock::to_time_t(time_point));
}
// Thread-safe replacement for std::gmtime // Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) { inline std::tm gmtime(std::time_t time) {
struct dispatcher { struct dispatcher {
@@ -359,21 +339,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {} dispatcher(std::time_t t) : time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_)); return handle(gmtime_r(&time_, &tm_));
} }
bool handle(std::tm* tm) { return tm != nullptr; } bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) { bool handle(detail::null<>) {
using namespace fmt::internal; using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_)); return fallback(gmtime_s(&tm_, &time_));
} }
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_); std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != nullptr; return tm != nullptr;
@@ -386,17 +366,33 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_; return gt.tm_;
} }
namespace internal { inline std::tm gmtime(
inline std::size_t strftime(char* str, std::size_t count, const char* format, std::chrono::time_point<std::chrono::system_clock> time_point) {
const std::tm* time) { return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time); return std::strftime(str, count, format, time);
} }
inline std::size_t strftime(wchar_t* str, std::size_t count, inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const wchar_t* format, const std::tm* time) { const std::tm* time) {
return std::wcsftime(str, count, format, time); return std::wcsftime(str, count, format, time);
} }
} // namespace internal } // namespace detail
template <typename Char>
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> {
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx);
}
};
template <typename Char> struct formatter<std::tm, Char> { template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext> template <typename ParseContext>
@@ -405,7 +401,7 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it != ctx.end() && *it == ':') ++it; if (it != ctx.end() && *it == ':') ++it;
auto end = it; auto end = it;
while (end != ctx.end() && *end != '}') ++end; while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1)); tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end); tm_format.append(it, end);
tm_format.push_back('\0'); tm_format.push_back('\0');
return end; return end;
@@ -414,11 +410,10 @@ template <typename Char> struct formatter<std::tm, Char> {
template <typename FormatContext> template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
std::size_t start = buf.size(); size_t start = buf.size();
for (;;) { for (;;) {
std::size_t size = buf.capacity() - start; size_t size = buf.capacity() - start;
std::size_t count = size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) { if (count != 0) {
buf.resize(start + count); buf.resize(start + count);
break; break;
@@ -430,7 +425,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367 // https://github.com/fmtlib/fmt/issues/367
break; break;
} }
const std::size_t MIN_GROWTH = 10; const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
return std::copy(buf.begin(), buf.end(), ctx.out()); return std::copy(buf.begin(), buf.end(), ctx.out());
@@ -439,7 +434,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format; basic_memory_buffer<Char> tm_format;
}; };
namespace internal { namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() { template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr; return nullptr;
} }
@@ -768,19 +763,25 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format, return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val); val);
} }
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt> template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) { OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) { if (const char* unit = get_units<Period>())
string_view s(unit); return copy_unit(string_view(unit), out, Char());
if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
const Char num_f[] = {'[', '{', '}', ']', 's', 0}; const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num); if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den); return format_to(out, num_def_f, Period::num, Period::den);
} }
@@ -874,9 +875,9 @@ struct chrono_formatter {
if (isnan(value)) return write_nan(); if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n = uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>())); to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n); int num_digits = detail::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits); out = format_decimal<char_type>(out, n, num_digits).end;
} }
void write_nan() { std::copy_n("nan", 3, out); } void write_nan() { std::copy_n("nan", 3, out); }
@@ -1004,14 +1005,14 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out); out = format_duration_unit<char_type, Period>(out);
} }
}; };
} // namespace internal } // namespace detail
template <typename Rep, typename Period, typename Char> template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> { struct formatter<std::chrono::duration<Rep, Period>, Char> {
private: private:
basic_format_specs<Char> specs; basic_format_specs<Char> specs;
int precision; int precision;
using arg_ref_type = internal::arg_ref<Char>; using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref; arg_ref_type width_ref;
arg_ref_type precision_ref; arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str; mutable basic_string_view<Char> format_str;
@@ -1032,7 +1033,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id); return arg_ref_type(arg_id);
} }
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) { FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
return arg_ref_type(context.next_arg_id()); return arg_ref_type(context.next_arg_id());
} }
@@ -1062,17 +1063,17 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto begin = ctx.begin(), end = ctx.end(); auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin}; if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str}; spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler); begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler); begin = detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
if (*begin == '.') { if (*begin == '.') {
if (std::is_floating_point<Rep>::value) if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler); begin = detail::parse_precision(begin, end, handler);
else else
handler.on_error("precision not allowed for this argument type"); handler.on_error("precision not allowed for this argument type");
} }
end = parse_chrono_format(begin, end, internal::chrono_format_checker()); end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end}; return {begin, end};
} }
@@ -1083,7 +1084,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto range = do_parse(ctx); auto range = do_parse(ctx);
format_str = basic_string_view<Char>( format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin)); &*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end; return range.end;
} }
@@ -1094,23 +1095,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified. // is not specified.
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>; detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
internal::basic_writer<range> w(range(ctx.out())); ctx);
internal::handle_dynamic_spec<internal::width_checker>(specs.width, detail::handle_dynamic_spec<detail::precision_checker>(precision,
width_ref, ctx); precision_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = internal::format_duration_value<Char>(out, d.count(), precision); out = detail::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out); detail::format_duration_unit<Char, Period>(out);
} else { } else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f( detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d); ctx, out, d);
f.precision = precision; f.precision = precision;
parse_chrono_format(begin, end, f); parse_chrono_format(begin, end, f);
} }
w.write(buf.data(), buf.size(), specs); return detail::write(
return w.out(); ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
} }
}; };

View File

@@ -198,7 +198,7 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
namespace internal { namespace detail {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
@@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace internal } // namespace detail
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style {
@@ -298,11 +298,11 @@ class text_style {
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
@@ -313,7 +313,7 @@ class text_style {
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(), : set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems() {
@@ -326,23 +326,23 @@ class text_style {
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background) friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT; FMT_NOEXCEPT;
internal::color_type foreground_color; detail::color_type foreground_color;
internal::color_type background_color; detail::color_type background_color;
bool set_foreground_color; bool set_foreground_color;
bool set_background_color; bool set_background_color;
emphasis ems; emphasis ems;
}; };
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT { FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground); return text_style(/*is_foreground=*/true, foreground);
} }
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT { FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background); return text_style(/*is_foreground=*/false, background);
} }
@@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
namespace internal { namespace detail {
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT { const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color; bool is_background = esc == detail::data::background_color;
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) value += 10u; if (is_background) value += 10u;
std::size_t index = 0; size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
@@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue; if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
@@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color); return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT { detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color); return ansi_color_escape<Char>(background, detail::data::background_color);
} }
template <typename Char> template <typename Char>
@@ -455,71 +455,71 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream); fputs(detail::data::reset_color, stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream); fputs(detail::data::wreset_color, stream);
} }
template <typename Char> template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT { inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color; const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1; const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end); buffer.append(begin, end);
} }
template <typename Char> template <typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts, void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis()); auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end()); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
auto foreground = auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end()); buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
auto background = auto background = detail::make_background_color<Char>(ts.get_background());
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
internal::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf); if (has_style) detail::reset_color<Char>(buf);
} }
} // namespace internal } // namespace detail
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format, void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args); detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0)); buf.push_back(Char(0));
internal::fputs(buf.data(), f); detail::fputs(buf.data(), f);
} }
/** /**
\rst
Formats a string and prints it to the specified file stream using ANSI Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting. escape sequences to specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); vprint(f, ts, format_str,
using context = buffer_context<char_t<S>>; fmt::make_args_checked<Args...>(format_str, args...));
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
} }
/** /**
@@ -530,7 +530,7 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) { void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
@@ -540,7 +540,7 @@ inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str, const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args); detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
} }
@@ -560,7 +560,42 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return vformat(ts, to_string_view(format_str), return vformat(ts, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...)); fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -13,7 +13,33 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a // Part of a compiled format string. It can be either literal text or a
// replacement field. // replacement field.
@@ -62,13 +88,15 @@ template <typename Char> struct part_counter {
if (begin != end) ++num_parts; if (begin != end) ++num_parts;
} }
FMT_CONSTEXPR void on_arg_id() { ++num_parts; } FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; } FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; } FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {} FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) { const Char* end) {
// Find the matching brace. // Find the matching brace.
unsigned brace_counter = 0; unsigned brace_counter = 0;
@@ -116,25 +144,28 @@ class format_string_compiler : public error_handler {
handler_(part::make_text({begin, to_unsigned(end - begin)})); handler_(part::make_text({begin, to_unsigned(end - begin)}));
} }
FMT_CONSTEXPR void on_arg_id() { FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id()); part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
} }
FMT_CONSTEXPR void on_arg_id(int id) { FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id); parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id); part_ = part::make_arg_index(id);
return 0;
} }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) { FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id); part_ = part::make_arg_name(id);
return 0;
} }
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) { FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr; part_.arg_id_end = ptr;
handler_(part_); handler_(part_);
} }
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) { const Char* end) {
auto repl = typename part::replacement(); auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler( dynamic_specs_handler<basic_format_parse_context<Char>> handler(
@@ -160,23 +191,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
format_string_compiler<Char, PartHandler>(format_str, handler)); format_string_compiler<Char, PartHandler>(format_str, handler));
} }
template <typename Range, typename Context, typename Id> template <typename OutputIt, typename Context, typename Id>
void format_arg( void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx, basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) { Context& ctx, Id arg_id) {
ctx.advance_to( ctx.advance_to(visit_format_arg(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id))); arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
} }
// vformat_to is defined in a subnamespace to prevent ADL. // vformat_to is defined in a subnamespace to prevent ADL.
namespace cf { namespace cf {
template <typename Context, typename Range, typename CompiledFormat> template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args) auto vformat_to(OutputIt out, CompiledFormat& cf,
-> typename Context::iterator { basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx( basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_)); to_string_view(cf.format_str_));
Context ctx(out.begin(), args); Context ctx(out, args);
const auto& parts = cf.parts(); const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts); for (auto part_it = std::begin(parts); part_it != std::end(parts);
@@ -197,12 +229,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
case format_part_t::kind::arg_index: case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index); detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break; break;
case format_part_t::kind::arg_name: case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str); detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break; break;
case format_part_t::kind::replacement: { case format_part_t::kind::replacement: {
@@ -226,7 +258,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to( ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg)); visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break; break;
} }
} }
@@ -240,7 +274,7 @@ struct basic_compiled_format {};
template <typename S, typename = void> template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format { struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>; using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>; using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts; parts_container compiled_parts;
@@ -305,7 +339,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
const parts_container& parts() const { const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts = static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>( compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S())); detail::to_string_view(S()));
return compiled_parts.data; return compiled_parts.data;
} }
}; };
@@ -318,8 +352,8 @@ class compiled_format : private compiled_format_base<S> {
private: private:
basic_string_view<char_type> format_str_; basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat> template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf, friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> basic_format_args<Context> args) ->
typename Context::iterator; typename Context::iterator;
@@ -334,7 +368,8 @@ template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...]. // Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args> template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) { constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0) if constexpr (N == 0)
return first; return first;
@@ -359,8 +394,7 @@ template <typename Char> struct text {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve return write<Char>(out, data);
return copy_str<Char>(data.begin(), data.end(), out);
} }
}; };
@@ -373,32 +407,18 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}}; return {{&s[pos], size}};
} }
template <typename Char, typename OutputIt, typename T, template <typename Char> struct code_unit {
std::enable_if_t<std::is_integral_v<T>, int> = 0> Char value;
OutputIt format_default(OutputIt out, T value) { using char_type = Char;
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt> template <typename OutputIt, typename... Args>
OutputIt format_default(OutputIt out, double value) { OutputIt format(OutputIt out, const Args&...) const {
writer w(out); return write<Char>(out, value);
w.write(value); }
return w.out(); };
}
template <typename Char, typename OutputIt> template <typename Char>
OutputIt format_default(OutputIt out, Char value) { struct is_compiled_format<code_unit<Char>> : std::true_type {};
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N. // A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field { template <typename Char, typename T, int N> struct field {
@@ -408,13 +428,32 @@ template <typename Char, typename T, int N> struct field {
OutputIt format(OutputIt out, const Args&... args) const { OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`. // This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...); const T& arg = get<N>(args...);
return format_default<Char>(out, arg); return write<Char>(out, arg);
} }
}; };
template <typename Char, typename T, int N> template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {}; struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat { template <typename L, typename R> struct concat {
L lhs; L lhs;
R rhs; R rhs;
@@ -450,7 +489,8 @@ constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S> template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) { constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) { if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>()) unknown_format>())
@@ -462,6 +502,22 @@ constexpr auto parse_tail(T head, S format_str) {
} }
} }
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
}
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input. // or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
@@ -475,12 +531,13 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') { } else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>; using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) { return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), format_str);
format_str); } else if constexpr (str[POS + 1] == ':') {
} else { using type = get_type<ID, Args>;
return unknown_format(); constexpr auto result = parse_specs<type>(str, POS + 2, ID);
} return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else { } else {
return unknown_format(); return unknown_format();
} }
@@ -490,104 +547,153 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else { } else {
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), if constexpr (end - POS > 1) {
format_str); return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
} }
} }
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)> FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str; constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str); format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) { detail::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str)); return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else { } else {
return result; return result;
} }
} }
} }
#else
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)> FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> { constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str)); return detail::compiled_format<S, Args...>(to_string_view(format_str));
} }
# endif // __cpp_if_constexpr #endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal. // Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N> template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> { -> detail::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>( return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1)); basic_string_view<Char>(format_str, N - 1));
} }
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type, typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
CompiledFormat>::value)> FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) { const Args&... args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>; cf.format(detail::buffer_appender<Char>(buffer), args...);
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer); return to_string(buffer);
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)> CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf, OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
using char_type = typename CompiledFormat::char_type; using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>; using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(range(out), cf, return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...)); make_format_args<context>(args...));
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, OutputIt format_to(OutputIt out, const S&, const Args&... args) {
const CompiledFormat& cf, constexpr auto compiled = detail::compile<Args...>(S());
const Args&... args) { return format_to(out, compiled, args...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it = auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...); format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename CompiledFormat, typename... Args> template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) { size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count(); return format_to(detail::counting_iterator(), cf, args...).count();
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -14,63 +14,49 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char> template <typename Char>
std::basic_string<Char> vformat( std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str, const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer); return fmt::to_string(buffer);
} }
} // namespace internal } // namespace detail
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args); return detail::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args, typename Char = char_t<S>> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc, inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
return internal::vformat( return detail::vformat(loc, to_string_view(format_str),
loc, to_string_view(format_str), fmt::make_args_checked<Args...>(format_str, args...));
internal::make_args_checked<Args...>(format_str, args...));
} }
template <typename S, typename OutputIt, typename... Args, template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t< typename Char = char_t<S>,
internal::is_output_iterator<OutputIt>::value, char_t<S>>> FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to( inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str, OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = internal::output_range<OutputIt, Char>; decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
return vformat_to<arg_formatter<range>>( vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
range(out), to_string_view(format_str), args, internal::locale_ref(loc)); return detail::get_iterator(buf);
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&& bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
internal::is_string<S>::value)> inline auto format_to(OutputIt out, const std::locale& loc,
inline OutputIt format_to(OutputIt out, const std::locale& loc, const S& format_str, Args&&... args) ->
const S& format_str, Args&&... args) { typename std::enable_if<enable, OutputIt>::type {
internal::check_format_string<Args...>(format_str); const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
using context = format_context_t<OutputIt, char_t<S>>; return vformat_to(out, loc, to_string_view(format_str), vargs);
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -0,0 +1,480 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
namespace detail {
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) {
this->oflag = oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class ostream final : private detail::buffer<char> {
private:
file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
FMT_API void grow(size_t) override final;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
void print(const S& format_str, const Args&... args) {
format_to(detail::buffer_appender<char>(*this), format_str, args...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@@ -14,10 +14,10 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename CHar> class basic_printf_parse_context; template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context; template <typename OutputIt, typename Char> class basic_printf_context;
namespace internal { namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> { template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private: private:
@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
} }
}; };
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> { template <typename Char> struct test_stream : std::basic_ostream<Char> {
private: private:
// Hide all operator<< from std::basic_ostream<Char>. void_t<> operator<<(converter);
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
}; };
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of // Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream). // std::ostream).
template <typename T, typename Char> class is_streamable { template <typename T, typename Char> class is_streamable {
@@ -80,7 +90,7 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os. // Write the content of buf to os.
template <typename Char> template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
@@ -101,16 +111,17 @@ void format_value(buffer<Char>& buf, const T& value,
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>()); if (loc) output.imbue(loc.get<std::locale>());
#endif #endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
buf.resize(buf.size()); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
} }
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> { : private formatter<basic_string_view<Char>, Char> {
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx); return formatter<basic_string_view<Char>, Char>::parse(ctx);
} }
template <typename ParseCtx, template <typename ParseCtx,
@@ -136,14 +147,14 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
return std::copy(buffer.begin(), buffer.end(), ctx.out()); return std::copy(buffer.begin(), buffer.end(), ctx.out());
} }
}; };
} // namespace internal } // namespace detail
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
internal::write(os, buffer); detail::write_buffer(os, buffer);
} }
/** /**
@@ -156,10 +167,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str), vprint(os, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...)); fmt::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -1,2 +1,2 @@
#include "os.h" #include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead" #warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@@ -14,7 +14,7 @@
#include "ostream.h" #include "ostream.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
@@ -90,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value))); static_cast<int>(static_cast<target_type>(value)));
} else { } else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value))); static_cast<unsigned>(static_cast<unsigned_type>(value)));
} }
} else { } else {
@@ -102,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
// glibc's printf doesn't sign extend arguments of smaller types: // glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254" // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB. // but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value)); arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else { } else {
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value)); static_cast<typename make_unsigned_or_bool<U>::type>(value));
} }
} }
@@ -133,7 +133,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value)); static_cast<typename Context::char_type>(value));
} }
@@ -141,6 +141,13 @@ template <typename Context> class char_converter {
void operator()(T) {} // No conversion needed for non-integral types. void operator()(T) {} // No conversion needed for non-integral types.
}; };
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { template <typename Char> class printf_width_handler {
@@ -155,7 +162,7 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) { if (detail::is_negative(value)) {
specs_.align = align::left; specs_.align = align::left;
width = 0 - width; width = 0 - width;
} }
@@ -172,22 +179,20 @@ template <typename Char> class printf_width_handler {
}; };
template <typename Char, typename Context> template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format, void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format(); Context(buffer_appender<Char>(buf), format, args).format();
} }
} // namespace detail
template <typename OutputIt, typename Char, typename Context> // For printing into memory_buffer.
internal::truncating_iterator<OutputIt> printf( template <typename Char, typename Context>
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format, FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_format_args<Context> args) { basic_string_view<Char> format,
return Context(it, format, args).format(); basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
} }
} // namespace internal using detail::vprintf;
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
template <typename Char> template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> { class basic_printf_parse_context : public basic_format_parse_context<Char> {
@@ -200,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter. The ``printf`` argument formatter.
\endrst \endrst
*/ */
template <typename Range> template <typename OutputIt, typename Char>
class printf_arg_formatter : public internal::arg_formatter_base<Range> { class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public: public:
using iterator = typename Range::iterator; using iterator = OutputIt;
private: private:
using char_type = typename Range::value_type; using char_type = Char;
using base = internal::arg_formatter_base<Range>; using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<iterator, char_type>; using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_; context_type& context_;
@@ -233,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst \endrst
*/ */
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {} : base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) { iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead. // use std::is_same instead.
@@ -250,7 +255,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
fmt_specs.alt = false; fmt_specs.alt = false;
fmt_specs.align = align::right; fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value); return base::operator()(value);
} else { } else {
return base::operator()(value); return base::operator()(value);
@@ -316,12 +325,14 @@ template <typename T> struct printf_formatter {
template <typename FormatContext> template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value); detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out(); return ctx.out();
} }
}; };
/** This template formats data and writes the output to a writer. */ /**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context { template <typename OutputIt, typename Char> class basic_printf_context {
public: public:
/** The character type for the output. */ /** The character type for the output. */
@@ -351,9 +362,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public: public:
/** /**
\rst \rst
Constructs a ``printf_context`` object. References to the arguments and Constructs a ``printf_context`` object. References to the arguments are
the writer are stored in the context object so make sure they have stored in the context object so make sure they have appropriate lifetimes.
appropriate lifetimes.
\endrst \endrst
*/ */
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@@ -363,7 +373,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; } OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; } void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; } detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); } format_arg arg(int id) const { return args_.get(id); }
@@ -374,7 +384,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
} }
/** Formats stored arguments and writes the output to the range. */ /** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>> template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format(); OutputIt format();
}; };
@@ -394,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0'; specs.fill[0] = '0';
break; break;
case ' ': case ' ':
specs.sign = sign::space; if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break; break;
case '#': case '#':
specs.alt = true; specs.alt = true;
@@ -412,7 +424,7 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
arg_index = parse_ctx_.next_arg_id(); arg_index = parse_ctx_.next_arg_id();
else else
parse_ctx_.check_arg_id(--arg_index); parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index); return detail::get_arg(*this, arg_index);
} }
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
@@ -424,7 +436,7 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
internal::error_handler eh; detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh); int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index if (it != end && *it == '$') { // value is an argument index
++it; ++it;
@@ -443,12 +455,12 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width. // Parse width.
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
internal::error_handler eh; detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh); specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg())); detail::printf_width_handler<char_type>(specs), get_arg()));
} }
} }
return arg_index; return arg_index;
@@ -476,38 +488,52 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs); int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range"); if (arg_index == 0) on_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
++it; ++it;
c = it != end ? *it : 0; c = it != end ? *it : 0;
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9') {
internal::error_handler eh; detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh); specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision = static_cast<int>(
visit_format_arg(internal::printf_precision_handler(), get_arg())); visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
} }
format_arg arg = get_arg(arg_index); format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg)) // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false; specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0') {
if (arg.is_arithmetic()) if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric; specs.align = align::numeric;
else else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types. specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
} }
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0; char_type t = it != end ? *it : 0;
using internal::convert_arg; using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
if (t == 'h') { if (t == 'h') {
@@ -531,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t); convert_arg<intmax_t>(arg, t);
break; break;
case 'z': case 'z':
convert_arg<std::size_t>(arg, t); convert_arg<size_t>(arg, t);
break; break;
case 't': case 't':
convert_arg<std::ptrdiff_t>(arg, t); convert_arg<std::ptrdiff_t>(arg, t);
@@ -556,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd'; specs.type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg), visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg); arg);
break; break;
} }
@@ -565,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it; start = it;
// Format argument. // Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg); out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
} }
return std::copy(start, it, out); return std::copy(start, it, out);
} }
template <typename Char> template <typename Char>
using basic_printf_context_t = using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>, basic_printf_context<detail::buffer_appender<Char>, Char>;
Char>;
using printf_context = basic_printf_context_t<char>; using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>; using wprintf_context = basic_printf_context_t<wchar_t>;
@@ -610,7 +635,7 @@ inline std::basic_string<Char> vsprintf(
const S& format, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(format), args);
return to_string(buffer); return to_string(buffer);
} }
@@ -624,7 +649,7 @@ inline std::basic_string<Char> vsprintf(
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...)); return vsprintf(to_string_view(format), make_format_args<context>(args...));
@@ -635,8 +660,8 @@ inline int vfprintf(
std::FILE* f, const S& format, std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(format), args);
std::size_t size = buffer.size(); size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
: static_cast<int>(size); : static_cast<int>(size);
@@ -652,7 +677,7 @@ inline int vfprintf(
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) { inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(format),
@@ -676,7 +701,7 @@ inline int vprintf(
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) { inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>; using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str), return vprintf(to_string_view(format_str),
@@ -688,8 +713,8 @@ inline int vfprintf(
std::basic_ostream<Char>& os, const S& format, std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(format), args);
internal::write(os, buffer); detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size()); return static_cast<int>(buffer.size());
} }
@@ -698,7 +723,7 @@ template <typename ArgFormatter, typename Char,
typename Context = typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>> basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf( typename ArgFormatter::iterator vprintf(
internal::buffer<Char>& out, basic_string_view<Char> format_str, detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) { basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out); typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>(); Context(iter, format_str, args).template format<ArgFormatter>();

View File

@@ -33,7 +33,7 @@ template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range. // range.
Char prefix; Char prefix;
@@ -54,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
}; };
namespace internal { namespace detail {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) { OutputIterator copy(const RangeT& range, OutputIterator out) {
@@ -118,26 +118,24 @@ template <typename T> class is_tuple_like_ {
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>; template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N> template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
using make_index_sequence = std::make_index_sequence<N>;
#else #else
template <typename T, T... N> struct integer_sequence { template <typename T, T... N> struct integer_sequence {
using value_type = T; using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); } static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
}; };
template <std::size_t... N> template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
using index_sequence = integer_sequence<std::size_t, N...>;
template <typename T, std::size_t N, T... Ns> template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns> template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N> template <size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>; using make_index_sequence = make_integer_sequence<size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
@@ -159,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string< template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)> typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
@@ -184,12 +185,11 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'"; return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace detail
} // namespace internal
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
}; };
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
@@ -202,17 +202,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
out = internal::copy(formatting.delimiter, out); out = detail::copy(formatting.delimiter, out);
} }
out = format_to(out, out = format_to(out,
internal::format_str_quoted( detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v), (formatting.add_delimiter_spaces && i > 0), v),
v); v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
std::size_t& i; size_t& i;
typename std::add_lvalue_reference<decltype( typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out; std::declval<FormatContext>().out())>::type out;
}; };
@@ -228,14 +228,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
std::size_t i = 0; size_t i = 0;
internal::copy(formatting.prefix, out); detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out}); detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
internal::copy(formatting.postfix, out); detail::copy(formatting.postfix, out);
return ctx.out(); return ctx.out();
} }
@@ -243,15 +243,23 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
template <typename RangeT, typename Char> template <typename T, typename Char>
struct formatter<RangeT, Char, struct formatter<
enable_if_t<fmt::is_range<RangeT, Char>::value>> { T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&&
(has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
#endif
>> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
@@ -260,17 +268,18 @@ struct formatter<RangeT, Char,
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values, typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
FormatContext& ctx) { auto out = detail::copy(formatting.prefix, ctx.out());
auto out = internal::copy(formatting.prefix, ctx.out()); size_t i = 0;
std::size_t i = 0; auto it = values.begin();
for (auto it = values.begin(), end = values.end(); it != end; ++it) { auto end = values.end();
for (; it != end; ++it) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' '; if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out); out = detail::copy(formatting.delimiter, out);
} }
out = format_to(out, out = format_to(out,
internal::format_str_quoted( detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it), (formatting.add_delimiter_spaces && i > 0), *it),
*it); *it);
if (++i > formatting.range_length_limit) { if (++i > formatting.range_length_limit) {
@@ -279,11 +288,11 @@ struct formatter<RangeT, Char,
} }
} }
if (formatting.add_prepostfix_space) *out++ = ' '; if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out); return detail::copy(formatting.postfix, out);
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : internal::view { template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple; const std::tuple<T...>& tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
@@ -301,14 +310,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{}); return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
} }
private: private:
template <typename FormatContext, size_t... N> template <typename FormatContext, size_t... N>
typename FormatContext::iterator format( typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) { detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...); return format_args(value, ctx, std::get<N>(value.tuple)...);
} }
@@ -371,14 +380,14 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst \endrst
*/ */
template <typename T> template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join( arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
std::initializer_list<T> list, string_view sep) { string_view sep) {
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
template <typename T> template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join( arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
std::initializer_list<T> list, wstring_view sep) { wstring_view sep) {
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }

View File

@@ -0,0 +1,20 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// include bundled or external copy of fmtlib's chrono support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#endif
#include <spdlog/fmt/bundled/chrono.h>
#else
#include <fmt/chrono.h>
#endif

View File

@@ -17,6 +17,8 @@
#ifndef FMT_USE_WINDOWS_H #ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0 #define FMT_USE_WINDOWS_H 0
#endif #endif
// enable the 'n' flag in for backward compatibility with fmt 6.x
#define FMT_DEPRECATED_N_SPECIFIER
#include <spdlog/fmt/bundled/core.h> #include <spdlog/fmt/bundled/core.h>
#include <spdlog/fmt/bundled/format.h> #include <spdlog/fmt/bundled/format.h>
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib

View File

@@ -247,7 +247,11 @@ SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
auto tm_time = details::os::localtime(system_clock::to_time_t(now)); auto tm_time = details::os::localtime(system_clock::to_time_t(now));
char date_buf[64]; char date_buf[64];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str()); #if defined(USING_R) && defined(R_R_H) // if in R environment
REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
#else
std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
#endif
} }
} }
} // namespace spdlog } // namespace spdlog

View File

@@ -75,58 +75,58 @@ public:
// FormatString is a type derived from fmt::compile_string // FormatString is a type derived from fmt::compile_string
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args> template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args) void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{ {
log_(loc, lvl, fmt, args...); log_(loc, lvl, fmt, std::forward<Args>(args)...);
} }
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one // FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
template<typename... Args> template<typename... Args>
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args) void log(source_loc loc, level::level_enum lvl, string_view_t fmt, Args&&...args)
{ {
log_(loc, lvl, fmt, args...); log_(loc, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void log(level::level_enum lvl, const FormatString &fmt, const Args &... args) void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
{ {
log(source_loc{}, lvl, fmt, args...); log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void trace(const FormatString &fmt, const Args &... args) void trace(const FormatString &fmt, Args&&...args)
{ {
log(level::trace, fmt, args...); log(level::trace, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void debug(const FormatString &fmt, const Args &... args) void debug(const FormatString &fmt, Args&&...args)
{ {
log(level::debug, fmt, args...); log(level::debug, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void info(const FormatString &fmt, const Args &... args) void info(const FormatString &fmt, Args&&...args)
{ {
log(level::info, fmt, args...); log(level::info, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void warn(const FormatString &fmt, const Args &... args) void warn(const FormatString &fmt, Args&&...args)
{ {
log(level::warn, fmt, args...); log(level::warn, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void error(const FormatString &fmt, const Args &... args) void error(const FormatString &fmt, Args&&...args)
{ {
log(level::err, fmt, args...); log(level::err, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void critical(const FormatString &fmt, const Args &... args) void critical(const FormatString &fmt, Args&&...args)
{ {
log(level::critical, fmt, args...); log(level::critical, fmt, std::forward<Args>(args)...);
} }
template<typename T> template<typename T>
@@ -225,7 +225,7 @@ public:
#else #else
template<typename... Args> template<typename... Args>
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args) void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args&&...args)
{ {
bool log_enabled = should_log(lvl); bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled(); bool traceback_enabled = tracer_.enabled();
@@ -237,7 +237,7 @@ public:
{ {
// format to wmemory_buffer and convert to utf8 // format to wmemory_buffer and convert to utf8
fmt::wmemory_buffer wbuf; fmt::wmemory_buffer wbuf;
fmt::format_to(wbuf, fmt, args...); fmt::format_to(wbuf, fmt, std::forward<Args>(args)...);
memory_buf_t buf; memory_buf_t buf;
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
@@ -326,7 +326,7 @@ protected:
// common implementation for after templated public api has been resolved // common implementation for after templated public api has been resolved
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args) void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{ {
bool log_enabled = should_log(lvl); bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled(); bool traceback_enabled = tracer_.enabled();
@@ -337,7 +337,7 @@ protected:
SPDLOG_TRY SPDLOG_TRY
{ {
memory_buf_t buf; memory_buf_t buf;
fmt::format_to(buf, fmt, args...); fmt::format_to(buf, fmt, std::forward<Args>(args)...);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled); log_it_(log_msg, log_enabled, traceback_enabled);
} }

View File

@@ -45,12 +45,12 @@ public:
return; return;
} }
if (padinfo_.side_ == padding_info::left) if (padinfo_.side_ == padding_info::pad_side::left)
{ {
pad_it(remaining_pad_); pad_it(remaining_pad_);
remaining_pad_ = 0; remaining_pad_ = 0;
} }
else if (padinfo_.side_ == padding_info::center) else if (padinfo_.side_ == padding_info::pad_side::center)
{ {
auto half_pad = remaining_pad_ / 2; auto half_pad = remaining_pad_ / 2;
auto reminder = remaining_pad_ & 1; auto reminder = remaining_pad_ & 1;
@@ -1246,9 +1246,24 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
default: // Unknown flag appears as is default: // Unknown flag appears as is
auto unknown_flag = details::make_unique<details::aggregate_formatter>(); auto unknown_flag = details::make_unique<details::aggregate_formatter>();
unknown_flag->add_ch('%');
unknown_flag->add_ch(flag); if (!padding.truncate_)
formatters_.push_back((std::move(unknown_flag))); {
unknown_flag->add_ch('%');
unknown_flag->add_ch(flag);
formatters_.push_back((std::move(unknown_flag)));
}
// fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag)
// spdlog::set_pattern("[%10!] %v") => "[ main] some message"
// spdlog::set_pattern("[%3!!] %v") => "[mai] some message"
else
{
padding.truncate_ = false;
formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
unknown_flag->add_ch(flag);
formatters_.push_back((std::move(unknown_flag)));
}
break; break;
} }
} }
@@ -1270,15 +1285,15 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
switch (*it) switch (*it)
{ {
case '-': case '-':
side = padding_info::right; side = padding_info::pad_side::right;
++it; ++it;
break; break;
case '=': case '=':
side = padding_info::center; side = padding_info::pad_side::center;
++it; ++it;
break; break;
default: default:
side = details::padding_info::left; side = details::padding_info::pad_side::left;
break; break;
} }

View File

@@ -22,7 +22,7 @@ namespace details {
// padding information. // padding information.
struct padding_info struct padding_info
{ {
enum pad_side enum class pad_side
{ {
left, left,
right, right,
@@ -42,7 +42,7 @@ struct padding_info
return enabled_; return enabled_;
} }
size_t width_ = 0; size_t width_ = 0;
pad_side side_ = left; pad_side side_ = pad_side::left;
bool truncate_ = false; bool truncate_ = false;
bool enabled_ = false; bool enabled_ = false;
}; };
@@ -92,9 +92,9 @@ public:
void format(const details::log_msg &msg, memory_buf_t &dest) override; void format(const details::log_msg &msg, memory_buf_t &dest) override;
template<typename T, typename... Args> template<typename T, typename... Args>
pattern_formatter &add_flag(char flag, const Args &... args) pattern_formatter &add_flag(char flag, Args&&...args)
{ {
custom_handlers_[flag] = details::make_unique<T>(args...); custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
return *this; return *this;
} }
void set_pattern(std::string pattern); void set_pattern(std::string pattern);

View File

@@ -64,7 +64,7 @@ protected:
{ {
memory_buf_t buf; memory_buf_t buf;
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_); fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}}; details::log_msg skipped_msg{msg.logger_name, level::info, string_view_t{buf.data(), buf.size()}};
dist_sink<Mutex>::sink_it_(skipped_msg); dist_sink<Mutex>::sink_it_(skipped_msg);
} }

View File

@@ -8,12 +8,13 @@
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#include <spdlog/details/windows_include.h>
#include <winbase.h>
#include <mutex> #include <mutex>
#include <string> #include <string>
// Avoid including windows.h (https://stackoverflow.com/a/30741042)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
/* /*
@@ -23,12 +24,11 @@ template<typename Mutex>
class msvc_sink : public base_sink<Mutex> class msvc_sink : public base_sink<Mutex>
{ {
public: public:
explicit msvc_sink() {} msvc_sink() = default;
protected: protected:
void sink_it_(const details::log_msg &msg) override void sink_it_(const details::log_msg &msg) override
{ {
memory_buf_t formatted; memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted); base_sink<Mutex>::formatter_->format(msg, formatted);
OutputDebugStringA(fmt::to_string(formatted).c_str()); OutputDebugStringA(fmt::to_string(formatted).c_str());

View File

@@ -11,6 +11,15 @@
#include <spdlog/pattern_formatter.h> #include <spdlog/pattern_formatter.h>
#include <memory> #include <memory>
#ifdef _WIN32
// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
// so instead we use ::FileWrite
#include <spdlog/details/windows_include.h>
#include <fileapi.h> // WriteFile (..)
#include <io.h> // _get_osfhandle(..)
#include <stdio.h> // _fileno(..)
#endif // WIN32
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
@@ -20,7 +29,16 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
: mutex_(ConsoleMutex::mutex()) : mutex_(ConsoleMutex::mutex())
, file_(file) , file_(file)
, formatter_(details::make_unique<spdlog::pattern_formatter>()) , formatter_(details::make_unique<spdlog::pattern_formatter>())
{} {
#ifdef _WIN32
// get windows handle from the FILE* object
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
if (handle_ == INVALID_HANDLE_VALUE)
{
throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
}
#endif // WIN32
}
template<typename ConsoleMutex> template<typename ConsoleMutex>
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg) SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
@@ -28,8 +46,19 @@ SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &m
std::lock_guard<mutex_t> lock(mutex_); std::lock_guard<mutex_t> lock(mutex_);
memory_buf_t formatted; memory_buf_t formatted;
formatter_->format(msg, formatted); formatter_->format(msg, formatted);
fwrite(formatted.data(), sizeof(char), formatted.size(), file_); #ifdef _WIN32
fflush(file_); // flush every line to terminal ::fflush(file_); // flush in case there is somthing in this file_ already
auto size = static_cast<DWORD>(formatted.size());
DWORD bytes_written = 0;
bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
if (!ok)
{
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
}
#else
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
::fflush(file_); // flush every line to terminal
#endif // WIN32
} }
template<typename ConsoleMutex> template<typename ConsoleMutex>

View File

@@ -8,6 +8,10 @@
#include <spdlog/sinks/sink.h> #include <spdlog/sinks/sink.h>
#include <cstdio> #include <cstdio>
#ifdef _WIN32
#include <spdlog/details/windows_include.h>
#endif
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
@@ -36,6 +40,9 @@ protected:
mutex_t &mutex_; mutex_t &mutex_;
FILE *file_; FILE *file_;
std::unique_ptr<spdlog::formatter> formatter_; std::unique_ptr<spdlog::formatter> formatter_;
#ifdef _WIN32
HANDLE handle_;
#endif // WIN32
}; };
template<typename ConsoleMutex> template<typename ConsoleMutex>

View File

@@ -146,22 +146,12 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_
return; return;
} }
auto size = static_cast<DWORD>(formatted.size()); auto size = static_cast<DWORD>(formatted.size());
if (size == 0) DWORD bytes_written = 0;
bool ok = ::WriteFile(out_handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
if (!ok)
{ {
return; throw_spdlog_ex("wincolor_sink: ::WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
} }
DWORD total_written = 0;
do
{
DWORD bytes_written = 0;
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
if (!ok || bytes_written == 0)
{
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
}
total_written += bytes_written;
} while (total_written < size);
} }
// wincolor_stdout_sink // wincolor_stdout_sink

View File

@@ -47,6 +47,16 @@ SPDLOG_INLINE void dump_backtrace()
default_logger_raw()->dump_backtrace(); default_logger_raw()->dump_backtrace();
} }
SPDLOG_INLINE level::level_enum get_level()
{
return default_logger_raw()->level();
}
SPDLOG_INLINE bool should_log(level::level_enum log_level)
{
return default_logger_raw()->should_log(log_level);
}
SPDLOG_INLINE void set_level(level::level_enum log_level) SPDLOG_INLINE void set_level(level::level_enum log_level)
{ {
details::registry::instance().set_level(log_level); details::registry::instance().set_level(log_level);
@@ -112,4 +122,4 @@ SPDLOG_INLINE void set_default_logger(std::shared_ptr<spdlog::logger> default_lo
details::registry::instance().set_default_logger(std::move(default_logger)); details::registry::instance().set_default_logger(std::move(default_logger));
} }
} // namespace spdlog } // namespace spdlog

View File

@@ -31,7 +31,7 @@ using default_factory = synchronous_factory;
// Example: // Example:
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59); // spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
template<typename Sink, typename... SinkArgs> template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args) inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...sink_args)
{ {
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...); return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
} }
@@ -67,9 +67,15 @@ SPDLOG_API void disable_backtrace();
// call dump backtrace on default logger // call dump backtrace on default logger
SPDLOG_API void dump_backtrace(); SPDLOG_API void dump_backtrace();
// Get global logging level
SPDLOG_API level::level_enum get_level();
// Set global logging level // Set global logging level
SPDLOG_API void set_level(level::level_enum log_level); SPDLOG_API void set_level(level::level_enum log_level);
// Determine whether the default logger should log messages with a certain level
SPDLOG_API bool should_log(level::level_enum lvl);
// Set global flush level // Set global flush level
SPDLOG_API void flush_on(level::level_enum log_level); SPDLOG_API void flush_on(level::level_enum log_level);
@@ -105,7 +111,7 @@ SPDLOG_API void set_automatic_registration(bool automatic_registration);
// //
// The default logger object can be accessed using the spdlog::default_logger(): // The default logger object can be accessed using the spdlog::default_logger():
// For example, to add another sink to it: // For example, to add another sink to it:
// spdlog::default_logger()->sinks()->push_back(some_sink); // spdlog::default_logger()->sinks().push_back(some_sink);
// //
// The default logger can replaced using spdlog::set_default_logger(new_logger). // The default logger can replaced using spdlog::set_default_logger(new_logger).
// For example, to replace it with a file logger. // For example, to replace it with a file logger.
@@ -122,51 +128,51 @@ SPDLOG_API spdlog::logger *default_logger_raw();
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger); SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, const Args &... args) inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->log(source, lvl, fmt, args...); default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void log(level::level_enum lvl, const FormatString &fmt, const Args &... args) inline void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->log(source_loc{}, lvl, fmt, args...); default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void trace(const FormatString &fmt, const Args &... args) inline void trace(const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->trace(fmt, args...); default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void debug(const FormatString &fmt, const Args &... args) inline void debug(const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->debug(fmt, args...); default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void info(const FormatString &fmt, const Args &... args) inline void info(const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->info(fmt, args...); default_logger_raw()->info(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void warn(const FormatString &fmt, const Args &... args) inline void warn(const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->warn(fmt, args...); default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void error(const FormatString &fmt, const Args &... args) inline void error(const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->error(fmt, args...); default_logger_raw()->error(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename FormatString, typename... Args>
inline void critical(const FormatString &fmt, const Args &... args) inline void critical(const FormatString &fmt, Args&&...args)
{ {
default_logger_raw()->critical(fmt, args...); default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
} }
template<typename T> template<typename T>

View File

@@ -0,0 +1,61 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/fmt/fmt.h>
// Stopwatch support for spdlog (using std::chrono::steady_clock).
// Displays elapsed seconds since construction as double.
//
// Usage:
//
// spdlog::stopwatch sw;
// ...
// spdlog::debug("Elapsed: {} seconds", sw); => "Elapsed 0.005116733 seconds"
// spdlog::info("Elapsed: {:.6} seconds", sw); => "Elapsed 0.005163 seconds"
//
//
// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use "duration_cast<..>(sw.elapsed())":
//
// #include <spdlog/fmt/chrono.h>
//..
// using std::chrono::duration_cast;
// using std::chrono::milliseconds;
// spdlog::info("Elapsed {}", duration_cast<milliseconds>(sw.elapsed())); => "Elapsed 5ms"
namespace spdlog {
class stopwatch
{
using clock = std::chrono::steady_clock;
std::chrono::time_point<clock> start_tp_;
public:
stopwatch()
: start_tp_{clock::now()}
{}
std::chrono::duration<double> elapsed() const
{
return std::chrono::duration<double>(clock::now() - start_tp_);
}
void reset()
{
start_tp_ = clock ::now();
}
};
} // namespace spdlog
// Support for fmt formatting (e.g. "{:012.9}" or just "{}")
namespace fmt {
template<>
struct formatter<spdlog::stopwatch> : formatter<double>
{
template<typename FormatContext>
auto format(const spdlog::stopwatch &sw, FormatContext &ctx) -> decltype(ctx.out())
{
return formatter<double>::format(sw.elapsed().count(), ctx);
}
};
} // namespace fmt

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#define SPDLOG_VER_MAJOR 1 #define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 7 #define SPDLOG_VER_MINOR 8
#define SPDLOG_VER_PATCH 0 #define SPDLOG_VER_PATCH 2
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)

View File

@@ -9,181 +9,95 @@
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/bundled/format-inl.h> #include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
template<typename T> template <typename T>
int format_float(char *buf, std::size_t size, const char *format, int precision, T value) int format_float(char* buf, std::size_t size, const char* format, int precision,
{ T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef FMT_FUZZ
if (precision > 100000) if (precision > 100000)
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf"); throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif #endif
// Suppress the warning about nonliteral format string. // Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF; int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value); return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
} }
struct sprintf_specs
{
int precision;
char type;
bool alt : 1;
template<typename Char> template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
constexpr sprintf_specs(basic_format_specs<Char> specs) FMT_NOEXCEPT;
: precision(specs.precision) template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
, type(specs.type) FMT_NOEXCEPT;
, alt(specs.alt)
{}
constexpr bool has_precision() const // DEPRECATED! This function exists for ABI compatibility.
{ template <typename Char>
return precision >= 0; typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
} Char>::iterator
}; vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
// This is deprecated and is kept only to preserve ABI compatibility. std::back_insert_iterator<buffer<type_identity_t<Char>>>,
template<typename Double> type_identity_t<Char>>>
char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs specs) args) {
{ using iterator = std::back_insert_iterator<buffer<char>>;
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. using context = basic_format_context<
FMT_ASSERT(buf.capacity() != 0, "empty buffer"); std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
// Build format string. auto out = iterator(buf);
enum format_handler<iterator, Char, context> h(out, format_str, args, {});
{ parse_format_string<false>(format_str, h);
max_format_size = 10 return out;
}; // longest format: %#-*.*Lg
char format[max_format_size];
char *format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type)
*format_ptr++ = '#';
if (specs.precision >= 0)
{
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value)
*format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F')
{
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char *start = nullptr;
char *decimal_point_pos = nullptr;
for (;;)
{
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result = format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0)
{
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity())
{
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-')
++p;
if (specs.type != 'a' && specs.type != 'A')
{
while (p < end && *p >= '0' && *p <= '9')
++p;
if (p < end && *p != 'e' && *p != 'E')
{
decimal_point_pos = p;
if (!specs.type)
{
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0')
++p;
while (p != end && *p >= '1' && *p <= '9')
++p;
char *where = p;
while (p != end && *p == '0')
++p;
if (p == end || *p < '0' || *p > '9')
{
if (p != end)
std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(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.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
} }
} // namespace internal template basic_format_context<std::back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs); template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs, internal::buffer<char> &) = internal::format_float; int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc); template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const; template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif #endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref); template FMT_API std::string detail::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref); template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref); template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char *, const char *); template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args); template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>); template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>); template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
template FMT_API int internal::snprintf_float(double, int, internal::float_specs, internal::buffer<char> &); detail::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int, internal::float_specs, internal::buffer<char> &); template FMT_API int detail::format_float(double, int, detail::float_specs,
template FMT_API int internal::format_float(double, int, internal::float_specs, internal::buffer<char> &); detail::buffer<char>&);
template FMT_API int internal::format_float(long double, int, internal::float_specs, internal::buffer<char> &); template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref); template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref); template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *); template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // !SPDLOG_FMT_EXTERNAL
#endif // !SPDLOG_FMT_EXTERNAL

View File

@@ -1,47 +1,47 @@
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.10)
project(spdlog_utests CXX) project(spdlog_utests CXX)
if (NOT TARGET spdlog) if(NOT TARGET spdlog)
# Stand-alone build # Stand-alone build
find_package(spdlog REQUIRED) find_package(spdlog REQUIRED)
endif () endif()
include(../cmake/utils.cmake) include(../cmake/utils.cmake)
find_package(PkgConfig) find_package(PkgConfig)
if (PkgConfig_FOUND) if(PkgConfig_FOUND)
pkg_check_modules(systemd libsystemd) pkg_check_modules(systemd libsystemd)
endif () endif()
set(SPDLOG_UTESTS_SOURCES set(SPDLOG_UTESTS_SOURCES
test_file_helper.cpp test_file_helper.cpp
test_file_logging.cpp test_file_logging.cpp
test_daily_logger.cpp test_daily_logger.cpp
test_misc.cpp test_misc.cpp
test_eventlog.cpp test_eventlog.cpp
test_pattern_formatter.cpp test_pattern_formatter.cpp
test_async.cpp test_async.cpp
test_registry.cpp test_registry.cpp
test_macros.cpp test_macros.cpp
utils.cpp utils.cpp
main.cpp main.cpp
test_mpmc_q.cpp test_mpmc_q.cpp
test_dup_filter.cpp test_dup_filter.cpp
test_fmt_helper.cpp test_fmt_helper.cpp
test_stdout_api.cpp test_stdout_api.cpp
test_backtrace.cpp test_backtrace.cpp
test_create_dir.cpp test_create_dir.cpp
test_cfg.cpp test_cfg.cpp
test_time_point.cpp) test_time_point.cpp
test_stopwatch.cpp)
if (NOT SPDLOG_NO_EXCEPTIONS) if(NOT SPDLOG_NO_EXCEPTIONS)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp) list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
endif () endif()
if (systemd_FOUND) if(systemd_FOUND)
list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp) list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp)
endif () endif()
enable_testing() enable_testing()
@@ -49,22 +49,22 @@ function(spdlog_prepare_test test_target spdlog_lib)
add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES}) add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES})
spdlog_enable_warnings(${test_target}) spdlog_enable_warnings(${test_target})
target_link_libraries(${test_target} PRIVATE ${spdlog_lib}) target_link_libraries(${test_target} PRIVATE ${spdlog_lib})
if (systemd_FOUND) if(systemd_FOUND)
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES}) target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
endif () endif()
if (SPDLOG_SANITIZE_ADDRESS) if(SPDLOG_SANITIZE_ADDRESS)
spdlog_enable_sanitizer(${test_target}) spdlog_enable_sanitizer(${test_target})
endif () endif()
add_test(NAME ${test_target} COMMAND ${test_target}) add_test(NAME ${test_target} COMMAND ${test_target})
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON) set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
endfunction() endfunction()
# The compiled library tests # The compiled library tests
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL) if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
spdlog_prepare_test(spdlog-utests spdlog::spdlog) spdlog_prepare_test(spdlog-utests spdlog::spdlog)
endif () endif()
# The header-only library version tests # The header-only library version tests
if (SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL) if(SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only) spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only)
endif () endif()

View File

@@ -87,7 +87,7 @@ TEST_CASE("async periodic flush", "[async]")
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]); auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
spdlog::flush_every(std::chrono::seconds(1)); spdlog::flush_every(std::chrono::seconds(1));
std::this_thread::sleep_for(std::chrono::milliseconds(1100)); std::this_thread::sleep_for(std::chrono::milliseconds(1700));
REQUIRE(test_sink->flush_counter() == 1); REQUIRE(test_sink->flush_counter() == 1);
spdlog::flush_every(std::chrono::seconds(0)); spdlog::flush_every(std::chrono::seconds(0));
spdlog::drop_all(); spdlog::drop_all();

View File

@@ -1,3 +1,4 @@
#include "includes.h" #include "includes.h"
#include "test_sink.h" #include "test_sink.h"
@@ -41,21 +42,23 @@ TEST_CASE("argv2", "[cfg]")
auto l1 = spdlog::create<test_sink_st>("l1"); auto l1 = spdlog::create<test_sink_st>("l1");
REQUIRE(l1->level() == spdlog::level::warn); REQUIRE(l1->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace); REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
spdlog::set_level(spdlog::level::info);
} }
TEST_CASE("argv3", "[cfg]") TEST_CASE("argv3", "[cfg]")
{ {
spdlog::set_level(spdlog::level::trace);
spdlog::drop("l1"); spdlog::drop("l1");
const char *argv[] = {"ignore", "SPDLOG_LEVEL="}; const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk_name=warn"};
load_argv_levels(2, argv); load_argv_levels(2, argv);
auto l1 = spdlog::create<test_sink_st>("l1"); auto l1 = spdlog::create<test_sink_st>("l1");
REQUIRE(l1->level() == spdlog::level::info); REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
} }
TEST_CASE("argv4", "[cfg]") TEST_CASE("argv4", "[cfg]")
{ {
spdlog::set_level(spdlog::level::info);
spdlog::drop("l1"); spdlog::drop("l1");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"}; const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"};
load_argv_levels(2, argv); load_argv_levels(2, argv);
@@ -65,6 +68,7 @@ TEST_CASE("argv4", "[cfg]")
TEST_CASE("argv5", "[cfg]") TEST_CASE("argv5", "[cfg]")
{ {
spdlog::set_level(spdlog::level::info);
spdlog::drop("l1"); spdlog::drop("l1");
const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"}; const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"};
load_argv_levels(3, argv); load_argv_levels(3, argv);
@@ -91,3 +95,89 @@ TEST_CASE("argv7", "[cfg]")
REQUIRE(spdlog::default_logger()->level() == spdlog::level::err); REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
spdlog::set_level(spdlog::level::info); spdlog::set_level(spdlog::level::info);
} }
TEST_CASE("level-not-set-test1", "[cfg]")
{
spdlog::drop("l1");
const char *argv[] = {"ignore", ""};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
l1->set_level(spdlog::level::trace);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test2", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
l1->set_level(spdlog::level::warn);
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
l2->set_level(spdlog::level::warn);
load_argv_levels(2, argv);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test3", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::info);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test4", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace,warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
}
TEST_CASE("level-not-set-test5", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=junk,warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::warn);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
}
TEST_CASE("restore-to-default", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=info"};
load_argv_levels(2, argv);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}

View File

@@ -5,7 +5,7 @@ using spdlog::memory_buf_t;
// log to str and return it // log to str and return it
template<typename... Args> template<typename... Args>
static std::string log_to_str(const std::string &msg, const Args &... args) static std::string log_to_str(const std::string &msg, const Args &...args)
{ {
std::ostringstream oss; std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss); auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
@@ -227,7 +227,7 @@ TEST_CASE("paddinng_truncate", "[pattern_formatter]")
REQUIRE(log_to_str("123456", "%0!v", spdlog::pattern_time_type::local, "\n") == "\n"); REQUIRE(log_to_str("123456", "%0!v", spdlog::pattern_time_type::local, "\n") == "\n");
} }
TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]") TEST_CASE("padding_truncate_funcname", "[pattern_formatter]")
{ {
spdlog::sinks::test_sink_st test_sink; spdlog::sinks::test_sink_st test_sink;
@@ -237,13 +237,28 @@ TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]")
spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"}; spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg1); test_sink.log(msg1);
REQUIRE(test_sink.lines()[0] == "message [ func]");
spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "function"}, "test_logger", spdlog::level::info, "message"}; spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "function"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg2); test_sink.log(msg2);
REQUIRE(test_sink.lines()[1] == "message [funct]");
}
auto lines = test_sink.lines(); TEST_CASE("padding_funcname", "[pattern_formatter]")
REQUIRE(lines[0] == "message [ func]"); {
REQUIRE(lines[1] == "message [funct]"); spdlog::sinks::test_sink_st test_sink;
const char *pattern = "%v [%10!]";
auto formatter = std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(pattern));
test_sink.set_formatter(std::move(formatter));
spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg1);
REQUIRE(test_sink.lines()[0] == "message [ func]");
spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "func567890123"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg2);
REQUIRE(test_sink.lines()[1] == "message [func567890123]");
} }
TEST_CASE("clone-default-formatter", "[pattern_formatter]") TEST_CASE("clone-default-formatter", "[pattern_formatter]")

35
tests/test_stopwatch.cpp Normal file
View File

@@ -0,0 +1,35 @@
#include "includes.h"
#include "test_sink.h"
#include "spdlog/stopwatch.h"
TEST_CASE("stopwatch1", "[stopwatch]")
{
using std::chrono::milliseconds;
milliseconds wait_ms(250);
milliseconds tolerance_ms(250);
spdlog::stopwatch sw;
std::this_thread::sleep_for(wait_ms);
REQUIRE(sw.elapsed() >= wait_ms);
REQUIRE(sw.elapsed() <= wait_ms + tolerance_ms);
}
TEST_CASE("stopwatch2", "[stopwatch]")
{
using spdlog::sinks::test_sink_st;
std::chrono::duration<double> wait_duration(0.250);
std::chrono::duration<double> tolerance_duration(0.250);
auto test_sink = std::make_shared<test_sink_st>();
spdlog::stopwatch sw;
spdlog::logger logger("test-stopwatch", test_sink);
logger.set_pattern("%v");
std::this_thread::sleep_for(wait_duration);
logger.info("{}", sw);
auto val = std::stod(test_sink->lines()[0]);
REQUIRE(val >= wait_duration.count());
REQUIRE(val <= (wait_duration + tolerance_duration).count());
}