Compare commits

..

29 Commits

Author SHA1 Message Date
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
VZ
287333ee00 Remove unnecessary and inconsistent "final" from color sinks (#3430)
The use of "final" differed between ansicolor_sink and wincolor_sink,
resulting in the code inheriting from std{err,out}_color_sink classes,
which are defined as one or the other on different platforms, being able
to override most of the functions under non-Windows platforms, but not
under Windows.

This seems gratuitously inconsistent, so just remove all "final"
keywords from both classes, especially because there doesn't seem any
good reason to use it and the other sink classes don't use it (with the
exception of base_sink, which is special).

This also incidentally fixes using "final override" in most places but
"override final" in wincolor_sink.h.

Fixes #3429.
2025-06-30 07:39:32 +03:00
电线杆
ad725d34cc Use std::getenv #3414 (#3415) 2025-06-08 23:16:34 +03:00
Gabi Melman
e655dbb685 Fix issue #3408
Remove including core.h or base.h
2025-06-07 13:44:09 +03:00
gabime
b18a234ed6 Fix coverity ci 2025-05-12 17:44:17 +03:00
Gabi Melman
5d89b5b91c Update jetbrains logo (#3401)
* Update jetbrains logo
2025-05-12 16:04:04 +03:00
Gabi Melman
37ff466454 Add coverity scan to CI and fix warnings (#3400)
* Move callback function in thread_pool ctor

* Added const qualifiers to logger.h

* Remove unused includes from file_helper-inl.h

* Fix comments and remove unused include from helpers-inl.h

* Fix typo in comment for set_default_logger method.

* Use `std::move` for `old_logger` in `set_default_logger`.

* Use std::move in example

* Wrap `main` content in try block for exception safety.

* Added coverity to ci
2025-05-12 15:36:07 +03:00
Gabi Melman
677a2d93e6 Update test_stopwatch.cpp 2025-05-09 13:22:39 +03:00
Gabi Melman
6fa36017cf Version 1.15.3 2025-05-09 08:55:57 +03:00
Gabi Melman
c73b8cc400 Update comment 2025-05-09 02:50:11 +03:00
Gabi Melman
7ca6a4fb27 Update commemt 2025-05-09 02:49:36 +03:00
Gabi Melman
070e1c9747 Update comment 2025-05-09 02:46:10 +03:00
Gabi Melman
0d31acae28 Fmt 11.2.0 (#3399)
* Bump fmt to 11.2.0
2025-05-08 20:13:03 +03:00
Gabi Melman
943fcbd761 Register replace logger (#3398)
* Add register_or_replace(logger) to atomically replace logger in registry
* fix some  comments
2025-05-08 15:42:54 +03:00
43 changed files with 1894 additions and 1556 deletions

52
.github/workflows/coverity_scan.yml vendored Normal file
View File

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

View File

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

View File

@@ -75,74 +75,5 @@ jobs:
run: |
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
# ---------------------------------------------------------------------------------------
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)
endif()
endif()
# ---------------------------------------------------------------------------------------
# Compiler config
@@ -62,6 +66,9 @@ option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled heade
# build position independent code
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
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
@@ -191,7 +198,7 @@ spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
${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)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)

View File

@@ -80,7 +80,7 @@ int main()
spdlog::info("Positional args are {1} {0}..", "too", "supported");
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..");
// 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
```c++
@@ -524,6 +547,7 @@ Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki)
---
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
### Powered by
<a href="https://jb.gg/OpenSource">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="JetBrains logo" width="200">
</a>

View File

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

View File

@@ -33,6 +33,7 @@ void mdc_example();
#include "spdlog/fmt/ostr.h" // support for user defined types
int main(int, char *[]) {
try {
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
load_levels_example();
@@ -58,8 +59,8 @@ int main(int, char *[]) {
spdlog::set_level(spdlog::level::info);
// Backtrace support
// Loggers can store in a ring buffer all messages (including debug/trace) for later inspection.
// When needed, call dump_backtrace() to see what happened:
// Loggers can store in a ring buffer all messages (including debug/trace) for later
// inspection. When needed, call dump_backtrace() to see what happened:
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
for (int i = 0; i < 100; i++) {
spdlog::debug("Backtrace message {}", i); // not logged..
@@ -67,7 +68,6 @@ int main(int, char *[]) {
// e.g. if some error happened:
spdlog::dump_backtrace(); // log them now!
try {
stdout_logger_example();
basic_example();
rotating_example();
@@ -371,15 +371,13 @@ void replace_default_logger_example() {
// store the old logger so we don't break other examples.
auto old_logger = spdlog::default_logger();
auto new_logger =
spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
spdlog::set_default_logger(new_logger);
auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/somelog.txt", true);
spdlog::set_default_logger(std::move(new_logger));
spdlog::set_level(spdlog::level::info);
spdlog::debug("This message should not be displayed!");
spdlog::set_level(spdlog::level::trace);
spdlog::debug("This message should be displayed..");
spdlog::set_default_logger(old_logger);
spdlog::set_default_logger(std::move(old_logger));
}
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread

View File

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

View File

@@ -11,10 +11,8 @@
#include <spdlog/details/os.h>
#include <cerrno>
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <tuple>
namespace spdlog {

View File

@@ -23,7 +23,6 @@
#ifdef _WIN32
#include <spdlog/details/windows_include.h>
#include <fileapi.h> // for FlushFileBuffers
#include <io.h> // for _get_osfhandle, _isatty, _fileno
#include <process.h> // for _get_pid
@@ -490,7 +489,7 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
result_size =
::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(), result_size);
if (result_size > 0) {
assert(result_size == target.size());
assert(result_size == static_cast<int>(target.size()));
return;
}
}
@@ -563,21 +562,21 @@ SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif // _MSC_VER
std::string SPDLOG_INLINE getenv(const char *field) {
#if defined(_MSC_VER)
#if defined(__cplusplus_winrt)
#if defined(_MSC_VER) && defined(__cplusplus_winrt)
return std::string{}; // not supported under uwp
#else
size_t len = 0;
char buf[128];
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
return ok ? buf : std::string{};
#endif
#else // revert to getenv
char *buf = ::getenv(field);
char *buf = std::getenv(field);
return buf ? buf : std::string{};
#endif
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
// Do fsync by FILE handlerpointer
// Return true on success

View File

@@ -101,7 +101,7 @@ SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); }
// set default logger.
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
// the default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
if (new_default_logger != nullptr) {

View File

@@ -58,8 +58,81 @@ public:
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
void connect(const std::string &host, int port) {
void connect(const std::string &host, int port, int timeout_ms = 0) {
if (is_connected()) {
close();
}
@@ -71,6 +144,10 @@ public:
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
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);
struct addrinfo *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).
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socket_ == INVALID_SOCKET) {
@@ -90,18 +166,24 @@ public:
WSACleanup();
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;
} else {
last_error = ::WSAGetLastError();
close();
}
last_error = WSAGetLastError();
::closesocket(socket_);
socket_ = INVALID_SOCKET;
}
::freeaddrinfo(addrinfo_result);
if (socket_ == INVALID_SOCKET) {
WSACleanup();
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
int enable_flag = 1;

View File

@@ -39,8 +39,72 @@ public:
~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
void connect(const std::string &host, int port) {
void connect(const std::string &host, int port, int timeout_ms = 0) {
close();
struct addrinfo hints {};
memset(&hints, 0, sizeof(struct addrinfo));
@@ -49,6 +113,10 @@ public:
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
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);
struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
@@ -69,8 +137,9 @@ public:
last_errno = errno;
continue;
}
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
if (rv == 0) {
::fcntl(socket_, F_SETFD, FD_CLOEXEC);
if (connect_socket_with_timeout(socket_, rp->ai_addr, rp->ai_addrlen, tv) == 0) {
last_errno = 0;
break;
}
last_errno = errno;
@@ -82,6 +151,12 @@ public:
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
int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),

View File

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

View File

@@ -71,7 +71,7 @@ class dynamic_arg_list {
* It can be implicitly converted into `fmt::basic_format_args` for passing
* 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:
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.
size_t size() const noexcept { return data_.size(); }
auto size() const noexcept -> size_t { return data_.size(); }
};
FMT_END_NAMESPACE

View File

@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110104
#define FMT_VERSION 120000
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -201,28 +201,6 @@
# define FMT_NODISCARD
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
#ifdef FMT_ALWAYS_INLINE
// Use the provided definition.
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE inline
#endif
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
#ifdef NDEBUG
# define FMT_INLINE FMT_ALWAYS_INLINE
#else
# define FMT_INLINE inline
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
@@ -249,10 +227,32 @@
# define FMT_MSC_WARNING(...)
#endif
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
# define FMT_GCC_OPTIMIZED
#endif
FMT_PRAGMA_CLANG(diagnostic push)
#ifdef FMT_ALWAYS_INLINE
// Use the provided definition.
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE inline
#endif
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
# define FMT_INLINE FMT_ALWAYS_INLINE
#else
# define FMT_INLINE inline
#endif
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
inline namespace v11 {
inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
@@ -297,13 +297,6 @@
using unused = int[]; \
(void)unused { 0, (expr, 0)... }
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
#endif
FMT_PRAGMA_CLANG(diagnostic push)
FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other metafunctions for older systems.
@@ -325,8 +318,8 @@ using underlying_t = typename std::underlying_type<T>::type;
template <typename T> using decay_t = typename std::decay<T>::type;
using nullptr_t = decltype(nullptr);
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.9 to make void_t work in a SFINAE context.
#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION
// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl {
using type = void;
};
@@ -355,6 +348,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -395,7 +391,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
: ::fmt::assert_fail(__FILE__, __LINE__, (message)))
#endif
#ifdef FMT_USE_INT128
@@ -462,12 +458,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
return s;
}
template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
-> int {
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1;
@@ -526,20 +523,20 @@ template <typename Char> class basic_string_view {
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
/// Constructs a string reference object from a C string and a size.
/// Constructs a string view object from a C string and a size.
constexpr basic_string_view(const Char* s, size_t count) noexcept
: data_(s), size_(count) {}
constexpr basic_string_view(nullptr_t) = delete;
/// Constructs a string reference object from a C string.
/// Constructs a string view object from a C string.
#if FMT_GCC_VERSION
FMT_ALWAYS_INLINE
#endif
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
if (std::is_same<Char, char>::value) {
size_ = __builtin_strlen(detail::narrow(s));
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
@@ -548,7 +545,7 @@ template <typename Char> class basic_string_view {
size_ = len;
}
/// Constructs a string reference from a `std::basic_string` or a
/// Constructs a string view from a `std::basic_string` or a
/// `std::basic_string_view` object.
template <typename S,
FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
@@ -585,7 +582,6 @@ template <typename Char> class basic_string_view {
return starts_with(basic_string_view<Char>(s));
}
// Lexicographically compare this string reference to other.
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
int result =
detail::compare(data_, other.data_, min_of(size_, other.size_));
@@ -616,19 +612,6 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>;
/// Specifies if `T` is an extended character type. Can be specialized by users.
template <typename T> struct is_xchar : std::false_type {};
template <> struct is_xchar<wchar_t> : std::true_type {};
template <> struct is_xchar<char16_t> : std::true_type {};
template <> struct is_xchar<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_xchar<char8_t> : std::true_type {};
#endif
// DEPRECATED! Will be replaced with an alias to prevent specializations.
template <typename T> struct is_char : is_xchar<T> {};
template <> struct is_char<char> : std::true_type {};
template <typename T> class basic_appender;
using appender = basic_appender<char>;
@@ -781,7 +764,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift);
}
constexpr bool dynamic() const {
constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0;
}
@@ -921,14 +904,47 @@ template <typename Char = char> class parse_context {
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);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
FMT_END_EXPORT
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
// to it, deducing Char. Explicitly convertible types such as the ones returned
// 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> {
return s;
}
@@ -1032,6 +1048,11 @@ enum {
struct view {};
template <typename T, typename Enable = std::true_type>
struct is_view : std::false_type {};
template <typename T>
struct is_view<T, bool_constant<sizeof(T) != 0>> : std::is_base_of<view, T> {};
template <typename Char, typename T> struct named_arg;
template <typename T> struct is_named_arg : std::false_type {};
template <typename T> struct is_static_named_arg : std::false_type {};
@@ -1052,11 +1073,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_named_args() -> int {
return count<is_named_arg<T>::value...>();
}
template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<T>::value...>();
}
template <typename Char> struct named_arg_info {
@@ -1064,6 +1085,16 @@ template <typename Char> struct named_arg_info {
int id;
};
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index,
basic_string_view<Char> arg_name) {
for (int i = 0; i < named_arg_index; ++i) {
if (named_args[i].name == arg_name) report_error("duplicate named arg");
}
}
template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
++arg_index;
@@ -1071,6 +1102,7 @@ void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
int& named_arg_index, const T& arg) {
check_for_duplicate<Char>(named_args, named_arg_index, arg.name);
named_args[named_arg_index++] = {arg.name, arg_index++};
}
@@ -1084,12 +1116,13 @@ template <typename T, typename Char,
FMT_ENABLE_IF(is_static_named_arg<T>::value)>
FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
int& arg_index, int& named_arg_index) {
check_for_duplicate<Char>(named_args, named_arg_index, T::name);
named_args[named_arg_index++] = {T::name, arg_index++};
}
// To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size.
enum { long_short = sizeof(long) == sizeof(int) };
enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES };
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
@@ -1156,7 +1189,7 @@ template <typename Char> struct type_mapper {
static auto map(ubitint<N>)
-> 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<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
@@ -1662,12 +1695,12 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
type types_[max_of<size_t>(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
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:
template <typename... T>
@@ -1706,7 +1739,17 @@ class format_string_checker {
-> const Char* {
context_.advance_to(begin);
if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
while (begin != end && *begin != '}') ++begin;
// If id is out of range, it means we do not know the type and cannot parse
// the format at compile time. Instead, skip over content until we finish
// the format spec, accounting for any nested replacements.
for (int bracket_count = 0;
begin != end && (bracket_count > 0 || *begin != '}'); ++begin) {
if (*begin == '{')
++bracket_count;
else if (*begin == '}')
--bracket_count;
}
return begin;
}
@@ -2006,6 +2049,17 @@ struct has_back_insert_iterator_container_append<
.append(std::declval<InputIt>(),
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).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
@@ -2020,6 +2074,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template <typename T, typename InputIt, typename OutputIt,
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_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
@@ -2029,7 +2085,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
}
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 {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
@@ -2149,7 +2209,7 @@ template <typename Context> class value {
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) {
static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
@@ -2225,7 +2285,7 @@ template <typename Context> class value {
custom.value = const_cast<value_type*>(&x);
#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>())>
@@ -2236,10 +2296,10 @@ template <typename Context> class value {
}
// 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,
Context& ctx) {
auto f = Formatter();
auto f = formatter<T, char_type>();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>;
@@ -2266,35 +2326,14 @@ struct is_output_iterator<
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
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 {
return 0;
}
template <typename Context, typename Arg, typename... Args>
template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
(encode_types<Context, T...>() << packed_arg_bits);
}
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
@@ -2311,8 +2350,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
named_arg_info<typename Context::char_type>
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2331,8 +2371,8 @@ struct named_arg_store {
}
named_arg_store(const named_arg_store& rhs) = delete;
named_arg_store& operator=(const named_arg_store& rhs) = delete;
named_arg_store& operator=(named_arg_store&& rhs) = delete;
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
};
@@ -2345,7 +2385,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
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>>;
type args;
};
@@ -2629,22 +2669,17 @@ class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public:
/// The character type for the output.
using char_type = char;
using char_type = char; ///< The character type for the output.
using iterator = appender;
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 };
/// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
@@ -2665,7 +2700,7 @@ class context {
// Advances the begin iterator to `it`.
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 {
@@ -2703,7 +2738,7 @@ template <typename... T> struct fstring {
template <size_t N>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
using namespace detail;
static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
std::is_reference<T>::value)...>() == 0,
"passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
@@ -2752,9 +2787,6 @@ template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif
template <typename T, typename Char>
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
// A formatter specialization for natively supported types.
template <typename T, typename Char>
struct formatter<T, Char,
@@ -2951,9 +2983,9 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY

View File

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

View File

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

View File

@@ -22,8 +22,6 @@ FMT_EXPORT class compiled_string {};
template <typename 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
* compile time and converted into efficient formatting code. Requires C++17
@@ -41,18 +39,40 @@ namespace detail {
# define FMT_COMPILE(s) FMT_STRING(s)
#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
namespace detail {
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;
}
#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...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
constexpr auto get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) -> const auto& {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -84,8 +104,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) -> int {
return get_arg_index_by_name<Args...>(name);
}
@@ -105,8 +125,8 @@ template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
return write<Char>(out, data);
}
};
@@ -115,8 +135,8 @@ template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
-> text<Char> {
return {{&s[pos], size}};
}
@@ -124,8 +144,8 @@ template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
*out++ = value;
return out;
}
@@ -133,7 +153,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`.
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...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
@@ -146,13 +166,13 @@ template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// 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;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
const V& arg = get_arg_checked<V, N>(args...);
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
@@ -170,10 +190,10 @@ template <typename Char> struct runtime_named_field {
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
constexpr static auto try_format_argument(
OutputIt& out,
// [[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 (arg_name == arg.name) {
out = write<Char>(out, arg.value);
@@ -183,8 +203,8 @@ template <typename Char> struct runtime_named_field {
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
@@ -197,17 +217,17 @@ template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
template <typename Char, typename V, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
formatter<V, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
-> OutputIt {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
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 +239,8 @@ template <typename L, typename R> struct concat {
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
@@ -230,14 +250,14 @@ template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
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};
}
struct unknown_format {};
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) {
if (str[pos] == '{' || str[pos] == '}') break;
}
@@ -270,8 +290,8 @@ template <typename T, typename Char> struct parse_specs_result {
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
int next_arg_id) -> parse_specs_result<T, Char> {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
@@ -285,16 +305,16 @@ template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
constexpr auto on_auto() -> int {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
constexpr auto on_index(int id) -> int {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
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;
arg_id = arg_ref<Char>(id);
return 0;
@@ -433,27 +453,28 @@ FMT_BEGIN_EXPORT
#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,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
const T&... args)
-> std::basic_string<Char> {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
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)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
const T&... args) -> OutputIt {
return cf.format(out, args...);
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
-> std::basic_string<typename S::char_type> {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
@@ -466,72 +487,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)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} 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_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
return fmt::format_to(out, compiled, std::forward<T>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
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> {
using traits = detail::fixed_buffer_traits;
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()};
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
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();
}
template <typename S, typename... Args,
template <typename S, typename... T,
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();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
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)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
void print(const S& fmt, T&&... args) {
print(stdout, fmt, std::forward<T>(args)...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
template <size_t N> class static_format_result {
private:
char data[N];
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_NAMESPACE

View File

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

View File

@@ -22,7 +22,7 @@
#include "format.h"
#if FMT_USE_LOCALE
#if FMT_USE_LOCALE && !defined(FMT_MODULE)
# include <locale>
#endif
@@ -31,14 +31,49 @@
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#ifndef FMT_CUSTOM_ASSERT_FAIL
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort();
}
#endif
#if FMT_USE_LOCALE
namespace detail {
using std::locale;
using std::numpunct;
using std::use_facet;
} // namespace detail
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
#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,
string_view message) noexcept {
@@ -79,33 +114,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
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>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
@@ -133,14 +141,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
} // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#if FMT_MSC_VERSION || defined(__NVCC__)
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (!b) return;
#endif
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -174,11 +181,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
}
// 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;
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;
return (n >> r) | (n << (64 - r));
}
@@ -212,7 +219,7 @@ inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
return (e * 631305 - 261663) >> 21;
}
FMT_INLINE_VARIABLE constexpr struct {
FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct {
uint32_t divisor;
int shift_amount;
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@@ -275,7 +282,7 @@ template <> struct cache_accessor<float> {
static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
static constexpr uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -370,7 +377,7 @@ template <> struct cache_accessor<double> {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr const uint128_fallback pow10_significands[] = {
static constexpr uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1037,7 +1044,7 @@ template <> struct cache_accessor<double> {
#if FMT_USE_FULL_CACHE_DRAGONBOX
return pow10_significands[k - float_info<double>::min_k];
#else
static constexpr const uint64_t powers_of_5_64[] = {
static constexpr uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1097,7 +1104,7 @@ template <> struct cache_accessor<double> {
return {r.high(), r.low() == 0};
}
static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
-> uint32_t {
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
}
@@ -1149,8 +1156,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
}
// 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 {
// Remove trailing zeros from n and return the number of zeros removed (float).
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd;
@@ -1170,22 +1177,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
return s;
}
// Removes trailing zeros and returns the number of zeros removed (double)
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Removes trailing zeros and returns the number of zeros removed (double).
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
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?
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...
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
int s = remove_trailing_zeros(n32, 8);
int num_zeros = remove_trailing_zeros(n32, 8);
n = n32;
return s;
return num_zeros;
}
// If n is not divisible by 10^8, work with n itself.
@@ -1210,7 +1214,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// The main algorithm for shorter interval case
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;
// Compute k and beta
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
@@ -1454,8 +1458,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
parse_format_string(fmt,
format_handler<>{parse_context<>(fmt), {out, args, loc}});
}
template <typename T> struct span {
@@ -1526,9 +1530,8 @@ template <typename F> class glibc_file : public file_base<F> {
}
void init_buffer() {
if (this->file_->_IO_write_ptr) return;
if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return;
// Force buffer initialization by placing and removing a char in a buffer.
assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr;
}
@@ -1547,10 +1550,11 @@ template <typename F> class glibc_file : public file_base<F> {
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;
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_); }
@@ -1574,7 +1578,7 @@ template <typename F> class apple_file : public file_base<F> {
void init_buffer() {
if (this->file_->_p) return;
// 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_->_w;
}
@@ -1595,7 +1599,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size;
}
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
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__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
!defined(__wasm__)
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else

View File

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

View File

@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <algorithm> // std::find
# include <limits> // std::numeric_limits
#endif
@@ -18,10 +18,6 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
@@ -33,8 +29,6 @@ template <typename Char> class basic_printf_context {
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// 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_; }
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> {
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
// 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 {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
return value <= to_unsigned(max_value<int>());
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
@@ -95,7 +88,7 @@ struct printf_precision_handler {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
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)>
@@ -410,7 +403,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
arg_index = parse_ctx.next_arg_id();
else
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();
@@ -571,14 +566,18 @@ inline auto vsprintf(basic_string_view<Char> fmt,
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
template <typename... T>
inline auto sprintf(string_view fmt, const T&... args) -> std::string {
return vsprintf(fmt, make_printf_args(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>
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 {
auto buf = basic_memory_buffer<Char>();
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");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
template <typename... T>
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
template <typename... T>
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
}
/**
@@ -621,11 +617,6 @@ template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
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_NAMESPACE

View File

@@ -11,7 +11,6 @@
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
@@ -31,7 +30,7 @@ template <typename T> class is_map {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -40,17 +39,16 @@ template <typename T> class is_set {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// 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* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
@@ -120,7 +118,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -154,7 +152,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
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 <size_t... Is>
@@ -170,7 +168,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{}));
public:
static constexpr const bool value =
static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
@@ -208,7 +206,7 @@ template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
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...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
@@ -219,7 +217,7 @@ template <typename R> struct range_reference_type_impl {
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&;
};
@@ -281,14 +279,15 @@ template <typename FormatContext> struct format_tuple_element {
} // namespace detail
FMT_EXPORT
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;
};
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
@@ -343,8 +342,9 @@ struct formatter<Tuple, Char,
}
};
FMT_EXPORT
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;
};
@@ -368,6 +368,7 @@ template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@@ -670,7 +671,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;
basic_string_view<Char> sep;
@@ -685,15 +687,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
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 {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@@ -725,14 +727,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
}
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 ->
typename FormatContext::iterator {
return ctx.out();
}
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 ->
typename FormatContext::iterator {
using std::get;
@@ -754,7 +756,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -774,13 +776,13 @@ struct formatter<
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& t) -> all {
return {t.*(&getter::c)}; // Access c through the derived class.
static auto get(const T& v) -> all {
return {v.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(t), ctx);
return formatter<all>::format(getter::get(value), ctx);
}
};
@@ -825,7 +827,7 @@ auto join(Range&& r, string_view sep)
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
}

View File

@@ -15,15 +15,13 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
# include <typeinfo> // std::type_info
# include <utility> // std::make_index_sequence
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
@@ -62,27 +60,26 @@
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
#ifdef FMT_CPP_LIB_FILESYSTEM
// Use the provided definition.
#elif defined(__cpp_lib_filesystem)
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
#else
# define FMT_CPP_LIB_FILESYSTEM 0
#endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
#ifdef FMT_CPP_LIB_VARIANT
// Use the provided definition.
#elif defined(__cpp_lib_variant)
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
#else
# define FMT_CPP_LIB_VARIANT 0
#endif
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
@@ -111,9 +108,168 @@ 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>
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);
}
#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
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), &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 (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
}
#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
FMT_EXPORT
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
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
@@ -163,40 +319,20 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
template <size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
// This is a functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename 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'));
}
return out;
}
};
@@ -209,14 +345,10 @@ struct formatter<std::bitset<N>, Char>
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
@@ -255,31 +387,9 @@ struct formatter<std::optional<T>, Char,
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#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
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
@@ -306,12 +416,9 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
@@ -329,45 +436,14 @@ template <> struct formatter<std::source_location> {
return out;
}
};
FMT_END_NAMESPACE
#endif
#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 {
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;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
@@ -380,12 +456,11 @@ template <typename Char> struct formatter<std::monostate, Char> {
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
struct formatter<Variant, Char,
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* {
return ctx.begin();
}
@@ -410,15 +485,14 @@ struct formatter<
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
bool debug_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
@@ -426,11 +500,19 @@ template <> struct formatter<std::error_code> {
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
if (it != end && ((c >= '0' && c <= '9') || c == '{'))
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && *it == 's') {
specs_.set_type(presentation_type::string);
++it;
}
return it;
}
@@ -440,113 +522,48 @@ template <> struct formatter<std::error_code> {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
memory_buffer buf;
auto buf = memory_buffer();
if (specs_.type() == presentation_type::string) {
buf.append(ec.message());
} else {
buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
specs);
}
auto quoted = memory_buffer();
auto str = string_view(buf.data(), buf.size());
if (debug_) {
detail::write_escaped_string<char>(std::back_inserter(quoted), str);
str = string_view(quoted.data(), quoted.size());
}
return detail::write<char>(ctx.out(), str, specs);
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
template <> struct formatter<std::type_info> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> 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
FMT_EXPORT
template <typename T, typename Char>
template <typename T>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
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 end = ctx.end();
if (it == end || *it == '}') return it;
@@ -563,47 +580,18 @@ struct formatter<
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
out = detail::write_demangled_name(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#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
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
@@ -615,15 +603,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();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@@ -636,7 +615,6 @@ struct formatter<std::atomic<T>, Char,
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
@@ -647,7 +625,6 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
@@ -710,10 +687,13 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
}
};
FMT_EXPORT
template <typename T, typename 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> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
@@ -723,4 +703,5 @@ struct formatter<std::reference_wrapper<T>, Char,
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

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

View File

@@ -20,11 +20,7 @@
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
#include <spdlog/fmt/bundled/core.h>
#include <spdlog/fmt/bundled/format.h>
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
#include <fmt/core.h>
#include <fmt/format.h>
#endif

View File

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

View File

@@ -363,14 +363,14 @@ protected:
virtual void sink_it_(const details::log_msg &msg);
virtual void flush_();
void dump_backtrace_();
bool should_flush_(const details::log_msg &msg);
bool should_flush_(const details::log_msg &msg) const;
// handle errors during logging.
// default handler prints the error to stderr at max rate of 1 message/sec.
void err_handler_(const std::string &msg);
void err_handler_(const std::string &msg) const;
};
void swap(logger &a, logger &b);
void swap(logger &a, logger &b) noexcept;
} // namespace spdlog

View File

@@ -40,7 +40,7 @@ public:
void log(const details::log_msg &msg) override;
void flush() override;
void set_pattern(const std::string &pattern) final override;
void set_pattern(const std::string &pattern) override;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
// Formatting codes
@@ -78,8 +78,10 @@ public:
const string_view_t red_bold = "\033[31m\033[1m";
const string_view_t bold_on_red = "\033[1m\033[41m";
private:
protected:
FILE *target_file_;
private:
mutex_t &mutex_;
bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_;

View File

@@ -160,8 +160,8 @@ protected:
payload = QString::fromUtf8(str.data(), static_cast<int>(str.size()));
// convert color ranges from byte index to character index.
if (msg.color_range_start < msg.color_range_end) {
color_range_start = QString::fromUtf8(str.data(), msg.color_range_start).size();
color_range_end = QString::fromUtf8(str.data(), msg.color_range_end).size();
color_range_start = 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 {
payload = QString::fromLatin1(str.data(), static_cast<int>(str.size()));
@@ -171,7 +171,7 @@ protected:
qt_text_edit_, // text edit to append to
std::move(payload), // text to append
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_end}; // color range end

View File

@@ -21,7 +21,11 @@ template <typename Mutex>
class ringbuffer_sink final : public base_sink<Mutex> {
public:
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::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);

View File

@@ -31,6 +31,8 @@ namespace sinks {
struct tcp_sink_config {
std::string server_host;
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
tcp_sink_config(std::string host, int port)
@@ -44,10 +46,22 @@ public:
// connect to tcp host/port or throw if failed
// 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)
: config_{std::move(sink_config)} {
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::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
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());
}

View File

@@ -31,10 +31,10 @@ public:
// change the color for the given level
void set_color(level::level_enum level, std::uint16_t color);
void log(const details::log_msg &msg) final override;
void flush() final override;
void set_pattern(const std::string &pattern) override final;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
void log(const details::log_msg &msg) override;
void flush() override;
void set_pattern(const std::string &pattern) override;
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
void set_color_mode(color_mode mode);
protected:

View File

@@ -95,7 +95,7 @@ SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
// Register the given logger with the given name
// Will replace any the existing logger with the same name if exists.
// Will replace any existing logger with the same name.
SPDLOG_API void register_or_replace(std::shared_ptr<logger> logger);
// Apply a user-defined function on all registered loggers

View File

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

View File

@@ -11,36 +11,35 @@
#include <spdlog/fmt/bundled/format-inl.h>
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
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
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.
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;
// DEPRECATED!
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.
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;
// DEPRECATED!
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail

View File

@@ -49,7 +49,8 @@ set(SPDLOG_UTESTS_SOURCES
test_time_point.cpp
test_stopwatch.cpp
test_circular_q.cpp
test_bin_to_hex.cpp)
test_bin_to_hex.cpp
test_ringbuffer.cpp)
if(NOT SPDLOG_NO_EXCEPTIONS)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
@@ -74,7 +75,7 @@ function(spdlog_prepare_test test_target spdlog_lib)
elseif(SPDLOG_SANITIZE_THREAD)
spdlog_enable_thread_sanitizer(${test_target})
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)
endfunction()

View File

@@ -27,12 +27,13 @@ TEST_CASE("explicit register", "[registry]") {
TEST_CASE("register_or_replace", "[registry]") {
spdlog::drop_all();
auto logger1 = std::make_shared<spdlog::logger>(tested_logger_name,
std::make_shared<spdlog::sinks::null_sink_st>());
auto logger1 = std::make_shared<spdlog::logger>(
tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
spdlog::register_logger(logger1);
REQUIRE(spdlog::get(tested_logger_name) == logger1);
auto logger2 = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
auto logger2 = std::make_shared<spdlog::logger>(
tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
spdlog::register_or_replace(logger2);
REQUIRE(spdlog::get(tested_logger_name) == logger2);
}

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,7 +5,7 @@
TEST_CASE("stopwatch1", "[stopwatch]") {
using std::chrono::milliseconds;
using clock = std::chrono::steady_clock;
milliseconds wait_ms(200);
milliseconds wait_ms(500);
milliseconds tolerance_ms(250);
auto start = clock::now();
spdlog::stopwatch sw;
@@ -22,7 +22,7 @@ TEST_CASE("stopwatch2", "[stopwatch]") {
using std::chrono::milliseconds;
using clock = std::chrono::steady_clock;
clock::duration wait_duration(milliseconds(200));
clock::duration wait_duration(milliseconds(500));
clock::duration tolerance_duration(milliseconds(250));
auto test_sink = std::make_shared<test_sink_st>();