Compare commits

...

26 Commits

Author SHA1 Message Date
SamareshSingh
0209b12c50 tests: fix unit tests to not be affected by custom level names (#3492)
The unit tests were failing when users defined custom SPDLOG_LEVEL_NAMES
or SPDLOG_SHORT_LEVEL_NAMES in tweakme.h. This happened because the tests
expected the default level names but were getting the customized ones instead.

For example, with custom short names defined, the test would fail like this:
  REQUIRE( spdlog::level::to_string_view(spdlog::level::trace) == "trace" )
  with expansion: "TRC" == "trace"

This fix undefines these macros in tests/includes.h (right after setting
SPDLOG_ACTIVE_LEVEL) so that unit tests always use spdlog's default level
names, regardless of any customizations in tweakme.h.

Fixes #3466
2025-12-15 09:49:15 +02:00
maordadush
d2100d5d0e Fix: include <fcntl.h> in tcp_client.h to avoid compilation failures on Unix (#3497) 2025-12-15 09:46:34 +02:00
gabime
3f03542d2e Remove warning 4834 suppression 2025-11-30 11:16:53 +02:00
gabime
2c1eafc884 Backport warning fix from fmt head 2025-11-30 11:16:28 +02:00
gabime
878ad2e30b Supress MSVC C4834 warning triggeed by fmt 12.1.0 2025-11-28 20:17:02 +02:00
gabime
c5061bb970 Update LICENSE file 2025-11-28 19:13:40 +02:00
gabime
ea3e747eea Bump fmt to 12.1.0 2025-11-28 19:00:23 +02:00
gabime
b3688ba102 Set IndentPPDirectives to "None" on clang-format 2025-11-28 18:41:08 +02:00
Gabi Melman
6004e3d14a Fix issue #3483 (#3491)
Fix issue #3483
2025-11-28 18:20:28 +02:00
Jérémie Dumas
8806ca6509 Fix UWP detection. (#3489)
* Fix UWP detection.

* Fix conditional compilation for getenv function

---------

Co-authored-by: Jérémie Dumas <jedumas@adobe.com>
2025-11-17 22:34:37 +02:00
SamareshSingh
cdbd64e230 Fix sign conversion warnings in qt_sinks.h (#3487)
Add static_cast to fix compiler warnings when building with
-Werror=sign-conversion and -Werror=shorten-64-to-32:

1. Cast msg.color_range_start/end to qsizetype when passing to
   QString::fromUtf8(), since QString expects signed qsizetype but
   the message fields are size_t (unsigned).

2. Cast msg.level to size_t when indexing into colors_ array, since
   level_enum is a signed int but array indexing expects size_t.

These casts are safe because:
- qsizetype is guaranteed to be the same size as size_t per Qt docs
- color_range values come from valid string positions
- level values are from a small enum range

Fixes #3321
2025-11-14 01:44:43 +02:00
fab4100
88a0e07ad5 Change access scope for ANSI target_file_ from private to protected (#3486)
This change allows for a custom minimal ANSI color sink implementation
that supports, for example, splitting between `stdout` and `stderr` file
streams depending on the log level.  An example application specific
custom sink that realizes this behavior would look like:

```cpp
template <typename ConsoleMutex>
class SplitSink : public sinks::ansicolor_sink<ConsoleMutex>
{
    using Base = sinks::ansicolor_sink<ConsoleMutex>;

public:
    SplitSink(color_mode mode = color_mode::automatic) : Base(stdout, mode) {}

    void log(const details::log_msg &msg) override
    {
        if (msg.level <= SPDLOG_LEVEL_WARN) {
            this->target_file_ = stdout;
        } else {
            this->target_file_ = stderr;
        }
        Base::log(msg);
    }
};
```

Inspired by https://github.com/gabime/spdlog/issues/345 and
https://github.com/eic/EICrecon/issues/456.  This commit aims at reusing
all of the `ansicolor_sink` code with the exception of dynamic target
file selection that can be implemented in application code based on
example above.

Co-authored-by: Fabian Wermelinger <info@0xfab.ch>
2025-11-09 19:38:28 +02:00
Aleksandr
3f7e502859 fix sign-compare warning (#3479) 2025-11-01 18:03:07 +02:00
Ashish Ahuja
dd3ca04a7a set CMAKE_BUILD_TYPE only in top-level project (#3480) 2025-11-01 17:55:59 +02:00
gabime
486b55554f Version 1.16.0 2025-10-11 15:53:05 +03:00
gabime
1bea38edcc clang-format 2025-10-11 15:50:16 +03:00
gabime
4418909af2 Bump fmt to 12.0.0 2025-10-11 15:18:15 +03:00
Angelio Mason
f1d748e5e3 Remove the fileapi.h include in os-inl.h (#3444)
* Replaced fileapi.h include with windows.h, as instructed in https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
Otherwise this causes compilation error on older sdks.

* Removed the fileapi.h include entirely, since windows.h is already included before

---------

Co-authored-by: MasonAngelio <MasonAngelio>
2025-08-07 23:47:11 +03:00
Vitaly
3edc8036db Run tests in the order they are declared in the source file. (#3451)
Fixes an issue with running tests in random order in Catch2 3.9.0+.
2025-08-07 23:38:29 +03:00
Mihir Patel
9ecdf5c8a1 Added timeout for TCP calls such as connect, send, recv (#3432)
* Now lets test on windows

* I guess testing on windows passes.

* Update tcp_client-windows.h

Added default value to argument

* Final edit

* Update tcp_client-windows.h

Changed improper misplaced includes.
2025-07-17 23:47:35 +03:00
Gabi Melman
737347d2df Update linux.yml 2025-07-16 09:55:57 +03:00
Alexander
4f2b3d52f9 Update README.md (#3437)
* Update README.md

add example showcasing 2 loggers and `spdlog::set_level()` 
which set level not only to default logger, but to all registed loggers

* Update README.md

* simplify

* simplify
2025-07-16 08:59:51 +03:00
Joshua Chapman
4397dac510 chore(cmake): add option to override CMAKE_DEBUG_POSTFIX (#3433)
This will make it possible to use the pkg-config with CMake debug build.
2025-07-08 22:52:42 +03:00
Gabi Melman
6fd67ce169 Update windows.yml
remove msvc 2019 build
2025-07-06 10:08:55 +03:00
Gabi Melman
4619e18a16 Update windows.yml 2025-07-06 09:53:42 +03:00
Gabi Melman
a6215527f4 Fix ringbuffer tests for newline (#3436) 2025-07-06 08:38:48 +03:00
108 changed files with 1992 additions and 1702 deletions

View File

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

View File

@@ -18,9 +18,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
config: config:
- { compiler: gcc, version: 7, build_type: Release, cppstd: 11 } - { compiler: gcc, version: 9, build_type: Release, cppstd: 11 }
- { compiler: gcc, version: 9, build_type: Release, cppstd: 17 } - { compiler: gcc, version: 11, build_type: Debug, 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,74 +75,5 @@ 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,8 +19,12 @@ 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
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) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
endif() endif()
endif()
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
# Compiler config # Compiler config
@@ -62,6 +66,9 @@ 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)
@@ -90,6 +97,7 @@ 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")
@@ -191,7 +199,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 d) set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX ${SPDLOG_DEBUG_POSTFIX})
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)
@@ -279,7 +287,8 @@ 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})

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 Gabi Melman. Copyright (c) 2016 - present, Gabi Melman and spdlog contributors.
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
@@ -23,4 +23,3 @@ 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
@@ -241,6 +241,29 @@ 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++

View File

@@ -23,7 +23,6 @@
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/windows_include.h> #include <spdlog/details/windows_include.h>
#include <fileapi.h> // for FlushFileBuffers
#include <io.h> // for _get_osfhandle, _isatty, _fileno #include <io.h> // for _get_osfhandle, _isatty, _fileno
#include <process.h> // for _get_pid #include <process.h> // for _get_pid
@@ -245,6 +244,7 @@ SPDLOG_INLINE size_t filesize(FILE *f) {
#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
@@ -263,47 +263,12 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
offset -= tzinfo.StandardBias; offset -= tzinfo.StandardBias;
} }
return offset; return offset;
#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 #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
@@ -490,7 +455,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 == target.size()); assert(result_size == static_cast<int>(target.size()));
return; return;
} }
} }
@@ -568,7 +533,8 @@ SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
#pragma warning(disable : 4996) #pragma warning(disable : 4996)
#endif // _MSC_VER #endif // _MSC_VER
std::string SPDLOG_INLINE getenv(const char *field) { std::string SPDLOG_INLINE getenv(const char *field) {
#if defined(_MSC_VER) && defined(__cplusplus_winrt) #if defined(_MSC_VER) && defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_DESKTOP_APP) && \
(WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
return std::string{}; // not supported under uwp return std::string{}; // not supported under uwp
#else #else
char *buf = std::getenv(field); char *buf = std::getenv(field);

View File

@@ -58,8 +58,81 @@ 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) { void connect(const std::string &host, int port, int timeout_ms = 0) {
if (is_connected()) { if (is_connected()) {
close(); close();
} }
@@ -71,6 +144,10 @@ 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);
@@ -82,7 +159,6 @@ 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) {
@@ -90,18 +166,24 @@ public:
WSACleanup(); WSACleanup();
continue; continue;
} }
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) { if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 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

@@ -17,6 +17,7 @@
#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>
@@ -39,8 +40,72 @@ 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) { void connect(const std::string &host, int port, int timeout_ms = 0) {
close(); close();
struct addrinfo hints {}; struct addrinfo hints {};
memset(&hints, 0, sizeof(struct addrinfo)); memset(&hints, 0, sizeof(struct addrinfo));
@@ -49,6 +114,10 @@ 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);
@@ -69,8 +138,9 @@ public:
last_errno = errno; last_errno = errno;
continue; continue;
} }
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen); ::fcntl(socket_, F_SETFD, FD_CLOEXEC);
if (rv == 0) { if (connect_socket_with_timeout(socket_, rp->ai_addr, rp->ai_addrlen, tv) == 0) {
last_errno = 0;
break; break;
} }
last_errno = errno; last_errno = errno;
@@ -82,6 +152,12 @@ 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),

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`.
*/ */
template <typename Context> class dynamic_format_arg_store { FMT_EXPORT 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 @@ template <typename Context> class dynamic_format_arg_store {
} }
/// Returns the number of elements in the store. /// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); } auto size() const noexcept -> size_t { 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 110200 #define FMT_VERSION 120100
// Detect compiler versions. // Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__) #if defined(__clang__) && !defined(__ibmxl__)
@@ -114,7 +114,9 @@
#endif #endif
// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. // Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated.
#if !defined(__cpp_lib_is_constant_evaluated) #ifdef FMT_USE_CONSTEVAL
// 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
@@ -201,14 +203,6 @@
# 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
#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
@@ -242,6 +236,7 @@ FMT_PRAGMA_GCC(optimize("Og"))
# define FMT_GCC_OPTIMIZED # define FMT_GCC_OPTIMIZED
#endif #endif
FMT_PRAGMA_CLANG(diagnostic push) FMT_PRAGMA_CLANG(diagnostic push)
FMT_PRAGMA_GCC(diagnostic push)
#ifdef FMT_ALWAYS_INLINE #ifdef FMT_ALWAYS_INLINE
// Use the provided definition. // Use the provided definition.
@@ -260,7 +255,7 @@ FMT_PRAGMA_CLANG(diagnostic push)
#ifndef FMT_BEGIN_NAMESPACE #ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \ # define FMT_BEGIN_NAMESPACE \
namespace fmt { \ namespace fmt { \
inline namespace v11 { inline namespace v12 {
# define FMT_END_NAMESPACE \ # define FMT_END_NAMESPACE \
} \ } \
} }
@@ -356,6 +351,9 @@ 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/.
@@ -396,7 +394,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::detail::assert_fail(__FILE__, __LINE__, (message))) : ::fmt::assert_fail(__FILE__, __LINE__, (message)))
#endif #endif
#ifdef FMT_USE_INT128 #ifdef FMT_USE_INT128
@@ -419,8 +417,12 @@ inline auto map(int128_opt) -> monostate { return {}; }
inline auto map(uint128_opt) -> monostate { return {}; } inline auto map(uint128_opt) -> monostate { return {}; }
#endif #endif
#ifndef FMT_USE_BITINT #ifdef FMT_USE_BITINT
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500) // Use the provided definition.
#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
@@ -463,12 +465,13 @@ 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 const char* narrow(const T*) { return nullptr; } template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
return s;
}
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
-> 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;
@@ -540,7 +543,7 @@ template <typename Char> class basic_string_view {
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 && !detail::is_constant_evaluated()) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return; return;
} }
#endif #endif
@@ -616,19 +619,6 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>; using string_view = basic_string_view<char>;
// DEPRECATED! Will be merged with is_char and moved to detail.
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
// Specifies if `T` is a character (code unit) type.
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>;
@@ -781,7 +771,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift); (static_cast<unsigned>(p) << precision_shift);
} }
constexpr bool dynamic() const { constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0; return (data_ & (width_mask | precision_mask)) != 0;
} }
@@ -921,14 +911,50 @@ 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_char<Char>::value)> template <typename Char, FMT_ENABLE_IF(is_code_unit<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;
} }
@@ -1057,11 +1083,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... Args> constexpr auto count_named_args() -> int { template <typename... T> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>(); return count<is_named_arg<T>::value...>();
} }
template <typename... Args> constexpr auto count_static_named_args() -> int { template <typename... T> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>(); return count<is_static_named_arg<T>::value...>();
} }
template <typename Char> struct named_arg_info { template <typename Char> struct named_arg_info {
@@ -1069,7 +1095,7 @@ template <typename Char> struct named_arg_info {
int id; int id;
}; };
// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. // named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char> template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args, FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index, int named_arg_index,
@@ -1173,7 +1199,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_char<T>::value)> template <typename T, FMT_ENABLE_IF(is_code_unit<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>;
@@ -1679,12 +1705,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(1, NUM_ARGS)]; type types_[max_of<size_t>(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)]; named_arg_info<Char> named_args_[max_of<size_t>(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(1, NUM_ARGS)]; parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
public: public:
template <typename... T> template <typename... T>
@@ -1828,12 +1854,17 @@ 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);
try_reserve(size_ + count); if (free_cap < count) {
auto free_cap = capacity_ - size_; grow_(*this, size + count);
if (free_cap < count) count = free_cap; size = size_;
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;
@@ -2033,6 +2064,17 @@ 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&&
@@ -2047,6 +2089,8 @@ 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 {
@@ -2056,7 +2100,11 @@ 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;
@@ -2176,7 +2224,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_char<T>::value)> template <typename T, FMT_ENABLE_IF(is_code_unit<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,
@@ -2252,7 +2300,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, formatter<value_type, char_type>>; custom.format = format_custom<value_type>;
} }
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())> template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
@@ -2263,10 +2311,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, typename Formatter> template <typename T>
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(); auto f = formatter<T, char_type>();
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>;
@@ -2293,35 +2341,14 @@ 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 Arg, typename... Args> template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long { constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) | return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits); (encode_types<Context, T...>() << 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)>
@@ -2338,8 +2365,9 @@ 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[1 + NUM_ARGS]; arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS]; named_arg_info<typename Context::char_type>
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)
@@ -2358,8 +2386,8 @@ struct named_arg_store {
} }
named_arg_store(const named_arg_store& rhs) = delete; named_arg_store(const named_arg_store& rhs) = delete;
named_arg_store& operator=(const named_arg_store& rhs) = delete; auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
named_arg_store& operator=(named_arg_store&& rhs) = delete; auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; } operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
}; };
@@ -2372,7 +2400,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(1, NUM_ARGS)], arg_t<Context, NUM_ARGS>[max_of<size_t>(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;
}; };
@@ -2656,22 +2684,17 @@ class context {
private: private:
appender out_; appender out_;
format_args args_; format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public: public:
/// The character type for the output. using char_type = char; ///< 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, FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
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;
@@ -2692,7 +2715,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 -> detail::locale_ref { return loc_; } FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
}; };
template <typename Char = char> struct runtime_format_string { template <typename Char = char> struct runtime_format_string {
@@ -2779,9 +2802,6 @@ 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,
@@ -2978,9 +2998,10 @@ 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_END_EXPORT FMT_PRAGMA_GCC(diagnostic pop)
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

@@ -38,6 +38,7 @@ 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 ==
@@ -161,17 +162,6 @@ 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:
@@ -282,8 +272,6 @@ 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<>(); }
@@ -326,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& {
} }
template <typename CodeUnit> struct codecvt_result { template <typename CodeUnit> struct codecvt_result {
static constexpr const size_t max_size = 32; static constexpr size_t max_size = 32;
CodeUnit buf[max_size]; CodeUnit buf[max_size];
CodeUnit* end; CodeUnit* end;
}; };
@@ -443,11 +431,7 @@ 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)) {
@@ -458,6 +442,7 @@ 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));
@@ -471,6 +456,8 @@ 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;
@@ -487,7 +474,7 @@ template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF( FMT_ENABLE_IF(
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)> !is_similar_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);
} }
@@ -501,86 +488,10 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
.count(); .count();
} }
namespace tz {
// DEPRECATED!
struct time_zone {
template <typename Duration, typename LocalTime>
auto to_sys(LocalTime) -> sys_time<Duration> {
return {};
}
};
template <typename... T> auto current_zone(T...) -> time_zone* {
return nullptr;
}
template <typename... T> void _tzset(T...) {}
} // namespace tz
// DEPRECATED!
inline void tzset_once() {
static bool init = []() {
using namespace tz;
_tzset();
return false;
}();
ignore_unused(init);
}
} // 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.
*/
FMT_DEPRECATED 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_DEPRECATED auto localtime(std::chrono::local_time<Duration> time)
-> std::tm {
using namespace std::chrono;
using namespace detail::tz;
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
@@ -652,7 +563,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 const size_t len = 8; constexpr 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);
@@ -1000,16 +911,16 @@ template <typename T>
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {}; struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)> template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
bool set_tm_zone(T& time, char* tz) { auto set_tm_zone(T& time, char* tz) -> bool {
time.tm_zone = tz; time.tm_zone = tz;
return true; return true;
} }
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)> template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
bool set_tm_zone(T&, char*) { auto set_tm_zone(T&, char*) -> bool {
return false; return false;
} }
inline char* utc() { inline auto utc() -> char* {
static char tz[] = "UTC"; static char tz[] = "UTC";
return tz; return tz;
} }
@@ -1683,8 +1594,13 @@ 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) if (!localized) return;
::new (&locale_) std::locale(loc.template get<std::locale>()); ignore_unused(loc);
::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();
@@ -2230,7 +2146,7 @@ template <typename Char> struct formatter<std::tm, Char> {
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 loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref(); auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref); detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>( auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs); loc, out, tm, subsecs);

View File

@@ -375,19 +375,17 @@ template <typename Char> struct ansi_color_escape {
// 10 more. // 10 more.
if (is_background) value += 10u; if (is_background) value += 10u;
size_t index = 0; buffer[size++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('\x1b'); buffer[size++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) { if (value >= 100u) {
buffer[index++] = static_cast<Char>('1'); buffer[size++] = static_cast<Char>('1');
value %= 100u; value %= 100u;
} }
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>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m'); buffer[size++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return; return;
} }
@@ -398,7 +396,7 @@ template <typename Char> struct ansi_color_escape {
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');
buffer[19] = static_cast<Char>(0); size = 19;
} }
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] = {};
@@ -411,26 +409,28 @@ 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;
size_t index = 0; buffer[size++] = static_cast<Char>('\x1b');
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[index++] = static_cast<Char>('\x1b'); buffer[size++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('['); buffer[size++] = 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_CONSTEXPR20 auto end() const noexcept -> const Char* { FMT_CONSTEXPR auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size(); return buffer + size;
} }
private: private:
static constexpr size_t num_emphases = 8; static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u]; Char buffer[7u + 4u * num_emphases] = {};
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 {

View File

@@ -15,15 +15,14 @@
#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.
FMT_EXPORT class compiled_string {}; 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
@@ -41,18 +40,42 @@ namespace detail {
# 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>
auto first(const T& value, const Tail&...) -> const T& { constexpr 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... Args> struct type_list {}; template <typename... T> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...]. // Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args> template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first, constexpr auto get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) { [[maybe_unused]] const Args&... rest) -> const auto& {
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;
@@ -84,8 +107,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 int get_arg_index_by_name(basic_string_view<Char> name, constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) { type_list<Args...>) -> int {
return get_arg_index_by_name<Args...>(name); return get_arg_index_by_name<Args...>(name);
} }
@@ -105,8 +128,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... Args> template <typename OutputIt, typename... T>
constexpr OutputIt format(OutputIt out, const Args&...) const { constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
return write<Char>(out, data); return write<Char>(out, data);
} }
}; };
@@ -115,8 +138,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 text<Char> make_text(basic_string_view<Char> s, size_t pos, constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
size_t size) { -> text<Char> {
return {{&s[pos], size}}; return {{&s[pos], size}};
} }
@@ -124,8 +147,8 @@ template <typename Char> struct code_unit {
Char value; Char value;
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... T>
constexpr OutputIt format(OutputIt out, const Args&...) const { constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
*out++ = value; *out++ = value;
return out; return out;
} }
@@ -133,7 +156,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 const T& get_arg_checked(const Args&... args) { constexpr auto get_arg_checked(const Args&... args) -> const T& {
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;
@@ -146,13 +169,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 T, int N> struct field { template <typename Char, typename V, int N> struct field {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... T>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
const T& arg = get_arg_checked<T, N>(args...); const V& arg = get_arg_checked<V, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) { if constexpr (std::is_convertible<V, 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 {
@@ -170,10 +193,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 bool try_format_argument( constexpr static auto 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) { [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
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);
@@ -183,8 +206,8 @@ template <typename Char> struct runtime_named_field {
return false; return false;
} }
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... T>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
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"));
@@ -197,17 +220,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 T, int N> struct spec_field { template <typename Char, typename V, int N> struct spec_field {
using char_type = Char; using char_type = Char;
formatter<T, Char> fmt; formatter<V, Char> fmt;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... T>
constexpr FMT_INLINE OutputIt format(OutputIt out, constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
const Args&... args) const { -> OutputIt {
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<T, N>(args...), ctx); return fmt.format(get_arg_checked<V, N>(args...), ctx);
} }
}; };
@@ -219,8 +242,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... Args> template <typename OutputIt, typename... T>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
out = lhs.format(out, args...); out = lhs.format(out, args...);
return rhs.format(out, args...); return rhs.format(out, args...);
} }
@@ -230,14 +253,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 concat<L, R> make_concat(L lhs, R rhs) { constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
return {lhs, rhs}; return {lhs, rhs};
} }
struct unknown_format {}; struct unknown_format {};
template <typename Char> template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
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;
} }
@@ -270,8 +293,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 parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
size_t pos, int next_arg_id) { int next_arg_id) -> parse_specs_result<T, Char> {
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);
@@ -285,16 +308,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 int on_auto() { constexpr auto on_auto() -> int {
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 int on_index(int id) { constexpr auto on_index(int id) -> int {
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 int on_name(basic_string_view<Char> id) { constexpr auto on_name(basic_string_view<Char> id) -> int {
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;
@@ -433,27 +456,28 @@ 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... Args, template <typename CompiledFormat, typename... T,
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 std::basic_string<Char> format(const CompiledFormat& cf, FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
const Args&... args) { const T&... 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... Args, template <typename OutputIt, typename CompiledFormat, typename... T,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) { const T&... args) -> OutputIt {
return cf.format(out, args...); return cf.format(out, args...);
} }
template <typename S, typename... Args, template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&, FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
Args&&... args) { -> std::basic_string<typename S::char_type> {
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] == '}') {
@@ -466,72 +490,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
} }
} }
} }
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<T...>(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<Args>(args)...); std::forward<T>(args)...);
} else { } else {
return fmt::format(compiled, std::forward<Args>(args)...); return fmt::format(compiled, std::forward<T>(args)...);
} }
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<T...>(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<Args>(args)...); std::forward<T>(args)...);
} else { } else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...); return fmt::format_to(out, compiled, std::forward<T>(args)...);
} }
} }
#endif #endif
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... T,
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, Args&&... args) auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... 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<Args>(args)...); fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename S, typename... Args, template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
-> size_t {
auto buf = detail::counting_buffer<>(); auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...); fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return buf.count(); return buf.count();
} }
template <typename S, typename... Args, template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) { void print(std::FILE* f, const S& fmt, T&&... args) {
auto buf = memory_buffer(); auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...); fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
detail::print(f, {buf.data(), buf.size()}); detail::print(f, {buf.data(), buf.size()});
} }
template <typename S, typename... Args, template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)> FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) { void print(const S& fmt, T&&... args) {
print(stdout, fmt, args...); print(stdout, fmt, std::forward<T>(args)...);
} }
#if FMT_USE_NONTYPE_TEMPLATE_ARGS template <size_t N> class static_format_result {
inline namespace literals { private:
template <detail::fixed_string Str> constexpr auto operator""_cf() { char data[N];
return FMT_COMPILE(Str.data);
public:
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
} }
} // namespace literals
#endif 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

@@ -22,7 +22,7 @@
#include "format.h" #include "format.h"
#if FMT_USE_LOCALE #if FMT_USE_LOCALE && !defined(FMT_MODULE)
# include <locale> # include <locale>
#endif #endif
@@ -31,14 +31,44 @@
#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.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); std::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 {
@@ -79,33 +109,6 @@ 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>());
@@ -133,14 +136,13 @@ 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_USE_EXCEPTIONS #if FMT_MSC_VERSION || defined(__NVCC__)
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings // Silence unreachable code warnings in MSVC and NVCC because these
// from MSVC. // are nearly impossible to fix in a generic code.
FMT_THROW(format_error(message)); volatile bool b = true;
#else if (!b) return;
fputs(message, stderr);
abort();
#endif #endif
FMT_THROW(format_error(message));
} }
template <typename Locale> typename Locale::id format_facet<Locale>::id; template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -174,11 +176,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_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { FMT_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_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { FMT_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));
} }
@@ -275,7 +277,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 const uint64_t pow10_significands[] = { static constexpr uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -370,7 +372,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 const uint128_fallback pow10_significands[] = { static constexpr uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX #if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad}, {0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1037,7 +1039,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 const uint64_t powers_of_5_64[] = { static constexpr uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1149,8 +1151,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 int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
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;
@@ -1170,22 +1172,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
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 int remove_trailing_zeros(uint64_t& n) noexcept { FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
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?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { constexpr uint32_t ten_pow_8 = 100000000u;
if ((n % ten_pow_8) == 0) {
// If yes, work with the quotient... // If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64)); auto n32 = static_cast<uint32_t>(n / ten_pow_8);
// ... and use the 32 bit variant of the function // ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8); int num_zeros = remove_trailing_zeros(n32, 8);
n = n32; n = n32;
return s; return num_zeros;
} }
// If n is not divisible by 10^8, work with n itself. // If n is not divisible by 10^8, work with n itself.
@@ -1210,7 +1209,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// The main algorithm for shorter interval case // The main algorithm for shorter interval case
template <typename T> template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept { FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> {
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);
@@ -1454,8 +1453,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( parse_format_string(fmt,
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}}); format_handler<>{parse_context<>(fmt), {out, args, loc}});
} }
template <typename T> struct span { template <typename T> struct span {
@@ -1546,10 +1545,11 @@ 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; }
bool needs_flush() const { auto needs_flush() const -> bool {
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;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0);
return memchr(end, '\n', static_cast<size_t>(size));
} }
void flush() { fflush_unlocked(this->file_); } void flush() { fflush_unlocked(this->file_); }
@@ -1573,7 +1573,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.
putc_unlocked(0, this->file_); if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_);
--this->file_->_p; --this->file_->_p;
++this->file_->_w; ++this->file_->_w;
} }
@@ -1594,7 +1594,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size; this->file_->_w -= size;
} }
bool needs_flush() const { auto needs_flush() const -> bool {
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,7 +29,8 @@
# 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
@@ -135,10 +136,9 @@ 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 'madeup': The system cannot find the file * // cannot open file 'foo': The system cannot find the file specified.
* specified. * // or similar (system message may vary) if the file doesn't exist.
* // or similar (system message may vary). * const char *filename = "foo";
* 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 FMT_API ostream : private detail::buffer<char> { class ostream : private detail::buffer<char> {
private: private:
file file_; file file_;
ostream(cstring_view path, const detail::ostream_params& params); FMT_API ostream(cstring_view path, const detail::ostream_params& params);
static void grow(buffer<char>& buf, size_t); FMT_API static void grow(buffer<char>& buf, size_t);
public: public:
ostream(ostream&& other) noexcept; FMT_API ostream(ostream&& other) noexcept;
~ostream(); FMT_API ~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 instantion in every translation unit using a tag // Generate a unique explicit instantiation in every translation unit using a
// type in an anonymous namespace. // tag type in an anonymous namespace.
namespace { namespace {
struct file_access_tag {}; struct file_access_tag {};
} // namespace } // namespace

View File

@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#ifndef FMT_MODULE #ifndef FMT_MODULE
# include <algorithm> // std::max # include <algorithm> // std::find
# include <limits> // std::numeric_limits # include <limits> // std::numeric_limits
#endif #endif
@@ -18,10 +18,6 @@
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_;
@@ -33,8 +29,6 @@ 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
@@ -46,7 +40,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() -> detail::locale_ref { return {}; } auto locale() -> 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);
@@ -74,10 +68,9 @@ 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 IsSigned> struct int_checker { template <bool IS_SIGNED> 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 {
unsigned max = to_unsigned(max_value<int>()); return value <= 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; }
}; };
@@ -95,7 +88,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 (std::max)(static_cast<int>(value), 0); return max_of(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)>
@@ -410,7 +403,9 @@ 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);
return detail::get_arg(context, arg_index); auto arg = context.arg(arg_index);
if (!arg) report_error("argument not found");
return arg;
}; };
const Char* start = parse_ctx.begin(); const Char* start = parse_ctx.begin();
@@ -571,14 +566,18 @@ 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 S, typename... T, typename Char = detail::char_t<S>> template <typename... T>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(string_view fmt, const T&... args) -> std::string {
return vsprintf(detail::to_string_view(fmt), return vsprintf(fmt, make_printf_args(args...));
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>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt, 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);
@@ -596,17 +595,14 @@ inline 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 S, typename... T, typename Char = detail::char_t<S>> template <typename... T>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt), return vfprintf(f, fmt, make_printf_args(args...));
make_printf_args<Char>(args...));
} }
template <typename... T>
template <typename Char> FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt, const T&... args) -> int {
typename vprintf_args<Char>::type args) return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
-> int {
return vfprintf(stdout, fmt, args);
} }
/** /**
@@ -621,11 +617,6 @@ 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,7 +11,6 @@
#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>
@@ -19,6 +18,13 @@
#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
@@ -31,7 +37,7 @@ template <typename T> class is_map {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr const bool value = static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
@@ -40,17 +46,16 @@ template <typename T> class is_set {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr const bool value = static constexpr 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, std::size_t N> template <typename T, 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, std::size_t N> template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N; return arr + N;
} }
@@ -120,7 +125,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr const bool value = static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
@@ -154,7 +159,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 const bool value = false; static constexpr 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>
@@ -170,7 +175,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{})); C>::value)...>{}));
public: public:
static constexpr const bool value = static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value; decltype(check(tuple_index_sequence<T>{}))::value;
}; };
@@ -208,7 +213,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, std::size_t... Is> template <typename Tuple, typename Char, 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
@@ -219,7 +224,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, std::size_t N> struct range_reference_type_impl<T[N]> { template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
using type = T&; using type = T&;
}; };
@@ -236,14 +241,6 @@ 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,
@@ -281,14 +278,15 @@ 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 const bool value = static constexpr 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 const bool value = static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
detail::is_tuple_formattable_<T, C>::value;
}; };
template <typename Tuple, typename Char> template <typename Tuple, typename Char>
@@ -343,8 +341,9 @@ struct formatter<Tuple, Char,
} }
}; };
FMT_EXPORT
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static constexpr const bool value = static constexpr 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;
}; };
@@ -368,6 +367,7 @@ 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,7 +670,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
} }
}; };
template <typename Char, typename Tuple> struct tuple_join_view : detail::view { FMT_EXPORT
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;
@@ -685,15 +686,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0 # define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif #endif
template <typename Char, typename Tuple> template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Char, Tuple>, Char, struct formatter<tuple_join_view<Tuple, Char>, 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<Char, Tuple>& value, auto format(const tuple_join_view<Tuple, Char>& 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>());
} }
@@ -725,14 +726,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
} }
template <typename FormatContext> template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx, auto do_format(const tuple_join_view<Tuple, Char>&, 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<Char, Tuple>& value, FormatContext& ctx, auto do_format(const tuple_join_view<Tuple, Char>& 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;
@@ -754,7 +755,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static constexpr const bool value = static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
@@ -819,13 +820,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, string_view sep) FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep)
-> tuple_join_view<char, Tuple> { -> tuple_join_view<Tuple, char> {
return {tuple, sep}; return {tuple, sep};
} }

View File

@@ -15,15 +15,13 @@
# include <atomic> # include <atomic>
# include <bitset> # include <bitset>
# include <complex> # include <complex>
# include <cstdlib>
# include <exception> # include <exception>
# include <functional> # include <functional> // std::reference_wrapper
# include <memory> # include <memory>
# include <thread> # include <thread>
# include <type_traits> # include <type_traits>
# include <typeinfo> # include <typeinfo> // std::type_info
# include <utility> # include <utility> // std::make_index_sequence
# 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
@@ -62,27 +60,26 @@
# endif # endif
#endif #endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. #ifdef FMT_CPP_LIB_FILESYSTEM
#ifndef FMT_CPP_LIB_FILESYSTEM // Use the provided definition.
# ifdef __cpp_lib_filesystem #elif defined(__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
#ifndef FMT_CPP_LIB_VARIANT #ifdef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant // Use the provided definition.
#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,
@@ -111,8 +108,180 @@ 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>
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_;
@@ -177,24 +346,20 @@ 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
FMT_BEGIN_NAMESPACE template <size_t N, typename Char>
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:
// Functor because C++11 doesn't support generic lambdas. // This is a 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;
} }
}; };
@@ -209,33 +374,22 @@ struct formatter<std::bitset<N>, Char>
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
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<T, Char> underlying_; formatter<std::remove_cv_t<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) {
maybe_set_debug_format(underlying_, true); detail::maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx); return underlying_.parse(ctx);
} }
@@ -251,30 +405,9 @@ 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
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 ||
@@ -292,20 +425,18 @@ 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); out = detail::write_escaped_alternative<Char>(out, *value, ctx);
} else { } else {
out = detail::write<Char>(out, "unexpected("); out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error()); out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
} }
*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
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(); }
@@ -323,42 +454,12 @@ 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>
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 { template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value; static constexpr 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;
}; };
template <typename Char> struct formatter<std::monostate, Char> { template <typename Char> struct formatter<std::monostate, Char> {
@@ -374,10 +475,10 @@ template <typename Char> struct formatter<std::monostate, Char> {
}; };
template <typename Variant, typename Char> template <typename Variant, typename Char>
struct formatter< struct formatter<Variant, Char,
Variant, Char,
std::enable_if_t<std::conjunction_v< std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { is_variant_like<Variant>,
detail::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();
} }
@@ -391,7 +492,7 @@ struct formatter<
FMT_TRY { FMT_TRY {
std::visit( std::visit(
[&](const auto& v) { [&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v); out = detail::write_escaped_alternative<Char>(out, v, ctx);
}, },
value); value);
} }
@@ -402,10 +503,9 @@ struct formatter<
return out; return out;
} }
}; };
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT #endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::error_code> { template <> struct formatter<std::error_code> {
private: private:
format_specs specs_; format_specs specs_;
@@ -413,6 +513,8 @@ template <> struct formatter<std::error_code> {
bool debug_ = false; 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;
@@ -459,101 +561,29 @@ template <> struct formatter<std::error_code> {
}; };
#if FMT_USE_RTTI #if FMT_USE_RTTI
namespace detail { template <> struct formatter<std::type_info> {
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
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public: public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { FMT_CONSTEXPR auto parse(parse_context<>& 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<Char>(ctx.out(), ti); return detail::write_demangled_name(ctx.out(), ti);
} }
}; };
#endif #endif // FMT_USE_RTTI
template <typename T, typename Char> template <typename T>
struct formatter< struct formatter<
T, Char, // DEPRECATED! Mixing code unit types. T, char,
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<Char>& ctx) -> const Char* { FMT_CONSTEXPR auto parse(parse_context<>& 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;
@@ -570,43 +600,15 @@ 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<Char>(out, typeid(ex)); out = detail::write_demangled_name(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.
@@ -621,14 +623,6 @@ 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();
}
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>>
@@ -715,7 +709,11 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char, struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>> // Guard against format_as because reference_wrapper is
// 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
@@ -725,4 +723,5 @@ struct formatter<std::reference_wrapper<T>, Char,
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@@ -55,6 +55,16 @@ 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
@@ -112,10 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}}; return {{s}};
} }
#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...)) {
@@ -151,13 +157,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<wchar_t, Tuple> { -> tuple_join_view<Tuple, wchar_t> {
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,
typename detail::vformat_args<Char>::type args) basic_format_args<buffered_context<Char>> 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);
@@ -187,24 +193,20 @@ 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 Locale, typename S, template <typename S, 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 vformat(locale_ref loc, const S& fmt,
detail::is_exotic_char<Char>::value)> basic_format_args<buffered_context<Char>> args)
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, detail::vformat_to(buf, detail::to_string_view(fmt), args, loc);
detail::locale_ref(loc));
return {buf.data(), buf.size()}; return {buf.data(), buf.size()};
} }
template <typename Locale, typename S, typename... T, template <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_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
detail::is_exotic_char<Char>::value)> inline auto format(locale_ref loc, const S& fmt, T&&... args)
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...));
@@ -215,7 +217,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,
typename detail::vformat_args<Char>::type args) -> OutputIt { basic_format_args<buffered_context<Char>> 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);
@@ -231,27 +233,24 @@ 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 Locale, typename S, typename OutputIt, typename... Args, template <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_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt,
typename detail::vformat_args<Char>::type args) basic_format_args<buffered_context<Char>> 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, detail::locale_ref(loc)); vformat_to(buf, detail::to_string_view(fmt), args, loc);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
template <typename Locale, typename OutputIt, typename S, typename... T, template <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, const Locale& loc, const S& fmt, inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args)
T&&... args) -> -> typename std::enable_if<enable, OutputIt>::type {
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...));
} }
@@ -260,7 +259,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,
typename detail::vformat_args<Char>::type args) basic_format_args<buffered_context<Char>> 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);
@@ -331,18 +330,6 @@ inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
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, 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(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

@@ -510,6 +510,7 @@ 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:
@@ -524,6 +525,10 @@ 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) {
@@ -536,6 +541,7 @@ 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:
@@ -1154,12 +1160,10 @@ 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

@@ -78,8 +78,10 @@ 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";
private: protected:
FILE *target_file_; FILE *target_file_;
private:
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_;

View File

@@ -160,8 +160,12 @@ 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 = QString::fromUtf8(str.data(), msg.color_range_start).size(); color_range_start =
color_range_end = QString::fromUtf8(str.data(), msg.color_range_end).size(); QString::fromUtf8(str.data(), static_cast<qsizetype>(msg.color_range_start))
.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()));
@@ -171,7 +175,7 @@ protected:
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(msg.level), // color to apply colors_.at(static_cast<size_t>(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

View File

@@ -21,7 +21,11 @@ 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

@@ -31,6 +31,8 @@ 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)
@@ -44,10 +46,22 @@ 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) {
this->client_.connect(config_.server_host, config_.server_port); client_.connect(config_.server_host, config_.server_port, config_.timeout_ms);
} }
} }
@@ -58,7 +72,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); client_.connect(config_.server_host, config_.server_port, config_.timeout_ms);
} }
client_.send(formatted.data(), formatted.size()); client_.send(formatted.data(), formatted.size());
} }

View File

@@ -315,8 +315,7 @@ inline void critical(const T &msg) {
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
#define SPDLOG_LOGGER_INFO(logger, ...) \ #define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __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
@@ -324,8 +323,7 @@ inline void critical(const T &msg) {
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
#define SPDLOG_LOGGER_WARN(logger, ...) \ #define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __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
@@ -333,8 +331,7 @@ inline void critical(const T &msg) {
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
#define SPDLOG_LOGGER_ERROR(logger, ...) \ #define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __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

View File

@@ -4,8 +4,8 @@
#pragma once #pragma once
#define SPDLOG_VER_MAJOR 1 #define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 15 #define SPDLOG_VER_MINOR 16
#define SPDLOG_VER_PATCH 3 #define SPDLOG_VER_PATCH 0
#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

@@ -11,36 +11,35 @@
#include <spdlog/fmt/bundled/format-inl.h> #include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp<float>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp<double>;
#if FMT_USE_LOCALE #if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED!
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif #endif
namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept
-> dragonbox::decimal_fp<float>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<char>; template FMT_API auto thousands_sep_impl(locale_ref)
-> 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) -> thousands_sep_result<wchar_t>; template FMT_API auto thousands_sep_impl(locale_ref)
-> 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

@@ -49,7 +49,8 @@ set(SPDLOG_UTESTS_SOURCES
test_time_point.cpp test_time_point.cpp
test_stopwatch.cpp test_stopwatch.cpp
test_circular_q.cpp test_circular_q.cpp
test_bin_to_hex.cpp) test_bin_to_hex.cpp
test_ringbuffer.cpp)
if(NOT SPDLOG_NO_EXCEPTIONS) if(NOT SPDLOG_NO_EXCEPTIONS)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp) list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
@@ -74,7 +75,7 @@ function(spdlog_prepare_test test_target spdlog_lib)
elseif(SPDLOG_SANITIZE_THREAD) elseif(SPDLOG_SANITIZE_THREAD)
spdlog_enable_thread_sanitizer(${test_target}) spdlog_enable_thread_sanitizer(${test_target})
endif() endif()
add_test(NAME ${test_target} COMMAND ${test_target}) add_test(NAME ${test_target} COMMAND ${test_target} --order decl)
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON) set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
endfunction() endfunction()

View File

@@ -23,6 +23,9 @@
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#undef SPDLOG_LEVEL_NAMES
#undef SPDLOG_SHORT_LEVEL_NAMES
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/async.h" #include "spdlog/async.h"
#include "spdlog/details/fmt_helper.h" #include "spdlog/details/fmt_helper.h"

View File

@@ -82,8 +82,13 @@ TEST_CASE("GMT offset ", "[pattern_formatter]") {
const auto now = std::chrono::system_clock::now(); const auto now = std::chrono::system_clock::now();
const auto yesterday = now - 24h; const auto yesterday = now - 24h;
#ifndef SPDLOG_NO_TZ_OFFSET
const std::string expected_result = "+00:00\n";
#else
const std::string expected_result = "+??:??\n";
#endif
REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc, REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc,
"\n") == "+00:00\n"); "\n") == expected_result);
} }
TEST_CASE("color range test1", "[pattern_formatter]") { TEST_CASE("color range test1", "[pattern_formatter]") {

52
tests/test_ringbuffer.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include "includes.h"
#include "spdlog/sinks/ringbuffer_sink.h"
TEST_CASE("ringbuffer invalid size", "[ringbuffer]") {
REQUIRE_THROWS_AS(spdlog::sinks::ringbuffer_sink_mt(0), spdlog::spdlog_ex);
}
TEST_CASE("ringbuffer stores formatted messages", "[ringbuffer]") {
spdlog::sinks::ringbuffer_sink_st sink(3);
sink.set_pattern("%v");
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg1"});
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg2"});
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg3"});
auto formatted = sink.last_formatted();
REQUIRE(formatted.size() == 3);
using spdlog::details::os::default_eol;
REQUIRE(formatted[0] == spdlog::fmt_lib::format("msg1{}", default_eol));
REQUIRE(formatted[1] == spdlog::fmt_lib::format("msg2{}", default_eol));
REQUIRE(formatted[2] == spdlog::fmt_lib::format("msg3{}", default_eol));
}
TEST_CASE("ringbuffer overrun keeps last items", "[ringbuffer]") {
spdlog::sinks::ringbuffer_sink_st sink(2);
sink.set_pattern("%v");
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "first"});
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "second"});
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "third"});
auto formatted = sink.last_formatted();
REQUIRE(formatted.size() == 2);
using spdlog::details::os::default_eol;
REQUIRE(formatted[0] == spdlog::fmt_lib::format("second{}", default_eol));
REQUIRE(formatted[1] == spdlog::fmt_lib::format("third{}", default_eol));
}
TEST_CASE("ringbuffer retrieval limit", "[ringbuffer]") {
spdlog::sinks::ringbuffer_sink_st sink(3);
sink.set_pattern("%v");
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "A"});
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "B"});
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "C"});
auto formatted = sink.last_formatted(2);
REQUIRE(formatted.size() == 2);
using spdlog::details::os::default_eol;
REQUIRE(formatted[0] == spdlog::fmt_lib::format("B{}", default_eol));
REQUIRE(formatted[1] == spdlog::fmt_lib::format("C{}", default_eol));
}

View File

@@ -5,6 +5,7 @@
#include "includes.h" #include "includes.h"
#include "spdlog/sinks/stdout_sinks.h" #include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/stdout_color_sinks.h"
TEST_CASE("stdout_st", "[stdout]") { TEST_CASE("stdout_st", "[stdout]") {
auto l = spdlog::stdout_logger_st("test"); auto l = spdlog::stdout_logger_st("test");
l->set_pattern("%+"); l->set_pattern("%+");
@@ -72,8 +73,14 @@ TEST_CASE("stderr_color_mt", "[stderr]") {
spdlog::drop_all(); spdlog::drop_all();
} }
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT TEST_CASE("show_utc_offset", "[stdout]") {
auto l = spdlog::stdout_color_mt("test");
l->set_pattern("[%c %z] [%n] [%^%l%$] %v");
l->info("Full date");
spdlog::drop_all();
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
TEST_CASE("wchar_api", "[stdout]") { TEST_CASE("wchar_api", "[stdout]") {
auto l = spdlog::stdout_logger_st("wchar_logger"); auto l = spdlog::stdout_logger_st("wchar_logger");
l->set_pattern("%+"); l->set_pattern("%+");
@@ -86,5 +93,4 @@ TEST_CASE("wchar_api", "[stdout]") {
SPDLOG_LOGGER_DEBUG(l, L"Test SPDLOG_LOGGER_DEBUG {}", L"param"); SPDLOG_LOGGER_DEBUG(l, L"Test SPDLOG_LOGGER_DEBUG {}", L"param");
spdlog::drop_all(); spdlog::drop_all();
} }
#endif #endif