From fca3b644b31a19bb12194b40fd3086688f012789 Mon Sep 17 00:00:00 2001
From: irisz64
Date: Thu, 26 Jun 2025 22:16:38 +0200
Subject: [PATCH] Squashed 'external/fmt/' changes from 093b39ca5..353bd895a
353bd895a Add FMT_EXPORT on ranges.h customization points (#4476)
953cffa70 Replace memset with constexpr fill_n in bigint::align (#4471)
571c02d47 Add xchar support for std::byte formatter (#4480)
f4345467f Fix compilation on clang-21 / libc++-21 (#4477)
1ef834807 Properly constrain `detail::copy` optimization (#4474)
a5dccffa5 Add double and float support to scan test
4a149f513 Test non-SSO constexpr string formatting
067bc479b Avoid redundant work when processing UTF-8 strings (#4475)
730fd4d9a Remove redundant tests
5860688d7 Enable constexpr support for fmt::format (fmtlib#3403) (#4456)
46be88bc1 Cleanup FP formatting
cc8891490 Export fmt::dynamic_format_arg_store in fmt module (#4459)
fc0c76a07 Handle large precision
6332a3852 Bump ossf/scorecard-action from 2.4.0 to 2.4.2 (#4462)
02de29e00 Remove a reference to a compromised account
6d51c78c1 Cleanup FP formatting
0f4e9d0bd Cleanup FP formatting
d9d50495a Optimize the default FP formatting
befbc5fdb Fix ADL lookup for memory_buffer
8aa1d6a9f Minor cleanup
6d79757a3 Interpret precision as display width (#4443)
1ff0b7f5e Cleanup warning suppression
ea985e84f Remove some implicit conversions (#4447)
f7033da09 Avoid include locale inline if C++20 modules are enabled (#4451)
b723c021d Give useful error when misusing fmt::ptr. (#4453)
3ba3c390f Clarify that formatting of pointers is disallowed
ab161a71c Fix some typos in comments (#4448)
b5266fd3b Remove some redundant `const`s (#4445)
9b0ebd443 Cleanup base-test
7af94e559 Remove old gcc workaround
2924fcf8f Cleanup base-test
102752ad4 Update docs
a6cd72c9e Cleanup base-test
07885271a Minor cleanup
4999416e5 Fix reference_wrapper ambiguity with format_as (#4434)
55a8f6a4b Change component prefix for NSIS compatibility (#4442)
eb9a95d42 Clarify that formatting of pointers is disallowed
d5c33e4f4 Make template parameter order consistent
a2225f288 Remove unused include
b43b2f953 Cleanup standard formatters
1312b4a16 Cleanup standard formatters
4404dc05d Consolidate implementation details
7bb6fcb32 Bump version
59259a5fd Make a doc directory if it doesn't exist
542ea7c40 Clarify that Formatter parameter is deprecated
40626af88 Update version
7fdd6846b Bump version
6caff7ed9 Cleanup test
71a548387 Update changelog
448929d49 Update and apply clang-format
26d87edab Bump github/codeql-action from 3.28.13 to 3.28.16 (#4432)
505ee058f Update changelog
ccab41719 Update changelog
ec1349d34 Update changelog
0ed2a65a8 Clarify why we use __builtin_strlen instead of strlen
e22c94307 Update changelog
b252bad3c Update changelog
268083123 Cleanun string_view
8978ab09b Avoiding __builtin_strlen (#4429)
c936e2e44 Implement debug format for error_code
a7d7b894c Implement the s specifier for error_code
e98155a6f Remove redundant specializations
41b3bed4d Clarify why we don't use qualified names
67d9e4932 Update changelog
9db5e4df2 Don't specialize std::is_floating_point
906eaf2dd Make specifier order consistent
9f6c12c3d Remove deprecated localtime from docs
2d0518b5f Fix cmake error in pedantic mode (#4426)
c81cbed2b Simplify test
c7925241c Remove `core.h` from README (#4422)
c70913835 Add support for incomplete types
db405954c Remove `fmt/core.h` from docs (#4421)
0a917ee2f Minor comment tweak
969d4aef6 Update doc image
8061c7c8c Cleanup duration formatter
7b59df411 Remove redundant member
b8192d233 Fix build error with MSVC v141 (#4413)
e814b5fab Reduce template parametrization
ed0d216f7 Fix localization and formatting of timezone names
bd9554a29 Fix formatting of timezone names
f086dc0d2 Fix timezone handling in tm
f10b6dd81 Improve chrono formatting
f470b9c56 Cleanup chrono tests and set consistent TZ
b28214487 Fix handling of %Z
6d69f0c5f Improve chorno tests
da776c9a6 Test timezone
64db979e3 Added a missing FMT_STRING in fmt::println() (#4407)
5f2e61fdd Cleanup chrono detail
b3d45e1d3 Remove fmt_detail
5f6fb96df Bump github/codeql-action from 3.28.8 to 3.28.13 (#4403)
5199e0f88 Fix a flush issue on libstdc++
2f5843057 Move buffering tests to os-test
d5d32c1e8 Bazel support: Update platforms to 0.0.11 (#4400)
204661287 Improve local_time test
e1ab38336 Report an error when timezone is not available
b9e0e94a0 Enable more chrono tests on Windows
a81842428 Update changelog
f53055efe Revert "Workaround an ABI issue in spdlog"
b2dfcb2b8 Fix local_time test
7ac97cbd1 Enable some local_time tests and make them deterministic
17898794a Use fmt::local_time
443a8ef34 Deprecate fmt::localtime
3607e92dc Bump version
43e31614c Test ambiguous time
989826ce5 Update changelog
9d6e24c64 Fix handling of long with FMT_BUILTIN_TYPES=0
0843317e0 Update changelog
784eac839 Workaround an ABI issue in spdlog
6fdf225a3 Always inline value ctors in optimized gcc mode only
332da79bf Always inline value ctors
7b273fbb5 Minor cleanup
191c504b1 Cleanup build config
d13fb6092 Cleanup build config
dd780fde4 Add clang-3.4
37e647471 Fix dynamic named arg format spec handling (#4361)
77c0fc07d Switch to supported ubuntu image
9212ff6ca Apply coding conventions and use constexpr
864bdf963 Report error on duplicate named arg names (#4367)
b776cf66f Optimize `text_style` using bit packing (#4363)
bdbf957b9 Bump msys2/setup-msys2 from 2.25.0 to 2.27.0
577fd3be8 Fix TU-local entity exposition error in GCC 15
faac8b1fa Remove exports in std.h
123913715 Update version
8c1059b92 Update changelog
4e5aafbf4 Bump version
db30fb3b8 Update changelog
3401ce2be Fix ABI compatibility
7f7695524 Fix conflict with std::ignore (#4356)
251320fcb Add .vs folder to .gitignore (#4355)
94ab51cb8 Simplify implementation of `operator""_cf` (#4349)
0ca42e836 Workaround an MSVC v140 bug
ed27df576 Replace forward slashes by backslashes in BMI path for MSVC. (#4344)
d42a068db Apply coding conventions
f2cec917d Move is_compiled_string to public API (#4342)
d5b866e24 fix gcc 8.3 compile errors (#4336)
5676e408f Bump github/codeql-action from 3.27.0 to 3.28.8 (#4337)
71d24b564 Bump actions/upload-artifact from 4.4.0 to 4.6.0 (#4339)
c9267da4d Fix typo in `FMT_HAS_BUILTIN` check
373855c1b Clarify difference in FP representation
52eeeb52a Make exponent threshold depend on representation (#3649)
9cf9f38ed Update version
4946bdb72 Update changelog
01a5b56f0 Fix error of unitialized variable FMT_HEADERS
cb6fdf219 Restore constraint on map formatter (#4326)
f841ae61e Fix #4303: avoid instantiating formatter (#4325)
a3d05d70c Silence a constexpr warning when compiling with MSVC and /W4 (#4322)
41539c29f Workaround a bug in gcc 6 (#4318)
aabe63910 Tweak changelog
f90090be2 Update changelog
9ff9c695d Bump version
06ad1224e Update changelog
5f0572acd Workaround a compilation error on gcc 9.4
898d43857 Fix formatting into std::ostreambuf_iterator using a compiled format (#4312)
937b7c5c1 Add args() accessor back to fmt::format_context (#4310)
01914f038 Reduce size of basic_specs
c43da3570 Workaround an ICE when using modules with gcc 14.2 and earlier
8303d140a Update version
b0b3dc5ff Bump version
586ea06f0 Rename set_fill to copy_fill_from
5750f434f Update changelog
bfbdc2be9 Add parameter to the fallback to_sys function.
87e007267 Update changelog
d57040f94 Prefix components
21aa0956d Restore ABI compatibility
3f864a450 Address MSVC C4127 warning when formatting non unicode `tm` (#4299)
git-subtree-dir: external/fmt
git-subtree-split: 353bd895a2bf9d0b1bc5977dc002fb6e0cdb0960
---
.clang-format | 1 +
.github/workflows/cifuzz.yml | 2 +-
.github/workflows/doc.yml | 5 +-
.github/workflows/linux.yml | 156 ++++++----
.github/workflows/macos.yml | 2 +-
.github/workflows/scorecard.yml | 6 +-
.github/workflows/windows.yml | 4 +-
.gitignore | 1 +
CMakeLists.txt | 24 +-
ChangeLog.md | 184 ++++++++++++
README.md | 4 +-
doc/ChangeLog-old.md | 2 +-
doc/api.md | 28 +-
doc/index.md | 2 +-
include/fmt/args.h | 2 +-
include/fmt/base.h | 171 +++++++----
include/fmt/chrono.h | 416 +++++++++++++--------------
include/fmt/color.h | 203 +++++++------
include/fmt/compile.h | 42 +--
include/fmt/format-inl.h | 17 +-
include/fmt/format.h | 486 ++++++++++++++++++-------------
include/fmt/ostream.h | 9 +-
include/fmt/ranges.h | 60 ++--
include/fmt/std.h | 433 ++++++++++++++--------------
include/fmt/xchar.h | 43 +--
src/fmt.cc | 2 +
src/os.cc | 8 +-
support/bazel/.bazelversion | 2 +-
support/bazel/BUILD.bazel | 2 +
support/bazel/MODULE.bazel | 3 +-
support/bazel/README.md | 2 +-
support/mkdocs | 4 +-
test/base-test.cc | 496 +++++++++++++++++---------------
test/chrono-test.cc | 355 ++++++++++-------------
test/color-test.cc | 59 +++-
test/compile-test.cc | 51 +++-
test/format-impl-test.cc | 7 +-
test/format-test.cc | 127 ++++----
test/no-builtin-types-test.cc | 5 +-
test/os-test.cc | 76 +++++
test/ranges-test.cc | 32 ++-
test/scan.h | 53 ++++
test/std-test.cc | 68 +++--
test/xchar-test.cc | 108 +------
44 files changed, 2166 insertions(+), 1597 deletions(-)
diff --git a/.clang-format b/.clang-format
index 31f8c343..08fa7ba8 100644
--- a/.clang-format
+++ b/.clang-format
@@ -7,6 +7,7 @@ IndentCaseLabels: false
AlwaysBreakTemplateDeclarations: false
DerivePointerAlignment: false
AllowShortCaseLabelsOnASingleLine: true
+QualifierAlignment: Left
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: true
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 50182ee9..3769e2a8 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -25,7 +25,7 @@ jobs:
language: c++
- name: Upload crash
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index 019a85f1..d76eb47e 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -7,8 +7,7 @@ permissions:
jobs:
build:
- # Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken.
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
@@ -25,7 +24,7 @@ jobs:
run: |
sudo apt update
sudo apt install doxygen
- pip install mkdocs-material==9.5.25 mkdocstrings==0.25.1 mike==2.1.1
+ pip install mkdocs-material==9.5.25 mkdocstrings==0.26.1 mike==2.1.1
cmake -E make_directory ${{runner.workspace}}/build
# Workaround https://github.com/actions/checkout/issues/13:
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 97150467..42a2d2ce 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -7,87 +7,143 @@ permissions:
jobs:
build:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
strategy:
matrix:
- cxx: [g++-4.9, g++-10, clang++-9]
+ cxx: [g++-4.9, g++-11, clang++-3.6, clang++-11]
build_type: [Debug, Release]
std: [11]
shared: [""]
include:
- cxx: g++-4.9
- install: sudo apt install g++-4.9
- - cxx: g++-8
+ - cxx: clang++-3.6
+ - cxx: g++-11
build_type: Debug
std: 14
- install: sudo apt install g++-8
- - cxx: g++-8
- build_type: Debug
- std: 17
- install: sudo apt install g++-8
- - cxx: g++-9
- build_type: Debug
- std: 17
- - cxx: g++-10
+ install: sudo apt install g++-11
+ - cxx: g++-11
build_type: Debug
std: 17
- cxx: g++-11
build_type: Debug
std: 20
install: sudo apt install g++-11
- - cxx: clang++-8
- build_type: Debug
- std: 17
- cxxflags: -stdlib=libc++
- install: sudo apt install clang-8 libc++-8-dev libc++abi-8-dev
- - cxx: clang++-9
- install: sudo apt install clang-9
- - cxx: clang++-9
- build_type: Debug
- fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
- std: 17
- install: sudo apt install clang-9
- - cxx: clang++-11
- build_type: Debug
- std: 20
- - cxx: clang++-11
- build_type: Debug
- std: 20
- cxxflags: -stdlib=libc++
- install: sudo apt install libc++-11-dev libc++abi-11-dev
- cxx: g++-13
build_type: Release
std: 23
install: sudo apt install g++-13
shared: -DBUILD_SHARED_LIBS=ON
+ - cxx: clang++-11
+ build_type: Debug
+ std: 17
+ cxxflags: -stdlib=libc++
+ install: sudo apt install clang-11 libc++-11-dev libc++abi-11-dev
+ - cxx: clang++-11
+ install: sudo apt install clang-11
+ - cxx: clang++-11
+ build_type: Debug
+ fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
+ std: 17
+ install: sudo apt install clang-11
+ - cxx: clang++-14
+ build_type: Debug
+ std: 20
+ - cxx: clang++-14
+ build_type: Debug
+ std: 20
+ cxxflags: -stdlib=libc++
+ install: sudo apt install libc++-14-dev libc++abi-14-dev
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set timezone
- run: sudo timedatectl set-timezone 'Asia/Yekaterinburg'
+ run: sudo timedatectl set-timezone 'Europe/Kyiv'
- - name: Add repositories for older GCC
+ - name: Install GCC 4.9
run: |
- # Below repo provides GCC 4.9.
- sudo apt-add-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ xenial main'
- sudo apt-add-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe'
+ sudo apt update
+ sudo apt install libatomic1 libc6-dev libgomp1 libitm1 libmpc3
+ # https://launchpad.net/ubuntu/xenial/amd64/g++-4.9/4.9.3-13ubuntu2
+ wget --no-verbose \
+ http://launchpadlibrarian.net/230069137/libmpfr4_3.1.3-2_amd64.deb \
+ http://launchpadlibrarian.net/253728424/libasan1_4.9.3-13ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/445346135/libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346112/libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/253728426/libgcc-4.9-dev_4.9.3-13ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/253728432/libstdc++-4.9-dev_4.9.3-13ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/253728314/gcc-4.9-base_4.9.3-13ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/445345919/gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/253728399/cpp-4.9_4.9.3-13ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/253728404/gcc-4.9_4.9.3-13ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/253728401/g++-4.9_4.9.3-13ubuntu2_amd64.deb
+ sudo dpkg -i \
+ libmpfr4_3.1.3-2_amd64.deb \
+ libasan1_4.9.3-13ubuntu2_amd64.deb \
+ libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libgcc-4.9-dev_4.9.3-13ubuntu2_amd64.deb \
+ libstdc++-4.9-dev_4.9.3-13ubuntu2_amd64.deb \
+ gcc-4.9-base_4.9.3-13ubuntu2_amd64.deb \
+ gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ cpp-4.9_4.9.3-13ubuntu2_amd64.deb \
+ gcc-4.9_4.9.3-13ubuntu2_amd64.deb \
+ g++-4.9_4.9.3-13ubuntu2_amd64.deb
if: ${{ matrix.cxx == 'g++-4.9' }}
+ - name: Install Clang 3.6
+ run: |
+ sudo apt update
+ sudo apt install libtinfo5
+ # https://code.launchpad.net/ubuntu/xenial/amd64/clang-3.6/1:3.6.2-3ubuntu2
+ wget --no-verbose \
+ http://launchpadlibrarian.net/230019046/libffi6_3.2.1-4_amd64.deb \
+ http://launchpadlibrarian.net/445346109/libasan2_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346135/libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346112/libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346128/libmpx0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346113/libgcc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346131/libstdc++-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/445346022/libobjc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/254405108/libllvm3.6v5_3.6.2-3ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/254405097/libclang-common-3.6-dev_3.6.2-3ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/254405101/libclang1-3.6_3.6.2-3ubuntu2_amd64.deb \
+ http://launchpadlibrarian.net/445345919/gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ http://launchpadlibrarian.net/254405091/clang-3.6_3.6.2-3ubuntu2_amd64.deb
+ sudo dpkg -i \
+ libffi6_3.2.1-4_amd64.deb \
+ libasan2_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libmpx0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libgcc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libstdc++-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libobjc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ libllvm3.6v5_3.6.2-3ubuntu2_amd64.deb \
+ libclang-common-3.6-dev_3.6.2-3ubuntu2_amd64.deb \
+ libclang1-3.6_3.6.2-3ubuntu2_amd64.deb \
+ gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \
+ clang-3.6_3.6.2-3ubuntu2_amd64.deb
+ if: ${{ matrix.cxx == 'clang++-3.6' }}
+
- name: Add repositories for newer GCC
run: |
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
- if: ${{ matrix.cxx == 'g++-11' || matrix.cxx == 'g++-13' }}
+ if: ${{ matrix.cxx == 'g++-13' }}
- name: Add Ubuntu mirrors
run: |
- # Github Actions caching proxy is at times unreliable
- # see https://github.com/actions/runner-images/issues/7048
- printf 'http://azure.archive.ubuntu.com/ubuntu\tpriority:1\n' | sudo tee /etc/apt/mirrors.txt
- curl http://mirrors.ubuntu.com/mirrors.txt | sudo tee --append /etc/apt/mirrors.txt
- sudo sed -i 's~http://azure.archive.ubuntu.com/ubuntu/~mirror+file:/etc/apt/mirrors.txt~' /etc/apt/sources.list
+ # GitHub Actions caching proxy is at times unreliable
+ # see https://github.com/actions/runner-images/issues/7048.
+ mirrors=/etc/apt/mirrors.txt
+ printf 'http://azure.archive.ubuntu.com/ubuntu\tpriority:1\n' | \
+ sudo tee $mirrors
+ curl http://mirrors.ubuntu.com/mirrors.txt | sudo tee --append $mirrors
+ sudo sed -i \
+ "s~http://azure.archive.ubuntu.com/ubuntu/~mirror+file:$mirrors~" \
+ /etc/apt/sources.list
- - name: Create Build Environment
+ - name: Create build environment
run: |
sudo apt update
${{matrix.install}}
@@ -100,10 +156,12 @@ jobs:
CXX: ${{matrix.cxx}}
CXXFLAGS: ${{matrix.cxxflags}}
run: |
- cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.fuzz}} ${{matrix.shared}} \
- -DCMAKE_CXX_STANDARD=${{matrix.std}} -DFMT_DOC=OFF \
- -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \
- -DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE
+ cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
+ -DCMAKE_CXX_STANDARD=${{matrix.std}} \
+ -DCMAKE_CXX_VISIBILITY_PRESET=hidden \
+ -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \
+ -DFMT_DOC=OFF -DFMT_PEDANTIC=ON -DFMT_WERROR=ON \
+ ${{matrix.fuzz}} ${{matrix.shared}} $GITHUB_WORKSPACE
- name: Build
working-directory: ${{runner.workspace}}/build
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 3543ef57..1a297e6d 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set timezone
- run: sudo systemsetup -settimezone 'Asia/Yekaterinburg'
+ run: sudo systemsetup -settimezone 'Europe/Minsk'
- name: Select Xcode 14.3 (macOS 13)
run: sudo xcode-select -s "/Applications/Xcode_14.3.app"
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index b363a6f8..cb435568 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -34,7 +34,7 @@ jobs:
persist-credentials: false
- name: "Run analysis"
- uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
+ uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
@@ -52,7 +52,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: SARIF file
path: results.sarif
@@ -60,6 +60,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
+ uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
with:
sarif_file: results.sarif
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 5403e652..931e7589 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -39,7 +39,7 @@ jobs:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set timezone
- run: tzutil /s "Ekaterinburg Standard Time"
+ run: tzutil /s "FLE Standard Time"
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
@@ -78,7 +78,7 @@ jobs:
- name: Set timezone
run: tzutil /s "Ekaterinburg Standard Time"
shell: cmd
- - uses: msys2/setup-msys2@c52d1fa9c7492275e60fe763540fb601f5f232a1 # v2.25.0
+ - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0
with:
release: false
msystem: ${{matrix.sys}}
diff --git a/.gitignore b/.gitignore
index 2635026a..edd4b086 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
*.xcodeproj
*~
.vscode/
+.vs/
/CMakeScripts
/Testing
/_CPack_Packages
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 586ead51..dd0e0743 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,13 @@ endfunction()
# DEPRECATED! Should be merged into add_module_library.
function(enable_module target)
if (MSVC)
- set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
+ if(CMAKE_GENERATOR STREQUAL "Ninja")
+ # Ninja dyndep expects the .ifc output to be located in a specific relative path
+ file(RELATIVE_PATH BMI_DIR "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir")
+ else()
+ set(BMI_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ file(TO_NATIVE_PATH "${BMI_DIR}/${target}.ifc" BMI)
target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI})
@@ -69,8 +75,6 @@ function(add_module_library name)
target_compile_options(${name} PUBLIC -fmodules-ts)
endif ()
- target_compile_definitions(${name} PRIVATE FMT_MODULE)
-
if (FMT_USE_CMAKE_MODULES)
target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
FILES ${sources})
@@ -201,8 +205,7 @@ if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
endif ()
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
- "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(CheckCXXCompilerFlag)
include(JoinPaths)
@@ -295,6 +298,7 @@ function(add_headers VAR)
endfunction()
# Define the fmt library, its includes and the needed defines.
+set(FMT_HEADERS)
add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
format-inl.h os.h ostream.h printf.h ranges.h std.h
xchar.h)
@@ -427,7 +431,7 @@ if (FMT_INSTALL)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS}
- COMPONENT core
+ COMPONENT fmt_core
EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
@@ -443,13 +447,13 @@ if (FMT_INSTALL)
# Install version, config and target files.
install(FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR}
- COMPONENT core)
+ COMPONENT fmt_core)
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::
- COMPONENT core)
+ COMPONENT fmt_core)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
- COMPONENT core)
+ COMPONENT fmt_core)
endif ()
function(add_doc_target)
@@ -486,7 +490,7 @@ function(add_doc_target)
include(GNUInstallDirs)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
- COMPONENT doc OPTIONAL)
+ COMPONENT fmt_doc OPTIONAL)
endfunction()
if (FMT_DOC)
diff --git a/ChangeLog.md b/ChangeLog.md
index 09ebaed6..56baba39 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,187 @@
+# 11.2.0 - 2025-05-03
+
+- Added the `s` specifier for `std::error_code`. It allows formatting an error
+ message as a string. For example:
+
+ ```c++
+ #include
+
+ int main() {
+ auto ec = std::make_error_code(std::errc::no_such_file_or_directory);
+ fmt::print("{:s}\n", ec);
+ }
+ ```
+
+ prints
+
+ ```
+ No such file or directory
+ ```
+ (The actual message is platform-specific.)
+
+- Fixed formatting of `std::chrono::local_time` and `tm`
+ (https://github.com/fmtlib/fmt/issues/3815,
+ https://github.com/fmtlib/fmt/issues/4350).
+ For example ([godbolt](https://www.godbolt.org/z/8o4b1PPn5)):
+
+ ```c++
+ #include
+
+ int main() {
+ std::chrono::zoned_time zt(
+ std::chrono::current_zone(),
+ std::chrono::system_clock::now());
+ fmt::print("{}", zt.get_local_time());
+ }
+ ```
+
+ is now formatted consistenly across platforms.
+
+- Added diagnostics for cases when timezone information is not available.
+ For example:
+
+ ```c++
+ fmt::print("{:Z}", std::chrono::local_seconds());
+ ```
+
+ now gives a compile-time error.
+
+- Deprecated `fmt::localtime` in favor of `std::localtime`.
+
+- Fixed compilation with GCC 15 and C++20 modules enabled
+ (https://github.com/fmtlib/fmt/pull/4347). Thanks @tkhyn.
+
+- Fixed handling of named arguments in format specs
+ (https://github.com/fmtlib/fmt/issues/4360,
+ https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight.
+
+- Added error reporting for duplicate named arguments
+ (https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
+
+- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0`
+ (https://github.com/fmtlib/fmt/issues/4375,
+ https://github.com/fmtlib/fmt/issues/4394).
+
+- Optimized `text_style` using bit packing
+ (https://github.com/fmtlib/fmt/pull/4363). Thanks @LocalSpook.
+
+- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180,
+ https://github.com/fmtlib/fmt/pull/4383). Thanks @LocalSpook.
+
+- Fixed a flush issue in `fmt::print` when using libstdc++
+ (https://github.com/fmtlib/fmt/issues/4398).
+
+- Fixed `fmt::println` usage with `FMT_ENFORCE_COMPILE_STRING` and legacy
+ compile-time checks (https://github.com/fmtlib/fmt/pull/4407).
+ Thanks @madmaxoft.
+
+- Removed legacy header `fmt/core.h` from docs
+ (https://github.com/fmtlib/fmt/pull/4421,
+ https://github.com/fmtlib/fmt/pull/4422). Thanks @krzysztofkortas.
+
+- Worked around limitations of `__builtin_strlen` during constant evaluation
+ (https://github.com/fmtlib/fmt/issues/4423,
+ https://github.com/fmtlib/fmt/pull/4429). Thanks @BRevzin.
+
+- Worked around a bug in MSVC v141 (https://github.com/fmtlib/fmt/issues/4412,
+ https://github.com/fmtlib/fmt/pull/4413). Thanks @hirohira9119.
+
+- Removed the `fmt_detail` namespace
+ (https://github.com/fmtlib/fmt/issues/4324).
+
+- Removed specializations of `std::is_floating_point` in tests
+ (https://github.com/fmtlib/fmt/issues/4417).
+
+- Fixed a CMake error when setting `CMAKE_MODULE_PATH` in the pedantic mode
+ (https://github.com/fmtlib/fmt/pull/4426). Thanks @rlalik.
+
+- Updated the Bazel config (https://github.com/fmtlib/fmt/pull/4400).
+ Thanks @Vertexwahn.
+
+# 11.1.4 - 2025-02-26
+
+- Fixed ABI compatibility with earlier 11.x versions on Windows
+ (https://github.com/fmtlib/fmt/issues/4359).
+
+- Improved the logic of switching between fixed and exponential format for
+ `float` (https://github.com/fmtlib/fmt/issues/3649).
+
+- Moved `is_compiled_string` to the public API
+ (https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
+
+- Simplified implementation of `operator""_cf`
+ (https://github.com/fmtlib/fmt/pull/4349). Thanks @LocalSpook.
+
+- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
+ Thanks @LocalSpook.
+
+- Fixed handling of BMI paths with the Ninja generator
+ (https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.
+
+- Fixed gcc 8.3 compile errors (https://github.com/fmtlib/fmt/issues/4331,
+ https://github.com/fmtlib/fmt/pull/4336). Thanks @sergiud.
+
+- Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/pull/4356).
+ Thanks @dinomight.
+
+# 11.1.3 - 2025-01-25
+
+- Fixed compilation on GCC 9.4 (https://github.com/fmtlib/fmt/issues/4313).
+
+- Worked around an internal compiler error when using C++20 modules with GCC
+ 14.2 and earlier (https://github.com/fmtlib/fmt/issues/4295).
+
+- Worked around a bug in GCC 6 (https://github.com/fmtlib/fmt/issues/4318).
+
+- Fixed an issue caused by instantiating `formatter`
+ (https://github.com/fmtlib/fmt/issues/4303,
+ https://github.com/fmtlib/fmt/pull/4325). Thanks @timsong-cpp.
+
+- Fixed formatting into `std::ostreambuf_iterator` when using format string
+ compilation (https://github.com/fmtlib/fmt/issues/4309,
+ https://github.com/fmtlib/fmt/pull/4312). Thanks @phprus.
+
+- Restored a constraint on the map formatter so that it correctly reports as
+ unformattable when the element is (https://github.com/fmtlib/fmt/pull/4326).
+ Thanks @timsong-cpp.
+
+- Reduced the size of format specs (https://github.com/fmtlib/fmt/issues/4298).
+
+- Readded `args()` to `fmt::format_context`
+ (https://github.com/fmtlib/fmt/issues/4307,
+ https://github.com/fmtlib/fmt/pull/4310). Thanks @Erroneous1.
+
+- Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/issues/4314,
+ https://github.com/fmtlib/fmt/pull/4322). Thanks @ZehMatt.
+
+- Fixed a pedantic mode error in the CMake config
+ (https://github.com/fmtlib/fmt/pull/4327). Thanks @rlalik.
+
+# 11.1.2 - 2025-01-12
+
+- Fixed ABI compatibility with earlier 11.x versions
+ (https://github.com/fmtlib/fmt/issues/4292).
+
+- Added `wchar_t` support to the `std::bitset` formatter
+ (https://github.com/fmtlib/fmt/issues/4285,
+ https://github.com/fmtlib/fmt/pull/4286,
+ https://github.com/fmtlib/fmt/issues/4289,
+ https://github.com/fmtlib/fmt/pull/4290). Thanks @phprus.
+
+- Prefixed CMake components with `fmt-` to simplify usage of {fmt} via
+ `add_subdirectory` (https://github.com/fmtlib/fmt/issues/4283).
+
+- Updated docs for meson (https://github.com/fmtlib/fmt/pull/4291).
+ Thanks @trim21.
+
+- Fixed a compilation error in chrono on nvcc
+ (https://github.com/fmtlib/fmt/issues/4297,
+ https://github.com/fmtlib/fmt/pull/4301). Thanks @breyerml.
+
+- Fixed various warnings
+ (https://github.com/fmtlib/fmt/pull/4288,
+ https://github.com/fmtlib/fmt/pull/4299). Thanks @GamesTrap and @edo9300.
+
# 11.1.1 - 2024-12-27
- Fixed ABI compatibility with earlier 11.x versions
diff --git a/README.md b/README.md
index fd845db2..638ec522 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v).
hundred million integers to strings per
second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html)
- Small code size both in terms of source code with the minimum
- configuration consisting of just three files, `core.h`, `format.h`
+ configuration consisting of just three files, `base.h`, `format.h`
and `format-inl.h`, and compiled code; see [Compile time and code
bloat](#compile-time-and-code-bloat)
- Reliability: the library has an extensive set of
@@ -74,7 +74,7 @@ See the [documentation](https://fmt.dev) for more details.
**Print to stdout** ([run](https://godbolt.org/z/Tevcjh))
``` c++
-#include
+#include
int main() {
fmt::print("Hello, world!\n");
diff --git a/doc/ChangeLog-old.md b/doc/ChangeLog-old.md
index 3f31d1e9..e8f993c4 100644
--- a/doc/ChangeLog-old.md
+++ b/doc/ChangeLog-old.md
@@ -674,7 +674,7 @@
https://github.com/fmtlib/fmt/issues/1747,
https://github.com/fmtlib/fmt/pull/1750).
Thanks @gsjaardema, @gabime, @johnor, @Kurkin, @invexed, @peterbell10,
- @daixtrose, @petrutlucian94, @Neargye, @ambitslix, @gabime, @erthink,
+ @daixtrose, @petrutlucian94, @Neargye, @ambitslix, @gabime,
@tohammer and @0x8000-0000.
# 6.2.1 - 2020-05-09
diff --git a/doc/api.md b/doc/api.md
index e86f0b06..61e5c3de 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -79,6 +79,8 @@ time formatting and [`fmt/std.h`](#std-api) for other standard library types.
There are two ways to make a user-defined type formattable: providing a
`format_as` function or specializing the `formatter` struct template.
+Formatting of non-void pointer types is intentionally disallowed and they
+cannot be made formattable via either extension API.
Use `format_as` if you want to make your type formattable as some other
type with the same format specifiers. The `format_as` function should
@@ -220,7 +222,7 @@ You can also write a formatter for a hierarchy of classes:
```c++
// demo.h:
#include
-#include
+#include
struct A {
virtual ~A() {}
@@ -403,7 +405,7 @@ All formatting is locale-independent by default. Use the `'L'` format
specifier to insert the appropriate number separator characters from the
locale:
- #include
+ #include
#include
std::locale::global(std::locale("en_US.UTF-8"));
@@ -413,11 +415,11 @@ locale:
that take `std::locale` as a parameter. The locale type is a template
parameter to avoid the expensive `` include.
-::: format(detail::locale_ref, format_string, T&&...)
+::: format(const Locale&, format_string, T&&...)
-::: format_to(OutputIt, detail::locale_ref, format_string, T&&...)
+::: format_to(OutputIt, const Locale&, format_string, T&&...)
-::: formatted_size(detail::locale_ref, format_string, T&&...)
+::: formatted_size(const Locale&, format_string, T&&...)
### Legacy Compile-Time Checks
@@ -473,9 +475,9 @@ chrono-format-specifications).
#include
int main() {
- std::time_t t = std::time(nullptr);
+ auto now = std::chrono::system_clock::now();
- fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
+ fmt::print("The date is {:%Y-%m-%d}.\n", now);
// Output: The date is 2020-11-07.
// (with 2020-11-07 replaced by the current date)
@@ -488,8 +490,6 @@ chrono-format-specifications).
// Output: strftime-like format: 03:15:30
}
-::: localtime(std::time_t)
-
::: gmtime(std::time_t)
@@ -580,7 +580,7 @@ performance bottleneck.
`fmt/color.h` provides support for terminal color and text style output.
-::: print(const text_style&, format_string, T&&...)
+::: print(text_style, format_string, T&&...)
::: fg(detail::color_type)
@@ -669,5 +669,13 @@ following differences:
- Names are defined in the `fmt` namespace instead of `std` to avoid
collisions with standard library implementations.
+
- Width calculation doesn't use grapheme clusterization. The latter has
been implemented in a separate branch but hasn't been integrated yet.
+
+- The default floating-point representation in {fmt} uses the smallest
+ precision that provides round-trip guarantees similarly to other languages
+ like Java and Python. `std::format` is currently specified in terms of
+ `std::to_chars` which tries to generate the smallest number of characters
+ (ignoring redundant digits and sign in exponent) and may procude more
+ decimal digits than necessary.
diff --git a/doc/index.md b/doc/index.md
index 4f28e114..a2694736 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -122,7 +122,7 @@ hide:
The library is highly portable and requires only a minimal subset of
- C++11 features which are available in GCC 4.9, Clang 3.4, MSVC 19.10
+ C++11 features which are available in GCC 4.9, Clang 3.6, MSVC 19.10
(2017) and later. Newer compiler and standard library features are used
if available, and enable additional functionality.
diff --git a/include/fmt/args.h b/include/fmt/args.h
index 3ff47880..d8fe241c 100644
--- a/include/fmt/args.h
+++ b/include/fmt/args.h
@@ -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 class dynamic_format_arg_store {
+FMT_EXPORT template class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
diff --git a/include/fmt/base.h b/include/fmt/base.h
index a6948d40..81d8fc03 100644
--- a/include/fmt/base.h
+++ b/include/fmt/base.h
@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 110101
+#define FMT_VERSION 110201
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -96,9 +96,9 @@
// Detect C++14 relaxed constexpr.
#ifdef FMT_USE_CONSTEXPR
// Use the provided definition.
-#elif FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L
-// GCC only allows throw in constexpr since version 6:
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371.
+#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L
+// GCC only allows constexpr member functions in non-literal types since 7.2:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297.
# define FMT_USE_CONSTEXPR 1
#elif FMT_ICC_VERSION
# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628
@@ -209,20 +209,6 @@
# 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,6 +235,28 @@
# 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 { \
@@ -294,15 +302,8 @@
#endif
#define FMT_APPLY_VARIADIC(expr) \
- using ignore = int[]; \
- (void)ignore { 0, (expr, 0)... }
-
-// Enable minimal optimizations for more compact code in debug mode.
-FMT_PRAGMA_GCC(push_options)
-#if !defined(__OPTIMIZE__) && !defined(__CUDACC__)
-FMT_PRAGMA_GCC(optimize("Og"))
-#endif
-FMT_PRAGMA_CLANG(diagnostic push)
+ using unused = int[]; \
+ (void)unused { 0, (expr, 0)... }
FMT_BEGIN_NAMESPACE
@@ -325,8 +326,8 @@ using underlying_t = typename std::underlying_type::type;
template using decay_t = typename std::decay::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 struct void_t_impl {
using type = void;
};
@@ -466,8 +467,7 @@ template constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
template
-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 +526,20 @@ template 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(__buitin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
- if (std::is_same::value) {
- size_ = __builtin_strlen(detail::narrow(s));
+#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
+ if (std::is_same::value && !detail::is_constant_evaluated()) {
+ size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
@@ -548,7 +548,7 @@ template 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 ::value&& std::is_same<
@@ -585,7 +585,6 @@ template class basic_string_view {
return starts_with(basic_string_view(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,7 +615,7 @@ template class basic_string_view {
using string_view = basic_string_view;
-/// Specifies if `T` is an extended character type. Can be specialized by users.
+// DEPRECATED! Will be merged with is_char and moved to detail.
template struct is_xchar : std::false_type {};
template <> struct is_xchar : std::true_type {};
template <> struct is_xchar : std::true_type {};
@@ -625,7 +624,7 @@ template <> struct is_xchar : std::true_type {};
template <> struct is_xchar : std::true_type {};
#endif
-// DEPRECATED! Will be replaced with an alias to prevent specializations.
+// Specifies if `T` is a character (code unit) type.
template struct is_char : is_xchar {};
template <> struct is_char : std::true_type {};
@@ -739,13 +738,15 @@ class basic_specs {
max_fill_size = 4
};
- size_t data_ = 1 << fill_size_shift;
+ unsigned data_ = 1 << fill_size_shift;
+ static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, "");
// Character (code unit) type is erased to prevent template bloat.
char fill_data_[max_fill_size] = {' '};
FMT_CONSTEXPR void set_fill_size(size_t size) {
- data_ = (data_ & ~fill_size_mask) | (size << fill_size_shift);
+ data_ = (data_ & ~fill_size_mask) |
+ (static_cast(size) << fill_size_shift);
}
public:
@@ -843,7 +844,7 @@ class basic_specs {
fill_data_[i & 3] = static_cast(s[i]);
}
- FMT_CONSTEXPR void set_fill(const basic_specs& specs) {
+ FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) {
set_fill_size(specs.fill_size());
for (size_t i = 0; i < max_fill_size; ++i)
fill_data_[i] = specs.fill_data_[i];
@@ -1030,6 +1031,11 @@ enum {
struct view {};
+template
+struct is_view : std::false_type {};
+template
+struct is_view> : std::is_base_of {};
+
template struct named_arg;
template struct is_named_arg : std::false_type {};
template struct is_static_named_arg : std::false_type {};
@@ -1062,6 +1068,16 @@ template struct named_arg_info {
int id;
};
+// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
+template
+FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args,
+ int named_arg_index,
+ basic_string_view arg_name) {
+ for (int i = 0; i < named_arg_index; ++i) {
+ if (named_args[i].name == arg_name) report_error("duplicate named arg");
+ }
+}
+
template ::value)>
void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) {
++arg_index;
@@ -1069,6 +1085,7 @@ void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) {
template ::value)>
void init_named_arg(named_arg_info* named_args, int& arg_index,
int& named_arg_index, const T& arg) {
+ check_for_duplicate(named_args, named_arg_index, arg.name);
named_args[named_arg_index++] = {arg.name, arg_index++};
}
@@ -1082,12 +1099,13 @@ template ::value)>
FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args,
int& arg_index, int& named_arg_index) {
+ check_for_duplicate(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;
using ulong_type = conditional_t;
@@ -1119,7 +1137,7 @@ using use_formatter =
bool_constant<(std::is_class::value || std::is_enum::value ||
std::is_union::value || std::is_array::value) &&
!has_to_string_view::value && !is_named_arg::value &&
- !use_format_as::value && !use_format_as_member::value>;
+ !use_format_as::value && !use_format_as_member::value>;
template >
auto has_formatter_impl(T* p, buffered_context* ctx = nullptr)
@@ -1660,12 +1678,12 @@ template struct arg_pack {};
template
class format_string_checker {
private:
- type types_[max_of(1, NUM_ARGS)];
- named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)];
+ type types_[max_of(1, NUM_ARGS)];
+ named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)];
compile_parse_context context_;
using parse_func = auto (*)(parse_context&) -> const Char*;
- parse_func parse_funcs_[max_of(1, NUM_ARGS)];
+ parse_func parse_funcs_[max_of(1, NUM_ARGS)];
public:
template
@@ -1704,7 +1722,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;
}
@@ -2004,6 +2032,17 @@ struct has_back_insert_iterator_container_append<
.append(std::declval(),
std::declval()))>> : std::true_type {};
+template
+struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
+
+template
+struct has_back_insert_iterator_container_insert_at_end<
+ OutputIt, InputIt,
+ void_t())
+ .insert(get_container(std::declval()).end(),
+ std::declval(),
+ std::declval()))>> : std::true_type {};
+
// An optimized version of std::copy with the output value type (T).
template ::value&&
@@ -2018,6 +2057,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template ::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 {
@@ -2027,7 +2068,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
}
template ::value)>
+ FMT_ENABLE_IF(!(is_back_insert_iterator::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(*begin++);
return out;
@@ -2234,6 +2279,7 @@ template class value {
}
// Formats an argument of a custom type, such as a user-defined class.
+ // DEPRECATED! Formatter template parameter will be removed.
template
static void format_custom(void* arg, parse_context& parse_ctx,
Context& ctx) {
@@ -2261,28 +2307,27 @@ template <> struct is_output_iterator : std::true_type {};
template
struct is_output_iterator<
It, T,
- void_t&>()++ = std::declval())>>
- : std::true_type {};
+ enable_if_t&>()++),
+ 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 include.
-struct locale_ref {
+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
- locale_ref(const Locale& loc);
+ template locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
+ public:
template auto get() const -> Locale;
};
@@ -2310,8 +2355,9 @@ template
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
- arg_t args[1 + NUM_ARGS];
- named_arg_info named_args[NUM_NAMED_ARGS];
+ arg_t args[1u + NUM_ARGS];
+ named_arg_info
+ named_args[static_cast(NUM_NAMED_ARGS)];
template
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2344,7 +2390,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t[max_of(1, NUM_ARGS)],
+ arg_t[max_of(1, NUM_ARGS)],
named_arg_store>;
type args;
};
@@ -2656,6 +2702,7 @@ class context {
FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
return args_.get_id(name);
}
+ auto args() const -> const format_args& { return args_; }
// Returns an iterator to the beginning of the output range.
FMT_CONSTEXPR auto out() const -> iterator { return out_; }
@@ -2701,7 +2748,7 @@ template struct fstring {
template
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
using namespace detail;
- static_assert(count<(std::is_base_of>::value &&
+ static_assert(count<(is_view>::value &&
std::is_reference::value)...>() == 0,
"passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack()));
@@ -2728,9 +2775,9 @@ template struct fstring {
std::is_same::value)>
FMT_ALWAYS_INLINE fstring(const S&) : str(S()) {
FMT_CONSTEXPR auto sv = string_view(S());
- FMT_CONSTEXPR int ignore =
+ FMT_CONSTEXPR int unused =
(parse_format_string(sv, checker(sv, arg_pack())), 0);
- detail::ignore_unused(ignore);
+ detail::ignore_unused(unused);
}
fstring(runtime_format_string<> fmt) : str(fmt.str) {}
diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h
index abf3671e..6f9a363e 100644
--- a/include/fmt/chrono.h
+++ b/include/fmt/chrono.h
@@ -22,21 +22,6 @@
#include "format.h"
-namespace fmt_detail {
-struct time_zone {
- template
- auto to_sys(T)
- -> std::chrono::time_point {
- return {};
- }
-};
-template inline auto current_zone(T...) -> time_zone* {
- return nullptr;
-}
-
-template inline void _tzset(T...) {}
-} // namespace fmt_detail
-
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
@@ -261,7 +246,7 @@ namespace detail {
using utc_clock = std::chrono::utc_clock;
#else
struct utc_clock {
- void to_sys();
+ template void to_sys(T);
};
#endif
@@ -341,7 +326,7 @@ inline auto get_classic_locale() -> const std::locale& {
}
template struct codecvt_result {
- static constexpr const size_t max_size = 32;
+ static constexpr size_t max_size = 32;
CodeUnit buf[max_size];
CodeUnit* end;
};
@@ -364,7 +349,7 @@ void write_codecvt(codecvt_result& out, string_view in,
template
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
-> OutputIt {
- if (detail::use_utf8 && loc != get_classic_locale()) {
+ if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VERSION != 0 || \
@@ -435,14 +420,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
-struct is_same_arithmetic_type
- : public std::integral_constant::value &&
- std::is_integral::value) ||
- (std::is_floating_point::value &&
- std::is_floating_point::value)> {
-};
+template
+using is_similar_arithmetic_type =
+ bool_constant<(std::is_integral::value && std::is_integral::value) ||
+ (std::is_floating_point::value &&
+ std::is_floating_point::value)>;
FMT_NORETURN inline void throw_duration_error() {
FMT_THROW(format_error("cannot format duration"));
@@ -501,9 +483,9 @@ auto duration_cast(std::chrono::duration from) -> To {
#endif
}
-template <
- typename To, typename FromRep, typename FromPeriod,
- FMT_ENABLE_IF(!is_same_arithmetic_type::value)>
+template ::value)>
auto duration_cast(std::chrono::duration from) -> To {
// Mixed integer <-> float cast is not supported by safe_duration_cast.
return std::chrono::duration_cast(from);
@@ -519,12 +501,30 @@ auto to_time_t(sys_time 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 FMT_CONSTEXPR auto has_current_zone() -> bool {
- using namespace std::chrono;
- using namespace fmt_detail;
- return !std::is_same::value;
+namespace tz {
+
+// DEPRECATED!
+struct time_zone {
+ template
+ auto to_sys(LocalTime) -> sys_time {
+ return {};
+ }
+};
+template auto current_zone(T...) -> time_zone* {
+ return nullptr;
+}
+
+template void _tzset(T...) {}
+} // namespace tz
+
+// DEPRECATED!
+inline void tzset_once() {
+ static bool init = []() {
+ using namespace tz;
+ _tzset();
+ return false;
+ }();
+ ignore_unused(init);
}
} // namespace detail
@@ -535,7 +535,7 @@ FMT_BEGIN_EXPORT
* expressed in local time. Unlike `std::localtime`, this function is
* thread-safe on most platforms.
*/
-inline auto localtime(std::time_t time) -> std::tm {
+FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
@@ -572,11 +572,11 @@ inline auto localtime(std::time_t time) -> std::tm {
}
#if FMT_USE_LOCAL_TIME
-template ())>
-inline auto localtime(std::chrono::local_time time) -> std::tm {
+template
+FMT_DEPRECATED auto localtime(std::chrono::local_time time)
+ -> std::tm {
using namespace std::chrono;
- using namespace fmt_detail;
+ using namespace detail::tz;
return localtime(detail::to_time_t(current_zone()->to_sys(time)));
}
#endif
@@ -652,7 +652,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 +911,14 @@ template struct null_chrono_spec_handler {
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
};
-struct tm_format_checker : null_chrono_spec_handler {
+class tm_format_checker : public null_chrono_spec_handler {
+ 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 +956,12 @@ struct tm_format_checker : null_chrono_spec_handler {
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 +991,27 @@ inline auto tm_mon_short_name(int mon) -> const char* {
}
template
-struct has_member_data_tm_gmtoff : std::false_type {};
+struct has_tm_gmtoff : std::false_type {};
template
-struct has_member_data_tm_gmtoff>
- : std::true_type {};
+struct has_tm_gmtoff> : std::true_type {};
-template
-struct has_member_data_tm_zone : std::false_type {};
+template struct has_tm_zone : std::false_type {};
template
-struct has_member_data_tm_zone>
- : std::true_type {};
+struct has_tm_zone> : std::true_type {};
-inline void tzset_once() {
- static bool init = []() {
- using namespace fmt_detail;
- _tzset();
- return false;
- }();
- ignore_unused(init);
+template ::value)>
+bool set_tm_zone(T& time, char* tz) {
+ time.tm_zone = tz;
+ return true;
+}
+template ::value)>
+bool set_tm_zone(T&, char*) {
+ return false;
+}
+
+inline char* utc() {
+ static char tz[] = "UTC";
+ return tz;
}
// Converts value to Int and checks that it's in the range [0, upper).
@@ -1005,7 +1019,7 @@ template ::value)>
inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (!std::is_unsigned::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(value);
}
@@ -1090,7 +1104,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
void write_floating_seconds(memory_buffer& buf, Duration duration,
int num_fractional_digits = -1) {
@@ -1124,7 +1138,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 +1174,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 +1191,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 +1205,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 +1250,8 @@ class tm_writer {
uint32_or_64_or_128_t 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(out_, n, num_digits);
}
@@ -1259,45 +1272,22 @@ class tm_writer {
write2(static_cast(offset % 60));
}
- template ::value)>
- void format_utc_offset_impl(const T& tm, numeric_system ns) {
+ template ::value)>
+ void format_utc_offset(const T& tm, numeric_system ns) {
write_utc_offset(tm.tm_gmtoff, ns);
}
- template ::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(>m);
- std::tm ltm = gmtime(gt);
- std::time_t lt = std::mktime(<m);
- long long offset = gt - lt;
- write_utc_offset(offset, ns);
-#endif
+ template ::value)>
+ void format_utc_offset(const T&, numeric_system ns) {
+ write_utc_offset(0, ns);
}
- template ::value)>
- void format_tz_name_impl(const T& tm) {
- if (is_classic_)
- out_ = write_tm_str(out_, tm.tm_zone, loc_);
- else
- format_localized('Z');
+ template ::value)>
+ void format_tz_name(const T& tm) {
+ out_ = write_tm_str(out_, tm.tm_zone, loc_);
}
- template ::value)>
- void format_tz_name_impl(const T&) {
- format_localized('Z');
+ template ::value)>
+ void format_tz_name(const T&) {
+ out_ = std::copy_n(utc(), 3, out_);
}
void format_localized(char format, char modifier = 0) {
@@ -1408,8 +1398,8 @@ class tm_writer {
out_ = copy(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 +1473,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 +1613,16 @@ template ::value)>
inline auto get_milliseconds(std::chrono::duration d)
-> std::chrono::duration {
- // 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::type;
- const auto d_as_common = detail::duration_cast(d);
- const auto d_as_whole_seconds =
+ auto d_as_common = detail::duration_cast(d);
+ auto d_as_whole_seconds =
detail::duration_cast(d_as_common);
- // this conversion should be nonproblematic
- const auto diff = d_as_common - d_as_whole_seconds;
- const auto ms =
- detail::duration_cast>(diff);
+ // This conversion should be nonproblematic.
+ auto diff = d_as_common - d_as_whole_seconds;
+ auto ms = detail::duration_cast>(diff);
return ms;
#else
auto s = detail::duration_cast(d);
@@ -1707,32 +1694,28 @@ class get_locale {
}
};
-template
-struct chrono_formatter {
- FormatContext& context;
- OutputIt out;
- int precision;
- bool localized = false;
+template
+struct duration_formatter {
+ using iterator = basic_appender;
+ iterator out;
// rep is unsigned to avoid overflow.
using rep =
conditional_t::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged::type>;
rep val;
+ int precision;
+ locale_ref locale;
+ bool localized = false;
using seconds = std::chrono::duration;
seconds s;
using milliseconds = std::chrono::duration;
bool negative;
- using char_type = typename FormatContext::char_type;
- using tm_writer_type = tm_writer;
+ using tm_writer_type = tm_writer;
- chrono_formatter(FormatContext& ctx, OutputIt o,
- std::chrono::duration d)
- : context(ctx),
- out(o),
- val(static_cast(d.count())),
- negative(false) {
+ duration_formatter(iterator o, std::chrono::duration d,
+ locale_ref loc)
+ : out(o), val(static_cast(d.count())), locale(loc), negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
@@ -1746,19 +1729,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,10 +1766,9 @@ struct chrono_formatter {
}
void write_sign() {
- if (negative) {
- *out++ = '-';
- negative = false;
- }
+ if (!negative) return;
+ *out++ = '-';
+ negative = false;
}
void write(Rep value, int width, pad_type pad = pad_type::zero) {
@@ -1801,24 +1780,22 @@ struct chrono_formatter {
if (width > num_digits) {
out = detail::write_padding(out, pad, width - num_digits);
}
- out = format_decimal(out, n, num_digits);
+ out = format_decimal(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
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(begin, end, out);
+ void on_text(const Char* begin, const Char* end) {
+ copy(begin, end, out);
}
// These are not implemented because durations don't have date information.
@@ -1888,13 +1865,12 @@ struct chrono_formatter {
write_floating_seconds(buf, std::chrono::duration(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(buf.begin(), buf.end(), out);
+ out = copy(buf.begin(), buf.end(), out);
} else {
write(second(), 2, pad);
- write_fractional_seconds(
+ write_fractional_seconds(
out, std::chrono::duration(val), precision);
}
return;
@@ -1936,12 +1912,10 @@ struct chrono_formatter {
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
- out = format_duration_value(out, val, precision);
+ out = format_duration_value(out, val, precision);
}
- void on_duration_unit() {
- out = format_duration_unit(out);
- }
+ void on_duration_unit() { out = format_duration_unit(out); }
};
} // namespace detail
@@ -2011,12 +1985,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
struct formatter : private formatter {
private:
- bool localized_ = false;
bool use_tm_formatter_ = false;
public:
@@ -2024,8 +1997,7 @@ struct formatter : private formatter {
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::parse(ctx) : it;
@@ -2036,7 +2008,7 @@ struct formatter : private formatter {
auto time = std::tm();
time.tm_wday = static_cast(wd.c_encoding());
if (use_tm_formatter_) return formatter::format(time, ctx);
- detail::get_locale loc(localized_, ctx.locale());
+ detail::get_locale loc(this->localized(), ctx.locale());
auto w = detail::tm_writer(loc, ctx.out(), time);
w.on_abbr_weekday();
return w.out();
@@ -2070,7 +2042,6 @@ struct formatter : private formatter {
template
struct formatter : private formatter {
private:
- bool localized_ = false;
bool use_tm_formatter_ = false;
public:
@@ -2078,8 +2049,7 @@ struct formatter : private formatter {
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::parse(ctx) : it;
@@ -2090,7 +2060,7 @@ struct formatter : private formatter {
auto time = std::tm();
time.tm_mon = static_cast(static_cast(m)) - 1;
if (use_tm_formatter_) return formatter::format(time, ctx);
- detail::get_locale loc(localized_, ctx.locale());
+ detail::get_locale loc(this->localized(), ctx.locale());
auto w = detail::tm_writer(loc, ctx.out(), time);
w.on_abbr_month();
return w.out();
@@ -2154,7 +2124,6 @@ struct formatter, Char> {
format_specs specs_;
detail::arg_ref width_ref_;
detail::arg_ref precision_ref_;
- bool localized_ = false;
basic_string_view fmt_;
public:
@@ -2177,7 +2146,7 @@ struct formatter, 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 +2173,10 @@ struct formatter, Char> {
out = detail::format_duration_value(out, d.count(), precision);
detail::format_duration_unit