Compare commits

..

2 Commits

Author SHA1 Message Date
gabime
78e19d66d3 Add register_or_replace(logger) to atomically replace logger in registry 2025-05-08 15:33:08 +03:00
gabime
5dc6cec466 fix comments grammar 2025-05-08 15:00:12 +03:00
114 changed files with 2132 additions and 2506 deletions

View File

@@ -13,6 +13,7 @@ AlignEscapedNewlines: Left
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: Yes
PackConstructorInitializers: Never PackConstructorInitializers: Never
BreakConstructorInitializersBeforeComma: false BreakConstructorInitializersBeforeComma: false
IndentPPDirectives: None IndentPPDirectives: BeforeHash
SortIncludes: Never SortIncludes: Never
... ...

View File

@@ -1,52 +0,0 @@
name: coverity-linux
on: [push, pull_request]
permissions:
contents: read
jobs:
coverity_scan:
runs-on: ubuntu-latest
name: Coverity Scan
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y curl build-essential cmake pkg-config libsystemd-dev
- name: Download Coverity Tool
run: |
curl -s -L --output coverity_tool.tgz "https://scan.coverity.com/download/linux64?token=${{ secrets.COVERITY_TOKEN }}&project=gabime%2Fspdlog"
mkdir coverity_tool
tar -C coverity_tool --strip-components=1 -xf coverity_tool.tgz
echo "$PWD/coverity_tool/bin" >> $GITHUB_PATH
- name: Build with Coverity
run: |
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=17
cd ..
cov-build --dir cov-int make -C build -j4
- name: Submit results to Coverity
run: |
tar czf cov-int.tgz cov-int
response=$(curl --silent --show-error --fail \
--form email="${{ secrets.EMAIL }}" \
--form token="${{ secrets.COVERITY_TOKEN }}" \
--form file=@cov-int.tgz \
--form version="GitHub PR #${{ github.event.pull_request.number }}" \
--form description="CI run for PR" \
https://scan.coverity.com/builds?project=gabime%2Fspdlog)
echo "$response"
if echo "$response" | grep -qi "Build successfully submitted"; then
echo "Coverity upload succeeded"
else
echo "Coverity upload failed or was rejected"
exit 1
fi

View File

@@ -18,8 +18,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
config: config:
- { compiler: gcc, version: 9, build_type: Release, cppstd: 11 } - { compiler: gcc, version: 7, build_type: Release, cppstd: 11 }
- { compiler: gcc, version: 11, build_type: Debug, cppstd: 17 } - { compiler: gcc, version: 9, build_type: Release, cppstd: 17 }
- { compiler: gcc, version: 11, build_type: Debug, cppstd: 20 }
- { compiler: gcc, version: 12, build_type: Release, cppstd: 20 } - { compiler: gcc, version: 12, build_type: Release, cppstd: 20 }
- { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON } - { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON }
- { compiler: clang, version: 12, build_type: Debug, cppstd: 17 } - { compiler: clang, version: 12, build_type: Debug, cppstd: 17 }

View File

@@ -75,5 +75,74 @@ jobs:
run: | run: |
build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe
# -----------------------------------------------------------------------
# MSVC 2019 build matrix
# -----------------------------------------------------------------------
build_2019:
runs-on: windows-2019
strategy:
fail-fast: true
matrix:
config:
- GENERATOR: "Visual Studio 16 2019"
BUILD_TYPE: Release
BUILD_SHARED: 'ON'
FATAL_ERRORS: 'ON'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 17
- GENERATOR: "Visual Studio 16 2019"
BUILD_TYPE: Release
BUILD_SHARED: 'ON'
FATAL_ERRORS: 'ON'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 14
- GENERATOR: "Visual Studio 16 2019"
BUILD_TYPE: Release
BUILD_SHARED: 'ON'
FATAL_ERRORS: 'ON'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 11
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: CMake ${{ matrix.config.GENERATOR }} CXX=${{matrix.config.CXX_STANDARD}} WCHAR=${{matrix.config.WCHAR_FILES}} STD_FORMAT=${{matrix.config.USE_STD_FORMAT}}
shell: pwsh
run: |
mkdir build
cd build
cmake -G "${{ matrix.config.GENERATOR }}" -A x64 `
-D CMAKE_BUILD_TYPE=${{ matrix.config.BUILD_TYPE }} `
-D BUILD_SHARED_LIBS=${{ matrix.config.BUILD_SHARED }} `
-D SPDLOG_WCHAR_SUPPORT=${{ matrix.config.WCHAR }} `
-D SPDLOG_WCHAR_FILENAMES=${{ matrix.config.WCHAR_FILES }} `
-D SPDLOG_BUILD_EXAMPLE=${{ matrix.config.BUILD_EXAMPLE }} `
-D SPDLOG_BUILD_EXAMPLE_HO=${{ matrix.config.BUILD_EXAMPLE }} `
-D SPDLOG_BUILD_TESTS=ON `
-D SPDLOG_BUILD_TESTS_HO=OFF `
-D SPDLOG_BUILD_WARNINGS=${{ matrix.config.FATAL_ERRORS }} `
-D SPDLOG_USE_STD_FORMAT=${{ matrix.config.USE_STD_FORMAT }} `
-D CMAKE_CXX_STANDARD=${{ matrix.config.CXX_STANDARD }} ..
- name: Build
shell: pwsh
run: |
cd build
cmake --build . --parallel --config ${{ matrix.config.BUILD_TYPE }}
- name: Run Tests
shell: pwsh
env:
PATH: ${{ env.PATH }};${{ github.workspace }}\build\_deps\catch2-build\src\${{ matrix.config.BUILD_TYPE }};${{ github.workspace }}\build\${{ matrix.config.BUILD_TYPE }}
run: |
build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe

View File

@@ -19,11 +19,7 @@ include(GNUInstallDirs)
# Set default build to release # Set default build to release
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
# Set CMAKE_BUILD_TYPE only if this project is top-level set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
if((DEFINED PROJECT_IS_TOP_LEVEL AND PROJECT_IS_TOP_LEVEL) OR (NOT DEFINED PROJECT_IS_TOP_LEVEL
AND CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR))
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
endif()
endif() endif()
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
@@ -66,9 +62,6 @@ option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled heade
# build position independent code # build position independent code
option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF) option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF)
# debug build postfix
set(SPDLOG_DEBUG_POSTFIX "d" CACHE STRING "Filename postfix for libraries in debug builds")
# example options # example options
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT}) option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF) option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
@@ -97,7 +90,6 @@ option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library." OFF)
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF) option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF) option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
option(SPDLOG_NO_TZ_OFFSET "Omit %z timezone offset (use on platforms without tm_gmtoff)" OFF)
if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO) if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
@@ -199,7 +191,7 @@ spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX ${SPDLOG_DEBUG_POSTFIX}) set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH) if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
@@ -287,8 +279,7 @@ foreach(
SPDLOG_NO_TLS SPDLOG_NO_TLS
SPDLOG_NO_ATOMIC_LEVELS SPDLOG_NO_ATOMIC_LEVELS
SPDLOG_DISABLE_DEFAULT_LOGGER SPDLOG_DISABLE_DEFAULT_LOGGER
SPDLOG_USE_STD_FORMAT SPDLOG_USE_STD_FORMAT)
SPDLOG_NO_TZ_OFFSET)
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})

51
LICENSE
View File

@@ -1,25 +1,26 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 - present, Gabi Melman and spdlog contributors. Copyright (c) 2016 Gabi Melman.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
-- NOTE: Third party dependency used by this software -- -- NOTE: Third party dependency used by this software --
This software depends on the fmt lib (MIT License), This software depends on the fmt lib (MIT License),
and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE

View File

@@ -80,7 +80,7 @@ int main()
spdlog::info("Positional args are {1} {0}..", "too", "supported"); spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned"); spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set *global* log level to debug spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed.."); spdlog::debug("This message should be displayed..");
// change log pattern // change log pattern
@@ -224,7 +224,7 @@ void binary_example()
```c++ ```c++
// create a logger with 2 targets, with different log levels and formats. // create a logger with 2 targets, with different log levels and formats.
// The console will show only warnings or errors, while the file will log all. // The console will show only warnings or errors, while the file will log all.
void multi_sink_example() void multi_sink_example()
{ {
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
@@ -241,29 +241,6 @@ void multi_sink_example()
} }
``` ```
---
#### Register several loggers - change global level
```c++
// Creation of loggers. Set levels to all registered loggers.
void set_level_example()
{
auto logger1 = spdlog::basic_logger_mt("logger1", "logs/logger1.txt");
auto logger2 = spdlog::basic_logger_mt("logger2", "logs/logger2.txt");
spdlog::set_default_logger(logger2);
spdlog::default_logger()->set_level(spdlog::level::trace); // set level for the default logger (logger2) to trace
spdlog::trace("trace message to the logger2 (specified as default)");
spdlog::set_level(spdlog::level::off) // (sic!) set level for *all* registered loggers to off (disable)
logger1.warn("warn message will not appear because the level set to off");
logger2.warn("warn message will not appear because the level set to off");
spdlog::warn("warn message will not appear because the level set to off");
}
```
--- ---
#### User-defined callbacks about log events #### User-defined callbacks about log events
```c++ ```c++
@@ -547,7 +524,6 @@ Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki)
--- ---
### Powered by Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
<a href="https://jb.gg/OpenSource">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="JetBrains logo" width="200">
</a>

View File

@@ -11,11 +11,11 @@
#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/basic_file_sink.h"
#if defined(SPDLOG_USE_STD_FORMAT) #if defined(SPDLOG_USE_STD_FORMAT)
#include <format> #include <format>
#elif defined(SPDLOG_FMT_EXTERNAL) #elif defined(SPDLOG_FMT_EXTERNAL)
#include <fmt/format.h> #include <fmt/format.h>
#else #else
#include "spdlog/fmt/bundled/format.h" #include "spdlog/fmt/bundled/format.h"
#endif #endif
#include "utils.h" #include "utils.h"
@@ -34,9 +34,9 @@ using namespace utils;
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); 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) {
int counter = 0; int counter = 0;
@@ -62,7 +62,7 @@ void verify_file(const char *filename, int expected_count) {
} }
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) #pragma warning(pop)
#endif #endif
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

View File

@@ -13,11 +13,11 @@
#include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h"
#if defined(SPDLOG_USE_STD_FORMAT) #if defined(SPDLOG_USE_STD_FORMAT)
#include <format> #include <format>
#elif defined(SPDLOG_FMT_EXTERNAL) #elif defined(SPDLOG_FMT_EXTERNAL)
#include <fmt/format.h> #include <fmt/format.h>
#else #else
#include "spdlog/fmt/bundled/format.h" #include "spdlog/fmt/bundled/format.h"
#endif #endif
#include "utils.h" #include "utils.h"

View File

@@ -10,6 +10,7 @@
// details/file_helper-inl.h // details/file_helper-inl.h
// details/os-inl.h // details/os-inl.h
// fmt/bundled/core.h
// fmt/bundled/posix.h // fmt/bundled/posix.h
// logger-inl.h // logger-inl.h
// sinks/daily_file_sink.h // sinks/daily_file_sink.h
@@ -22,6 +23,7 @@
// details/os-inl.h // details/os-inl.h
// details/pattern_formatter-inl.h // details/pattern_formatter-inl.h
// fmt/bundled/core.h
// fmt/bundled/format-inl.h // fmt/bundled/format-inl.h
#include <cstring> #include <cstring>
@@ -71,6 +73,7 @@
// details/registry.h // details/registry.h
// details/tcp_client-windows.h // details/tcp_client-windows.h
// details/tcp_client.h // details/tcp_client.h
// fmt/bundled/core.h
// sinks/android_sink.h // sinks/android_sink.h
// sinks/ansicolor_sink.h // sinks/ansicolor_sink.h
// sinks/basic_file_sink.h // sinks/basic_file_sink.h
@@ -147,6 +150,7 @@
// common.h // common.h
// details/fmt_helper.h // details/fmt_helper.h
// fmt/bundled/core.h
// fmt/bundled/ranges.h // fmt/bundled/ranges.h
#include <type_traits> #include <type_traits>
@@ -251,4 +255,4 @@
#include <mutex> #include <mutex>
// spdlog // spdlog
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -33,41 +33,41 @@ void mdc_example();
#include "spdlog/fmt/ostr.h" // support for user defined types #include "spdlog/fmt/ostr.h" // support for user defined types
int main(int, char *[]) { int main(int, char *[]) {
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
load_levels_example();
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR,
SPDLOG_VER_PATCH);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
// Runtime log levels
spdlog::set_level(spdlog::level::info); // Set global log level to info
spdlog::debug("This message should not be displayed!");
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
spdlog::debug("This message should be displayed..");
// Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
spdlog::info("This an info message with custom format");
spdlog::set_pattern("%+"); // back to default format
spdlog::set_level(spdlog::level::info);
// Backtrace support
// Loggers can store in a ring buffer all messages (including debug/trace) for later inspection.
// When needed, call dump_backtrace() to see what happened:
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
for (int i = 0; i < 100; i++) {
spdlog::debug("Backtrace message {}", i); // not logged..
}
// e.g. if some error happened:
spdlog::dump_backtrace(); // log them now!
try { try {
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
load_levels_example();
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR,
SPDLOG_VER_PATCH);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
// Runtime log levels
spdlog::set_level(spdlog::level::info); // Set global log level to info
spdlog::debug("This message should not be displayed!");
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
spdlog::debug("This message should be displayed..");
// Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
spdlog::info("This an info message with custom format");
spdlog::set_pattern("%+"); // back to default format
spdlog::set_level(spdlog::level::info);
// Backtrace support
// Loggers can store in a ring buffer all messages (including debug/trace) for later
// inspection. When needed, call dump_backtrace() to see what happened:
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
for (int i = 0; i < 100; i++) {
spdlog::debug("Backtrace message {}", i); // not logged..
}
// e.g. if some error happened:
spdlog::dump_backtrace(); // log them now!
stdout_logger_example(); stdout_logger_example();
basic_example(); basic_example();
rotating_example(); rotating_example();
@@ -183,7 +183,7 @@ void async_example() {
// {:n} - don't split the output to lines. // {:n} - don't split the output to lines.
#if !defined SPDLOG_USE_STD_FORMAT || defined(_MSC_VER) #if !defined SPDLOG_USE_STD_FORMAT || defined(_MSC_VER)
#include "spdlog/fmt/bin_to_hex.h" #include "spdlog/fmt/bin_to_hex.h"
void binary_example() { void binary_example() {
std::vector<char> buf; std::vector<char> buf;
for (int i = 0; i < 80; i++) { for (int i = 0; i < 80; i++) {
@@ -207,7 +207,7 @@ void binary_example() {
// Log a vector of numbers // Log a vector of numbers
#ifndef SPDLOG_USE_STD_FORMAT #ifndef SPDLOG_USE_STD_FORMAT
#include "spdlog/fmt/ranges.h" #include "spdlog/fmt/ranges.h"
void vector_example() { void vector_example() {
std::vector<int> vec = {1, 2, 3}; std::vector<int> vec = {1, 2, 3};
spdlog::info("Vector example: {}", vec); spdlog::info("Vector example: {}", vec);
@@ -301,7 +301,7 @@ void err_handler_example() {
// syslog example (linux/osx/freebsd) // syslog example (linux/osx/freebsd)
#ifndef _WIN32 #ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h" #include "spdlog/sinks/syslog_sink.h"
void syslog_example() { void syslog_example() {
std::string ident = "spdlog-example"; std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
@@ -311,7 +311,7 @@ void syslog_example() {
// Android example. // Android example.
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "spdlog/sinks/android_sink.h" #include "spdlog/sinks/android_sink.h"
void android_example() { void android_example() {
std::string tag = "spdlog-android"; std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger_mt("android", tag); auto android_logger = spdlog::android_logger_mt("android", tag);
@@ -371,13 +371,15 @@ void replace_default_logger_example() {
// store the old logger so we don't break other examples. // store the old logger so we don't break other examples.
auto old_logger = spdlog::default_logger(); auto old_logger = spdlog::default_logger();
auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/somelog.txt", true); auto new_logger =
spdlog::set_default_logger(std::move(new_logger)); spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
spdlog::set_default_logger(new_logger);
spdlog::set_level(spdlog::level::info); spdlog::set_level(spdlog::level::info);
spdlog::debug("This message should not be displayed!"); spdlog::debug("This message should not be displayed!");
spdlog::set_level(spdlog::level::trace); spdlog::set_level(spdlog::level::trace);
spdlog::debug("This message should be displayed.."); spdlog::debug("This message should be displayed..");
spdlog::set_default_logger(std::move(old_logger));
spdlog::set_default_logger(old_logger);
} }
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread // Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread
@@ -386,7 +388,7 @@ void replace_default_logger_example() {
// thread-local storage. // thread-local storage.
#ifndef SPDLOG_NO_TLS #ifndef SPDLOG_NO_TLS
#include "spdlog/mdc.h" #include "spdlog/mdc.h"
void mdc_example() { void mdc_example() {
spdlog::mdc::put("key1", "value1"); spdlog::mdc::put("key1", "value1");
spdlog::mdc::put("key2", "value2"); spdlog::mdc::put("key2", "value2");

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/async_logger.h> #include <spdlog/async_logger.h>
#endif #endif
#include <spdlog/details/thread_pool.h> #include <spdlog/details/thread_pool.h>

View File

@@ -70,5 +70,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "async_logger-inl.h" #include "async_logger-inl.h"
#endif #endif

View File

@@ -4,11 +4,12 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/cfg/helpers.h> #include <spdlog/cfg/helpers.h>
#endif #endif
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#include <spdlog/details/registry.h> #include <spdlog/details/registry.h>
#include <spdlog/spdlog.h>
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
@@ -35,7 +36,7 @@ inline std::string &trim_(std::string &str) {
return str; return str;
} }
// return (name,value) trimmed pair from the given "name = value" string. // return (name,value) trimmed pair from given "name=value" string.
// return empty string on missing parts // return empty string on missing parts
// "key=val" => ("key", "val") // "key=val" => ("key", "val")
// " key = val " => ("key", "val") // " key = val " => ("key", "val")
@@ -54,7 +55,7 @@ inline std::pair<std::string, std::string> extract_kv_(char sep, const std::stri
return std::make_pair(trim_(k), trim_(v)); return std::make_pair(trim_(k), trim_(v));
} }
// return vector of key/value pairs from a sequence of "K1=V1,K2=V2,.." // return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...} // "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str) { inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str) {
std::string token; std::string token;
@@ -71,7 +72,7 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
} }
SPDLOG_INLINE void load_levels(const std::string &input) { SPDLOG_INLINE void load_levels(const std::string &input) {
if (input.empty() || input.size() >= 32768) { if (input.empty() || input.size() > 512) {
return; return;
} }
@@ -81,14 +82,14 @@ SPDLOG_INLINE void load_levels(const std::string &input) {
bool global_level_found = false; bool global_level_found = false;
for (auto &name_level : key_vals) { for (auto &name_level : key_vals) {
const auto &logger_name = name_level.first; auto &logger_name = name_level.first;
const 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);
// ignore unrecognized level names // ignore unrecognized level names
if (level == level::off && level_name != "off") { if (level == level::off && level_name != "off") {
continue; continue;
} }
if (logger_name.empty()) // no logger name indicates global level if (logger_name.empty()) // no logger name indicate global level
{ {
global_level_found = true; global_level_found = true;
global_level = level; global_level = level;

View File

@@ -25,5 +25,5 @@ SPDLOG_API void load_levels(const std::string &txt);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "helpers-inl.h" #include "helpers-inl.h"
#endif // SPDLOG_HEADER_ONLY #endif // SPDLOG_HEADER_ONLY

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/common.h> #include <spdlog/common.h>
#endif #endif
#include <algorithm> #include <algorithm>

View File

@@ -17,57 +17,57 @@
#include <type_traits> #include <type_traits>
#ifdef SPDLOG_USE_STD_FORMAT #ifdef SPDLOG_USE_STD_FORMAT
#include <version> #include <version>
#if __cpp_lib_format >= 202207L #if __cpp_lib_format >= 202207L
#include <format> #include <format>
#else #else
#include <string_view> #include <string_view>
#endif #endif
#endif #endif
#ifdef SPDLOG_COMPILED_LIB #ifdef SPDLOG_COMPILED_LIB
#undef SPDLOG_HEADER_ONLY #undef SPDLOG_HEADER_ONLY
#if defined(SPDLOG_SHARED_LIB) #if defined(SPDLOG_SHARED_LIB)
#if defined(_WIN32) #if defined(_WIN32)
#ifdef spdlog_EXPORTS #ifdef spdlog_EXPORTS
#define SPDLOG_API __declspec(dllexport) #define SPDLOG_API __declspec(dllexport)
#else // !spdlog_EXPORTS #else // !spdlog_EXPORTS
#define SPDLOG_API __declspec(dllimport) #define SPDLOG_API __declspec(dllimport)
#endif #endif
#else // !defined(_WIN32) #else // !defined(_WIN32)
#define SPDLOG_API __attribute__((visibility("default"))) #define SPDLOG_API __attribute__((visibility("default")))
#endif #endif
#else // !defined(SPDLOG_SHARED_LIB) #else // !defined(SPDLOG_SHARED_LIB)
#define SPDLOG_API #define SPDLOG_API
#endif #endif
#define SPDLOG_INLINE #define SPDLOG_INLINE
#else // !defined(SPDLOG_COMPILED_LIB) #else // !defined(SPDLOG_COMPILED_LIB)
#define SPDLOG_API #define SPDLOG_API
#define SPDLOG_HEADER_ONLY #define SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE inline #define SPDLOG_INLINE inline
#endif // #ifdef SPDLOG_COMPILED_LIB #endif // #ifdef SPDLOG_COMPILED_LIB
#include <spdlog/fmt/fmt.h> #include <spdlog/fmt/fmt.h>
#if !defined(SPDLOG_USE_STD_FORMAT) && \ #if !defined(SPDLOG_USE_STD_FORMAT) && \
FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8 FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string) #define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
#define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string) #define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
#include <spdlog/fmt/xchar.h> #include <spdlog/fmt/xchar.h>
#endif #endif
#else #else
#define SPDLOG_FMT_RUNTIME(format_string) format_string #define SPDLOG_FMT_RUNTIME(format_string) format_string
#define SPDLOG_FMT_STRING(format_string) format_string #define SPDLOG_FMT_STRING(format_string) format_string
#endif #endif
// visual studio up to 2013 does not support noexcept nor constexpr // visual studio up to 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900) #if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT _NOEXCEPT #define SPDLOG_NOEXCEPT _NOEXCEPT
#define SPDLOG_CONSTEXPR #define SPDLOG_CONSTEXPR
#else #else
#define SPDLOG_NOEXCEPT noexcept #define SPDLOG_NOEXCEPT noexcept
#define SPDLOG_CONSTEXPR constexpr #define SPDLOG_CONSTEXPR constexpr
#endif #endif
// If building with std::format, can just use constexpr, otherwise if building with fmt // If building with std::format, can just use constexpr, otherwise if building with fmt
@@ -76,48 +76,48 @@
// depending on the compiler // depending on the compiler
// If fmt determines it can't use constexpr, we should inline the function instead // If fmt determines it can't use constexpr, we should inline the function instead
#ifdef SPDLOG_USE_STD_FORMAT #ifdef SPDLOG_USE_STD_FORMAT
#define SPDLOG_CONSTEXPR_FUNC constexpr #define SPDLOG_CONSTEXPR_FUNC constexpr
#else // Being built with fmt #else // Being built with fmt
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
#define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR #define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR
#else #else
#define SPDLOG_CONSTEXPR_FUNC inline #define SPDLOG_CONSTEXPR_FUNC inline
#endif #endif
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated)) #define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated) #define SPDLOG_DEPRECATED __declspec(deprecated)
#else #else
#define SPDLOG_DEPRECATED #define SPDLOG_DEPRECATED
#endif #endif
// disable thread local on msvc 2013 // disable thread local on msvc 2013
#ifndef SPDLOG_NO_TLS #ifndef SPDLOG_NO_TLS
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) #if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
#define SPDLOG_NO_TLS 1 #define SPDLOG_NO_TLS 1
#endif #endif
#endif #endif
#ifndef SPDLOG_FUNCTION #ifndef SPDLOG_FUNCTION
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__) #define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
#endif #endif
#ifdef SPDLOG_NO_EXCEPTIONS #ifdef SPDLOG_NO_EXCEPTIONS
#define SPDLOG_TRY #define SPDLOG_TRY
#define SPDLOG_THROW(ex) \ #define SPDLOG_THROW(ex) \
do { \ do { \
printf("spdlog fatal error: %s\n", ex.what()); \ printf("spdlog fatal error: %s\n", ex.what()); \
std::abort(); \ std::abort(); \
} while (0) } while (0)
#define SPDLOG_CATCH_STD #define SPDLOG_CATCH_STD
#else #else
#define SPDLOG_TRY try #define SPDLOG_TRY try
#define SPDLOG_THROW(ex) throw(ex) #define SPDLOG_THROW(ex) throw(ex)
#define SPDLOG_CATCH_STD \ #define SPDLOG_CATCH_STD \
catch (const std::exception &) { \ catch (const std::exception &) { \
} }
#endif #endif
namespace spdlog { namespace spdlog {
@@ -130,12 +130,12 @@ class sink;
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring; using filename_t = std::wstring;
// allow macro expansion to occur in SPDLOG_FILENAME_T // allow macro expansion to occur in SPDLOG_FILENAME_T
#define SPDLOG_FILENAME_T_INNER(s) L##s #define SPDLOG_FILENAME_T_INNER(s) L##s
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s) #define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
#else #else
using filename_t = std::string; using filename_t = std::string;
#define SPDLOG_FILENAME_T(s) s #define SPDLOG_FILENAME_T(s) s
#endif #endif
using log_clock = std::chrono::system_clock; using log_clock = std::chrono::system_clock;
@@ -149,28 +149,28 @@ using string_view_t = std::string_view;
using memory_buf_t = std::string; using memory_buf_t = std::string;
template <typename... Args> template <typename... Args>
#if __cpp_lib_format >= 202207L #if __cpp_lib_format >= 202207L
using format_string_t = std::format_string<Args...>; using format_string_t = std::format_string<Args...>;
#else #else
using format_string_t = std::string_view; using format_string_t = std::string_view;
#endif #endif
template <class T, class Char = char> template <class T, class Char = char>
struct is_convertible_to_basic_format_string struct is_convertible_to_basic_format_string
: std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value> {}; : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value> {};
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
using wstring_view_t = std::wstring_view; using wstring_view_t = std::wstring_view;
using wmemory_buf_t = std::wstring; using wmemory_buf_t = std::wstring;
template <typename... Args> template <typename... Args>
#if __cpp_lib_format >= 202207L #if __cpp_lib_format >= 202207L
using wformat_string_t = std::wformat_string<Args...>; using wformat_string_t = std::wformat_string<Args...>;
#else #else
using wformat_string_t = std::wstring_view; using wformat_string_t = std::wstring_view;
#endif #endif
#endif #endif
#define SPDLOG_BUF_TO_STRING(x) x #define SPDLOG_BUF_TO_STRING(x) x
#else // use fmt lib instead of std::format #else // use fmt lib instead of std::format
namespace fmt_lib = fmt; namespace fmt_lib = fmt;
@@ -184,11 +184,11 @@ template <class T>
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template <typename Char> template <typename Char>
#if FMT_VERSION >= 90101 #if FMT_VERSION >= 90101
using fmt_runtime_string = fmt::runtime_format_string<Char>; using fmt_runtime_string = fmt::runtime_format_string<Char>;
#else #else
using fmt_runtime_string = fmt::basic_runtime<Char>; using fmt_runtime_string = fmt::basic_runtime<Char>;
#endif #endif
// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the // clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the
// condition from basic_format_string here, in addition, fmt::basic_runtime<Char> is only // condition from basic_format_string here, in addition, fmt::basic_runtime<Char> is only
@@ -200,21 +200,21 @@ struct is_convertible_to_basic_format_string
std::is_same<remove_cvref_t<T>, fmt_runtime_string<Char>>::value> { std::is_same<remove_cvref_t<T>, fmt_runtime_string<Char>>::value> {
}; };
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
using wstring_view_t = fmt::basic_string_view<wchar_t>; using wstring_view_t = fmt::basic_string_view<wchar_t>;
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>; using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
template <typename... Args> template <typename... Args>
using wformat_string_t = fmt::wformat_string<Args...>; using wformat_string_t = fmt::wformat_string<Args...>;
#endif #endif
#define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x) #define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
#endif #endif
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32 #ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#endif // _WIN32 #endif // _WIN32
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <class T> template <class T>
struct is_convertible_to_any_format_string struct is_convertible_to_any_format_string
@@ -237,7 +237,7 @@ using level_t = std::atomic<int>;
#define SPDLOG_LEVEL_OFF 6 #define SPDLOG_LEVEL_OFF 6
#if !defined(SPDLOG_ACTIVE_LEVEL) #if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif #endif
// Log level enum // Log level enum
@@ -262,18 +262,18 @@ enum level_enum : int {
#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3) #define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
#if !defined(SPDLOG_LEVEL_NAMES) #if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES \ #define SPDLOG_LEVEL_NAMES \
{ \ { \
SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, \ SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, \
SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, \ SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, \
SPDLOG_LEVEL_NAME_OFF \ SPDLOG_LEVEL_NAME_OFF \
} }
#endif #endif
#if !defined(SPDLOG_SHORT_LEVEL_NAMES) #if !defined(SPDLOG_SHORT_LEVEL_NAMES)
#define SPDLOG_SHORT_LEVEL_NAMES \ #define SPDLOG_SHORT_LEVEL_NAMES \
{ "T", "D", "I", "W", "E", "C", "O" } { "T", "D", "I", "W", "E", "C", "O" }
#endif #endif
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
@@ -402,5 +402,5 @@ constexpr T conditional_static_cast(U value) {
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "common-inl.h" #include "common-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/backtracer.h> #include <spdlog/details/backtracer.h>
#endif #endif
namespace spdlog { namespace spdlog {
namespace details { namespace details {

View File

@@ -41,5 +41,5 @@ public:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "backtracer-inl.h" #include "backtracer-inl.h"
#endif #endif

View File

@@ -4,15 +4,17 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/file_helper.h> #include <spdlog/details/file_helper.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#include <cerrno> #include <cerrno>
#include <chrono>
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <thread>
#include <tuple> #include <tuple>
namespace spdlog { namespace spdlog {

View File

@@ -57,5 +57,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "file_helper-inl.h" #include "file_helper-inl.h"
#endif #endif

View File

@@ -9,8 +9,8 @@
#include <type_traits> #include <type_traits>
#ifdef SPDLOG_USE_STD_FORMAT #ifdef SPDLOG_USE_STD_FORMAT
#include <charconv> #include <charconv>
#include <limits> #include <limits>
#endif #endif
// Some fmt helpers to efficiently format and pad ints and strings // Some fmt helpers to efficiently format and pad ints and strings
@@ -70,13 +70,13 @@ inline unsigned int count_digits(T n) {
return count_digits_fallback(static_cast<count_type>(n)); return count_digits_fallback(static_cast<count_type>(n));
#else #else
return static_cast<unsigned int>(fmt:: return static_cast<unsigned int>(fmt::
// 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)));
#endif #endif
} }

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/log_msg.h> #include <spdlog/details/log_msg.h>
#endif #endif
#include <spdlog/details/os.h> #include <spdlog/details/os.h>

View File

@@ -36,5 +36,5 @@ struct SPDLOG_API log_msg {
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "log_msg-inl.h" #include "log_msg-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/log_msg_buffer.h> #include <spdlog/details/log_msg_buffer.h>
#endif #endif
namespace spdlog { namespace spdlog {

View File

@@ -28,5 +28,5 @@ public:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "log_msg_buffer-inl.h" #include "log_msg_buffer-inl.h"
#endif #endif

View File

@@ -5,8 +5,8 @@
// multi producer-multi consumer blocking queue. // multi producer-multi consumer blocking queue.
// enqueue(..) - will block until room found to put the new message. // enqueue(..) - will block until room found to put the new message.
// enqueue_nowait(..) - enqueue immediately. overruns oldest message if no // enqueue_nowait(..) - will return immediately with false if no room left in
// room left. // the queue.
// dequeue_for(..) - will block until the queue is not empty or timeout have // dequeue_for(..) - will block until the queue is not empty or timeout have
// passed. // passed.

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>
@@ -22,50 +22,51 @@
#include <thread> #include <thread>
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/windows_include.h> #include <spdlog/details/windows_include.h>
#include <io.h> // for _get_osfhandle, _isatty, _fileno #include <fileapi.h> // for FlushFileBuffers
#include <process.h> // for _get_pid #include <io.h> // for _get_osfhandle, _isatty, _fileno
#include <process.h> // for _get_pid
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
#endif #endif
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES) #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
#include <cassert> #include <cassert>
#include <limits> #include <limits>
#endif #endif
#include <direct.h> // for _mkdir/_wmkdir #include <direct.h> // for _mkdir/_wmkdir
#else // unix #else // unix
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#ifdef __linux__ #ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif defined(_AIX) #elif defined(_AIX)
#include <pthread.h> // for pthread_getthrds_np #include <pthread.h> // for pthread_getthrds_np
#elif defined(__DragonFly__) || defined(__FreeBSD__) #elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <pthread_np.h> // for pthread_getthreadid_np #include <pthread_np.h> // for pthread_getthreadid_np
#elif defined(__NetBSD__) #elif defined(__NetBSD__)
#include <lwp.h> // for _lwp_self #include <lwp.h> // for _lwp_self
#elif defined(__sun) #elif defined(__sun)
#include <thread.h> // for thr_self #include <thread.h> // for thr_self
#endif #endif
#endif // unix #endif // unix
#if defined __APPLE__ #if defined __APPLE__
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
#endif #endif
#ifndef __has_feature // Clang - feature checking macros. #ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers. #define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif #endif
namespace spdlog { namespace spdlog {
@@ -119,12 +120,12 @@ SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT {
// fopen_s on non windows for writing // fopen_s on non windows for writing
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) { SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#else #else
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#endif #endif
#if defined(SPDLOG_PREVENT_CHILD_FD) #if defined(SPDLOG_PREVENT_CHILD_FD)
if (*fp != nullptr) { if (*fp != nullptr) {
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp))); auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) { if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) {
@@ -132,9 +133,9 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
*fp = nullptr; *fp = nullptr;
} }
} }
#endif #endif
#else // unix #else // unix
#if defined(SPDLOG_PREVENT_CHILD_FD) #if defined(SPDLOG_PREVENT_CHILD_FD)
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC; const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
const int fd = const int fd =
::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644)); ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
@@ -145,9 +146,9 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
if (*fp == nullptr) { if (*fp == nullptr) {
::close(fd); ::close(fd);
} }
#else #else
*fp = ::fopen((filename.c_str()), mode.c_str()); *fp = ::fopen((filename.c_str()), mode.c_str());
#endif #endif
#endif #endif
return *fp == nullptr; return *fp == nullptr;
@@ -177,11 +178,11 @@ SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT { SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
#ifdef _WIN32 #ifdef _WIN32
struct _stat buffer; struct _stat buffer;
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
return (::_wstat(filename.c_str(), &buffer) == 0); return (::_wstat(filename.c_str(), &buffer) == 0);
#else #else
return (::_stat(filename.c_str(), &buffer) == 0); return (::_stat(filename.c_str(), &buffer) == 0);
#endif #endif
#else // common linux/unix all have the stat system call #else // common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
return (::stat(filename.c_str(), &buffer) == 0); return (::stat(filename.c_str(), &buffer) == 0);
@@ -189,9 +190,9 @@ SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
} }
#ifdef _MSC_VER #ifdef _MSC_VER
// avoid warning about unreachable statement at the end of filesize() // avoid warning about unreachable statement at the end of filesize()
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4702) #pragma warning(disable : 4702)
#endif #endif
// Return file size according to open FILE* object // Return file size according to open FILE* object
@@ -201,59 +202,58 @@ SPDLOG_INLINE size_t filesize(FILE *f) {
} }
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
int fd = ::_fileno(f); int fd = ::_fileno(f);
#if defined(_WIN64) // 64 bits #if defined(_WIN64) // 64 bits
__int64 ret = ::_filelengthi64(fd); __int64 ret = ::_filelengthi64(fd);
if (ret >= 0) { if (ret >= 0) {
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
#else // windows 32 bits #else // windows 32 bits
long ret = ::_filelength(fd); long ret = ::_filelength(fd);
if (ret >= 0) { if (ret >= 0) {
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
#endif #endif
#else // unix #else // unix
// OpenBSD and AIX doesn't compile with :: before the fileno(..) // OpenBSD and AIX doesn't compile with :: before the fileno(..)
#if defined(__OpenBSD__) || defined(_AIX) #if defined(__OpenBSD__) || defined(_AIX)
int fd = fileno(f); int fd = fileno(f);
#else #else
int fd = ::fileno(f); int fd = ::fileno(f);
#endif #endif
// 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated) // 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated)
#if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && \ #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && \
(defined(__LP64__) || defined(_LP64)) (defined(__LP64__) || defined(_LP64))
struct stat64 st; struct stat64 st;
if (::fstat64(fd, &st) == 0) { if (::fstat64(fd, &st) == 0) {
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
} }
#else // other unix or linux 32 bits or cygwin #else // other unix or linux 32 bits or cygwin
struct stat st; struct stat st;
if (::fstat(fd, &st) == 0) { if (::fstat(fd, &st) == 0) {
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
} }
#endif #endif
#endif #endif
throw_spdlog_ex("Failed getting file size from fd", errno); throw_spdlog_ex("Failed getting file size from fd", errno);
return 0; // will not be reached. return 0; // will not be reached.
} }
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) #pragma warning(pop)
#endif #endif
// Return utc offset in minutes or throw spdlog_ex on failure // Return utc offset in minutes or throw spdlog_ex on failure
#if !defined(SPDLOG_NO_TZ_OFFSET)
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) { SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
#ifdef _WIN32 #ifdef _WIN32
#if _WIN32_WINNT < _WIN32_WINNT_WS08 #if _WIN32_WINNT < _WIN32_WINNT_WS08
TIME_ZONE_INFORMATION tzinfo; TIME_ZONE_INFORMATION tzinfo;
auto rv = ::GetTimeZoneInformation(&tzinfo); auto rv = ::GetTimeZoneInformation(&tzinfo);
#else #else
DYNAMIC_TIME_ZONE_INFORMATION tzinfo; DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo); auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
#endif #endif
if (rv == TIME_ZONE_ID_INVALID) throw_spdlog_ex("Failed getting timezone info. ", errno); if (rv == TIME_ZONE_ID_INVALID) throw_spdlog_ex("Failed getting timezone info. ", errno);
int offset = -tzinfo.Bias; int offset = -tzinfo.Bias;
@@ -264,11 +264,46 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
} }
return offset; return offset;
#else #else
#if defined(sun) || defined(__sun) || defined(_AIX) || \
(defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \
(!defined(__APPLE__) && !defined(_BSD_SOURCE) && !defined(_GNU_SOURCE) && \
(!defined(_POSIX_VERSION) || (_POSIX_VERSION < 202405L)))
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper {
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(),
const std::tm &gmtm = details::os::gmtime()) {
int local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = (
// difference in day of year
localtm.tm_yday -
gmtm.tm_yday
// + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */
+ static_cast<long int>(local_year - gmt_year) * 365);
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
return secs;
}
};
auto offset_seconds = helper::calculate_gmt_offset(tm);
#else
auto offset_seconds = tm.tm_gmtoff; auto offset_seconds = tm.tm_gmtoff;
#endif
return static_cast<int>(offset_seconds / 60); return static_cast<int>(offset_seconds / 60);
#endif #endif
} }
#endif // SPDLOG_NO_TZ_OFFSET
// Return current thread id as size_t // Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially // It exists because the std::this_thread::get_id() is much slower(especially
@@ -277,9 +312,9 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT {
#ifdef _WIN32 #ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId()); return static_cast<size_t>(::GetCurrentThreadId());
#elif defined(__linux__) #elif defined(__linux__)
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
#define SYS_gettid __NR_gettid #define SYS_gettid __NR_gettid
#endif #endif
return static_cast<size_t>(::syscall(SYS_gettid)); return static_cast<size_t>(::syscall(SYS_gettid));
#elif defined(_AIX) #elif defined(_AIX)
struct __pthrdsinfo buf; struct __pthrdsinfo buf;
@@ -298,25 +333,25 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT {
return static_cast<size_t>(::thr_self()); return static_cast<size_t>(::thr_self());
#elif __APPLE__ #elif __APPLE__
uint64_t tid; uint64_t tid;
// There is no pthread_threadid_np prior to Mac OS X 10.6, and it is not supported on any PPC, // There is no pthread_threadid_np prior to Mac OS X 10.6, and it is not supported on any PPC,
// including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64. // including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64.
#ifdef MAC_OS_X_VERSION_MAX_ALLOWED #ifdef MAC_OS_X_VERSION_MAX_ALLOWED
{ {
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__) #if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
tid = pthread_mach_thread_np(pthread_self()); tid = pthread_mach_thread_np(pthread_self());
#elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060 #elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
if (&pthread_threadid_np) { if (&pthread_threadid_np) {
pthread_threadid_np(nullptr, &tid); pthread_threadid_np(nullptr, &tid);
} else { } else {
tid = pthread_mach_thread_np(pthread_self()); tid = pthread_mach_thread_np(pthread_self());
} }
#else #else
pthread_threadid_np(nullptr, &tid); pthread_threadid_np(nullptr, &tid);
#endif #endif
} }
#else #else
pthread_threadid_np(nullptr, &tid); pthread_threadid_np(nullptr, &tid);
#endif #endif
return static_cast<size_t>(tid); return static_cast<size_t>(tid);
#else // Default to standard C++11 (other Unix) #else // Default to standard C++11 (other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
@@ -455,7 +490,7 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
result_size = result_size =
::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(), result_size); ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(), result_size);
if (result_size > 0) { if (result_size > 0) {
assert(result_size == static_cast<int>(target.size())); assert(result_size == target.size());
return; return;
} }
} }
@@ -469,11 +504,11 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
// return true on success // return true on success
static SPDLOG_INLINE bool mkdir_(const filename_t &path) { static SPDLOG_INLINE bool mkdir_(const filename_t &path) {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
return ::_wmkdir(path.c_str()) == 0; return ::_wmkdir(path.c_str()) == 0;
#else #else
return ::_mkdir(path.c_str()) == 0; return ::_mkdir(path.c_str()) == 0;
#endif #endif
#else #else
return ::mkdir(path.c_str(), mode_t(0755)) == 0; return ::mkdir(path.c_str(), mode_t(0755)) == 0;
#endif #endif
@@ -528,22 +563,21 @@ SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{}; return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
} }
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif // _MSC_VER
std::string SPDLOG_INLINE getenv(const char *field) { std::string SPDLOG_INLINE getenv(const char *field) {
#if defined(_MSC_VER) && defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_DESKTOP_APP) && \ #if defined(_MSC_VER)
(WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP) #if defined(__cplusplus_winrt)
return std::string{}; // not supported under uwp return std::string{}; // not supported under uwp
#else #else
char *buf = std::getenv(field); size_t len = 0;
char buf[128];
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
return ok ? buf : std::string{};
#endif
#else // revert to getenv
char *buf = ::getenv(field);
return buf ? buf : std::string{}; return buf ? buf : std::string{};
#endif #endif
} }
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
// Do fsync by FILE handlerpointer // Do fsync by FILE handlerpointer
// Return true on success // Return true on success

View File

@@ -22,22 +22,22 @@ SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
// eol definition // eol definition
#if !defined(SPDLOG_EOL) #if !defined(SPDLOG_EOL)
#ifdef _WIN32 #ifdef _WIN32
#define SPDLOG_EOL "\r\n" #define SPDLOG_EOL "\r\n"
#else #else
#define SPDLOG_EOL "\n" #define SPDLOG_EOL "\n"
#endif #endif
#endif #endif
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
// folder separator // folder separator
#if !defined(SPDLOG_FOLDER_SEPS) #if !defined(SPDLOG_FOLDER_SEPS)
#ifdef _WIN32 #ifdef _WIN32
#define SPDLOG_FOLDER_SEPS "\\/" #define SPDLOG_FOLDER_SEPS "\\/"
#else #else
#define SPDLOG_FOLDER_SEPS "/" #define SPDLOG_FOLDER_SEPS "/"
#endif #endif
#endif #endif
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS; SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
@@ -123,5 +123,5 @@ SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "os-inl.h" #include "os-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/periodic_worker.h> #include <spdlog/details/periodic_worker.h>
#endif #endif
namespace spdlog { namespace spdlog {

View File

@@ -54,5 +54,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "periodic_worker-inl.h" #include "periodic_worker-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/registry.h> #include <spdlog/details/registry.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>
@@ -13,12 +13,12 @@
#include <spdlog/pattern_formatter.h> #include <spdlog/pattern_formatter.h>
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// support for the default stdout color logger // support for the default stdout color logger
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h> #include <spdlog/sinks/wincolor_sink.h>
#else #else
#include <spdlog/sinks/ansicolor_sink.h> #include <spdlog/sinks/ansicolor_sink.h>
#endif #endif
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER #endif // SPDLOG_DISABLE_DEFAULT_LOGGER
#include <chrono> #include <chrono>
@@ -33,12 +33,12 @@ namespace details {
SPDLOG_INLINE registry::registry() SPDLOG_INLINE registry::registry()
: formatter_(new pattern_formatter()) { : formatter_(new pattern_formatter()) {
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows). // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
#ifdef _WIN32 #ifdef _WIN32
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>(); auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
#else #else
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>(); auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
#endif #endif
const char *default_logger_name = ""; const char *default_logger_name = "";
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink)); default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
@@ -101,7 +101,7 @@ SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); } SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); }
// set default logger. // set default logger.
// the default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) { SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<std::mutex> lock(logger_map_mutex_);
if (new_default_logger != nullptr) { if (new_default_logger != nullptr) {

View File

@@ -127,5 +127,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "registry-inl.h" #include "registry-inl.h"
#endif #endif

View File

@@ -58,81 +58,8 @@ public:
SOCKET fd() const { return socket_; } SOCKET fd() const { return socket_; }
int connect_socket_with_timeout(SOCKET sockfd,
const struct sockaddr *addr,
int addrlen,
const timeval &tv) {
// If no timeout requested, do a normal blocking connect.
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
int rv = ::connect(sockfd, addr, addrlen);
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAEISCONN) {
return 0;
}
return rv;
}
// Switch to nonblocking mode
u_long mode = 1UL;
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
return SOCKET_ERROR;
}
int rv = ::connect(sockfd, addr, addrlen);
int last_error = WSAGetLastError();
if (rv == 0 || last_error == WSAEISCONN) {
mode = 0UL;
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
return SOCKET_ERROR;
}
return 0;
}
if (last_error != WSAEWOULDBLOCK) {
// Real error
mode = 0UL;
if (::ioctlsocket(sockfd, FIONBIO, &mode)) {
return SOCKET_ERROR;
}
return SOCKET_ERROR;
}
// Wait until socket is writable or timeout expires
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(sockfd, &wfds);
rv = ::select(0, nullptr, &wfds, nullptr, const_cast<timeval *>(&tv));
// Restore blocking mode regardless of select result
mode = 0UL;
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
return SOCKET_ERROR;
}
if (rv == 0) {
WSASetLastError(WSAETIMEDOUT);
return SOCKET_ERROR;
}
if (rv == SOCKET_ERROR) {
return SOCKET_ERROR;
}
int so_error = 0;
int len = sizeof(so_error);
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&so_error), &len) ==
SOCKET_ERROR) {
return SOCKET_ERROR;
}
if (so_error != 0 && so_error != WSAEISCONN) {
// connection failed
WSASetLastError(so_error);
return SOCKET_ERROR;
}
return 0; // success
}
// try to connect or throw on failure // try to connect or throw on failure
void connect(const std::string &host, int port, int timeout_ms = 0) { void connect(const std::string &host, int port) {
if (is_connected()) { if (is_connected()) {
close(); close();
} }
@@ -144,10 +71,6 @@ public:
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0; hints.ai_protocol = 0;
timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
auto port_str = std::to_string(port); auto port_str = std::to_string(port);
struct addrinfo *addrinfo_result; struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result); auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
@@ -159,6 +82,7 @@ public:
} }
// Try each address until we successfully connect(2). // Try each address until we successfully connect(2).
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) { for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socket_ == INVALID_SOCKET) { if (socket_ == INVALID_SOCKET) {
@@ -166,24 +90,18 @@ public:
WSACleanup(); WSACleanup();
continue; continue;
} }
if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) { if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
last_error = 0;
break; break;
} else {
last_error = ::WSAGetLastError();
close();
} }
last_error = WSAGetLastError();
::closesocket(socket_);
socket_ = INVALID_SOCKET;
} }
::freeaddrinfo(addrinfo_result); ::freeaddrinfo(addrinfo_result);
if (socket_ == INVALID_SOCKET) { if (socket_ == INVALID_SOCKET) {
WSACleanup(); WSACleanup();
throw_winsock_error_("connect failed", last_error); throw_winsock_error_("connect failed", last_error);
} }
if (timeout_ms > 0) {
DWORD tv = static_cast<DWORD>(timeout_ms);
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
}
// set TCP_NODELAY // set TCP_NODELAY
int enable_flag = 1; int enable_flag = 1;

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifdef _WIN32 #ifdef _WIN32
#error include tcp_client-windows.h instead #error include tcp_client-windows.h instead
#endif #endif
// tcp client helper // tcp client helper
@@ -17,7 +17,6 @@
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <string> #include <string>
@@ -40,72 +39,8 @@ public:
~tcp_client() { close(); } ~tcp_client() { close(); }
int connect_socket_with_timeout(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen,
const timeval &tv) {
// Blocking connect if timeout is zero
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
int rv = ::connect(sockfd, addr, addrlen);
if (rv < 0 && errno == EISCONN) {
// already connected, treat as success
return 0;
}
return rv;
}
// Non-blocking path
int orig_flags = ::fcntl(sockfd, F_GETFL, 0);
if (orig_flags < 0) {
return -1;
}
if (::fcntl(sockfd, F_SETFL, orig_flags | O_NONBLOCK) < 0) {
return -1;
}
int rv = ::connect(sockfd, addr, addrlen);
if (rv == 0 || (rv < 0 && errno == EISCONN)) {
// immediate connect or already connected
::fcntl(sockfd, F_SETFL, orig_flags);
return 0;
}
if (errno != EINPROGRESS) {
::fcntl(sockfd, F_SETFL, orig_flags);
return -1;
}
// wait for writability
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(sockfd, &wfds);
struct timeval tv_copy = tv;
rv = ::select(sockfd + 1, nullptr, &wfds, nullptr, &tv_copy);
if (rv <= 0) {
// timeout or error
::fcntl(sockfd, F_SETFL, orig_flags);
if (rv == 0) errno = ETIMEDOUT;
return -1;
}
// check socket error
int so_error = 0;
socklen_t len = sizeof(so_error);
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) {
::fcntl(sockfd, F_SETFL, orig_flags);
return -1;
}
::fcntl(sockfd, F_SETFL, orig_flags);
if (so_error != 0 && so_error != EISCONN) {
errno = so_error;
return -1;
}
return 0;
}
// try to connect or throw on failure // try to connect or throw on failure
void connect(const std::string &host, int port, int timeout_ms = 0) { void connect(const std::string &host, int port) {
close(); close();
struct addrinfo hints {}; struct addrinfo hints {};
memset(&hints, 0, sizeof(struct addrinfo)); memset(&hints, 0, sizeof(struct addrinfo));
@@ -114,10 +49,6 @@ public:
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0; hints.ai_protocol = 0;
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
auto port_str = std::to_string(port); auto port_str = std::to_string(port);
struct addrinfo *addrinfo_result; struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result); auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
@@ -138,9 +69,8 @@ public:
last_errno = errno; last_errno = errno;
continue; continue;
} }
::fcntl(socket_, F_SETFD, FD_CLOEXEC); rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
if (connect_socket_with_timeout(socket_, rp->ai_addr, rp->ai_addrlen, tv) == 0) { if (rv == 0) {
last_errno = 0;
break; break;
} }
last_errno = errno; last_errno = errno;
@@ -152,12 +82,6 @@ public:
throw_spdlog_ex("::connect failed", last_errno); throw_spdlog_ex("::connect failed", last_errno);
} }
if (timeout_ms > 0) {
// Set timeouts for send and recv
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
}
// set TCP_NODELAY // set TCP_NODELAY
int enable_flag = 1; int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
@@ -170,7 +94,7 @@ public:
#endif #endif
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
#error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available" #error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
#endif #endif
} }

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/thread_pool.h> #include <spdlog/details/thread_pool.h>
#endif #endif
#include <cassert> #include <cassert>
@@ -35,7 +35,7 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
size_t threads_n, size_t threads_n,
std::function<void()> on_thread_start) std::function<void()> on_thread_start)
: thread_pool(q_max_items, threads_n, std::move(on_thread_start), [] {}) {} : thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {} : thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
@@ -94,7 +94,8 @@ void SPDLOG_INLINE thread_pool::worker_loop_() {
} }
// process next message in the queue // process next message in the queue
// returns true if this thread should still be active (while no terminated msg was received) // return true if this thread should still be active (while no terminate msg
// was received)
bool SPDLOG_INLINE thread_pool::process_next_msg_() { bool SPDLOG_INLINE thread_pool::process_next_msg_() {
async_msg incoming_async_msg; async_msg incoming_async_msg;
q_.dequeue(incoming_async_msg); q_.dequeue(incoming_async_msg);

View File

@@ -113,5 +113,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "thread_pool-inl.h" #include "thread_pool-inl.h"
#endif #endif

View File

@@ -16,9 +16,9 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Mswsock.lib") #pragma comment(lib, "Mswsock.lib")
#pragma comment(lib, "AdvApi32.lib") #pragma comment(lib, "AdvApi32.lib")
#endif #endif
namespace spdlog { namespace spdlog {

View File

@@ -7,7 +7,7 @@
// Will throw on construction if the socket creation failed. // Will throw on construction if the socket creation failed.
#ifdef _WIN32 #ifdef _WIN32
#error "include udp_client-windows.h instead" #error "include udp_client-windows.h instead"
#endif #endif
#include <arpa/inet.h> #include <arpa/inet.h>

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max #define NOMINMAX // prevent windows redefining min/max
#endif #endif
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>

View File

@@ -9,13 +9,13 @@
#include <spdlog/common.h> #include <spdlog/common.h>
#if defined(__has_include) #if defined(__has_include)
#if __has_include(<version>) #if __has_include(<version>)
#include <version> #include <version>
#endif #endif
#endif #endif
#if __cpp_lib_span >= 202002L #if __cpp_lib_span >= 202002L
#include <span> #include <span>
#endif #endif
// //

View File

@@ -71,7 +71,7 @@ class dynamic_arg_list {
* It can be implicitly converted into `fmt::basic_format_args` for passing * It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`. * into type-erased formatting functions such as `fmt::vformat`.
*/ */
FMT_EXPORT template <typename Context> class dynamic_format_arg_store { template <typename Context> class dynamic_format_arg_store {
private: private:
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
@@ -212,7 +212,7 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
} }
/// Returns the number of elements in the store. /// Returns the number of elements in the store.
auto size() const noexcept -> size_t { return data_.size(); } size_t size() const noexcept { return data_.size(); }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -21,7 +21,7 @@
#endif #endif
// The fmt library version in the form major * 10000 + minor * 100 + patch. // The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 120100 #define FMT_VERSION 110104
// Detect compiler versions. // Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__) #if defined(__clang__) && !defined(__ibmxl__)
@@ -114,9 +114,7 @@
#endif #endif
// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. // Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated.
#ifdef FMT_USE_CONSTEVAL #if !defined(__cpp_lib_is_constant_evaluated)
// Use the provided definition.
#elif !defined(__cpp_lib_is_constant_evaluated)
# define FMT_USE_CONSTEVAL 0 # define FMT_USE_CONSTEVAL 0
#elif FMT_CPLUSPLUS < 201709L #elif FMT_CPLUSPLUS < 201709L
# define FMT_USE_CONSTEVAL 0 # define FMT_USE_CONSTEVAL 0
@@ -203,6 +201,28 @@
# define FMT_NODISCARD # define FMT_NODISCARD
#endif #endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
#ifdef FMT_ALWAYS_INLINE
// Use the provided definition.
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE inline
#endif
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
#ifdef NDEBUG
# define FMT_INLINE FMT_ALWAYS_INLINE
#else
# define FMT_INLINE inline
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION #if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value))) # define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else #else
@@ -229,33 +249,10 @@
# define FMT_MSC_WARNING(...) # define FMT_MSC_WARNING(...)
#endif #endif
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
# define FMT_GCC_OPTIMIZED
#endif
FMT_PRAGMA_CLANG(diagnostic push)
FMT_PRAGMA_GCC(diagnostic push)
#ifdef FMT_ALWAYS_INLINE
// Use the provided definition.
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE inline
#endif
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
# define FMT_INLINE FMT_ALWAYS_INLINE
#else
# define FMT_INLINE inline
#endif
#ifndef FMT_BEGIN_NAMESPACE #ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \ # define FMT_BEGIN_NAMESPACE \
namespace fmt { \ namespace fmt { \
inline namespace v12 { inline namespace v11 {
# define FMT_END_NAMESPACE \ # define FMT_END_NAMESPACE \
} \ } \
} }
@@ -300,6 +297,13 @@ FMT_PRAGMA_GCC(diagnostic push)
using unused = int[]; \ using unused = int[]; \
(void)unused { 0, (expr, 0)... } (void)unused { 0, (expr, 0)... }
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
#endif
FMT_PRAGMA_CLANG(diagnostic push)
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other metafunctions for older systems. // Implementations of enable_if_t and other metafunctions for older systems.
@@ -321,8 +325,8 @@ using underlying_t = typename std::underlying_type<T>::type;
template <typename T> using decay_t = typename std::decay<T>::type; template <typename T> using decay_t = typename std::decay<T>::type;
using nullptr_t = decltype(nullptr); using nullptr_t = decltype(nullptr);
#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context. // A workaround for gcc 4.9 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl { template <typename...> struct void_t_impl {
using type = void; using type = void;
}; };
@@ -351,9 +355,6 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b; return a > b ? a : b;
} }
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
namespace detail { namespace detail {
// Suppresses "unused variable" warnings with the method described in // Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -394,7 +395,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# define FMT_ASSERT(condition, message) \ # define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \ ? (void)0 \
: ::fmt::assert_fail(__FILE__, __LINE__, (message))) : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
#endif #endif
#ifdef FMT_USE_INT128 #ifdef FMT_USE_INT128
@@ -417,12 +418,8 @@ inline auto map(int128_opt) -> monostate { return {}; }
inline auto map(uint128_opt) -> monostate { return {}; } inline auto map(uint128_opt) -> monostate { return {}; }
#endif #endif
#ifdef FMT_USE_BITINT #ifndef FMT_USE_BITINT
// Use the provided definition. # define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__)
# define FMT_USE_BITINT 1
#else
# define FMT_USE_BITINT 0
#endif #endif
#if FMT_USE_BITINT #if FMT_USE_BITINT
@@ -465,13 +462,12 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
static_assert(!FMT_UNICODE || use_utf8, static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8"); "Unicode support requires compiling with /utf-8");
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; } template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* { constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
return s;
}
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int { FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
-> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) { for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1; if (*s1 < *s2) return -1;
@@ -530,20 +526,20 @@ template <typename Char> class basic_string_view {
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
/// Constructs a string view object from a C string and a size. /// Constructs a string reference object from a C string and a size.
constexpr basic_string_view(const Char* s, size_t count) noexcept constexpr basic_string_view(const Char* s, size_t count) noexcept
: data_(s), size_(count) {} : data_(s), size_(count) {}
constexpr basic_string_view(nullptr_t) = delete; constexpr basic_string_view(nullptr_t) = delete;
/// Constructs a string view object from a C string. /// Constructs a string reference object from a C string.
#if FMT_GCC_VERSION #if FMT_GCC_VERSION
FMT_ALWAYS_INLINE FMT_ALWAYS_INLINE
#endif #endif
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION #if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) { if (std::is_same<Char, char>::value) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr. size_ = __builtin_strlen(detail::narrow(s));
return; return;
} }
#endif #endif
@@ -552,7 +548,7 @@ template <typename Char> class basic_string_view {
size_ = len; size_ = len;
} }
/// Constructs a string view from a `std::basic_string` or a /// Constructs a string reference from a `std::basic_string` or a
/// `std::basic_string_view` object. /// `std::basic_string_view` object.
template <typename S, template <typename S,
FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same< FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
@@ -589,6 +585,7 @@ template <typename Char> class basic_string_view {
return starts_with(basic_string_view<Char>(s)); return starts_with(basic_string_view<Char>(s));
} }
// Lexicographically compare this string reference to other.
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
int result = int result =
detail::compare(data_, other.data_, min_of(size_, other.size_)); detail::compare(data_, other.data_, min_of(size_, other.size_));
@@ -619,6 +616,19 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>; using string_view = basic_string_view<char>;
/// Specifies if `T` is an extended character type. Can be specialized by users.
template <typename T> struct is_xchar : std::false_type {};
template <> struct is_xchar<wchar_t> : std::true_type {};
template <> struct is_xchar<char16_t> : std::true_type {};
template <> struct is_xchar<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_xchar<char8_t> : std::true_type {};
#endif
// DEPRECATED! Will be replaced with an alias to prevent specializations.
template <typename T> struct is_char : is_xchar<T> {};
template <> struct is_char<char> : std::true_type {};
template <typename T> class basic_appender; template <typename T> class basic_appender;
using appender = basic_appender<char>; using appender = basic_appender<char>;
@@ -771,7 +781,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift); (static_cast<unsigned>(p) << precision_shift);
} }
constexpr auto dynamic() const -> bool { constexpr bool dynamic() const {
return (data_ & (width_mask | precision_mask)) != 0; return (data_ & (width_mask | precision_mask)) != 0;
} }
@@ -911,50 +921,14 @@ template <typename Char = char> class parse_context {
FMT_CONSTEXPR void check_dynamic_spec(int arg_id); FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
}; };
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to std::locale to avoid the heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc) : locale_(&loc) {
// Check if std::isalpha is found via ADL to reduce the chance of misuse.
detail::ignore_unused(isalpha('x', loc));
}
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
FMT_END_EXPORT FMT_END_EXPORT
namespace detail { namespace detail {
// Specifies if `T` is a code unit type.
template <typename T> struct is_code_unit : std::false_type {};
template <> struct is_code_unit<char> : std::true_type {};
template <> struct is_code_unit<wchar_t> : std::true_type {};
template <> struct is_code_unit<char16_t> : std::true_type {};
template <> struct is_code_unit<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
#endif
// Constructs fmt::basic_string_view<Char> from types implicitly convertible // Constructs fmt::basic_string_view<Char> from types implicitly convertible
// to it, deducing Char. Explicitly convertible types such as the ones returned // to it, deducing Char. Explicitly convertible types such as the ones returned
// from FMT_STRING are intentionally excluded. // from FMT_STRING are intentionally excluded.
template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)> template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> { constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s; return s;
} }
@@ -1058,11 +1032,6 @@ enum {
struct view {}; struct view {};
template <typename T, typename Enable = std::true_type>
struct is_view : std::false_type {};
template <typename T>
struct is_view<T, bool_constant<sizeof(T) != 0>> : std::is_base_of<view, T> {};
template <typename Char, typename T> struct named_arg; template <typename Char, typename T> struct named_arg;
template <typename T> struct is_named_arg : std::false_type {}; template <typename T> struct is_named_arg : std::false_type {};
template <typename T> struct is_static_named_arg : std::false_type {}; template <typename T> struct is_static_named_arg : std::false_type {};
@@ -1083,11 +1052,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>(); return (B1 ? 1 : 0) + count<B2, Tail...>();
} }
template <typename... T> constexpr auto count_named_args() -> int { template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<T>::value...>(); return count<is_named_arg<Args>::value...>();
} }
template <typename... T> constexpr auto count_static_named_args() -> int { template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<T>::value...>(); return count<is_static_named_arg<Args>::value...>();
} }
template <typename Char> struct named_arg_info { template <typename Char> struct named_arg_info {
@@ -1095,16 +1064,6 @@ template <typename Char> struct named_arg_info {
int id; int id;
}; };
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index,
basic_string_view<Char> arg_name) {
for (int i = 0; i < named_arg_index; ++i) {
if (named_args[i].name == arg_name) report_error("duplicate named arg");
}
}
template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)> template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) { void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
++arg_index; ++arg_index;
@@ -1112,7 +1071,6 @@ void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)> template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
void init_named_arg(named_arg_info<Char>* named_args, int& arg_index, void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
int& named_arg_index, const T& arg) { int& named_arg_index, const T& arg) {
check_for_duplicate<Char>(named_args, named_arg_index, arg.name);
named_args[named_arg_index++] = {arg.name, arg_index++}; named_args[named_arg_index++] = {arg.name, arg_index++};
} }
@@ -1126,13 +1084,12 @@ template <typename T, typename Char,
FMT_ENABLE_IF(is_static_named_arg<T>::value)> FMT_ENABLE_IF(is_static_named_arg<T>::value)>
FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args, FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
int& arg_index, int& named_arg_index) { int& arg_index, int& named_arg_index) {
check_for_duplicate<Char>(named_args, named_arg_index, T::name);
named_args[named_arg_index++] = {T::name, arg_index++}; named_args[named_arg_index++] = {T::name, arg_index++};
} }
// To minimize the number of types we need to deal with, long is translated // To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size. // either to int or to long long depending on its size.
enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES }; enum { long_short = sizeof(long) == sizeof(int) };
using long_type = conditional_t<long_short, int, long long>; using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>; using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
@@ -1199,7 +1156,7 @@ template <typename Char> struct type_mapper {
static auto map(ubitint<N>) static auto map(ubitint<N>)
-> conditional_t<N <= 64, unsigned long long, void>; -> conditional_t<N <= 64, unsigned long long, void>;
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)> template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
static auto map(T) -> conditional_t< static auto map(T) -> conditional_t<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>; std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
@@ -1705,12 +1662,12 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES> template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker { class format_string_checker {
private: private:
type types_[max_of<size_t>(1, NUM_ARGS)]; type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)]; named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_; compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*; using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)]; parse_func parse_funcs_[max_of(1, NUM_ARGS)];
public: public:
template <typename... T> template <typename... T>
@@ -1749,17 +1706,7 @@ class format_string_checker {
-> const Char* { -> const Char* {
context_.advance_to(begin); context_.advance_to(begin);
if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
while (begin != end && *begin != '}') ++begin;
// If id is out of range, it means we do not know the type and cannot parse
// the format at compile time. Instead, skip over content until we finish
// the format spec, accounting for any nested replacements.
for (int bracket_count = 0;
begin != end && (bracket_count > 0 || *begin != '}'); ++begin) {
if (*begin == '{')
++bracket_count;
else if (*begin == '}')
--bracket_count;
}
return begin; return begin;
} }
@@ -1854,17 +1801,12 @@ template <typename T> class buffer {
void void
append(const U* begin, const U* end) { append(const U* begin, const U* end) {
while (begin != end) { while (begin != end) {
auto size = size_;
auto free_cap = capacity_ - size;
auto count = to_unsigned(end - begin); auto count = to_unsigned(end - begin);
if (free_cap < count) { try_reserve(size_ + count);
grow_(*this, size + count); auto free_cap = capacity_ - size_;
size = size_; if (free_cap < count) count = free_cap;
free_cap = capacity_ - size;
count = count < free_cap ? count : free_cap;
}
// A loop is faster than memcpy on small sizes. // A loop is faster than memcpy on small sizes.
T* out = ptr_ + size; T* out = ptr_ + size_;
for (size_t i = 0; i < count; ++i) out[i] = begin[i]; for (size_t i = 0; i < count; ++i) out[i] = begin[i];
size_ += count; size_ += count;
begin += count; begin += count;
@@ -2064,17 +2006,6 @@ struct has_back_insert_iterator_container_append<
.append(std::declval<InputIt>(), .append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {}; std::declval<InputIt>()))>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert(get_container(std::declval<OutputIt>()).end(),
std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T). // An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt, template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&& FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
@@ -2089,8 +2020,6 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template <typename T, typename InputIt, typename OutputIt, template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value && FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append< !has_back_insert_iterator_container_append<
OutputIt, InputIt>::value &&
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)> OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt { -> OutputIt {
@@ -2100,11 +2029,7 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
} }
template <typename T, typename InputIt, typename OutputIt, template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value && FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
(has_back_insert_iterator_container_append<
OutputIt, InputIt>::value ||
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++); while (begin != end) *out++ = static_cast<T>(*begin++);
return out; return out;
@@ -2224,7 +2149,7 @@ template <typename Context> class value {
static_assert(N <= 64, "unsupported _BitInt"); static_assert(N <= 64, "unsupported _BitInt");
} }
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)> template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
static_assert( static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value, std::is_same<T, char>::value || std::is_same<T, char_type>::value,
@@ -2300,7 +2225,7 @@ template <typename Context> class value {
custom.value = const_cast<value_type*>(&x); custom.value = const_cast<value_type*>(&x);
#endif #endif
} }
custom.format = format_custom<value_type>; custom.format = format_custom<value_type, formatter<value_type, char_type>>;
} }
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())> template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
@@ -2311,10 +2236,10 @@ template <typename Context> class value {
} }
// Formats an argument of a custom type, such as a user-defined class. // Formats an argument of a custom type, such as a user-defined class.
template <typename T> template <typename T, typename Formatter>
static void format_custom(void* arg, parse_context<char_type>& parse_ctx, static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
Context& ctx) { Context& ctx) {
auto f = formatter<T, char_type>(); auto f = Formatter();
parse_ctx.advance_to(f.parse(parse_ctx)); parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type = using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>; conditional_t<has_formatter<const T, char_type>(), const T, T>;
@@ -2341,14 +2266,35 @@ struct is_output_iterator<
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++), enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
T>::value>> : std::true_type {}; T>::value>> : std::true_type {};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long { template <typename> constexpr auto encode_types() -> unsigned long long {
return 0; return 0;
} }
template <typename Context, typename First, typename... T> template <typename Context, typename Arg, typename... Args>
constexpr auto encode_types() -> unsigned long long { constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<First, Context>::value) | return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
(encode_types<Context, T...>() << packed_arg_bits); (encode_types<Context, Args...>() << packed_arg_bits);
} }
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)> template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
@@ -2365,9 +2311,8 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC> unsigned long long DESC>
struct named_arg_store { struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args. // args_[0].named_args points to named_args to avoid bloating format_args.
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS]; arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T> template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2386,8 +2331,8 @@ struct named_arg_store {
} }
named_arg_store(const named_arg_store& rhs) = delete; named_arg_store(const named_arg_store& rhs) = delete;
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete; named_arg_store& operator=(const named_arg_store& rhs) = delete;
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete; named_arg_store& operator=(named_arg_store&& rhs) = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; } operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
}; };
@@ -2400,7 +2345,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type = using type =
conditional_t<NUM_NAMED_ARGS == 0, conditional_t<NUM_NAMED_ARGS == 0,
arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)], arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>; named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args; type args;
}; };
@@ -2684,17 +2629,22 @@ class context {
private: private:
appender out_; appender out_;
format_args args_; format_args args_;
FMT_NO_UNIQUE_ADDRESS locale_ref loc_; FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
public: public:
using char_type = char; ///< The character type for the output. /// The character type for the output.
using char_type = char;
using iterator = appender; using iterator = appender;
using format_arg = basic_format_arg<context>; using format_arg = basic_format_arg<context>;
using parse_context_type FMT_DEPRECATED = parse_context<>;
template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
enum { builtin_types = FMT_BUILTIN_TYPES }; enum { builtin_types = FMT_BUILTIN_TYPES };
/// Constructs a `context` object. References to the arguments are stored /// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes. /// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {}) FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {} : out_(out), args_(args), loc_(loc) {}
context(context&&) = default; context(context&&) = default;
context(const context&) = delete; context(const context&) = delete;
@@ -2715,7 +2665,7 @@ class context {
// Advances the begin iterator to `it`. // Advances the begin iterator to `it`.
FMT_CONSTEXPR void advance_to(iterator) {} FMT_CONSTEXPR void advance_to(iterator) {}
FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; } FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
}; };
template <typename Char = char> struct runtime_format_string { template <typename Char = char> struct runtime_format_string {
@@ -2753,7 +2703,7 @@ template <typename... T> struct fstring {
template <size_t N> template <size_t N>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
using namespace detail; using namespace detail;
static_assert(count<(is_view<remove_cvref_t<T>>::value && static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
std::is_reference<T>::value)...>() == 0, std::is_reference<T>::value)...>() == 0,
"passing views as lvalues is disallowed"); "passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack())); if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
@@ -2802,6 +2752,9 @@ template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value; concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif #endif
template <typename T, typename Char>
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
// A formatter specialization for natively supported types. // A formatter specialization for natively supported types.
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct formatter<T, Char,
@@ -2998,10 +2951,9 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
return fmt::println(stdout, fmt, static_cast<T&&>(args)...); return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
} }
FMT_PRAGMA_GCC(diagnostic pop) FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop) FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options) FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY #ifdef FMT_HEADER_ONLY

View File

@@ -22,6 +22,21 @@
#include "format.h" #include "format.h"
namespace fmt_detail {
struct time_zone {
template <typename Duration, typename T>
auto to_sys(T)
-> std::chrono::time_point<std::chrono::system_clock, Duration> {
return {};
}
};
template <typename... T> inline auto current_zone(T...) -> time_zone* {
return nullptr;
}
template <typename... T> inline void _tzset(T...) {}
} // namespace fmt_detail
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled. // Enable safe chrono durations, unless explicitly disabled.
@@ -38,7 +53,6 @@ FMT_BEGIN_NAMESPACE
// Copyright Paul Dreik 2019 // Copyright Paul Dreik 2019
namespace safe_duration_cast { namespace safe_duration_cast {
// DEPRECATED!
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 &&
std::numeric_limits<From>::is_signed == std::numeric_limits<From>::is_signed ==
@@ -162,6 +176,17 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To { int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>; using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0; ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type // the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this: // to count() in the To type, by multiplying it with this:
@@ -272,6 +297,8 @@ namespace detail {
#define FMT_NOMACRO #define FMT_NOMACRO
template <typename T = void> struct null {}; template <typename T = void> struct null {};
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); } inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); } inline auto gmtime_s(...) -> null<> { return null<>(); }
@@ -314,7 +341,7 @@ inline auto get_classic_locale() -> const std::locale& {
} }
template <typename CodeUnit> struct codecvt_result { template <typename CodeUnit> struct codecvt_result {
static constexpr size_t max_size = 32; static constexpr const size_t max_size = 32;
CodeUnit buf[max_size]; CodeUnit buf[max_size];
CodeUnit* end; CodeUnit* end;
}; };
@@ -408,11 +435,14 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
} }
template <typename T, typename U> template <typename Rep1, typename Rep2>
using is_similar_arithmetic_type = struct is_same_arithmetic_type
bool_constant<(std::is_integral<T>::value && std::is_integral<U>::value) || : public std::integral_constant<bool,
(std::is_floating_point<T>::value && (std::is_integral<Rep1>::value &&
std::is_floating_point<U>::value)>; std::is_integral<Rep2>::value) ||
(std::is_floating_point<Rep1>::value &&
std::is_floating_point<Rep2>::value)> {
};
FMT_NORETURN inline void throw_duration_error() { FMT_NORETURN inline void throw_duration_error() {
FMT_THROW(format_error("cannot format duration")); FMT_THROW(format_error("cannot format duration"));
@@ -431,7 +461,11 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
using common_rep = typename std::common_type<FromRep, typename To::rep, using common_rep = typename std::common_type<FromRep, typename To::rep,
decltype(factor::num)>::type; decltype(factor::num)>::type;
common_rep count = from.count(); // This conversion is lossless.
int ec = 0;
auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
from.count(), ec);
if (ec) throw_duration_error();
// Multiply from.count() by factor and check for overflow. // Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) { if (const_check(factor::num != 1)) {
@@ -442,7 +476,6 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
count *= factor::num; count *= factor::num;
} }
if (const_check(factor::den != 1)) count /= factor::den; if (const_check(factor::den != 1)) count /= factor::den;
int ec = 0;
auto to = auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>( To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
count, ec)); count, ec));
@@ -456,8 +489,6 @@ template <typename To, typename FromRep, typename FromPeriod,
std::is_floating_point<typename To::rep>::value)> std::is_floating_point<typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To { auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#if FMT_SAFE_DURATION_CAST #if FMT_SAFE_DURATION_CAST
// Preserve infinity and NaN.
if (!isfinite(from.count())) return static_cast<To>(from.count());
// Throwing version of safe_duration_cast is only available for // Throwing version of safe_duration_cast is only available for
// integer to integer or float to float casts. // integer to integer or float to float casts.
int ec; int ec;
@@ -470,11 +501,11 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#endif #endif
} }
template <typename To, typename FromRep, typename FromPeriod, template <
FMT_ENABLE_IF( typename To, typename FromRep, typename FromPeriod,
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)> FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To { auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
// Mixed integer <-> float cast is not supported by safe duration_cast. // Mixed integer <-> float cast is not supported by safe_duration_cast.
return std::chrono::duration_cast<To>(from); return std::chrono::duration_cast<To>(from);
} }
@@ -488,10 +519,68 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
.count(); .count();
} }
// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without
// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160.
template <typename T> FMT_CONSTEXPR auto has_current_zone() -> bool {
using namespace std::chrono;
using namespace fmt_detail;
return !std::is_same<decltype(current_zone()), fmt_detail::time_zone*>::value;
}
} // namespace detail } // namespace detail
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in local time. Unlike `std::localtime`, this function is
* thread-safe on most platforms.
*/
inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
inline dispatcher(std::time_t t) : time_(t) {}
inline auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
#if FMT_USE_LOCAL_TIME
template <typename Duration,
FMT_ENABLE_IF(detail::has_current_zone<Duration>())>
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
using namespace std::chrono;
using namespace fmt_detail;
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
}
#endif
/** /**
* Converts given time since epoch as `std::time_t` value into calendar time, * Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
@@ -563,7 +652,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Add ASCII '0' to each digit byte and insert separators. // Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr size_t len = 8; constexpr const size_t len = 8;
if (const_check(is_big_endian())) { if (const_check(is_big_endian())) {
char tmp[len]; char tmp[len];
std::memcpy(tmp, &digits, len); std::memcpy(tmp, &digits, len);
@@ -822,14 +911,7 @@ template <typename Derived> struct null_chrono_spec_handler {
FMT_CONSTEXPR void on_tz_name() { unsupported(); } FMT_CONSTEXPR void on_tz_name() { unsupported(); }
}; };
class tm_format_checker : public null_chrono_spec_handler<tm_format_checker> { struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
private:
bool has_timezone_ = false;
public:
constexpr explicit tm_format_checker(bool has_timezone)
: has_timezone_(has_timezone) {}
FMT_NORETURN inline void unsupported() { FMT_NORETURN inline void unsupported() {
FMT_THROW(format_error("no format")); FMT_THROW(format_error("no format"));
} }
@@ -867,12 +949,8 @@ class tm_format_checker : public null_chrono_spec_handler<tm_format_checker> {
FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_24_hour_time() {}
FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_iso_time() {}
FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_am_pm() {}
FMT_CONSTEXPR void on_utc_offset(numeric_system) { FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
if (!has_timezone_) FMT_THROW(format_error("no timezone")); FMT_CONSTEXPR void on_tz_name() {}
}
FMT_CONSTEXPR void on_tz_name() {
if (!has_timezone_) FMT_THROW(format_error("no timezone"));
}
}; };
inline auto tm_wday_full_name(int wday) -> const char* { inline auto tm_wday_full_name(int wday) -> const char* {
@@ -902,27 +980,24 @@ inline auto tm_mon_short_name(int mon) -> const char* {
} }
template <typename T, typename = void> template <typename T, typename = void>
struct has_tm_gmtoff : std::false_type {}; struct has_member_data_tm_gmtoff : std::false_type {};
template <typename T> template <typename T>
struct has_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type {}; struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
: std::true_type {};
template <typename T, typename = void> struct has_tm_zone : std::false_type {}; template <typename T, typename = void>
struct has_member_data_tm_zone : std::false_type {};
template <typename T> template <typename T>
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {}; struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
: std::true_type {};
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)> inline void tzset_once() {
auto set_tm_zone(T& time, char* tz) -> bool { static bool init = []() {
time.tm_zone = tz; using namespace fmt_detail;
return true; _tzset();
} return false;
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)> }();
auto set_tm_zone(T&, char*) -> bool { ignore_unused(init);
return false;
}
inline auto utc() -> char* {
static char tz[] = "UTC";
return tz;
} }
// Converts value to Int and checks that it's in the range [0, upper). // Converts value to Int and checks that it's in the range [0, upper).
@@ -930,7 +1005,7 @@ template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline auto to_nonnegative_int(T value, Int upper) -> Int { inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (!std::is_unsigned<Int>::value && if (!std::is_unsigned<Int>::value &&
(value < 0 || to_unsigned(value) > to_unsigned(upper))) { (value < 0 || to_unsigned(value) > to_unsigned(upper))) {
FMT_THROW(format_error("chrono value is out of range")); FMT_THROW(fmt::format_error("chrono value is out of range"));
} }
return static_cast<Int>(value); return static_cast<Int>(value);
} }
@@ -1015,7 +1090,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
// Format subseconds which are given as a floating point type with an // Format subseconds which are given as a floating point type with an
// appropriate number of digits. We cannot pass the Duration here, as we // appropriate number of digits. We cannot pass the Duration here, as we
// explicitly need to pass the Rep value in the duration_formatter. // explicitly need to pass the Rep value in the chrono_formatter.
template <typename Duration> template <typename Duration>
void write_floating_seconds(memory_buffer& buf, Duration duration, void write_floating_seconds(memory_buffer& buf, Duration duration,
int num_fractional_digits = -1) { int num_fractional_digits = -1) {
@@ -1049,7 +1124,7 @@ class tm_writer {
static constexpr int days_per_week = 7; static constexpr int days_per_week = 7;
const std::locale& loc_; const std::locale& loc_;
bool is_classic_; const bool is_classic_;
OutputIt out_; OutputIt out_;
const Duration* subsecs_; const Duration* subsecs_;
const std::tm& tm_; const std::tm& tm_;
@@ -1085,8 +1160,8 @@ class tm_writer {
} }
auto tm_hour12() const noexcept -> int { auto tm_hour12() const noexcept -> int {
auto h = tm_hour(); const auto h = tm_hour();
auto z = h < 12 ? h : h - 12; const auto z = h < 12 ? h : h - 12;
return z == 0 ? 12 : z; return z == 0 ? 12 : z;
} }
@@ -1102,11 +1177,11 @@ class tm_writer {
// Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
auto iso_year_weeks(long long curr_year) const noexcept -> int { auto iso_year_weeks(long long curr_year) const noexcept -> int {
auto prev_year = curr_year - 1; const auto prev_year = curr_year - 1;
auto curr_p = const auto curr_p =
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
days_per_week; days_per_week;
auto prev_p = const auto prev_p =
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
days_per_week; days_per_week;
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
@@ -1116,15 +1191,15 @@ class tm_writer {
days_per_week; days_per_week;
} }
auto tm_iso_week_year() const noexcept -> long long { auto tm_iso_week_year() const noexcept -> long long {
auto year = tm_year(); const auto year = tm_year();
auto w = iso_week_num(tm_yday(), tm_wday()); const auto w = iso_week_num(tm_yday(), tm_wday());
if (w < 1) return year - 1; if (w < 1) return year - 1;
if (w > iso_year_weeks(year)) return year + 1; if (w > iso_year_weeks(year)) return year + 1;
return year; return year;
} }
auto tm_iso_week_of_year() const noexcept -> int { auto tm_iso_week_of_year() const noexcept -> int {
auto year = tm_year(); const auto year = tm_year();
auto w = iso_week_num(tm_yday(), tm_wday()); const auto w = iso_week_num(tm_yday(), tm_wday());
if (w < 1) return iso_year_weeks(year - 1); if (w < 1) return iso_year_weeks(year - 1);
if (w > iso_year_weeks(year)) return 1; if (w > iso_year_weeks(year)) return 1;
return w; return w;
@@ -1161,8 +1236,9 @@ class tm_writer {
uint32_or_64_or_128_t<long long> n = to_unsigned(year); uint32_or_64_or_128_t<long long> n = to_unsigned(year);
const int num_digits = count_digits(n); const int num_digits = count_digits(n);
if (negative && pad == pad_type::zero) *out_++ = '-'; if (negative && pad == pad_type::zero) *out_++ = '-';
if (width > num_digits) if (width > num_digits) {
out_ = detail::write_padding(out_, pad, width - num_digits); out_ = detail::write_padding(out_, pad, width - num_digits);
}
if (negative && pad != pad_type::zero) *out_++ = '-'; if (negative && pad != pad_type::zero) *out_++ = '-';
out_ = format_decimal<Char>(out_, n, num_digits); out_ = format_decimal<Char>(out_, n, num_digits);
} }
@@ -1183,22 +1259,45 @@ class tm_writer {
write2(static_cast<int>(offset % 60)); write2(static_cast<int>(offset % 60));
} }
template <typename T, FMT_ENABLE_IF(has_tm_gmtoff<T>::value)> template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
void format_utc_offset(const T& tm, numeric_system ns) { void format_utc_offset_impl(const T& tm, numeric_system ns) {
write_utc_offset(tm.tm_gmtoff, ns); write_utc_offset(tm.tm_gmtoff, ns);
} }
template <typename T, FMT_ENABLE_IF(!has_tm_gmtoff<T>::value)> template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
void format_utc_offset(const T&, numeric_system ns) { void format_utc_offset_impl(const T& tm, numeric_system ns) {
write_utc_offset(0, ns); #if defined(_WIN32) && defined(_UCRT)
tzset_once();
long offset = 0;
_get_timezone(&offset);
if (tm.tm_isdst) {
long dstbias = 0;
_get_dstbias(&dstbias);
offset += dstbias;
}
write_utc_offset(-offset, ns);
#else
if (ns == numeric_system::standard) return format_localized('z');
// Extract timezone offset from timezone conversion functions.
std::tm gtm = tm;
std::time_t gt = std::mktime(&gtm);
std::tm ltm = gmtime(gt);
std::time_t lt = std::mktime(&ltm);
long long offset = gt - lt;
write_utc_offset(offset, ns);
#endif
} }
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)> template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
void format_tz_name(const T& tm) { void format_tz_name_impl(const T& tm) {
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_); if (is_classic_)
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
else
format_localized('Z');
} }
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)> template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
void format_tz_name(const T&) { void format_tz_name_impl(const T&) {
out_ = std::copy_n(utc(), 3, out_); format_localized('Z');
} }
void format_localized(char format, char modifier = 0) { void format_localized(char format, char modifier = 0) {
@@ -1309,8 +1408,8 @@ class tm_writer {
out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_); out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
} }
void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); } void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
void on_tz_name() { format_tz_name(tm_); } void on_tz_name() { format_tz_name_impl(tm_); }
void on_year(numeric_system ns, pad_type pad) { void on_year(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard) if (is_classic_ || ns == numeric_system::standard)
@@ -1384,10 +1483,11 @@ class tm_writer {
void on_day_of_year(pad_type pad) { void on_day_of_year(pad_type pad) {
auto yday = tm_yday() + 1; auto yday = tm_yday() + 1;
auto digit1 = yday / 100; auto digit1 = yday / 100;
if (digit1 != 0) if (digit1 != 0) {
write1(digit1); write1(digit1);
else } else {
out_ = detail::write_padding(out_, pad); out_ = detail::write_padding(out_, pad);
}
write2(yday % 100, pad); write2(yday % 100, pad);
} }
@@ -1524,16 +1624,18 @@ template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)> FMT_ENABLE_IF(std::is_integral<Rep>::value)>
inline auto get_milliseconds(std::chrono::duration<Rep, Period> d) inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
-> std::chrono::duration<Rep, std::milli> { -> std::chrono::duration<Rep, std::milli> {
// This may overflow and/or the result may not fit in the target type. // this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST #if FMT_SAFE_DURATION_CAST
using common_seconds_type = using CommonSecondsType =
typename std::common_type<decltype(d), std::chrono::seconds>::type; typename std::common_type<decltype(d), std::chrono::seconds>::type;
auto d_as_common = detail::duration_cast<common_seconds_type>(d); const auto d_as_common = detail::duration_cast<CommonSecondsType>(d);
auto d_as_whole_seconds = const auto d_as_whole_seconds =
detail::duration_cast<std::chrono::seconds>(d_as_common); detail::duration_cast<std::chrono::seconds>(d_as_common);
// This conversion should be nonproblematic. // this conversion should be nonproblematic
auto diff = d_as_common - d_as_whole_seconds; const auto diff = d_as_common - d_as_whole_seconds;
auto ms = detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff); const auto ms =
detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
return ms; return ms;
#else #else
auto s = detail::duration_cast<std::chrono::seconds>(d); auto s = detail::duration_cast<std::chrono::seconds>(d);
@@ -1594,13 +1696,8 @@ class get_locale {
public: public:
inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
if (!localized) return; if (localized)
ignore_unused(loc); ::new (&locale_) std::locale(loc.template get<std::locale>());
::new (&locale_) std::locale(
#if FMT_USE_LOCALE
loc.template get<std::locale>()
#endif
);
} }
inline ~get_locale() { inline ~get_locale() {
if (has_locale_) locale_.~locale(); if (has_locale_) locale_.~locale();
@@ -1610,28 +1707,32 @@ class get_locale {
} }
}; };
template <typename Char, typename Rep, typename Period> template <typename FormatContext, typename OutputIt, typename Rep,
struct duration_formatter { typename Period>
using iterator = basic_appender<Char>; struct chrono_formatter {
iterator out; FormatContext& context;
OutputIt out;
int precision;
bool localized = false;
// rep is unsigned to avoid overflow. // rep is unsigned to avoid overflow.
using rep = using rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int), conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged<Rep>::type>; unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
rep val; rep val;
int precision;
locale_ref locale;
bool localized = false;
using seconds = std::chrono::duration<rep>; using seconds = std::chrono::duration<rep>;
seconds s; seconds s;
using milliseconds = std::chrono::duration<rep, std::milli>; using milliseconds = std::chrono::duration<rep, std::milli>;
bool negative; bool negative;
using tm_writer_type = tm_writer<iterator, Char>; using char_type = typename FormatContext::char_type;
using tm_writer_type = tm_writer<OutputIt, char_type>;
duration_formatter(iterator o, std::chrono::duration<Rep, Period> d, chrono_formatter(FormatContext& ctx, OutputIt o,
locale_ref loc) std::chrono::duration<Rep, Period> d)
: out(o), val(static_cast<rep>(d.count())), locale(loc), negative(false) { : context(ctx),
out(o),
val(static_cast<rep>(d.count())),
negative(false) {
if (d.count() < 0) { if (d.count() < 0) {
val = 0 - val; val = 0 - val;
negative = true; negative = true;
@@ -1645,16 +1746,19 @@ struct duration_formatter {
// returns true if nan or inf, writes to out. // returns true if nan or inf, writes to out.
auto handle_nan_inf() -> bool { auto handle_nan_inf() -> bool {
if (isfinite(val)) return false; if (isfinite(val)) {
return false;
}
if (isnan(val)) { if (isnan(val)) {
write_nan(); write_nan();
return true; return true;
} }
// must be +-inf // must be +-inf
if (val > 0) if (val > 0) {
std::copy_n("inf", 3, out); write_pinf();
else } else {
std::copy_n("-inf", 4, out); write_ninf();
}
return true; return true;
} }
@@ -1682,9 +1786,10 @@ struct duration_formatter {
} }
void write_sign() { void write_sign() {
if (!negative) return; if (negative) {
*out++ = '-'; *out++ = '-';
negative = false; negative = false;
}
} }
void write(Rep value, int width, pad_type pad = pad_type::zero) { void write(Rep value, int width, pad_type pad = pad_type::zero) {
@@ -1696,22 +1801,24 @@ struct duration_formatter {
if (width > num_digits) { if (width > num_digits) {
out = detail::write_padding(out, pad, width - num_digits); out = detail::write_padding(out, pad, width - num_digits);
} }
out = format_decimal<Char>(out, n, num_digits); out = format_decimal<char_type>(out, n, num_digits);
} }
void write_nan() { std::copy_n("nan", 3, out); } void write_nan() { std::copy_n("nan", 3, out); }
void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); }
template <typename Callback, typename... Args> template <typename Callback, typename... Args>
void format_tm(const tm& time, Callback cb, Args... args) { void format_tm(const tm& time, Callback cb, Args... args) {
if (isnan(val)) return write_nan(); if (isnan(val)) return write_nan();
get_locale loc(localized, locale); get_locale loc(localized, context.locale());
auto w = tm_writer_type(loc, out, time); auto w = tm_writer_type(loc, out, time);
(w.*cb)(args...); (w.*cb)(args...);
out = w.out(); out = w.out();
} }
void on_text(const Char* begin, const Char* end) { void on_text(const char_type* begin, const char_type* end) {
copy<Char>(begin, end, out); copy<char_type>(begin, end, out);
} }
// These are not implemented because durations don't have date information. // These are not implemented because durations don't have date information.
@@ -1781,12 +1888,13 @@ struct duration_formatter {
write_floating_seconds(buf, std::chrono::duration<rep, Period>(val), write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
precision); precision);
if (negative) *out++ = '-'; if (negative) *out++ = '-';
if (buf.size() < 2 || buf[1] == '.') if (buf.size() < 2 || buf[1] == '.') {
out = detail::write_padding(out, pad); out = detail::write_padding(out, pad);
out = copy<Char>(buf.begin(), buf.end(), out); }
out = copy<char_type>(buf.begin(), buf.end(), out);
} else { } else {
write(second(), 2, pad); write(second(), 2, pad);
write_fractional_seconds<Char>( write_fractional_seconds<char_type>(
out, std::chrono::duration<rep, Period>(val), precision); out, std::chrono::duration<rep, Period>(val), precision);
} }
return; return;
@@ -1828,10 +1936,12 @@ struct duration_formatter {
void on_duration_value() { void on_duration_value() {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
write_sign(); write_sign();
out = format_duration_value<Char>(out, val, precision); out = format_duration_value<char_type>(out, val, precision);
} }
void on_duration_unit() { out = format_duration_unit<Char, Period>(out); } void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
}; };
} // namespace detail } // namespace detail
@@ -1901,11 +2011,12 @@ class year_month_day {
constexpr auto month() const noexcept -> fmt::month { return month_; } constexpr auto month() const noexcept -> fmt::month { return month_; }
constexpr auto day() const noexcept -> fmt::day { return day_; } constexpr auto day() const noexcept -> fmt::day { return day_; }
}; };
#endif // __cpp_lib_chrono >= 201907 #endif
template <typename Char> template <typename Char>
struct formatter<weekday, Char> : private formatter<std::tm, Char> { struct formatter<weekday, Char> : private formatter<std::tm, Char> {
private: private:
bool localized_ = false;
bool use_tm_formatter_ = false; bool use_tm_formatter_ = false;
public: public:
@@ -1913,7 +2024,8 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') { if (it != end && *it == 'L') {
++it; ++it;
this->set_localized(); localized_ = true;
return it;
} }
use_tm_formatter_ = it != end && *it != '}'; use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it; return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
@@ -1924,7 +2036,7 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
auto time = std::tm(); auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding()); time.tm_wday = static_cast<int>(wd.c_encoding());
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx); if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(this->localized(), ctx.locale()); detail::get_locale loc(localized_, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time); auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_weekday(); w.on_abbr_weekday();
return w.out(); return w.out();
@@ -1958,6 +2070,7 @@ struct formatter<day, Char> : private formatter<std::tm, Char> {
template <typename Char> template <typename Char>
struct formatter<month, Char> : private formatter<std::tm, Char> { struct formatter<month, Char> : private formatter<std::tm, Char> {
private: private:
bool localized_ = false;
bool use_tm_formatter_ = false; bool use_tm_formatter_ = false;
public: public:
@@ -1965,7 +2078,8 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') { if (it != end && *it == 'L') {
++it; ++it;
this->set_localized(); localized_ = true;
return it;
} }
use_tm_formatter_ = it != end && *it != '}'; use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it; return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
@@ -1976,7 +2090,7 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
auto time = std::tm(); auto time = std::tm();
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1; time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx); if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(this->localized(), ctx.locale()); detail::get_locale loc(localized_, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time); auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_month(); w.on_abbr_month();
return w.out(); return w.out();
@@ -2040,6 +2154,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
format_specs specs_; format_specs specs_;
detail::arg_ref<Char> width_ref_; detail::arg_ref<Char> width_ref_;
detail::arg_ref<Char> precision_ref_; detail::arg_ref<Char> precision_ref_;
bool localized_ = false;
basic_string_view<Char> fmt_; basic_string_view<Char> fmt_;
public: public:
@@ -2062,7 +2177,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
} }
if (it != end && *it == 'L') { if (it != end && *it == 'L') {
specs_.set_localized(); localized_ = true;
++it; ++it;
} }
end = detail::parse_chrono_format(it, end, checker); end = detail::parse_chrono_format(it, end, checker);
@@ -2089,10 +2204,11 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
out = detail::format_duration_value<Char>(out, d.count(), precision); out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out); detail::format_duration_unit<Char, Period>(out);
} else { } else {
auto f = using chrono_formatter =
detail::duration_formatter<Char, Rep, Period>(out, d, ctx.locale()); detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
auto f = chrono_formatter(ctx, out, d);
f.precision = precision; f.precision = precision;
f.localized = specs_.localized(); f.localized = localized_;
detail::parse_chrono_format(begin, end, f); detail::parse_chrono_format(begin, end, f);
} }
return detail::write( return detail::write(
@@ -2104,15 +2220,30 @@ template <typename Char> struct formatter<std::tm, Char> {
private: private:
format_specs specs_; format_specs specs_;
detail::arg_ref<Char> width_ref_; detail::arg_ref<Char> width_ref_;
basic_string_view<Char> fmt_ =
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
protected: protected:
auto localized() const -> bool { return specs_.localized(); } basic_string_view<Char> fmt_;
FMT_CONSTEXPR void set_localized() { specs_.set_localized(); }
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, bool has_timezone) template <typename Duration, typename FormatContext>
-> const Char* { auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w =
detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it; if (it == end || *it == '}') return it;
@@ -2125,41 +2256,12 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it == end) return it; if (it == end) return it;
} }
if (*it == 'L') { end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
specs_.set_localized();
++it;
}
end = detail::parse_chrono_format(it, end,
detail::tm_format_checker(has_timezone));
// Replace the default format string only if the new spec is not empty. // Replace the default format string only if the new spec is not empty.
if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
return end; return end;
} }
template <typename Duration, typename FormatContext>
auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs);
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, detail::has_tm_gmtoff<std::tm>::value);
}
template <typename FormatContext> template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) const auto format(const std::tm& tm, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
@@ -2167,11 +2269,10 @@ template <typename Char> struct formatter<std::tm, Char> {
} }
}; };
// DEPRECATED! Reversed order of template parameters.
template <typename Char, typename Duration> template <typename Char, typename Duration>
struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> { struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { FMT_CONSTEXPR formatter() {
return this->do_parse(ctx, true); this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
} }
template <typename FormatContext> template <typename FormatContext>
@@ -2182,7 +2283,6 @@ struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> {
if (detail::const_check( if (detail::const_check(
period::num == 1 && period::den == 1 && period::num == 1 && period::den == 1 &&
!std::is_floating_point<typename Duration::rep>::value)) { !std::is_floating_point<typename Duration::rep>::value)) {
detail::set_tm_zone(tm, detail::utc());
return formatter<std::tm, Char>::format(tm, ctx); return formatter<std::tm, Char>::format(tm, ctx);
} }
Duration epoch = val.time_since_epoch(); Duration epoch = val.time_since_epoch();
@@ -2190,13 +2290,11 @@ struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> {
epoch - detail::duration_cast<std::chrono::seconds>(epoch)); epoch - detail::duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) { if (subsecs.count() < 0) {
auto second = detail::duration_cast<Duration>(std::chrono::seconds(1)); auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
if (tm.tm_sec != 0) { if (tm.tm_sec != 0)
--tm.tm_sec; --tm.tm_sec;
} else { else
tm = gmtime(val - second); tm = gmtime(val - second);
detail::set_tm_zone(tm, detail::utc()); subsecs += detail::duration_cast<Duration>(std::chrono::seconds(1));
}
subsecs += second;
} }
return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs); return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
} }
@@ -2214,29 +2312,23 @@ struct formatter<utc_time<Duration>, Char>
}; };
template <typename Duration, typename Char> template <typename Duration, typename Char>
struct formatter<local_time<Duration>, Char> struct formatter<local_time<Duration>, Char> : formatter<std::tm, Char> {
: private formatter<std::tm, Char> { FMT_CONSTEXPR formatter() {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
return this->do_parse(ctx, false);
} }
template <typename FormatContext> template <typename FormatContext>
auto format(local_time<Duration> val, FormatContext& ctx) const auto format(local_time<Duration> val, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto time_since_epoch = val.time_since_epoch();
auto seconds_since_epoch =
detail::duration_cast<std::chrono::seconds>(time_since_epoch);
// Use gmtime to prevent time zone conversion since local_time has an
// unspecified time zone.
std::tm t = gmtime(seconds_since_epoch.count());
using period = typename Duration::period; using period = typename Duration::period;
if (period::num == 1 && period::den == 1 && if (period::num == 1 && period::den == 1 &&
!std::is_floating_point<typename Duration::rep>::value) { !std::is_floating_point<typename Duration::rep>::value) {
return formatter<std::tm, Char>::format(t, ctx); return formatter<std::tm, Char>::format(localtime(val), ctx);
} }
auto subsecs = auto epoch = val.time_since_epoch();
detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch); auto subsecs = detail::duration_cast<Duration>(
return formatter<std::tm, Char>::do_format(t, ctx, &subsecs); epoch - detail::duration_cast<std::chrono::seconds>(epoch));
return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
} }
}; };

View File

@@ -190,11 +190,11 @@ enum class emphasis : uint8_t {
// rgb is a struct for red, green and blue colors. // rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip. // Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb { struct rgb {
constexpr rgb() : r(0), g(0), b(0) {} FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
constexpr rgb(uint32_t hex) FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
constexpr rgb(color hex) FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), : r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {} b(uint32_t(hex) & 0xFF) {}
@@ -205,135 +205,97 @@ struct rgb {
namespace detail { namespace detail {
// A bit-packed variant of an RGB color, a terminal color, or unset color. // color is a struct of either a rgb color or a terminal color.
// see text_style for the bit-packing scheme.
struct color_type { struct color_type {
constexpr color_type() noexcept = default; FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
constexpr color_type(color rgb_color) noexcept FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
: value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {} value.rgb_color = static_cast<uint32_t>(rgb_color);
constexpr color_type(rgb rgb_color) noexcept
: color_type(static_cast<color>(
(static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
constexpr color_type(terminal_color term_color) noexcept
: value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
constexpr auto is_terminal_color() const noexcept -> bool {
return (value_ & (1 << 25)) != 0;
} }
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
constexpr auto value() const noexcept -> uint32_t { value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
return value_ & 0xFFFFFF; (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
constexpr color_type(uint32_t value) noexcept : value_(value) {} : is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color);
uint32_t value_ = 0; }
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
}; };
} // namespace detail } // namespace detail
/// A text style consisting of foreground and background colors and emphasis. /// A text style consisting of foreground and background colors and emphasis.
class text_style { class text_style {
// The information is packed as follows:
// ┌──┐
// │ 0│─┐
// │..│ ├── foreground color value
// │23│─┘
// ├──┤
// │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
// │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
// ├──┤
// │26│──── overflow bit, always zero (see below)
// ├──┤
// │27│─┐
// │..│ │
// │50│ │
// ├──┤ │
// │51│ ├── background color (same format as the foreground color)
// │52│ │
// ├──┤ │
// │53│─┘
// ├──┤
// │54│─┐
// │..│ ├── emphases
// │61│─┘
// ├──┤
// │62│─┬── unused
// │63│─┘
// └──┘
// The overflow bits are there to make operator|= efficient.
// When ORing, we must throw if, for either the foreground or background,
// one style specifies a terminal color and the other specifies any color
// (terminal or RGB); in other words, if one discriminator is 11 and the
// other is 11 or 01.
//
// We do that check by adding the styles. Consider what adding does to each
// possible pair of discriminators:
// 00 + 00 = 000
// 01 + 00 = 001
// 11 + 00 = 011
// 01 + 01 = 010
// 11 + 01 = 100 (!!)
// 11 + 11 = 110 (!!)
// In the last two cases, the ones we want to catch, the third bit——the
// overflow bit——is set. Bingo.
//
// We must take into account the possible carry bit from the bits
// before the discriminator. The only potentially problematic case is
// 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
// bit is impossible in that case, because 00 (unset color) means the
// 24 bits that precede the discriminator are all zero.
//
// This test can be applied to both colors simultaneously.
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: style_(static_cast<uint64_t>(em) << 54) {} : set_foreground_color(), set_background_color(), ems(em) {}
FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) if (!set_foreground_color) {
report_error("can't OR a terminal color"); set_foreground_color = rhs.set_foreground_color;
style_ |= rhs.style_; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this; return *this;
} }
friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
-> text_style { -> text_style {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
return style_ == rhs.style_;
}
FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
return !(*this == rhs);
}
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return (style_ & (1 << 24)) != 0; return set_foreground_color;
} }
FMT_CONSTEXPR auto has_background() const noexcept -> bool { FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return (style_ & (1ULL << 51)) != 0; return set_background_color;
} }
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return (style_ >> 54) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return style_ & 0x3FFFFFF; return foreground_color;
} }
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return (style_ >> 27) & 0x3FFFFFF; return background_color;
} }
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return static_cast<emphasis>(style_ >> 54); return ems;
} }
private: private:
FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) noexcept
: set_foreground_color(), set_background_color(), ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style; -> text_style;
@@ -341,19 +303,23 @@ class text_style {
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style; -> text_style;
uint64_t style_ = 0; detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
}; };
/// Creates a text style from the foreground (text) color. /// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style { -> text_style {
return foreground.value_; return text_style(true, foreground);
} }
/// Creates a text style from the background color. /// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style { -> text_style {
return static_cast<uint64_t>(background.value_) << 27; return text_style(false, background);
} }
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
@@ -368,35 +334,37 @@ template <typename Char> struct ansi_color_escape {
const char* esc) noexcept { const char* esc) 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_terminal_color()) { if (!text_color.is_rgb) {
bool is_background = esc == string_view("\x1b[48;2;"); bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value(); 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;
buffer[size++] = static_cast<Char>('\x1b'); size_t index = 0;
buffer[size++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) { if (value >= 100u) {
buffer[size++] = static_cast<Char>('1'); buffer[index++] = static_cast<Char>('1');
value %= 100u; value %= 100u;
} }
buffer[size++] = static_cast<Char>('0' + value / 10u); buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[size++] = static_cast<Char>('0' + value % 10u); buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[size++] = static_cast<Char>('m'); buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return; return;
} }
for (int i = 0; i < 7; i++) { for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]); buffer[i] = static_cast<Char>(esc[i]);
} }
rgb color(text_color.value()); rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';'); to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';'); to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
size = 19; buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {}; uint8_t em_codes[num_emphases] = {};
@@ -409,28 +377,26 @@ template <typename Char> struct ansi_color_escape {
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
buffer[size++] = static_cast<Char>('\x1b'); size_t index = 0;
buffer[size++] = static_cast<Char>('[');
for (size_t i = 0; i < num_emphases; ++i) { for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue; if (!em_codes[i]) continue;
buffer[size++] = static_cast<Char>('0' + em_codes[i]); buffer[index++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>(';'); buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
} }
buffer[index++] = static_cast<Char>(0);
buffer[size - 1] = static_cast<Char>('m');
} }
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR auto end() const noexcept -> const Char* { FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + size; return buffer + basic_string_view<Char>(buffer).size();
} }
private: private:
static constexpr size_t num_emphases = 8; static constexpr size_t num_emphases = 8;
Char buffer[7u + 4u * num_emphases] = {}; Char buffer[7u + 3u * num_emphases + 1u];
size_t size = 0;
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept { char delimiter) noexcept {
@@ -475,26 +441,32 @@ template <typename T> struct styled_arg : view {
}; };
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt, void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) { basic_format_args<buffered_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true;
auto emphasis = make_emphasis<Char>(ts.get_emphasis()); auto emphasis = 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;
auto foreground = make_foreground_color<Char>(ts.get_foreground()); auto foreground = 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;
auto background = make_background_color<Char>(ts.get_background()); auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
vformat_to(buf, fmt, args); vformat_to(buf, fmt, args);
if (ts != text_style()) reset_color<Char>(buf); if (has_style) reset_color<Char>(buf);
} }
} // namespace detail } // namespace detail
inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) {
auto buf = memory_buffer(); auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args); detail::vformat_to(buf, ts, fmt, args);
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
@@ -510,7 +482,8 @@ inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
* "Elapsed time: {0:.2f} seconds", 1.23); * "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename... T> template <typename... T>
void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) { void print(FILE* f, const text_style& ts, format_string<T...> fmt,
T&&... args) {
vprint(f, ts, fmt.str, vargs<T...>{{args...}}); vprint(f, ts, fmt.str, vargs<T...>{{args...}});
} }
@@ -524,11 +497,11 @@ void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
* "Elapsed time: {0:.2f} seconds", 1.23); * "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename... T> template <typename... T>
void print(text_style ts, format_string<T...> fmt, T&&... args) { void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...); return print(stdout, ts, fmt, std::forward<T>(args)...);
} }
inline auto vformat(text_style ts, string_view fmt, format_args args) inline auto vformat(const text_style& ts, string_view fmt, format_args args)
-> std::string { -> std::string {
auto buf = memory_buffer(); auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args); detail::vformat_to(buf, ts, fmt, args);
@@ -548,7 +521,7 @@ inline auto vformat(text_style ts, string_view fmt, format_args args)
* ``` * ```
*/ */
template <typename... T> template <typename... T>
inline auto format(text_style ts, format_string<T...> fmt, T&&... args) inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
-> std::string { -> std::string {
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}}); return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
} }
@@ -556,8 +529,8 @@ inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
/// Formats a string with the given text_style and writes the output to `out`. /// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt, template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
-> OutputIt { format_args args) -> OutputIt {
auto&& buf = detail::get_buffer<char>(out); auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args); detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
@@ -575,8 +548,8 @@ auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
*/ */
template <typename OutputIt, typename... T, template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt, inline auto format_to(OutputIt out, const text_style& ts,
T&&... args) -> OutputIt { format_string<T...> fmt, T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}}); return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
} }

View File

@@ -15,14 +15,15 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
// A compile-time string which is compiled into fast formatting code. // A compile-time string which is compiled into fast formatting code.
class compiled_string {}; FMT_EXPORT class compiled_string {};
template <typename S> template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/** /**
* Converts a string literal `s` into a format string that will be parsed at * 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 * compile time and converted into efficient formatting code. Requires C++17
@@ -40,42 +41,18 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
# define FMT_COMPILE(s) FMT_STRING(s) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
/**
* Converts a string literal into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires support
* for class types in constant template parameters (a C++20 feature).
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* using namespace fmt::literals;
* std::string s = fmt::format("{}"_cf, 42);
*/
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
FMT_END_EXPORT
namespace detail {
template <typename T, typename... Tail> template <typename T, typename... Tail>
constexpr auto first(const T& value, const Tail&...) -> const T& { auto first(const T& value, const Tail&...) -> const T& {
return value; return value;
} }
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... T> struct type_list {}; 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 auto get([[maybe_unused]] const T& first, constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) -> const auto& { [[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;
@@ -107,8 +84,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
} }
template <typename Char, typename... Args> template <typename Char, typename... Args>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name, constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) -> int { type_list<Args...>) {
return get_arg_index_by_name<Args...>(name); return get_arg_index_by_name<Args...>(name);
} }
@@ -128,8 +105,8 @@ template <typename Char> struct text {
basic_string_view<Char> data; basic_string_view<Char> data;
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... T> template <typename OutputIt, typename... Args>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data); return write<Char>(out, data);
} }
}; };
@@ -138,8 +115,8 @@ template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {}; struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char> template <typename Char>
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size) constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
-> text<Char> { size_t size) {
return {{&s[pos], size}}; return {{&s[pos], size}};
} }
@@ -147,8 +124,8 @@ template <typename Char> struct code_unit {
Char value; Char value;
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... T> template <typename OutputIt, typename... Args>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt { constexpr OutputIt format(OutputIt out, const Args&...) const {
*out++ = value; *out++ = value;
return out; return out;
} }
@@ -156,7 +133,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`. // This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args> template <typename T, int N, typename... Args>
constexpr auto get_arg_checked(const Args&... args) -> const T& { constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = detail::get<N>(args...); const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) { if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value; return arg.value;
@@ -169,13 +146,13 @@ template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {}; struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N. // A replacement field that refers to argument N.
template <typename Char, typename V, int N> struct field { template <typename Char, typename T, int N> struct field {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... T> template <typename OutputIt, typename... Args>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { constexpr OutputIt format(OutputIt out, const Args&... args) const {
const V& arg = get_arg_checked<V, N>(args...); const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) { if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg); auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out); return copy<Char>(s.begin(), s.end(), out);
} else { } else {
@@ -193,10 +170,10 @@ template <typename Char> struct runtime_named_field {
basic_string_view<Char> name; basic_string_view<Char> name;
template <typename OutputIt, typename T> template <typename OutputIt, typename T>
constexpr static auto try_format_argument( constexpr static bool try_format_argument(
OutputIt& out, OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool { [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) { if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) { if (arg_name == arg.name) {
out = write<Char>(out, arg.value); out = write<Char>(out, arg.value);
@@ -206,8 +183,8 @@ template <typename Char> struct runtime_named_field {
return false; return false;
} }
template <typename OutputIt, typename... T> template <typename OutputIt, typename... Args>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...); bool found = (try_format_argument(out, name, args) || ...);
if (!found) { if (!found) {
FMT_THROW(format_error("argument with specified name is not found")); FMT_THROW(format_error("argument with specified name is not found"));
@@ -220,17 +197,17 @@ template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {}; struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers. // A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename V, int N> struct spec_field { template <typename Char, typename T, int N> struct spec_field {
using char_type = Char; using char_type = Char;
formatter<V, Char> fmt; formatter<T, Char> fmt;
template <typename OutputIt, typename... T> template <typename OutputIt, typename... Args>
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const constexpr FMT_INLINE OutputIt format(OutputIt out,
-> OutputIt { const Args&... args) const {
const auto& vargs = const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...); fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs); basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<V, N>(args...), ctx); return fmt.format(get_arg_checked<T, N>(args...), ctx);
} }
}; };
@@ -242,8 +219,8 @@ template <typename L, typename R> struct concat {
R rhs; R rhs;
using char_type = typename L::char_type; using char_type = typename L::char_type;
template <typename OutputIt, typename... T> template <typename OutputIt, typename... Args>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...); out = lhs.format(out, args...);
return rhs.format(out, args...); return rhs.format(out, args...);
} }
@@ -253,14 +230,14 @@ template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {}; struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R> template <typename L, typename R>
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> { constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs}; return {lhs, rhs};
} }
struct unknown_format {}; struct unknown_format {};
template <typename Char> template <typename Char>
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t { constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) { for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break; if (str[pos] == '{' || str[pos] == '}') break;
} }
@@ -293,8 +270,8 @@ template <typename T, typename Char> struct parse_specs_result {
enum { manual_indexing_id = -1 }; enum { manual_indexing_id = -1 };
template <typename T, typename Char> template <typename T, typename Char>
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
int next_arg_id) -> parse_specs_result<T, Char> { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id); compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
@@ -308,16 +285,16 @@ template <typename Char> struct arg_id_handler {
arg_id_kind kind; arg_id_kind kind;
arg_ref<Char> arg_id; arg_ref<Char> arg_id;
constexpr auto on_auto() -> int { constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0; return 0;
} }
constexpr auto on_index(int id) -> int { constexpr int on_index(int id) {
kind = arg_id_kind::index; kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr auto on_name(basic_string_view<Char> id) -> int { constexpr int on_name(basic_string_view<Char> id) {
kind = arg_id_kind::name; kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
@@ -456,28 +433,27 @@ FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... T, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type, typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf, FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const T&... args) const Args&... args) {
-> std::basic_string<Char> {
auto s = std::basic_string<Char>(); auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...); cf.format(std::back_inserter(s), args...);
return s; return s;
} }
template <typename OutputIt, typename CompiledFormat, typename... T, template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf, constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const T&... args) -> OutputIt { const Args&... args) {
return cf.format(out, args...); return cf.format(out, args...);
} }
template <typename S, typename... T, template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args) FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
-> std::basic_string<typename S::char_type> { Args&&... args) {
if constexpr (std::is_same<typename S::char_type, char>::value) { if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S()); constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
@@ -490,97 +466,72 @@ FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
} }
} }
} }
constexpr auto compiled = detail::compile<T...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return fmt::format( return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()), static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<T>(args)...); std::forward<Args>(args)...);
} else { } else {
return fmt::format(compiled, std::forward<T>(args)...); return fmt::format(compiled, std::forward<Args>(args)...);
} }
} }
template <typename OutputIt, typename S, typename... T, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt { FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<T...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return fmt::format_to( return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()), out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<T>(args)...); std::forward<Args>(args)...);
} else { } else {
return fmt::format_to(out, compiled, std::forward<T>(args)...); return fmt::format_to(out, compiled, std::forward<Args>(args)...);
} }
} }
#endif #endif
template <typename OutputIt, typename S, typename... T, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits; using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n); auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...); fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename S, typename... T, template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t { FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
auto buf = detail::counting_buffer<>(); auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...); fmt::format_to(appender(buf), fmt, args...);
return buf.count(); return buf.count();
} }
template <typename S, typename... T, template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, T&&... args) { void print(std::FILE* f, const S& fmt, const Args&... args) {
auto buf = memory_buffer(); auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...); fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buf.data(), buf.size()}); detail::print(f, {buf.data(), buf.size()});
} }
template <typename S, typename... T, template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, T&&... args) { void print(const S& fmt, const Args&... args) {
print(stdout, fmt, std::forward<T>(args)...); print(stdout, fmt, args...);
} }
template <size_t N> class static_format_result { #if FMT_USE_NONTYPE_TEMPLATE_ARGS
private: inline namespace literals {
char data[N]; template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
public: }
template <typename S, typename... T, } // namespace literals
FMT_ENABLE_IF(is_compiled_string<S>::value)> #endif
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
}
auto str() const -> fmt::string_view { return {data, N - 1}; }
auto c_str() const -> const char* { return data; }
};
/**
* Formats arguments according to the format string `fmt_str` and produces
* a string of the exact required size at compile time. Both the format string
* and the arguments must be compile-time expressions.
*
* The resulting string can be accessed as a C string via `c_str()` or as
* a `fmt::string_view` via `str()`.
*
* **Example**:
*
* // Produces the static string "42" at compile time.
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
* const char* s = result.c_str();
*/
#define FMT_STATIC_FORMAT(fmt_str, ...) \
fmt::static_format_result< \
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
FMT_COMPILE(fmt_str), __VA_ARGS__)
FMT_END_EXPORT FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -1,4 +1,4 @@
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View File

@@ -22,7 +22,7 @@
#include "format.h" #include "format.h"
#if FMT_USE_LOCALE && !defined(FMT_MODULE) #if FMT_USE_LOCALE
# include <locale> # include <locale>
#endif #endif
@@ -31,44 +31,14 @@
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail {
#ifndef FMT_CUSTOM_ASSERT_FAIL
FMT_FUNC void assert_fail(const char* file, int line, const char* message) { FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when // Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails. // writing to stderr fails.
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort(); abort();
} }
#endif
#if FMT_USE_LOCALE
namespace detail {
using std::locale;
using std::numpunct;
using std::use_facet;
} // namespace detail
#else
namespace detail {
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
} // namespace detail
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
using namespace detail;
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
namespace detail {
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept { string_view message) noexcept {
@@ -109,6 +79,33 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
#if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, locale>::value, "");
}
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
template <typename Char> template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>()); auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
@@ -136,13 +133,14 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
} // namespace detail } // namespace detail
FMT_FUNC void report_error(const char* message) { FMT_FUNC void report_error(const char* message) {
#if FMT_MSC_VERSION || defined(__NVCC__) #if FMT_USE_EXCEPTIONS
// Silence unreachable code warnings in MSVC and NVCC because these // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// are nearly impossible to fix in a generic code. // from MSVC.
volatile bool b = true;
if (!b) return;
#endif
FMT_THROW(format_error(message)); FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#endif
} }
template <typename Locale> typename Locale::id format_facet<Locale>::id; template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -176,11 +174,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
} }
// Compilers should be able to optimize this into the ror instruction. // Compilers should be able to optimize this into the ror instruction.
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31; r &= 31;
return (n >> r) | (n << (32 - r)); return (n >> r) | (n << (32 - r));
} }
FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63; r &= 63;
return (n >> r) | (n << (64 - r)); return (n >> r) | (n << (64 - r));
} }
@@ -214,7 +212,7 @@ inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
return (e * 631305 - 261663) >> 21; return (e * 631305 - 261663) >> 21;
} }
FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { FMT_INLINE_VARIABLE constexpr struct {
uint32_t divisor; uint32_t divisor;
int shift_amount; int shift_amount;
} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@@ -277,7 +275,7 @@ template <> struct cache_accessor<float> {
static auto get_cached_power(int k) noexcept -> uint64_t { static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k, FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range"); "k is out of range");
static constexpr uint64_t pow10_significands[] = { static constexpr const uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -372,7 +370,7 @@ template <> struct cache_accessor<double> {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k, FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range"); "k is out of range");
static constexpr uint128_fallback pow10_significands[] = { static constexpr const uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX #if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad}, {0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1039,7 +1037,7 @@ template <> struct cache_accessor<double> {
#if FMT_USE_FULL_CACHE_DRAGONBOX #if FMT_USE_FULL_CACHE_DRAGONBOX
return pow10_significands[k - float_info<double>::min_k]; return pow10_significands[k - float_info<double>::min_k];
#else #else
static constexpr uint64_t powers_of_5_64[] = { static constexpr const uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1099,7 +1097,7 @@ template <> struct cache_accessor<double> {
return {r.high(), r.low() == 0}; return {r.high(), r.low() == 0};
} }
static auto compute_delta(const cache_entry_type& cache, int beta) noexcept static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
-> uint32_t { -> uint32_t {
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta)); return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
} }
@@ -1151,8 +1149,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
exponent <= case_shorter_interval_left_endpoint_upper_threshold; exponent <= case_shorter_interval_left_endpoint_upper_threshold;
} }
// Remove trailing zeros from n and return the number of zeros removed (float). // Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int { FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
FMT_ASSERT(n != 0, ""); FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd; constexpr uint32_t mod_inv_5 = 0xcccccccd;
@@ -1172,19 +1170,22 @@ FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
return s; return s;
} }
// Removes trailing zeros and returns the number of zeros removed (double). // Removes trailing zeros and returns the number of zeros removed (double)
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int { FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
FMT_ASSERT(n != 0, ""); FMT_ASSERT(n != 0, "");
// This magic number is ceil(2^90 / 10^8).
constexpr uint64_t magic_number = 12379400392853802749ull;
auto nm = umul128(n, magic_number);
// Is n is divisible by 10^8? // Is n is divisible by 10^8?
constexpr uint32_t ten_pow_8 = 100000000u; if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
if ((n % ten_pow_8) == 0) {
// If yes, work with the quotient... // If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(n / ten_pow_8); auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
// ... and use the 32 bit variant of the function // ... and use the 32 bit variant of the function
int num_zeros = remove_trailing_zeros(n32, 8); int s = remove_trailing_zeros(n32, 8);
n = n32; n = n32;
return num_zeros; return s;
} }
// If n is not divisible by 10^8, work with n itself. // If n is not divisible by 10^8, work with n itself.
@@ -1209,7 +1210,7 @@ FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
// The main algorithm for shorter interval case // The main algorithm for shorter interval case
template <typename T> template <typename T>
FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> { FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
decimal_fp<T> ret_value; decimal_fp<T> ret_value;
// Compute k and beta // Compute k and beta
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
@@ -1453,8 +1454,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
auto out = appender(buf); auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}")) if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out}); return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(fmt, parse_format_string(
format_handler<>{parse_context<>(fmt), {out, args, loc}}); fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
} }
template <typename T> struct span { template <typename T> struct span {
@@ -1525,8 +1526,9 @@ template <typename F> class glibc_file : public file_base<F> {
} }
void init_buffer() { void init_buffer() {
if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return; if (this->file_->_IO_write_ptr) return;
// Force buffer initialization by placing and removing a char in a buffer. // Force buffer initialization by placing and removing a char in a buffer.
assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
putc_unlocked(0, this->file_); putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr; --this->file_->_IO_write_ptr;
} }
@@ -1545,11 +1547,10 @@ template <typename F> class glibc_file : public file_base<F> {
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
auto needs_flush() const -> bool { bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false; if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end; char* end = this->file_->_IO_write_end;
auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0); return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
return memchr(end, '\n', static_cast<size_t>(size));
} }
void flush() { fflush_unlocked(this->file_); } void flush() { fflush_unlocked(this->file_); }
@@ -1573,7 +1574,7 @@ template <typename F> class apple_file : public file_base<F> {
void init_buffer() { void init_buffer() {
if (this->file_->_p) return; if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer. // Force buffer initialization by placing and removing a char in a buffer.
if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_); putc_unlocked(0, this->file_);
--this->file_->_p; --this->file_->_p;
++this->file_->_w; ++this->file_->_w;
} }
@@ -1594,7 +1595,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size; this->file_->_w -= size;
} }
auto needs_flush() const -> bool { bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false; if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n', return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w)); to_unsigned(-this->file_->_w));

File diff suppressed because it is too large Load Diff

View File

@@ -29,8 +29,7 @@
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ # if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \ defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \ (!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \ (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
!defined(__wasm__)
# include <fcntl.h> // for O_RDONLY # include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1 # define FMT_USE_FCNTL 1
# else # else
@@ -136,9 +135,10 @@ FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
* **Example**: * **Example**:
* *
* // This throws a system_error with the description * // This throws a system_error with the description
* // cannot open file 'foo': The system cannot find the file specified. * // cannot open file 'madeup': The system cannot find the file
* // or similar (system message may vary) if the file doesn't exist. * specified.
* const char *filename = "foo"; * // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT(); * LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ); * HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) { * if (file == HFILE_ERROR) {
@@ -364,17 +364,17 @@ FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
/// A fast buffered output stream for writing from a single thread. Writing from /// A fast buffered output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race. /// multiple threads without external synchronization may result in a data race.
class ostream : private detail::buffer<char> { class FMT_API ostream : private detail::buffer<char> {
private: private:
file file_; file file_;
FMT_API ostream(cstring_view path, const detail::ostream_params& params); ostream(cstring_view path, const detail::ostream_params& params);
FMT_API static void grow(buffer<char>& buf, size_t); static void grow(buffer<char>& buf, size_t);
public: public:
FMT_API ostream(ostream&& other) noexcept; ostream(ostream&& other) noexcept;
FMT_API ~ostream(); ~ostream();
operator writer() { operator writer() {
detail::buffer<char>& buf = *this; detail::buffer<char>& buf = *this;

View File

@@ -33,8 +33,8 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// Generate a unique explicit instantiation in every translation unit using a // Generate a unique explicit instantion in every translation unit using a tag
// tag type in an anonymous namespace. // type in an anonymous namespace.
namespace { namespace {
struct file_access_tag {}; struct file_access_tag {};
} // namespace } // namespace
@@ -158,8 +158,7 @@ void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
FMT_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) { void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, FMT_STRING("{}\n"), fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
fmt::format(fmt, std::forward<T>(args)...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#ifndef FMT_MODULE #ifndef FMT_MODULE
# include <algorithm> // std::find # include <algorithm> // std::max
# include <limits> // std::numeric_limits # include <limits> // std::numeric_limits
#endif #endif
@@ -18,6 +18,10 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context { template <typename Char> class basic_printf_context {
private: private:
basic_appender<Char> out_; basic_appender<Char> out_;
@@ -29,6 +33,8 @@ template <typename Char> class basic_printf_context {
public: public:
using char_type = Char; using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 }; enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are /// Constructs a `printf_context` object. References to the arguments are
@@ -40,7 +46,7 @@ template <typename Char> class basic_printf_context {
auto out() -> basic_appender<Char> { return out_; } auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {} void advance_to(basic_appender<Char>) {}
auto locale() -> locale_ref { return {}; } auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> { auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id); return args_.get(id);
@@ -68,9 +74,10 @@ inline auto find<false, char>(const char* first, const char* last, char value,
// 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.
template <bool IS_SIGNED> struct int_checker { template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool { template <typename T> static auto fits_in_int(T value) -> bool {
return value <= to_unsigned(max_value<int>()); unsigned max = to_unsigned(max_value<int>());
return value <= max;
} }
inline static auto fits_in_int(bool) -> bool { return true; } inline static auto fits_in_int(bool) -> bool { return true; }
}; };
@@ -88,7 +95,7 @@ struct printf_precision_handler {
auto operator()(T value) -> int { auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big"); report_error("number is too big");
return max_of(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -403,9 +410,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
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);
auto arg = context.arg(arg_index); return detail::get_arg(context, arg_index);
if (!arg) report_error("argument not found");
return arg;
}; };
const Char* start = parse_ctx.begin(); const Char* start = parse_ctx.begin();
@@ -566,19 +571,15 @@ inline auto vsprintf(basic_string_view<Char> fmt,
* *
* std::string message = fmt::sprintf("The answer is %d", 42); * std::string message = fmt::sprintf("The answer is %d", 42);
*/ */
template <typename... T> template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(string_view fmt, const T&... args) -> std::string { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(fmt, make_printf_args(args...)); return vsprintf(detail::to_string_view(fmt),
} fmt::make_format_args<basic_printf_context<Char>>(args...));
template <typename... T>
FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
-> std::wstring {
return vsprintf(fmt, make_printf_args<wchar_t>(args...));
} }
template <typename Char> template <typename Char>
auto vfprintf(std::FILE* f, basic_string_view<Char> fmt, inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int { typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args); detail::vprintf(buf, fmt, args);
size_t size = buf.size(); size_t size = buf.size();
@@ -595,14 +596,17 @@ auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
* *
* fmt::fprintf(stderr, "Don't %s!", "panic"); * fmt::fprintf(stderr, "Don't %s!", "panic");
*/ */
template <typename... T> template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args(args...)); return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
} }
template <typename... T>
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt, template <typename Char>
const T&... args) -> int { FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...)); typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
} }
/** /**
@@ -617,6 +621,11 @@ template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int { inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...)); return vfprintf(stdout, fmt, make_printf_args(args...));
} }
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -11,6 +11,7 @@
#ifndef FMT_MODULE #ifndef FMT_MODULE
# include <initializer_list> # include <initializer_list>
# include <iterator> # include <iterator>
# include <string>
# include <tuple> # include <tuple>
# include <type_traits> # include <type_traits>
# include <utility> # include <utility>
@@ -18,13 +19,6 @@
#include "format.h" #include "format.h"
#if FMT_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
# define FMT_LIFETIMEBOUND [[clang::lifetimebound]]
#else
# define FMT_LIFETIMEBOUND
#endif
FMT_PRAGMA_CLANG(diagnostic error "-Wreturn-stack-address")
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT FMT_EXPORT
@@ -37,7 +31,7 @@ template <typename T> class is_map {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
@@ -46,16 +40,17 @@ template <typename T> class is_set {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
}; };
// C array overload // C array overload
template <typename T, size_t N> template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* { auto range_begin(const T (&arr)[N]) -> const T* {
return arr; return arr;
} }
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* { template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N; return arr + N;
} }
@@ -125,7 +120,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
@@ -159,7 +154,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value> template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ { class is_tuple_formattable_ {
public: public:
static constexpr bool value = false; static constexpr const bool value = false;
}; };
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is> template <size_t... Is>
@@ -175,7 +170,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{})); C>::value)...>{}));
public: public:
static constexpr bool value = static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value; decltype(check(tuple_index_sequence<T>{}))::value;
}; };
@@ -213,7 +208,7 @@ template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>; using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get; using std::get;
template <typename Tuple, typename Char, size_t... Is> template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>) auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>; -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple } // namespace tuple
@@ -224,7 +219,7 @@ template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>())); using type = decltype(*detail::range_begin(std::declval<R&>()));
}; };
template <typename T, size_t N> struct range_reference_type_impl<T[N]> { template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&; using type = T&;
}; };
@@ -241,6 +236,14 @@ using range_reference_type =
template <typename Range> template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>; using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Formatter>
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
-> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
}
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T> template <typename T>
struct range_format_kind_ struct range_format_kind_
: std::integral_constant<range_format, : std::integral_constant<range_format,
@@ -278,15 +281,14 @@ template <typename FormatContext> struct format_tuple_element {
} // namespace detail } // namespace detail
FMT_EXPORT
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
static constexpr bool value = static constexpr const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
}; };
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable { template <typename T, typename C> struct is_tuple_formattable {
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value; static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
}; };
template <typename Tuple, typename Char> template <typename Tuple, typename Char>
@@ -341,9 +343,8 @@ struct formatter<Tuple, Char,
} }
}; };
FMT_EXPORT
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static constexpr bool value = static constexpr const bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value; detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
}; };
@@ -367,7 +368,6 @@ template <typename P1, typename... Pn>
struct conjunction<P1, Pn...> struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {}; : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void> template <typename T, typename Char, typename Enable = void>
struct range_formatter; struct range_formatter;
@@ -670,8 +670,7 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
} }
}; };
FMT_EXPORT template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple; const Tuple& tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
@@ -686,15 +685,15 @@ template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0 # define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif #endif
template <typename Tuple, typename Char> template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Tuple, Char>, Char, struct formatter<tuple_join_view<Char, Tuple>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> { enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>()); return do_parse(ctx, std::tuple_size<Tuple>());
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const tuple_join_view<Tuple, Char>& value, auto format(const tuple_join_view<Char, Tuple>& value,
FormatContext& ctx) const -> typename FormatContext::iterator { FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>()); return do_format(value, ctx, std::tuple_size<Tuple>());
} }
@@ -726,14 +725,14 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
} }
template <typename FormatContext> template <typename FormatContext>
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx, auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const -> std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
return ctx.out(); return ctx.out();
} }
template <typename FormatContext, size_t N> template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx, auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const -> std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
using std::get; using std::get;
@@ -755,7 +754,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
@@ -775,13 +774,13 @@ struct formatter<
: formatter<detail::all<typename T::container_type>, Char> { : formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>; using all = detail::all<typename T::container_type>;
template <typename FormatContext> template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T { struct getter : T {
static auto get(const T& v) -> all { static auto get(const T& t) -> all {
return {v.*(&getter::c)}; // Access c through the derived class. return {t.*(&getter::c)}; // Access c through the derived class.
} }
}; };
return formatter<all>::format(getter::get(value), ctx); return formatter<all>::format(getter::get(t), ctx);
} }
}; };
@@ -820,13 +819,13 @@ auto join(Range&& r, string_view sep)
* *
* **Example**: * **Example**:
* *
* auto t = std::tuple<int, char>(1, 'a'); * auto t = std::tuple<int, char>{1, 'a'};
* fmt::print("{}", fmt::join(t, ", ")); * fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a * // Output: 1, a
*/ */
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)> template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep) FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<Tuple, char> { -> tuple_join_view<char, Tuple> {
return {tuple, sep}; return {tuple, sep};
} }

View File

@@ -15,13 +15,15 @@
# include <atomic> # include <atomic>
# include <bitset> # include <bitset>
# include <complex> # include <complex>
# include <cstdlib>
# include <exception> # include <exception>
# include <functional> // std::reference_wrapper # include <functional>
# include <memory> # include <memory>
# include <thread> # include <thread>
# include <type_traits> # include <type_traits>
# include <typeinfo> // std::type_info # include <typeinfo>
# include <utility> // std::make_index_sequence # include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. // Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L # if FMT_CPLUSPLUS >= 201703L
@@ -60,26 +62,27 @@
# endif # endif
#endif #endif
#ifdef FMT_CPP_LIB_FILESYSTEM // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
// Use the provided definition. #ifndef FMT_CPP_LIB_FILESYSTEM
#elif defined(__cpp_lib_filesystem) # ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem # define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
#else # else
# define FMT_CPP_LIB_FILESYSTEM 0 # define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif #endif
#ifdef FMT_CPP_LIB_VARIANT #ifndef FMT_CPP_LIB_VARIANT
// Use the provided definition. # ifdef __cpp_lib_variant
#elif defined(__cpp_lib_variant) # define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant # else
#else # define FMT_CPP_LIB_VARIANT 0
# define FMT_CPP_LIB_VARIANT 0 # endif
#endif #endif
FMT_BEGIN_NAMESPACE
namespace detail {
#if FMT_CPP_LIB_FILESYSTEM #if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar> template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p, auto get_path_string(const std::filesystem::path& p,
@@ -108,180 +111,9 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
} }
} }
#endif // FMT_CPP_LIB_FILESYSTEM
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T, typename FormatContext>
auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
-> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
formatter<std::remove_cv_t<T>, Char> underlying;
maybe_set_debug_format(underlying, true);
return underlying.format(v, ctx);
}
#endif
#if FMT_CPP_LIB_VARIANT
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename Variant, typename Char> class is_variant_formattable {
template <size_t... Is>
static auto check(std::index_sequence<Is...>) -> std::conjunction<
is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
public:
static constexpr bool value = decltype(check(
std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
};
#endif // FMT_CPP_LIB_VARIANT
#if FMT_USE_RTTI
inline auto normalize_libcxx_inline_namespaces(string_view demangled_name_view,
char* begin) -> string_view {
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* to = begin + 5; // std::
for (const char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
const char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
return demangled_name_view;
}
template <class OutputIt>
auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out)
-> OutputIt {
const string_view demangled_name(abi_name_view);
for (size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
}
template <typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = normalize_libcxx_inline_namespaces(
demangled_name_ptr.get(), demangled_name_ptr.get());
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<char>(out, demangled_name_view);
# elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE)
return normalize_msvc_abi_name(ti.name(), out);
# elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION)
const string_view demangled_name = ti.name();
std::string name_copy(demangled_name.size(), '\0');
// normalize_msvc_abi_name removes class, struct, union etc that MSVC has in
// front of types
name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()),
name_copy.end());
// normalize_libcxx_inline_namespaces removes the inline __1, __2, etc
// namespaces libc++ uses for ABI versioning On MSVC ABI + libc++
// environments, we need to eliminate both of them.
const string_view normalized_name =
normalize_libcxx_inline_namespaces(name_copy, name_copy.data());
return detail::write_bytes<char>(out, normalized_name);
# else
return detail::write_bytes<char>(out, string_view(ti.name()));
# endif
}
#endif // FMT_USE_RTTI
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr bool value = std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value &&
has_flip<T>::value;
};
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr bool value = true;
};
#endif
template <typename T, typename Enable = void>
struct has_format_as : std::false_type {};
template <typename T>
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Enable = void>
struct has_format_as_member : std::false_type {};
template <typename T>
struct has_format_as_member<
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
: std::true_type {};
} // namespace detail } // namespace detail
template <typename T, typename Deleter> FMT_EXPORT
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
#if FMT_CPP_LIB_FILESYSTEM
template <typename Char> struct formatter<std::filesystem::path, Char> { template <typename Char> struct formatter<std::filesystem::path, Char> {
private: private:
format_specs specs_; format_specs specs_;
@@ -346,20 +178,25 @@ class path : public std::filesystem::path {
auto generic_system_string() const -> std::string { return generic_string(); } auto generic_system_string() const -> std::string { return generic_string(); }
}; };
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM #endif // FMT_CPP_LIB_FILESYSTEM
template <size_t N, typename Char> FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> { : nested_formatter<basic_string_view<Char>, Char> {
private: private:
// This is a functor because C++11 doesn't support generic lambdas. // Functor because C++11 doesn't support generic lambdas.
struct writer { struct writer {
const std::bitset<N>& bs; const std::bitset<N>& bs;
template <typename OutputIt> template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0')); out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out; return out;
} }
}; };
@@ -372,24 +209,37 @@ struct formatter<std::bitset<N>, Char>
} }
}; };
FMT_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional #ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::optional<T>, Char, struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> { std::enable_if_t<is_formattable<T, Char>::value>> {
private: private:
formatter<std::remove_cv_t<T>, Char> underlying_; formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional = static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l', detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{}; '('>{};
static constexpr basic_string_view<Char> none = static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{}; detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public: public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
detail::maybe_set_debug_format(underlying_, true); maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx); return underlying_.parse(ctx);
} }
@@ -405,9 +255,31 @@ struct formatter<std::optional<T>, Char,
return detail::write(out, ')'); return detail::write(out, ')');
} }
}; };
FMT_END_NAMESPACE
#endif // __cpp_lib_optional #endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected #ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char> template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char, struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value || std::enable_if_t<(std::is_void<T>::value ||
@@ -425,18 +297,21 @@ struct formatter<std::expected<T, E>, Char,
if (value.has_value()) { if (value.has_value()) {
out = detail::write<Char>(out, "expected("); out = detail::write<Char>(out, "expected(");
if constexpr (!std::is_void<T>::value) if constexpr (!std::is_void<T>::value)
out = detail::write_escaped_alternative<Char>(out, *value, ctx); out = detail::write_escaped_alternative<Char>(out, *value);
} else { } else {
out = detail::write<Char>(out, "unexpected("); out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx); out = detail::write_escaped_alternative<Char>(out, value.error());
} }
*out++ = ')'; *out++ = ')';
return out; return out;
} }
}; };
FMT_END_NAMESPACE
#endif // __cpp_lib_expected #endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location #ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> { template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
@@ -454,14 +329,45 @@ template <> struct formatter<std::source_location> {
return out; return out;
} }
}; };
FMT_END_NAMESPACE
#endif #endif
#if FMT_CPP_LIB_VARIANT #if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_variant_like { template <typename T>
static constexpr bool value = detail::is_variant_like_<T>::value; using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
}; };
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> { template <typename Char> struct formatter<std::monostate, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin(); return ctx.begin();
@@ -474,11 +380,12 @@ template <typename Char> struct formatter<std::monostate, Char> {
} }
}; };
FMT_EXPORT
template <typename Variant, typename Char> template <typename Variant, typename Char>
struct formatter<Variant, Char, struct formatter<
std::enable_if_t<std::conjunction_v< Variant, Char,
is_variant_like<Variant>, std::enable_if_t<std::conjunction_v<
detail::is_variant_formattable<Variant, Char>>>> { is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin(); return ctx.begin();
} }
@@ -492,7 +399,7 @@ struct formatter<Variant, Char,
FMT_TRY { FMT_TRY {
std::visit( std::visit(
[&](const auto& v) { [&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v, ctx); out = detail::write_escaped_alternative<Char>(out, v);
}, },
value); value);
} }
@@ -503,36 +410,27 @@ struct formatter<Variant, Char,
return out; return out;
} }
}; };
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT #endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::error_code> { template <> struct formatter<std::error_code> {
private: private:
format_specs specs_; format_specs specs_;
detail::arg_ref<char> width_ref_; detail::arg_ref<char> width_ref_;
bool debug_ = false;
public: public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (it == end) return it; if (it == end) return it;
it = detail::parse_align(it, end, specs_); it = detail::parse_align(it, end, specs_);
if (it == end) return it;
char c = *it; char c = *it;
if (it != end && ((c >= '0' && c <= '9') || c == '{')) if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx); it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && *it == 's') {
specs_.set_type(presentation_type::string);
++it;
}
return it; return it;
} }
@@ -542,48 +440,113 @@ template <> struct formatter<std::error_code> {
auto specs = specs_; auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx); ctx);
auto buf = memory_buffer(); memory_buffer buf;
if (specs_.type() == presentation_type::string) { buf.append(string_view(ec.category().name()));
buf.append(ec.message()); buf.push_back(':');
} else { detail::write<char>(appender(buf), ec.value());
buf.append(string_view(ec.category().name())); return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
buf.push_back(':'); specs);
detail::write<char>(appender(buf), ec.value());
}
auto quoted = memory_buffer();
auto str = string_view(buf.data(), buf.size());
if (debug_) {
detail::write_escaped_string<char>(std::back_inserter(quoted), str);
str = string_view(quoted.data(), quoted.size());
}
return detail::write<char>(ctx.out(), str, specs);
} }
}; };
#if FMT_USE_RTTI #if FMT_USE_RTTI
template <> struct formatter<std::type_info> { namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public: public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin(); return ctx.begin();
} }
template <typename Context> template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
return detail::write_demangled_name(ctx.out(), ti); return detail::write_demangled_name<Char>(ctx.out(), ti);
} }
}; };
#endif // FMT_USE_RTTI #endif
template <typename T> FMT_EXPORT
template <typename T, typename Char>
struct formatter< struct formatter<
T, char, T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> { typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private: private:
bool with_typename_ = false; bool with_typename_ = false;
public: public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin(); auto it = ctx.begin();
auto end = ctx.end(); auto end = ctx.end();
if (it == end || *it == '}') return it; if (it == end || *it == '}') return it;
@@ -600,18 +563,47 @@ struct formatter<
auto out = ctx.out(); auto out = ctx.out();
#if FMT_USE_RTTI #if FMT_USE_RTTI
if (with_typename_) { if (with_typename_) {
out = detail::write_demangled_name(out, typeid(ex)); out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':'; *out++ = ':';
*out++ = ' '; *out++ = ' ';
} }
#endif #endif
return detail::write_bytes<char>(out, string_view(ex.what())); return detail::write_bytes<Char>(out, string_view(ex.what()));
} }
}; };
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and // We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N // std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization. // in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char> template <typename BitRef, typename Char>
struct formatter<BitRef, Char, struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>> enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
@@ -623,6 +615,15 @@ struct formatter<BitRef, Char,
} }
}; };
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::atomic<T>, Char, struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>> enable_if_t<is_formattable<T, Char>::value>>
@@ -635,6 +636,7 @@ struct formatter<std::atomic<T>, Char,
}; };
#ifdef __cpp_lib_atomic_flag_test #ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> { struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext> template <typename FormatContext>
@@ -645,6 +647,7 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
}; };
#endif // __cpp_lib_atomic_flag_test #endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> { template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private: private:
detail::dynamic_format_specs<Char> specs_; detail::dynamic_format_specs<Char> specs_;
@@ -707,13 +710,10 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
} }
}; };
FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char, struct formatter<std::reference_wrapper<T>, Char,
// Guard against format_as because reference_wrapper is enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
// implicitly convertible to T&.
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
!detail::has_format_as<T>::value &&
!detail::has_format_as_member<T>::value>>
: formatter<remove_cvref_t<T>, Char> { : formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext> template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
@@ -723,5 +723,4 @@ struct formatter<std::reference_wrapper<T>, Char,
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@@ -55,16 +55,6 @@ inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
#endif #endif
return false; return false;
} }
template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args,
locale_ref loc = {}) {
static_assert(!std::is_same<Char, char>::value, "");
auto out = basic_appender<Char>(buf);
parse_format_string(
fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
}
} // namespace detail } // namespace detail
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
@@ -122,6 +112,14 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}}; return {{s}};
} }
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T> template <typename... T>
constexpr auto make_wformat_args(T&... args) constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) { -> decltype(fmt::make_format_args<wformat_context>(args...)) {
@@ -157,13 +155,13 @@ auto join(std::initializer_list<T> list, wstring_view sep)
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)> template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep) auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<Tuple, wchar_t> { -> tuple_join_view<wchar_t, Tuple> {
return {tuple, sep}; return {tuple, sep};
} }
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt, auto vformat(basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args); detail::vformat_to(buf, fmt, args);
@@ -193,20 +191,24 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
fmt::make_format_args<buffered_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename S, typename Char = detail::format_string_char_t<S>, template <typename Locale, typename S,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> typename Char = detail::format_string_char_t<S>,
inline auto vformat(locale_ref loc, const S& fmt, FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
basic_format_args<buffered_context<Char>> args) detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args, loc); detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
return {buf.data(), buf.size()}; return {buf.data(), buf.size()};
} }
template <typename S, typename... T, template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
inline auto format(locale_ref loc, const S& fmt, T&&... args) detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt), return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
@@ -217,7 +219,7 @@ template <typename OutputIt, typename S,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt, auto vformat_to(OutputIt out, const S& fmt,
basic_format_args<buffered_context<Char>> args) -> OutputIt { typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args); detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
@@ -233,24 +235,27 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffered_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename S, typename OutputIt, typename... Args, template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_locale<Locale>::value&&
inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt, detail::is_exotic_char<Char>::value)>
basic_format_args<buffered_context<Char>> args) inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, loc); vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... T, template <typename Locale, typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>, typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value && bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value> detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args) inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
-> typename std::enable_if<enable, OutputIt>::type { T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt), return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
@@ -259,7 +264,7 @@ template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt, inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits; using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n); auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
@@ -317,7 +322,7 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...)); return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
} }
inline auto vformat(text_style ts, wstring_view fmt, wformat_args args) inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
-> std::wstring { -> std::wstring {
auto buf = wmemory_buffer(); auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args); detail::vformat_to(buf, ts, fmt, args);
@@ -325,11 +330,23 @@ inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
} }
template <typename... T> template <typename... T>
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args) inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring { -> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
} }
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>(); auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buffer, fmt, args);

View File

@@ -10,14 +10,14 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_USE_STD_FORMAT)
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#endif #endif
#include <spdlog/fmt/bundled/chrono.h> #include <spdlog/fmt/bundled/chrono.h>
#else #else
#include <fmt/chrono.h> #include <fmt/chrono.h>
#endif #endif
#endif #endif

View File

@@ -10,14 +10,14 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_USE_STD_FORMAT)
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#endif #endif
#include <spdlog/fmt/bundled/compile.h> #include <spdlog/fmt/bundled/compile.h>
#else #else
#include <fmt/compile.h> #include <fmt/compile.h>
#endif #endif
#endif #endif

View File

@@ -12,15 +12,19 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format #if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format
#include <format> #include <format>
#elif !defined(SPDLOG_FMT_EXTERNAL) #elif !defined(SPDLOG_FMT_EXTERNAL)
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY) #if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#ifndef FMT_USE_WINDOWS_H #ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0 #define FMT_USE_WINDOWS_H 0
#endif #endif
#include <spdlog/fmt/bundled/format.h>
#include <spdlog/fmt/bundled/core.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
#include <fmt/format.h> #include <fmt/core.h>
#include <fmt/format.h>
#endif #endif

View File

@@ -10,14 +10,14 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_USE_STD_FORMAT)
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#endif #endif
#include <spdlog/fmt/bundled/ostream.h> #include <spdlog/fmt/bundled/ostream.h>
#else #else
#include <fmt/ostream.h> #include <fmt/ostream.h>
#endif #endif
#endif #endif

View File

@@ -10,14 +10,14 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_USE_STD_FORMAT)
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#endif #endif
#include <spdlog/fmt/bundled/ranges.h> #include <spdlog/fmt/bundled/ranges.h>
#else #else
#include <fmt/ranges.h> #include <fmt/ranges.h>
#endif #endif
#endif #endif

View File

@@ -11,14 +11,14 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_USE_STD_FORMAT)
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#endif #endif
#include <spdlog/fmt/bundled/std.h> #include <spdlog/fmt/bundled/std.h>
#else #else
#include <fmt/std.h> #include <fmt/std.h>
#endif #endif
#endif #endif

View File

@@ -10,14 +10,14 @@
#include <spdlog/tweakme.h> #include <spdlog/tweakme.h>
#if !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_USE_STD_FORMAT)
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#endif #endif
#endif #endif
#include <spdlog/fmt/bundled/xchar.h> #include <spdlog/fmt/bundled/xchar.h>
#else #else
#include <fmt/xchar.h> #include <fmt/xchar.h>
#endif #endif
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/logger.h> #include <spdlog/logger.h>
#endif #endif
#include <spdlog/details/backtracer.h> #include <spdlog/details/backtracer.h>
@@ -57,7 +57,7 @@ SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT {
std::swap(tracer_, other.tracer_); std::swap(tracer_, other.tracer_);
} }
SPDLOG_INLINE void swap(logger &a, logger &b) noexcept { a.swap(b); } SPDLOG_INLINE void swap(logger &a, logger &b) { a.swap(b); }
SPDLOG_INLINE void logger::set_level(level::level_enum log_level) { level_.store(log_level); } SPDLOG_INLINE void logger::set_level(level::level_enum log_level) { level_.store(log_level); }
@@ -163,12 +163,12 @@ SPDLOG_INLINE void logger::dump_backtrace_() {
} }
} }
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) const { SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) {
auto flush_level = flush_level_.load(std::memory_order_relaxed); auto flush_level = flush_level_.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off); return (msg.level >= flush_level) && (msg.level != level::off);
} }
SPDLOG_INLINE void logger::err_handler_(const std::string &msg) const { SPDLOG_INLINE void logger::err_handler_(const std::string &msg) {
if (custom_err_handler_) { if (custom_err_handler_) {
custom_err_handler_(msg); custom_err_handler_(msg);
} else { } else {

View File

@@ -19,30 +19,30 @@
#include <spdlog/details/log_msg.h> #include <spdlog/details/log_msg.h>
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32 #ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#endif #endif
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#endif #endif
#include <vector> #include <vector>
#ifndef SPDLOG_NO_EXCEPTIONS #ifndef SPDLOG_NO_EXCEPTIONS
#define SPDLOG_LOGGER_CATCH(location) \ #define SPDLOG_LOGGER_CATCH(location) \
catch (const std::exception &ex) { \ catch (const std::exception &ex) { \
if (location.filename) { \ if (location.filename) { \
err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), \ err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), \
location.filename, location.line)); \ location.filename, location.line)); \
} else { \ } else { \
err_handler_(ex.what()); \ err_handler_(ex.what()); \
} \ } \
} \ } \
catch (...) { \ catch (...) { \
err_handler_("Rethrowing unknown exception in logger"); \ err_handler_("Rethrowing unknown exception in logger"); \
throw; \ throw; \
} }
#else #else
#define SPDLOG_LOGGER_CATCH(location) #define SPDLOG_LOGGER_CATCH(location)
#endif #endif
namespace spdlog { namespace spdlog {
@@ -363,17 +363,17 @@ protected:
virtual void sink_it_(const details::log_msg &msg); virtual void sink_it_(const details::log_msg &msg);
virtual void flush_(); virtual void flush_();
void dump_backtrace_(); void dump_backtrace_();
bool should_flush_(const details::log_msg &msg) const; bool should_flush_(const details::log_msg &msg);
// handle errors during logging. // handle errors during logging.
// default handler prints the error to stderr at max rate of 1 message/sec. // default handler prints the error to stderr at max rate of 1 message/sec.
void err_handler_(const std::string &msg) const; void err_handler_(const std::string &msg);
}; };
void swap(logger &a, logger &b) noexcept; void swap(logger &a, logger &b);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "logger-inl.h" #include "logger-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#if defined(SPDLOG_NO_TLS) #if defined(SPDLOG_NO_TLS)
#error "This header requires thread local storage support, but SPDLOG_NO_TLS is defined." #error "This header requires thread local storage support, but SPDLOG_NO_TLS is defined."
#endif #endif
#include <map> #include <map>

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/pattern_formatter.h> #include <spdlog/pattern_formatter.h>
#endif #endif
#include <spdlog/details/fmt_helper.h> #include <spdlog/details/fmt_helper.h>
@@ -12,7 +12,7 @@
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#ifndef SPDLOG_NO_TLS #ifndef SPDLOG_NO_TLS
#include <spdlog/mdc.h> #include <spdlog/mdc.h>
#endif #endif
#include <spdlog/fmt/fmt.h> #include <spdlog/fmt/fmt.h>
@@ -510,7 +510,6 @@ public:
}; };
// ISO 8601 offset from UTC in timezone (+-HH:MM) // ISO 8601 offset from UTC in timezone (+-HH:MM)
// If SPDLOG_NO_TZ_OFFSET is defined, print "+??.??" instead.
template <typename ScopedPadder> template <typename ScopedPadder>
class z_formatter final : public flag_formatter { class z_formatter final : public flag_formatter {
public: public:
@@ -525,10 +524,6 @@ public:
const size_t field_size = 6; const size_t field_size = 6;
ScopedPadder p(field_size, padinfo_, dest); ScopedPadder p(field_size, padinfo_, dest);
#ifdef SPDLOG_NO_TZ_OFFSET
const char *str = "+??:??";
dest.append(str, str + 6);
#else
auto total_minutes = get_cached_offset(msg, tm_time); auto total_minutes = get_cached_offset(msg, tm_time);
bool is_negative = total_minutes < 0; bool is_negative = total_minutes < 0;
if (is_negative) { if (is_negative) {
@@ -541,7 +536,6 @@ public:
fmt_helper::pad2(total_minutes / 60, dest); // hours fmt_helper::pad2(total_minutes / 60, dest); // hours
dest.push_back(':'); dest.push_back(':');
fmt_helper::pad2(total_minutes % 60, dest); // minutes fmt_helper::pad2(total_minutes % 60, dest); // minutes
#endif // SPDLOG_NO_TZ_OFFSET
} }
private: private:
@@ -702,9 +696,9 @@ public:
: flag_formatter(padinfo) {} : flag_formatter(padinfo) {}
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4127) // consider using 'if constexpr' instead #pragma warning(disable : 4127) // consider using 'if constexpr' instead
#endif // _MSC_VER #endif // _MSC_VER
static const char *basename(const char *filename) { static const char *basename(const char *filename) {
// if the size is 2 (1 character + null terminator) we can use the more efficient strrchr // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
// the branch will be elided by optimizations // the branch will be elided by optimizations
@@ -721,7 +715,7 @@ public:
} }
} }
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) #pragma warning(pop)
#endif // _MSC_VER #endif // _MSC_VER
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override { void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
@@ -1160,10 +1154,12 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding)); formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
need_localtime_ = true; need_localtime_ = true;
break; break;
case ('z'): // timezone case ('z'): // timezone
formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding)); formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
need_localtime_ = true; need_localtime_ = true;
break; break;
case ('P'): // pid case ('P'): // pid
formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding)); formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));
break; break;

View File

@@ -114,5 +114,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "pattern_formatter-inl.h" #include "pattern_formatter-inl.h"
#endif #endif

View File

@@ -5,22 +5,22 @@
#ifdef __ANDROID__ #ifdef __ANDROID__
#include <spdlog/details/fmt_helper.h> #include <spdlog/details/fmt_helper.h>
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#include <spdlog/details/synchronous_factory.h> #include <spdlog/details/synchronous_factory.h>
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#include <android/log.h> #include <android/log.h>
#include <chrono> #include <chrono>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <type_traits> #include <type_traits>
#if !defined(SPDLOG_ANDROID_RETRIES) #if !defined(SPDLOG_ANDROID_RETRIES)
#define SPDLOG_ANDROID_RETRIES 2 #define SPDLOG_ANDROID_RETRIES 2
#endif #endif
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/ansicolor_sink.h> #include <spdlog/sinks/ansicolor_sink.h>
#endif #endif
#include <spdlog/details/os.h> #include <spdlog/details/os.h>

View File

@@ -40,7 +40,7 @@ public:
void log(const details::log_msg &msg) override; void log(const details::log_msg &msg) override;
void flush() override; void flush() override;
void set_pattern(const std::string &pattern) override; void set_pattern(const std::string &pattern) final override;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override; void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
// Formatting codes // Formatting codes
@@ -78,10 +78,8 @@ public:
const string_view_t red_bold = "\033[31m\033[1m"; const string_view_t red_bold = "\033[31m\033[1m";
const string_view_t bold_on_red = "\033[1m\033[41m"; const string_view_t bold_on_red = "\033[1m\033[41m";
protected:
FILE *target_file_;
private: private:
FILE *target_file_;
mutex_t &mutex_; mutex_t &mutex_;
bool should_do_colors_; bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_; std::unique_ptr<spdlog::formatter> formatter_;
@@ -114,5 +112,5 @@ using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmute
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "ansicolor_sink-inl.h" #include "ansicolor_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -47,5 +47,5 @@ protected:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "base_sink-inl.h" #include "base_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/basic_file_sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -62,5 +62,5 @@ inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name,
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "basic_file_sink-inl.h" #include "basic_file_sink-inl.h"
#endif #endif

View File

@@ -5,21 +5,21 @@
#if defined(_WIN32) #if defined(_WIN32)
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#endif #endif
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#include <mutex> #include <mutex>
#include <string> #include <string>
// Avoid including windows.h (https://stackoverflow.com/a/30741042) // Avoid including windows.h (https://stackoverflow.com/a/30741042)
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringW(const wchar_t *lpOutputString); extern "C" __declspec(dllimport) void __stdcall OutputDebugStringW(const wchar_t *lpOutputString);
#else #else
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString); extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
#endif #endif
extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
namespace spdlog { namespace spdlog {
@@ -42,13 +42,13 @@ protected:
memory_buf_t formatted; memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted); base_sink<Mutex>::formatter_->format(msg, formatted);
formatted.push_back('\0'); // add a null terminator for OutputDebugString formatted.push_back('\0'); // add a null terminator for OutputDebugString
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
wmemory_buf_t wformatted; wmemory_buf_t wformatted;
details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), wformatted); details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), wformatted);
OutputDebugStringW(wformatted.data()); OutputDebugStringW(wformatted.data());
#else #else
OutputDebugStringA(formatted.data()); OutputDebugStringA(formatted.data());
#endif #endif
} }
void flush_() override {} void flush_() override {}

View File

@@ -160,24 +160,20 @@ protected:
payload = QString::fromUtf8(str.data(), static_cast<int>(str.size())); payload = QString::fromUtf8(str.data(), static_cast<int>(str.size()));
// convert color ranges from byte index to character index. // convert color ranges from byte index to character index.
if (msg.color_range_start < msg.color_range_end) { if (msg.color_range_start < msg.color_range_end) {
color_range_start = color_range_start = QString::fromUtf8(str.data(), msg.color_range_start).size();
QString::fromUtf8(str.data(), static_cast<qsizetype>(msg.color_range_start)) color_range_end = QString::fromUtf8(str.data(), msg.color_range_end).size();
.size();
color_range_end =
QString::fromUtf8(str.data(), static_cast<qsizetype>(msg.color_range_end))
.size();
} }
} else { } else {
payload = QString::fromLatin1(str.data(), static_cast<int>(str.size())); payload = QString::fromLatin1(str.data(), static_cast<int>(str.size()));
} }
invoke_params params{max_lines_, // max lines invoke_params params{max_lines_, // max lines
qt_text_edit_, // text edit to append to qt_text_edit_, // text edit to append to
std::move(payload), // text to append std::move(payload), // text to append
default_color_, // default color default_color_, // default color
colors_.at(static_cast<size_t>(msg.level)), // color to apply colors_.at(msg.level), // color to apply
color_range_start, // color range start color_range_start, // color range start
color_range_end}; // color range end color_range_end}; // color range end
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
qt_text_edit_, [params]() { invoke_method_(params); }, Qt::AutoConnection); qt_text_edit_, [params]() { invoke_method_(params); }, Qt::AutoConnection);

View File

@@ -21,11 +21,7 @@ template <typename Mutex>
class ringbuffer_sink final : public base_sink<Mutex> { class ringbuffer_sink final : public base_sink<Mutex> {
public: public:
explicit ringbuffer_sink(size_t n_items) explicit ringbuffer_sink(size_t n_items)
: q_{n_items} { : q_{n_items} {}
if (n_items == 0) {
throw_spdlog_ex("ringbuffer_sink: n_items cannot be zero");
}
}
std::vector<details::log_msg_buffer> last_raw(size_t lim = 0) { std::vector<details::log_msg_buffer> last_raw(size_t lim = 0) {
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_); std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/rotating_file_sink.h> #include <spdlog/sinks/rotating_file_sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -89,5 +89,5 @@ std::shared_ptr<logger> rotating_logger_st(const std::string &logger_name,
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "rotating_file_sink-inl.h" #include "rotating_file_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/sink.h> #include <spdlog/sinks/sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -30,5 +30,5 @@ protected:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "sink-inl.h" #include "sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -4,9 +4,9 @@
#pragma once #pragma once
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h> #include <spdlog/sinks/wincolor_sink.h>
#else #else
#include <spdlog/sinks/ansicolor_sink.h> #include <spdlog/sinks/ansicolor_sink.h>
#endif #endif
#include <spdlog/details/synchronous_factory.h> #include <spdlog/details/synchronous_factory.h>
@@ -45,5 +45,5 @@ std::shared_ptr<logger> stderr_color_st(const std::string &logger_name,
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "stdout_color_sinks-inl.h" #include "stdout_color_sinks-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/stdout_sinks.h> #include <spdlog/sinks/stdout_sinks.h>
#endif #endif
#include <memory> #include <memory>
@@ -13,17 +13,17 @@
#include <spdlog/details/os.h> #include <spdlog/details/os.h>
#ifdef _WIN32 #ifdef _WIN32
// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675) // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
// so instead we use ::FileWrite // so instead we use ::FileWrite
#include <spdlog/details/windows_include.h> #include <spdlog/details/windows_include.h>
#ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp #ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp
#include <fileapi.h> // WriteFile (..) #include <fileapi.h> // WriteFile (..)
#endif #endif
#include <io.h> // _get_osfhandle(..) #include <io.h> // _get_osfhandle(..)
#include <stdio.h> // _fileno(..) #include <stdio.h> // _fileno(..)
#endif // _WIN32 #endif // _WIN32
namespace spdlog { namespace spdlog {

View File

@@ -9,7 +9,7 @@
#include <spdlog/sinks/sink.h> #include <spdlog/sinks/sink.h>
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/windows_include.h> #include <spdlog/details/windows_include.h>
#endif #endif
namespace spdlog { namespace spdlog {
@@ -80,5 +80,5 @@ std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "stdout_sinks-inl.h" #include "stdout_sinks-inl.h"
#endif #endif

View File

@@ -10,7 +10,7 @@
#include <array> #include <array>
#ifndef SD_JOURNAL_SUPPRESS_LOCATION #ifndef SD_JOURNAL_SUPPRESS_LOCATION
#define SD_JOURNAL_SUPPRESS_LOCATION #define SD_JOURNAL_SUPPRESS_LOCATION
#endif #endif
#include <systemd/sd-journal.h> #include <systemd/sd-journal.h>

View File

@@ -7,9 +7,9 @@
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/tcp_client-windows.h> #include <spdlog/details/tcp_client-windows.h>
#else #else
#include <spdlog/details/tcp_client.h> #include <spdlog/details/tcp_client.h>
#endif #endif
#include <chrono> #include <chrono>
@@ -31,8 +31,6 @@ namespace sinks {
struct tcp_sink_config { struct tcp_sink_config {
std::string server_host; std::string server_host;
int server_port; int server_port;
int timeout_ms =
0; // The timeout for all 3 major socket operations that is connect, send, and recv
bool lazy_connect = false; // if true connect on first log call instead of on construction bool lazy_connect = false; // if true connect on first log call instead of on construction
tcp_sink_config(std::string host, int port) tcp_sink_config(std::string host, int port)
@@ -46,22 +44,10 @@ public:
// connect to tcp host/port or throw if failed // connect to tcp host/port or throw if failed
// host can be hostname or ip address // host can be hostname or ip address
explicit tcp_sink(const std::string &host,
int port,
int timeout_ms = 0,
bool lazy_connect = false)
: config_{host, port} {
config_.timeout_ms = timeout_ms;
config_.lazy_connect = lazy_connect;
if (!config_.lazy_connect) {
client_.connect(config_.server_host, config_.server_port, config_.timeout_ms);
}
}
explicit tcp_sink(tcp_sink_config sink_config) explicit tcp_sink(tcp_sink_config sink_config)
: config_{std::move(sink_config)} { : config_{std::move(sink_config)} {
if (!config_.lazy_connect) { if (!config_.lazy_connect) {
client_.connect(config_.server_host, config_.server_port, config_.timeout_ms); this->client_.connect(config_.server_host, config_.server_port);
} }
} }
@@ -72,7 +58,7 @@ protected:
spdlog::memory_buf_t formatted; spdlog::memory_buf_t formatted;
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted); spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
if (!client_.is_connected()) { if (!client_.is_connected()) {
client_.connect(config_.server_host, config_.server_port, config_.timeout_ms); client_.connect(config_.server_host, config_.server_port);
} }
client_.send(formatted.data(), formatted.size()); client_.send(formatted.data(), formatted.size());
} }

View File

@@ -7,9 +7,9 @@
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/udp_client-windows.h> #include <spdlog/details/udp_client-windows.h>
#else #else
#include <spdlog/details/udp_client.h> #include <spdlog/details/udp_client.h>
#endif #endif
#include <chrono> #include <chrono>

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/wincolor_sink.h> #include <spdlog/sinks/wincolor_sink.h>
#endif #endif
#include <spdlog/details/windows_include.h> #include <spdlog/details/windows_include.h>

View File

@@ -31,10 +31,10 @@ public:
// change the color for the given level // change the color for the given level
void set_color(level::level_enum level, std::uint16_t color); void set_color(level::level_enum level, std::uint16_t color);
void log(const details::log_msg &msg) override; void log(const details::log_msg &msg) final override;
void flush() override; void flush() final override;
void set_pattern(const std::string &pattern) override; void set_pattern(const std::string &pattern) override final;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override; void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
void set_color_mode(color_mode mode); void set_color_mode(color_mode mode);
protected: protected:
@@ -78,5 +78,5 @@ using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "wincolor_sink-inl.h" #include "wincolor_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -95,7 +95,7 @@ SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
SPDLOG_API void register_logger(std::shared_ptr<logger> logger); SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
// Register the given logger with the given name // Register the given logger with the given name
// Will replace any existing logger with the same name. // Will replace any the existing logger with the same name if exists.
SPDLOG_API void register_or_replace(std::shared_ptr<logger> logger); SPDLOG_API void register_or_replace(std::shared_ptr<logger> logger);
// Apply a user-defined function on all registered loggers // Apply a user-defined function on all registered loggers
@@ -289,66 +289,69 @@ inline void critical(const T &msg) {
// //
#ifndef SPDLOG_NO_SOURCE_LOC #ifndef SPDLOG_NO_SOURCE_LOC
#define SPDLOG_LOGGER_CALL(logger, level, ...) \ #define SPDLOG_LOGGER_CALL(logger, level, ...) \
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_CALL(logger, level, ...) \ #define SPDLOG_LOGGER_CALL(logger, level, ...) \
(logger)->log(spdlog::source_loc{}, level, __VA_ARGS__) (logger)->log(spdlog::source_loc{}, level, __VA_ARGS__)
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
#define SPDLOG_LOGGER_TRACE(logger, ...) \ #define SPDLOG_LOGGER_TRACE(logger, ...) \
SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__)
#define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__) #define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_TRACE(logger, ...) (void)0 #define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
#define SPDLOG_TRACE(...) (void)0 #define SPDLOG_TRACE(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
#define SPDLOG_LOGGER_DEBUG(logger, ...) \ #define SPDLOG_LOGGER_DEBUG(logger, ...) \
SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)
#define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__) #define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0 #define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
#define SPDLOG_DEBUG(...) (void)0 #define SPDLOG_DEBUG(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
#define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__) #define SPDLOG_LOGGER_INFO(logger, ...) \
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_INFO(logger, ...) (void)0 #define SPDLOG_LOGGER_INFO(logger, ...) (void)0
#define SPDLOG_INFO(...) (void)0 #define SPDLOG_INFO(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
#define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__) #define SPDLOG_LOGGER_WARN(logger, ...) \
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_WARN(logger, ...) (void)0 #define SPDLOG_LOGGER_WARN(logger, ...) (void)0
#define SPDLOG_WARN(...) (void)0 #define SPDLOG_WARN(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
#define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__) #define SPDLOG_LOGGER_ERROR(logger, ...) \
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_ERROR(logger, ...) (void)0 #define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
#define SPDLOG_ERROR(...) (void)0 #define SPDLOG_ERROR(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
#define SPDLOG_LOGGER_CRITICAL(logger, ...) \ #define SPDLOG_LOGGER_CRITICAL(logger, ...) \
SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__)
#define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__) #define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0 #define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
#define SPDLOG_CRITICAL(...) (void)0 #define SPDLOG_CRITICAL(...) (void)0
#endif #endif
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "spdlog-inl.h" #include "spdlog-inl.h"
#endif #endif
#endif // SPDLOG_H #endif // SPDLOG_H

View File

@@ -4,8 +4,8 @@
#pragma once #pragma once
#define SPDLOG_VER_MAJOR 1 #define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 16 #define SPDLOG_VER_MINOR 15
#define SPDLOG_VER_PATCH 0 #define SPDLOG_VER_PATCH 2
#define SPDLOG_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch) #define SPDLOG_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch)
#define SPDLOG_VERSION SPDLOG_TO_VERSION(SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH) #define SPDLOG_VERSION SPDLOG_TO_VERSION(SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH)

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/async.h> #include <spdlog/async.h>

View File

@@ -3,43 +3,44 @@
// All rights reserved. // All rights reserved.
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#if !defined(SPDLOG_FMT_EXTERNAL) && !defined(SPDLOG_USE_STD_FORMAT) #if !defined(SPDLOG_FMT_EXTERNAL) && !defined(SPDLOG_USE_STD_FORMAT)
#include <spdlog/fmt/bundled/format-inl.h> #include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#if FMT_USE_LOCALE
template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED!
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
namespace detail { namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp<float>;
-> dragonbox::decimal_fp<float>; template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp<double>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>; #if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref) template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<char>;
-> thousands_sep_result<char>;
template FMT_API auto decimal_point_impl(locale_ref) -> char; template FMT_API auto decimal_point_impl(locale_ref) -> char;
// DEPRECATED! // DEPRECATED!
template FMT_API void buffer<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
template FMT_API void vformat_to(buffer<char>&,
string_view,
typename vformat_args<>::type,
locale_ref);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API auto thousands_sep_impl(locale_ref) template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<wchar_t>;
-> thousands_sep_result<wchar_t>;
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
// DEPRECATED!
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*); template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail } // namespace detail

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/cfg/helpers-inl.h> #include <spdlog/cfg/helpers-inl.h>

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <mutex> #include <mutex>
@@ -13,7 +13,7 @@
// color sinks // color sinks
// //
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/sinks/wincolor_sink-inl.h> #include <spdlog/sinks/wincolor_sink-inl.h>
template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_nullmutex>; template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_mutex>;
@@ -21,7 +21,7 @@ template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::c
template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_nullmutex>; template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_nullmutex>;
#else #else
#include "spdlog/sinks/ansicolor_sink-inl.h" #include "spdlog/sinks/ansicolor_sink-inl.h"
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_nullmutex>; template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_mutex>;

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/details/file_helper-inl.h> #include <spdlog/details/file_helper-inl.h>

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/common-inl.h> #include <spdlog/common-inl.h>

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. #error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <mutex> #include <mutex>

Some files were not shown because too many files have changed in this diff Show More