mirror of
https://github.com/fmtlib/fmt.git
synced 2025-12-06 16:57:03 +08:00
Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6e871e39b | ||
|
|
e137db699a | ||
|
|
d6712ff2c0 | ||
|
|
2d839bbc61 | ||
|
|
5bc56e24a9 | ||
|
|
2727215c11 | ||
|
|
790b9389ae | ||
|
|
a731c73fd5 | ||
|
|
3391f9e992 | ||
|
|
9f197b22ae | ||
|
|
f20b16617e | ||
|
|
14451704d5 | ||
|
|
7a0da1c68a | ||
|
|
e3d2174b3c | ||
|
|
a6fb4d3b06 | ||
|
|
706fecea30 | ||
|
|
e00fd756cc | ||
|
|
fc17e825d9 | ||
|
|
3fccfb8a80 | ||
|
|
690c9c71a0 | ||
|
|
1122268510 | ||
|
|
a4c7e17133 | ||
|
|
bfd0129b91 | ||
|
|
62f57b2496 | ||
|
|
ef7a566413 | ||
|
|
42840cb415 | ||
|
|
5ac44cd128 | ||
|
|
23c13b3060 | ||
|
|
29c46fb82d | ||
|
|
a0571f3f59 | ||
|
|
a195dd6b37 | ||
|
|
33ad559eb8 | ||
|
|
b6cd356196 | ||
|
|
c3be070b7e | ||
|
|
27bf8b47fe | ||
|
|
407c905e45 | ||
|
|
f781d2b932 | ||
|
|
5987082c47 | ||
|
|
681c9e689b | ||
|
|
913507044b | ||
|
|
ff357e9e4a | ||
|
|
728dfeab5b | ||
|
|
7adc922ebb | ||
|
|
70ed0ab82a | ||
|
|
b95fd6132b | ||
|
|
7241bbb149 | ||
|
|
c0ddbacfd3 | ||
|
|
9395ef5fcb | ||
|
|
9721d974fc | ||
|
|
d6bdb69c62 | ||
|
|
08d38d6e78 | ||
|
|
a2289b8593 | ||
|
|
e8da5ba275 | ||
|
|
e2aa06cd0a | ||
|
|
85f6ecc7a0 | ||
|
|
378a5ab3c1 | ||
|
|
656d14db8b | ||
|
|
31bed1bb65 | ||
|
|
8eebb4334b | ||
|
|
beefc1c14f | ||
|
|
81fe170849 | ||
|
|
491dc16a6d | ||
|
|
41326207c7 | ||
|
|
c4f70ab69e | ||
|
|
5e214f0c43 | ||
|
|
1234bc312e | ||
|
|
b77a751625 | ||
|
|
8db24c0ea9 | ||
|
|
03c7e28965 | ||
|
|
47a18b2fe9 | ||
|
|
eb44d87b52 | ||
|
|
b580360ab7 | ||
|
|
486e7ba579 | ||
|
|
27ea09836a | ||
|
|
b5b9317a3c | ||
|
|
d13e5d048d | ||
|
|
b9ac8b225d | ||
|
|
4801f54e59 | ||
|
|
5f66e07cb0 | ||
|
|
17be91c079 | ||
|
|
dcea616535 | ||
|
|
a2fd48e039 | ||
|
|
e28c371c0f | ||
|
|
6b6cdd9405 | ||
|
|
c5e55972ae | ||
|
|
dc409ee86d | ||
|
|
4cce5f458d | ||
|
|
aa8a30838a | ||
|
|
b18ece7d71 | ||
|
|
e424e3f2e6 | ||
|
|
ddd20d57ec | ||
|
|
594143e374 | ||
|
|
09005428ab | ||
|
|
aaefc029d1 | ||
|
|
9fb9f17dac | ||
|
|
a3b3d7ed46 | ||
|
|
9aee518f3e | ||
|
|
83189189a1 | ||
|
|
9bb14ffc47 | ||
|
|
556c4177b6 | ||
|
|
bfdef8b15d | ||
|
|
e33c76a1c2 | ||
|
|
f9face7147 | ||
|
|
36390db094 | ||
|
|
da97ba2914 | ||
|
|
79a5e93027 | ||
|
|
18e160eb4c | ||
|
|
53d006abfd | ||
|
|
63bef33999 | ||
|
|
d7cbfef11c | ||
|
|
26b033e239 | ||
|
|
609188cb92 | ||
|
|
84b9c00711 | ||
|
|
9908d00037 | ||
|
|
0a94e06750 | ||
|
|
053d262944 | ||
|
|
e72e43af1c | ||
|
|
80549a630e | ||
|
|
f17b9aa44c | ||
|
|
882702c219 | ||
|
|
16d371b649 | ||
|
|
619b3a5aa0 | ||
|
|
79c7f8a70b | ||
|
|
20e0d6d8dd | ||
|
|
8ba99c0f05 | ||
|
|
656228fbee | ||
|
|
02fb76d69b | ||
|
|
a57f196cad | ||
|
|
a75e8af487 | ||
|
|
e129591f02 | ||
|
|
13d4f8469e | ||
|
|
e2f89e6d21 | ||
|
|
489fd7ca4b | ||
|
|
69324379f2 | ||
|
|
ff1caa58c9 | ||
|
|
e181e94140 | ||
|
|
43c88d00ae | ||
|
|
33a7d55c9b | ||
|
|
8e4676e4a5 | ||
|
|
b5c4d25cd1 | ||
|
|
a77efa4b4a | ||
|
|
e7e71009c7 | ||
|
|
b7b261977e | ||
|
|
61e0503daf | ||
|
|
72c82296d6 | ||
|
|
a402614e8c | ||
|
|
e719c43cc6 | ||
|
|
8a8ff6177c | ||
|
|
5b99b334f0 | ||
|
|
9588458917 | ||
|
|
f91dc80f4c | ||
|
|
4120581167 | ||
|
|
7ffc3ca15b | ||
|
|
e9ddc97b9a | ||
|
|
0d145936ec | ||
|
|
8f3a965186 | ||
|
|
ae8cb1e8e6 | ||
|
|
add164f6b3 | ||
|
|
23059d558e | ||
|
|
505cc3d0c2 | ||
|
|
b8a502615d | ||
|
|
814f51eab6 | ||
|
|
93f03953af | ||
|
|
35dcc58263 | ||
|
|
553ec11ec0 | ||
|
|
a0ecfe3e1c | ||
|
|
127413ddaa | ||
|
|
7d29ebe4af | ||
|
|
20c8fdad06 | ||
|
|
300ce75ca6 | ||
|
|
513f978241 | ||
|
|
6a3b40524c | ||
|
|
2fa3e1a1bb | ||
|
|
fc8d07cfe5 | ||
|
|
27c5aab349 | ||
|
|
bc0193535a | ||
|
|
353bd895a2 | ||
|
|
953cffa701 | ||
|
|
571c02d475 | ||
|
|
f4345467fc | ||
|
|
1ef8348070 | ||
|
|
a5dccffa56 | ||
|
|
4a149f513f | ||
|
|
067bc479b4 | ||
|
|
730fd4d9a7 | ||
|
|
5860688d7e | ||
|
|
46be88bc1e | ||
|
|
cc88914904 | ||
|
|
fc0c76a075 | ||
|
|
6332a38529 | ||
|
|
02de29e003 | ||
|
|
6d51c78c1e | ||
|
|
0f4e9d0bde | ||
|
|
d9d50495ac | ||
|
|
befbc5fdb8 | ||
|
|
8aa1d6a9fb | ||
|
|
6d79757a38 | ||
|
|
1ff0b7f5e1 | ||
|
|
ea985e84f8 | ||
|
|
f7033da09e | ||
|
|
b723c021df | ||
|
|
3ba3c390fb | ||
|
|
ab161a71c6 | ||
|
|
b5266fd3b9 | ||
|
|
9b0ebd4435 | ||
|
|
7af94e5597 | ||
|
|
2924fcf8f6 | ||
|
|
102752ad45 | ||
|
|
a6cd72c9eb | ||
|
|
07885271a1 | ||
|
|
4999416e5c | ||
|
|
55a8f6a4be | ||
|
|
eb9a95d426 | ||
|
|
d5c33e4f45 | ||
|
|
a2225f2887 | ||
|
|
b43b2f9537 | ||
|
|
1312b4a162 | ||
|
|
4404dc05dd | ||
|
|
7bb6fcb325 | ||
|
|
59259a5fde | ||
|
|
542ea7c40b |
4
.clang-tidy
Normal file
4
.clang-tidy
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Checks: modernize-use-trailing-return-type
|
||||||
|
CheckOptions:
|
||||||
|
- key: modernize-use-trailing-return-type.TransformLambdas
|
||||||
|
value: none
|
||||||
2
.github/workflows/cifuzz.yml
vendored
2
.github/workflows/cifuzz.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
language: c++
|
language: c++
|
||||||
|
|
||||||
- name: Upload crash
|
- name: Upload crash
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
if: failure() && steps.build.outcome == 'success'
|
if: failure() && steps.build.outcome == 'success'
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
name: artifacts
|
||||||
|
|||||||
2
.github/workflows/doc.yml
vendored
2
.github/workflows/doc.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Add Ubuntu mirrors
|
- name: Add Ubuntu mirrors
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@ -13,16 +13,16 @@ jobs:
|
|||||||
format_code:
|
format_code:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install clang-format
|
- name: Install clang-format
|
||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
sudo bash ./llvm.sh 17
|
sudo bash ./llvm.sh 21
|
||||||
sudo apt install clang-format-17
|
sudo apt install clang-format-21
|
||||||
|
|
||||||
- name: Run clang-format
|
- name: Run clang-format
|
||||||
run: |
|
run: |
|
||||||
find include src -name '*.h' -o -name '*.cc' | \
|
find include src -name '*.h' -o -name '*.cc' | \
|
||||||
xargs clang-format-17 -i -style=file -fallback-style=none
|
xargs clang-format-21 -i -style=file -fallback-style=none
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|||||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -55,7 +55,7 @@ jobs:
|
|||||||
install: sudo apt install libc++-14-dev libc++abi-14-dev
|
install: sudo apt install libc++-14-dev libc++abi-14-dev
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: sudo timedatectl set-timezone 'Europe/Kyiv'
|
run: sudo timedatectl set-timezone 'Europe/Kyiv'
|
||||||
|
|||||||
9
.github/workflows/macos.yml
vendored
9
.github/workflows/macos.yml
vendored
@ -9,13 +9,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-13, macos-14]
|
os: [macos-14]
|
||||||
build_type: [Debug, Release]
|
build_type: [Debug, Release]
|
||||||
std: [11, 17, 20]
|
std: [11, 17, 20, 23]
|
||||||
shared: [""]
|
shared: [""]
|
||||||
exclude:
|
|
||||||
- { os: macos-13, std: 11 }
|
|
||||||
- { os: macos-13, std: 17 }
|
|
||||||
include:
|
include:
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
std: 23
|
std: 23
|
||||||
@ -25,7 +22,7 @@ jobs:
|
|||||||
runs-on: '${{ matrix.os }}'
|
runs-on: '${{ matrix.os }}'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: sudo systemsetup -settimezone 'Europe/Minsk'
|
run: sudo systemsetup -settimezone 'Europe/Minsk'
|
||||||
|
|||||||
8
.github/workflows/scorecard.yml
vendored
8
.github/workflows/scorecard.yml
vendored
@ -29,12 +29,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: "Run analysis"
|
- name: "Run analysis"
|
||||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||||
with:
|
with:
|
||||||
results_file: results.sarif
|
results_file: results.sarif
|
||||||
results_format: 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
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
@ -60,6 +60,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
16
.github/workflows/windows.yml
vendored
16
.github/workflows/windows.yml
vendored
@ -10,22 +10,18 @@ jobs:
|
|||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# windows-2019 has MSVC 2019 installed;
|
|
||||||
# windows-2022 has MSVC 2022 installed:
|
# windows-2022 has MSVC 2022 installed:
|
||||||
# https://github.com/actions/virtual-environments.
|
# https://github.com/actions/virtual-environments.
|
||||||
os: [windows-2019]
|
os: [windows-2022]
|
||||||
platform: [Win32, x64]
|
platform: [Win32, x64]
|
||||||
toolset: [v141, v142]
|
toolset: [v142]
|
||||||
standard: [14, 17, 20]
|
standard: [14, 17, 20]
|
||||||
shared: ["", -DBUILD_SHARED_LIBS=ON]
|
shared: ["", -DBUILD_SHARED_LIBS=ON]
|
||||||
build_type: [Debug, Release]
|
build_type: [Debug, Release]
|
||||||
exclude:
|
exclude:
|
||||||
- { toolset: v141, standard: 20 }
|
|
||||||
- { toolset: v142, standard: 14 }
|
- { toolset: v142, standard: 14 }
|
||||||
- { platform: Win32, toolset: v141 }
|
|
||||||
- { platform: Win32, standard: 14 }
|
- { platform: Win32, standard: 14 }
|
||||||
- { platform: Win32, standard: 20 }
|
- { platform: Win32, standard: 20 }
|
||||||
- { platform: x64, toolset: v141, shared: -DBUILD_SHARED_LIBS=ON }
|
|
||||||
- { platform: x64, standard: 14, shared: -DBUILD_SHARED_LIBS=ON }
|
- { platform: x64, standard: 14, shared: -DBUILD_SHARED_LIBS=ON }
|
||||||
- { platform: x64, standard: 20, shared: -DBUILD_SHARED_LIBS=ON }
|
- { platform: x64, standard: 20, shared: -DBUILD_SHARED_LIBS=ON }
|
||||||
include:
|
include:
|
||||||
@ -36,7 +32,7 @@ jobs:
|
|||||||
standard: 20
|
standard: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: tzutil /s "FLE Standard Time"
|
run: tzutil /s "FLE Standard Time"
|
||||||
@ -76,14 +72,14 @@ jobs:
|
|||||||
sys: [ mingw64, ucrt64 ]
|
sys: [ mingw64, ucrt64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: tzutil /s "Ekaterinburg Standard Time"
|
run: tzutil /s "FLE Standard Time"
|
||||||
shell: cmd
|
shell: cmd
|
||||||
- uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0
|
- uses: msys2/setup-msys2@40677d36a502eb2cf0fb808cc9dec31bf6152638 # v2.28.0
|
||||||
with:
|
with:
|
||||||
release: false
|
release: false
|
||||||
msystem: ${{matrix.sys}}
|
msystem: ${{matrix.sys}}
|
||||||
pacboy: cc:p cmake:p ninja:p lld:p
|
pacboy: cc:p cmake:p ninja:p lld:p
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
env: { LDFLAGS: -fuse-ld=lld }
|
env: { LDFLAGS: -fuse-ld=lld }
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,12 +3,14 @@
|
|||||||
*.xcodeproj
|
*.xcodeproj
|
||||||
*~
|
*~
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.cache/
|
||||||
.vs/
|
.vs/
|
||||||
/CMakeScripts
|
/CMakeScripts
|
||||||
/Testing
|
/Testing
|
||||||
/_CPack_Packages
|
/_CPack_Packages
|
||||||
/install_manifest.txt
|
/install_manifest.txt
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
|
CMakeUserPresets.json
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
CPack*.cmake
|
CPack*.cmake
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
|
|||||||
@ -9,7 +9,9 @@ endif ()
|
|||||||
# or if it is the master project.
|
# or if it is the master project.
|
||||||
if (NOT DEFINED FMT_MASTER_PROJECT)
|
if (NOT DEFINED FMT_MASTER_PROJECT)
|
||||||
set(FMT_MASTER_PROJECT OFF)
|
set(FMT_MASTER_PROJECT OFF)
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
# NOTE: source vs current_source detection is unreliable
|
||||||
|
# this heuristic is more generally applicable esp w.r.t FetchContent
|
||||||
|
if (NOT DEFINED PROJECT_NAME)
|
||||||
set(FMT_MASTER_PROJECT ON)
|
set(FMT_MASTER_PROJECT ON)
|
||||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||||
endif ()
|
endif ()
|
||||||
@ -27,18 +29,15 @@ endfunction()
|
|||||||
# DEPRECATED! Should be merged into add_module_library.
|
# DEPRECATED! Should be merged into add_module_library.
|
||||||
function(enable_module target)
|
function(enable_module target)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
if(CMAKE_GENERATOR STREQUAL "Ninja")
|
if(NOT 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}")
|
set(BMI_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
endif()
|
|
||||||
file(TO_NATIVE_PATH "${BMI_DIR}/${target}.ifc" BMI)
|
file(TO_NATIVE_PATH "${BMI_DIR}/${target}.ifc" BMI)
|
||||||
target_compile_options(${target}
|
target_compile_options(${target}
|
||||||
PRIVATE /interface /ifcOutput ${BMI}
|
PRIVATE /interface /ifcOutput ${BMI}
|
||||||
INTERFACE /reference fmt=${BMI})
|
INTERFACE /reference fmt=${BMI})
|
||||||
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
|
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
|
||||||
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
|
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
|
||||||
|
endif()
|
||||||
endif ()
|
endif ()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
@ -164,7 +163,7 @@ option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
|||||||
|
|
||||||
# Options that control generation of various targets.
|
# Options that control generation of various targets.
|
||||||
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
|
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_INSTALL "Generate the install target." ON)
|
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
|
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
||||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
||||||
@ -431,7 +430,7 @@ if (FMT_INSTALL)
|
|||||||
|
|
||||||
# Install the library and headers.
|
# Install the library and headers.
|
||||||
install(TARGETS ${INSTALL_TARGETS}
|
install(TARGETS ${INSTALL_TARGETS}
|
||||||
COMPONENT fmt-core
|
COMPONENT fmt_core
|
||||||
EXPORT ${targets_export_name}
|
EXPORT ${targets_export_name}
|
||||||
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
||||||
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
||||||
@ -447,13 +446,13 @@ if (FMT_INSTALL)
|
|||||||
# Install version, config and target files.
|
# Install version, config and target files.
|
||||||
install(FILES ${project_config} ${version_config}
|
install(FILES ${project_config} ${version_config}
|
||||||
DESTINATION ${FMT_CMAKE_DIR}
|
DESTINATION ${FMT_CMAKE_DIR}
|
||||||
COMPONENT fmt-core)
|
COMPONENT fmt_core)
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
||||||
NAMESPACE fmt::
|
NAMESPACE fmt::
|
||||||
COMPONENT fmt-core)
|
COMPONENT fmt_core)
|
||||||
|
|
||||||
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
|
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
|
||||||
COMPONENT fmt-core)
|
COMPONENT fmt_core)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
function(add_doc_target)
|
function(add_doc_target)
|
||||||
@ -490,7 +489,7 @@ function(add_doc_target)
|
|||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
|
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
|
||||||
COMPONENT fmt-doc OPTIONAL)
|
COMPONENT fmt_doc OPTIONAL)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
if (FMT_DOC)
|
if (FMT_DOC)
|
||||||
|
|||||||
261
ChangeLog.md
261
ChangeLog.md
@ -1,3 +1,250 @@
|
|||||||
|
# 12.1.0 - 2025-10-29
|
||||||
|
|
||||||
|
- Optimized `buffer::append`, resulting in up to ~16% improvement on spdlog
|
||||||
|
benchmarks (https://github.com/fmtlib/fmt/pull/4541). Thanks @fyrsta7.
|
||||||
|
|
||||||
|
- Worked around an ABI incompatibility in `std::locale_ref` between clang and
|
||||||
|
gcc (https://github.com/fmtlib/fmt/issues/4573).
|
||||||
|
|
||||||
|
- Made `std::variant` and `std::expected` formatters work with `format_as`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4574,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4575). Thanks @phprus.
|
||||||
|
|
||||||
|
- Made `fmt::join<string_view>` work with C++ modules
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4379,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4577). Thanks @Arghnews.
|
||||||
|
|
||||||
|
- Exported `fmt::is_compiled_string` and `operator""_cf` from the module
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4544). Thanks @CrackedMatter.
|
||||||
|
|
||||||
|
- Fixed a compatibility issue with C++ modules in clang
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4548). Thanks @tsarn.
|
||||||
|
|
||||||
|
- Added support for cv-qualified types to the `std::optional` formatter
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4561,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4562). Thanks @OleksandrKvl.
|
||||||
|
|
||||||
|
- Added demangling support (used in exception and `std::type_info` formatters)
|
||||||
|
for libc++ and clang-cl
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4542,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4560,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4568,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4571).
|
||||||
|
Thanks @FatihBAKIR and @rohitsutreja.
|
||||||
|
|
||||||
|
- Switched to global `malloc`/`free` to enable allocator customization
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4569,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4570). Thanks @rohitsutreja.
|
||||||
|
|
||||||
|
- Made the `FMT_USE_CONSTEVAL` macro configurable by users
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4546). Thanks @SnapperTT.
|
||||||
|
|
||||||
|
- Fixed compilation with locales disabled in the header-only mode
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4550).
|
||||||
|
|
||||||
|
- Fixed compilation with clang 21 and `-std=c++20`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4552).
|
||||||
|
|
||||||
|
- Fixed a dynamic linking issue with clang-cl
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4576,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4584). Thanks @FatihBAKIR.
|
||||||
|
|
||||||
|
- Fixed a warning suppression leakage on gcc
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4588). Thanks @ZedThree.
|
||||||
|
|
||||||
|
- Made more internal color APIs `constexpr`
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4581). Thanks @ishani.
|
||||||
|
|
||||||
|
- Fixed compatibility with clang as a host compiler for NVCC
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4564). Thanks @valgur.
|
||||||
|
|
||||||
|
- Fixed various warnings and lint issues
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4565,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4572,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4557).
|
||||||
|
Thanks @LiangHuDream and @teruyamato0731.
|
||||||
|
|
||||||
|
- Improved documentation
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4549,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4551,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4566,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4567,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4578,).
|
||||||
|
Thanks @teruyamato0731, @petersteneteg and @zimmerman-dev.
|
||||||
|
|
||||||
|
# 12.0.0 - 2025-09-17
|
||||||
|
|
||||||
|
- Optimized the default floating point formatting
|
||||||
|
(https://github.com/fmtlib/fmt/issues/3675,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4516). In particular, formatting a
|
||||||
|
`double` with format string compilation into a stack allocated buffer is
|
||||||
|
more than 60% faster in version 12.0 compared to 11.2 according to
|
||||||
|
[dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark):
|
||||||
|
|
||||||
|
```
|
||||||
|
Function Time (ns) Speedup
|
||||||
|
fmt11 34.471 1.00x
|
||||||
|
fmt12 21.000 1.64x
|
||||||
|
```
|
||||||
|
|
||||||
|
<img width="766" height="609" src="https://github.com/user-attachments/assets/d7d768ad-7543-468c-b0bb-449abf73b31b" />
|
||||||
|
|
||||||
|
- Added `constexpr` support to `fmt::format`. For example:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <fmt/compile.h>
|
||||||
|
|
||||||
|
using namespace fmt::literals;
|
||||||
|
std::string s = fmt::format(""_cf, 42);
|
||||||
|
```
|
||||||
|
|
||||||
|
now works at compile time provided that `std::string` supports `constexpr`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/3403,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4456). Thanks @msvetkin.
|
||||||
|
|
||||||
|
- Added `FMT_STATIC_FORMAT` that allows formatting into a string of the exact
|
||||||
|
required size at compile time.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <fmt/compile.h>
|
||||||
|
|
||||||
|
constexpr auto s = FMT_STATIC_FORMAT("{}", 42);
|
||||||
|
```
|
||||||
|
|
||||||
|
compiles to just
|
||||||
|
|
||||||
|
```s
|
||||||
|
__ZL1s:
|
||||||
|
.asciiz "42"
|
||||||
|
```
|
||||||
|
|
||||||
|
It can be accessed as a C string with `s.c_str()` or as a string view with
|
||||||
|
`s.str()`.
|
||||||
|
|
||||||
|
- Improved C++20 module support
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4451,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4459,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4476,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4488,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4491,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4495).
|
||||||
|
Thanks @arBmind, @tkhyn, @Mishura4, @anonymouspc and @autoantwort.
|
||||||
|
|
||||||
|
- Switched to using estimated display width in precision. For example:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱");
|
||||||
|
```
|
||||||
|
|
||||||
|
prints
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
because `🐱` has an estimated width of 2
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4272,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4443,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4475).
|
||||||
|
Thanks @nikhilreddydev and @localspook.
|
||||||
|
|
||||||
|
- Fix interaction between debug presentation, precision, and width for strings
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4478). Thanks @localspook.
|
||||||
|
|
||||||
|
- Implemented allocator propagation on `basic_memory_buffer` move
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4487,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4490). Thanks @toprakmurat.
|
||||||
|
|
||||||
|
- Fixed an ambiguity between `std::reference_wrapper<T>` and `format_as`
|
||||||
|
formatters (https://github.com/fmtlib/fmt/issues/4424,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4434). Thanks @jeremy-rifkin.
|
||||||
|
|
||||||
|
- Removed the following deprecated APIs:
|
||||||
|
|
||||||
|
- `has_formatter`: use `is_formattable` instead,
|
||||||
|
- `basic_format_args::parse_context_type`,
|
||||||
|
`basic_format_args::formatter_type` and similar aliases in context types,
|
||||||
|
- wide stream overload of `fmt::printf`,
|
||||||
|
- wide stream overloads of `fmt::print` that take text styles,
|
||||||
|
- `is_*char` traits,
|
||||||
|
- `fmt::localtime`.
|
||||||
|
|
||||||
|
- Deprecated wide overloads of `fmt::fprintf` and `fmt::sprintf`.
|
||||||
|
|
||||||
|
- Improved diagnostics for the incorrect usage of `fmt::ptr`
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4453). Thanks @TobiSchluter.
|
||||||
|
|
||||||
|
- Made handling of ANSI escape sequences more efficient
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4511,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4528).
|
||||||
|
Thanks @localspook and @Anas-Hamdane.
|
||||||
|
|
||||||
|
- Fixed a buffer overflow on all emphasis flags set
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4498). Thanks @dominicpoeschko.
|
||||||
|
|
||||||
|
- Fixed an integer overflow for precision close to the max `int` value.
|
||||||
|
|
||||||
|
- Fixed compatibility with WASI (https://github.com/fmtlib/fmt/issues/4496,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4497). Thanks @whitequark.
|
||||||
|
|
||||||
|
- Fixed `back_insert_iterator` detection, preventing a fallback on slower path
|
||||||
|
that handles arbitrary iterators (https://github.com/fmtlib/fmt/issues/4454).
|
||||||
|
|
||||||
|
- Fixed handling of invalid glibc `FILE` buffers
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4469).
|
||||||
|
|
||||||
|
- Added `wchar_t` support to the `std::byte` formatter
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4479,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4480). Thanks @phprus.
|
||||||
|
|
||||||
|
- Changed component prefix from `fmt-` to `fmt_` for compatibility with
|
||||||
|
NSIS/CPack on Windows, e.g. `fmt-doc` changed to `fmt_doc`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4441,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4442). Thanks @n-stein.
|
||||||
|
|
||||||
|
- Added the `FMT_CUSTOM_ASSERT_FAIL` macro to simplify providing a custom
|
||||||
|
`fmt::assert_fail` implementation (https://github.com/fmtlib/fmt/pull/4505).
|
||||||
|
Thanks @HazardyKnusperkeks.
|
||||||
|
|
||||||
|
- Switched to `FMT_THROW` on reporting format errors so that it can be
|
||||||
|
overriden by users when exceptions are disabled
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4521). Thanks @HazardyKnusperkeks.
|
||||||
|
|
||||||
|
- Improved master project detection and disabled install targets when using
|
||||||
|
{fmt} as a subproject by default (https://github.com/fmtlib/fmt/pull/4536).
|
||||||
|
Thanks @crueter.
|
||||||
|
|
||||||
|
- Made various code improvements
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4445,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4448,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4473,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4522).
|
||||||
|
Thanks @localspook, @tchaikov and @way4sahil.
|
||||||
|
|
||||||
|
- Added Conan instructions to the docs
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4537). Thanks @uilianries.
|
||||||
|
|
||||||
|
- Removed Bazel files to avoid issues with downstream packaging
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4530). Thanks @mering.
|
||||||
|
|
||||||
|
- Added more entries for generated files to `.gitignore`
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4355,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4512).
|
||||||
|
Thanks @dinomight and @localspook.
|
||||||
|
|
||||||
|
- Fixed various warnings and compilation issues
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4447,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4470,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4474,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4477,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4471,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4483,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4515,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4533,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4534).
|
||||||
|
Thanks @dodomorandi, @localspook, @remyjette, @Tomek-Stolarczyk, @Mishura4,
|
||||||
|
@mattiasljungstrom and @FatihBAKIR.
|
||||||
|
|
||||||
# 11.2.0 - 2025-05-03
|
# 11.2.0 - 2025-05-03
|
||||||
|
|
||||||
- Added the `s` specifier for `std::error_code`. It allows formatting an error
|
- Added the `s` specifier for `std::error_code`. It allows formatting an error
|
||||||
@ -56,17 +303,18 @@
|
|||||||
https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight.
|
https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight.
|
||||||
|
|
||||||
- Added error reporting for duplicate named arguments
|
- Added error reporting for duplicate named arguments
|
||||||
(https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
|
(https://github.com/fmtlib/fmt/issues/4282,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
|
||||||
|
|
||||||
- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0`
|
- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0`
|
||||||
(https://github.com/fmtlib/fmt/issues/4375,
|
(https://github.com/fmtlib/fmt/issues/4375,
|
||||||
https://github.com/fmtlib/fmt/issues/4394).
|
https://github.com/fmtlib/fmt/issues/4394).
|
||||||
|
|
||||||
- Optimized `text_style` using bit packing
|
- Optimized `text_style` using bit packing
|
||||||
(https://github.com/fmtlib/fmt/pull/4363). Thanks @LocalSpook.
|
(https://github.com/fmtlib/fmt/pull/4363). Thanks @localspook.
|
||||||
|
|
||||||
- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180,
|
- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180,
|
||||||
https://github.com/fmtlib/fmt/pull/4383). Thanks @LocalSpook.
|
https://github.com/fmtlib/fmt/pull/4383). Thanks @localspook.
|
||||||
|
|
||||||
- Fixed a flush issue in `fmt::print` when using libstdc++
|
- Fixed a flush issue in `fmt::print` when using libstdc++
|
||||||
(https://github.com/fmtlib/fmt/issues/4398).
|
(https://github.com/fmtlib/fmt/issues/4398).
|
||||||
@ -107,13 +355,14 @@
|
|||||||
`float` (https://github.com/fmtlib/fmt/issues/3649).
|
`float` (https://github.com/fmtlib/fmt/issues/3649).
|
||||||
|
|
||||||
- Moved `is_compiled_string` to the public API
|
- Moved `is_compiled_string` to the public API
|
||||||
(https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
|
(https://github.com/fmtlib/fmt/issues/4335,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
|
||||||
|
|
||||||
- Simplified implementation of `operator""_cf`
|
- Simplified implementation of `operator""_cf`
|
||||||
(https://github.com/fmtlib/fmt/pull/4349). Thanks @LocalSpook.
|
(https://github.com/fmtlib/fmt/pull/4349). Thanks @localspook.
|
||||||
|
|
||||||
- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
|
- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
|
||||||
Thanks @LocalSpook.
|
Thanks @localspook.
|
||||||
|
|
||||||
- Fixed handling of BMI paths with the Ninja generator
|
- Fixed handling of BMI paths with the Ninja generator
|
||||||
(https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.
|
(https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.
|
||||||
|
|||||||
41
README.md
41
README.md
@ -4,14 +4,15 @@
|
|||||||
[](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
|
[](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
|
||||||
[](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
|
[](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
|
||||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
|
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
|
||||||
[](https://stackoverflow.com/questions/tagged/fmt)
|
[](https://www.bestpractices.dev/projects/8880)
|
||||||
[](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
|
[](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
|
||||||
|
[](https://stackoverflow.com/questions/tagged/fmt)
|
||||||
|
|
||||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||||
alternative to C stdio and C++ iostreams.
|
alternative to C stdio and C++ iostreams.
|
||||||
|
|
||||||
If you like this project, please consider donating to one of the funds
|
If you like this project, please consider donating to one of the funds
|
||||||
that help victims of the war in Ukraine: <https://www.stopputin.net/>.
|
that help victims of the war in Ukraine: <https://u24.gov.ua/>.
|
||||||
|
|
||||||
[Documentation](https://fmt.dev)
|
[Documentation](https://fmt.dev)
|
||||||
|
|
||||||
@ -149,8 +150,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This can be [5 to 9 times faster than
|
This can be [up to 9 times faster than `fprintf`](
|
||||||
fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
|
http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
|
||||||
|
|
||||||
**Print with colors and text styles**
|
**Print with colors and text styles**
|
||||||
|
|
||||||
@ -177,17 +178,17 @@ Output on a modern terminal with Unicode support:
|
|||||||
|
|
||||||
| Library | Method | Run Time, s |
|
| Library | Method | Run Time, s |
|
||||||
|-------------------|---------------|-------------|
|
|-------------------|---------------|-------------|
|
||||||
| libc | printf | 0.91 |
|
| libc | printf | 0.66 |
|
||||||
| libc++ | std::ostream | 2.49 |
|
| libc++ | std::ostream | 1.63 |
|
||||||
| {fmt} 9.1 | fmt::print | 0.74 |
|
| {fmt} 12.1 | fmt::print | 0.44 |
|
||||||
| Boost Format 1.80 | boost::format | 6.26 |
|
| Boost Format 1.88 | boost::format | 3.89 |
|
||||||
| Folly Format | folly::format | 1.87 |
|
| Folly Format | folly::format | 1.28 |
|
||||||
|
|
||||||
{fmt} is the fastest of the benchmarked methods, \~20% faster than
|
{fmt} is the fastest of the benchmarked methods, \~50% faster than
|
||||||
`printf`.
|
`printf`.
|
||||||
|
|
||||||
The above results were generated by building `tinyformat_test.cpp` on
|
The above results were generated by building `tinyformat_test.cpp` on
|
||||||
macOS 12.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
|
macOS 15.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
|
||||||
taking the best of three runs. In the test, the format string
|
taking the best of three runs. In the test, the format string
|
||||||
`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000
|
`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000
|
||||||
times with output sent to `/dev/null`; for further details refer to the
|
times with output sent to `/dev/null`; for further details refer to the
|
||||||
@ -216,11 +217,11 @@ in the following tables.
|
|||||||
**Optimized build (-O3)**
|
**Optimized build (-O3)**
|
||||||
|
|
||||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||||
|---------------|-----------------|----------------------|--------------------|
|
|-----------------|-----------------|----------------------|--------------------|
|
||||||
| printf | 1.6 | 54 | 50 |
|
| printf | 1.6 | 54 | 50 |
|
||||||
| IOStreams | 25.9 | 98 | 84 |
|
| IOStreams | 28.4 | 98 | 84 |
|
||||||
| fmt 83652df | 4.8 | 54 | 50 |
|
| {fmt} `1122268` | 5.0 | 54 | 50 |
|
||||||
| tinyformat | 29.1 | 161 | 136 |
|
| tinyformat | 32.6 | 164 | 136 |
|
||||||
| Boost Format | 55.0 | 530 | 317 |
|
| Boost Format | 55.0 | 530 | 317 |
|
||||||
|
|
||||||
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
|
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
|
||||||
@ -229,12 +230,12 @@ binary size (within a rounding error on this system).
|
|||||||
**Non-optimized build**
|
**Non-optimized build**
|
||||||
|
|
||||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||||
|---------------|-----------------|----------------------|--------------------|
|
|-----------------|-----------------|----------------------|--------------------|
|
||||||
| printf | 1.4 | 54 | 50 |
|
| printf | 1.4 | 54 | 50 |
|
||||||
| IOStreams | 23.4 | 92 | 68 |
|
| IOStreams | 27.0 | 88 | 68 |
|
||||||
| {fmt} 83652df | 4.4 | 89 | 85 |
|
| {fmt} `1122268` | 4.7 | 87 | 84 |
|
||||||
| tinyformat | 24.5 | 204 | 161 |
|
| tinyformat | 28.1 | 185 | 145 |
|
||||||
| Boost Format | 36.4 | 831 | 462 |
|
| Boost Format | 38.9 | 678 | 381 |
|
||||||
|
|
||||||
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
|
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
|
||||||
to compare formatting function overhead only. Boost Format is a
|
to compare formatting function overhead only. Boost Format is a
|
||||||
|
|||||||
@ -674,7 +674,7 @@
|
|||||||
https://github.com/fmtlib/fmt/issues/1747,
|
https://github.com/fmtlib/fmt/issues/1747,
|
||||||
https://github.com/fmtlib/fmt/pull/1750).
|
https://github.com/fmtlib/fmt/pull/1750).
|
||||||
Thanks @gsjaardema, @gabime, @johnor, @Kurkin, @invexed, @peterbell10,
|
Thanks @gsjaardema, @gabime, @johnor, @Kurkin, @invexed, @peterbell10,
|
||||||
@daixtrose, @petrutlucian94, @Neargye, @ambitslix, @gabime, @erthink,
|
@daixtrose, @petrutlucian94, @Neargye, @ambitslix, @gabime,
|
||||||
@tohammer and @0x8000-0000.
|
@tohammer and @0x8000-0000.
|
||||||
|
|
||||||
# 6.2.1 - 2020-05-09
|
# 6.2.1 - 2020-05-09
|
||||||
|
|||||||
111
doc/api.md
111
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
|
There are two ways to make a user-defined type formattable: providing a
|
||||||
`format_as` function or specializing the `formatter` struct template.
|
`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
|
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
|
type with the same format specifiers. The `format_as` function should
|
||||||
@ -413,11 +415,11 @@ locale:
|
|||||||
that take `std::locale` as a parameter. The locale type is a template
|
that take `std::locale` as a parameter. The locale type is a template
|
||||||
parameter to avoid the expensive `<locale>` include.
|
parameter to avoid the expensive `<locale>` include.
|
||||||
|
|
||||||
::: format(const Locale&, format_string<T...>, T&&...)
|
::: format(locale_ref, format_string<T...>, T&&...)
|
||||||
|
|
||||||
::: format_to(OutputIt, const Locale&, format_string<T...>, T&&...)
|
::: format_to(OutputIt, locale_ref, format_string<T...>, T&&...)
|
||||||
|
|
||||||
::: formatted_size(const Locale&, format_string<T...>, T&&...)
|
::: formatted_size(locale_ref, format_string<T...>, T&&...)
|
||||||
|
|
||||||
<a id="legacy-checks"></a>
|
<a id="legacy-checks"></a>
|
||||||
### Legacy Compile-Time Checks
|
### Legacy Compile-Time Checks
|
||||||
@ -547,32 +549,63 @@ fmt::print("{}", +s.bit);
|
|||||||
This is a known limitation of "perfect" forwarding in C++.
|
This is a known limitation of "perfect" forwarding in C++.
|
||||||
|
|
||||||
<a id="compile-api"></a>
|
<a id="compile-api"></a>
|
||||||
## Format String Compilation
|
## Compile-Time Support
|
||||||
|
|
||||||
`fmt/compile.h` provides format string compilation and compile-time
|
`fmt/compile.h` provides format string compilation and compile-time
|
||||||
(`constexpr`) formatting enabled via the `FMT_COMPILE` macro or the `_cf`
|
(`constexpr`) formatting enabled via the `FMT_COMPILE` macro or the `_cf`
|
||||||
user-defined literal defined in namespace `fmt::literals`. Format strings
|
user-defined literal defined in namespace `fmt::literals`. Format strings
|
||||||
marked with `FMT_COMPILE` or `_cf` are parsed, checked and converted into
|
marked with `FMT_COMPILE` or `_cf` are parsed, checked and converted into
|
||||||
efficient formatting code at compile-time. This supports arguments of built-in
|
efficient formatting code at compile-time. This supports arguments of built-in
|
||||||
and string types as well as user-defined types with `format` functions taking
|
and string types as well as user-defined types with `format` methods taking
|
||||||
the format context type as a template parameter in their `formatter`
|
the format context type as a template parameter in their `formatter`
|
||||||
specializations. For example:
|
specializations. For example ([run](https://www.godbolt.org/z/3c13erEoq)):
|
||||||
|
|
||||||
|
struct point {
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
};
|
||||||
|
|
||||||
template <> struct fmt::formatter<point> {
|
template <> struct fmt::formatter<point> {
|
||||||
constexpr auto parse(format_parse_context& ctx);
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const point& p, FormatContext& ctx) const;
|
auto format(const point& p, FormatContext& ctx) const {
|
||||||
|
return format_to(ctx.out(), "({}, {})"_cf, p.x, p.y);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using namespace fmt::literals;
|
||||||
|
std::string s = fmt::format("{}"_cf, point(4, 2));
|
||||||
|
|
||||||
Format string compilation can generate more binary code compared to the
|
Format string compilation can generate more binary code compared to the
|
||||||
default API and is only recommended in places where formatting is a
|
default API and is only recommended in places where formatting is a
|
||||||
performance bottleneck.
|
performance bottleneck.
|
||||||
|
|
||||||
::: FMT_COMPILE
|
The same APIs support formatting at compile time e.g. in `constexpr`
|
||||||
|
and `consteval` functions. Additionally there is an experimental
|
||||||
|
`FMT_STATIC_FORMAT` that allows formatting into a string of the exact
|
||||||
|
required size at compile time. Compile-time formatting works with built-in
|
||||||
|
and user-defined formatters that have `constexpr` `format` methods.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
template <> struct fmt::formatter<point> {
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
constexpr auto format(const point& p, FormatContext& ctx) const {
|
||||||
|
return format_to(ctx.out(), "({}, {})"_cf, p.x, p.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto s = FMT_STATIC_FORMAT("{}", point(4, 2));
|
||||||
|
const char* cstr = s.c_str(); // Points the static string "(4, 2)".
|
||||||
|
|
||||||
::: operator""_cf
|
::: operator""_cf
|
||||||
|
|
||||||
|
::: FMT_COMPILE
|
||||||
|
|
||||||
|
::: FMT_STATIC_FORMAT
|
||||||
|
|
||||||
<a id="color-api"></a>
|
<a id="color-api"></a>
|
||||||
## Terminal Colors and Text Styles
|
## Terminal Colors and Text Styles
|
||||||
|
|
||||||
@ -591,6 +624,8 @@ performance bottleneck.
|
|||||||
|
|
||||||
::: ostream
|
::: ostream
|
||||||
|
|
||||||
|
::: output_file(cstring_view, T...)
|
||||||
|
|
||||||
::: windows_error
|
::: windows_error
|
||||||
|
|
||||||
<a id="ostream-api"></a>
|
<a id="ostream-api"></a>
|
||||||
@ -641,9 +676,9 @@ if an argument type doesn't match its format specification.
|
|||||||
|
|
||||||
::: printf(string_view, const T&...)
|
::: printf(string_view, const T&...)
|
||||||
|
|
||||||
::: fprintf(std::FILE*, const S&, const T&...)
|
::: fprintf(std::FILE*, string_view, const T&...)
|
||||||
|
|
||||||
::: sprintf(const S&, const T&...)
|
::: sprintf(string_view, const T&...)
|
||||||
|
|
||||||
<a id="xchar-api"></a>
|
<a id="xchar-api"></a>
|
||||||
## Wide Strings
|
## Wide Strings
|
||||||
@ -651,8 +686,6 @@ if an argument type doesn't match its format specification.
|
|||||||
The optional header `fmt/xchar.h` provides support for `wchar_t` and
|
The optional header `fmt/xchar.h` provides support for `wchar_t` and
|
||||||
exotic character types.
|
exotic character types.
|
||||||
|
|
||||||
::: is_char
|
|
||||||
|
|
||||||
::: wstring_view
|
::: wstring_view
|
||||||
|
|
||||||
::: wformat_context
|
::: wformat_context
|
||||||
@ -675,5 +708,55 @@ following differences:
|
|||||||
precision that provides round-trip guarantees similarly to other languages
|
precision that provides round-trip guarantees similarly to other languages
|
||||||
like Java and Python. `std::format` is currently specified in terms of
|
like Java and Python. `std::format` is currently specified in terms of
|
||||||
`std::to_chars` which tries to generate the smallest number of characters
|
`std::to_chars` which tries to generate the smallest number of characters
|
||||||
(ignoring redundant digits and sign in exponent) and may procude more
|
(ignoring redundant digits and sign in exponent) and may produce more
|
||||||
decimal digits than necessary.
|
decimal digits than necessary.
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
{fmt} provides configuration via CMake options and preprocessor macros to
|
||||||
|
enable or disable features and to optimize for binary size. For example, you
|
||||||
|
can disable OS-specific APIs defined in `fmt/os.h` with `-DFMT_OS=OFF` when
|
||||||
|
configuring CMake.
|
||||||
|
|
||||||
|
### CMake Options
|
||||||
|
|
||||||
|
- **`FMT_OS`**: When set to `OFF`, disables OS-specific APIs (`fmt/os.h`).
|
||||||
|
- **`FMT_UNICODE`**: When set of `OFF`, disables Unicode support on
|
||||||
|
Windows/MSVC. Unicode support is always enabled on other platforms.
|
||||||
|
|
||||||
|
### Macros
|
||||||
|
|
||||||
|
- **`FMT_HEADER_ONLY`**: Enables the header-only mode when defined. It is an
|
||||||
|
alternative to using the `fmt::fmt-header-only` CMake target.
|
||||||
|
Default: not defined.
|
||||||
|
|
||||||
|
- **`FMT_USE_EXCEPTIONS`**: Disables the use of exceptions when set to `0`.
|
||||||
|
Default: `1` (`0` if compiled with `-fno-exceptions`).
|
||||||
|
|
||||||
|
- **`FMT_USE_LOCALE`**: When set to `0`, disables locale support.
|
||||||
|
Default: `1` (`0` when `FMT_OPTIMIZE_SIZE > 1`).
|
||||||
|
|
||||||
|
- **`FMT_CUSTOM_ASSERT_FAIL`**: When set to `1`, allows users to provide a
|
||||||
|
custom `fmt::assert_fail` function which is called on assertion failures and,
|
||||||
|
if exceptions are disabled, on runtime errors. Default: `0`.
|
||||||
|
|
||||||
|
- **`FMT_BUILTIN_TYPES`**: When set to `0`, disables built-in handling of
|
||||||
|
arithmetic and string types other than `int`. This reduces library size at
|
||||||
|
the cost of per-call overhead. Default: `1`.
|
||||||
|
|
||||||
|
- **`FMT_OPTIMIZE_SIZE`**: Controls binary size optimizations:
|
||||||
|
- `0` - off (default)
|
||||||
|
- `1` - disables locale support and applies some optimizations
|
||||||
|
- `2` - disables some Unicode features, named arguments and applies more
|
||||||
|
aggressive optimizations
|
||||||
|
|
||||||
|
### Binary Size Optimization
|
||||||
|
|
||||||
|
To minimize the binary footprint of {fmt} as much as possible at the cost of
|
||||||
|
some features, you can use the following configuration:
|
||||||
|
|
||||||
|
- CMake options:
|
||||||
|
- `FMT_OS=OFF`
|
||||||
|
- Macros:
|
||||||
|
- `FMT_BUILTIN_TYPES=0`
|
||||||
|
- `FMT_OPTIMIZE_SIZE=2`
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
pre > code.decl {
|
pre > code.decl {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,6 +78,17 @@ community contributors. If the version is out of date, please [create an
|
|||||||
issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg
|
issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg
|
||||||
repository. -->
|
repository. -->
|
||||||
|
|
||||||
|
### Conan
|
||||||
|
|
||||||
|
You can download and install {fmt} using the [Conan](https://conan.io/) package manager:
|
||||||
|
|
||||||
|
conan install -r conancenter --requires="fmt/[*]" --build=missing
|
||||||
|
|
||||||
|
<!-- The {fmt} package in Conan Center is maintained by
|
||||||
|
[ConanCenterIndex](https://github.com/conan-io/conan-center-index) community.
|
||||||
|
If the version is out of date or the package does not work,
|
||||||
|
please create an issue or pull request on the Conan Center Index repository. -->
|
||||||
|
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
|
||||||
CMake works by generating native makefiles or project files that can be
|
CMake works by generating native makefiles or project files that can be
|
||||||
|
|||||||
@ -76,7 +76,7 @@ hide:
|
|||||||
<p>
|
<p>
|
||||||
The default is <b>locale-independent</b>, but you can opt into localized
|
The default is <b>locale-independent</b>, but you can opt into localized
|
||||||
formatting and {fmt} makes it work with Unicode, addressing issues in the
|
formatting and {fmt} makes it work with Unicode, addressing issues in the
|
||||||
standard libary.
|
standard library.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -718,7 +718,7 @@ These modifiers are only supported for the `'H'`, `'I'`, `'M'`, `'S'`, `'U'`,
|
|||||||
Format specifications for range types have the following syntax:
|
Format specifications for range types have the following syntax:
|
||||||
|
|
||||||
<pre><code class="language-json"
|
<pre><code class="language-json"
|
||||||
>range_format_spec ::= ["n"][range_type][range_underlying_spec]</code>
|
>range_format_spec ::= ["n"][range_type][":" range_underlying_spec]</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
The `'n'` option formats the range without the opening and closing brackets.
|
The `'n'` option formats the range without the opening and closing brackets.
|
||||||
@ -761,14 +761,16 @@ fmt::print("{::}", std::vector{'h', 'e', 'l', 'l', 'o'});
|
|||||||
// Output: [h, e, l, l, o]
|
// Output: [h, e, l, l, o]
|
||||||
fmt::print("{::d}", std::vector{'h', 'e', 'l', 'l', 'o'});
|
fmt::print("{::d}", std::vector{'h', 'e', 'l', 'l', 'o'});
|
||||||
// Output: [104, 101, 108, 108, 111]
|
// Output: [104, 101, 108, 108, 111]
|
||||||
|
fmt::print("{:n:f}", std::array{std::numbers::pi, std::numbers::e});
|
||||||
|
// Output: 3.141593, 2.718282
|
||||||
```
|
```
|
||||||
|
|
||||||
## Format Examples
|
## Format Examples
|
||||||
|
|
||||||
This section contains examples of the format syntax and comparison with
|
This section contains examples of the format syntax and comparison with
|
||||||
the printf formatting.
|
the `printf` formatting.
|
||||||
|
|
||||||
In most of the cases the syntax is similar to the printf formatting,
|
In most of the cases the syntax is similar to the `printf` formatting,
|
||||||
with the addition of the `{}` and with `:` used instead of `%`. For
|
with the addition of the `{}` and with `:` used instead of `%`. For
|
||||||
example, `"%03.2f"` can be translated to `"{:03.2f}"`.
|
example, `"%03.2f"` can be translated to `"{:03.2f}"`.
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,7 @@ class dynamic_arg_list {
|
|||||||
* It can be implicitly converted into `fmt::basic_format_args` for passing
|
* It can be implicitly converted into `fmt::basic_format_args` for passing
|
||||||
* into type-erased formatting functions such as `fmt::vformat`.
|
* into type-erased formatting functions such as `fmt::vformat`.
|
||||||
*/
|
*/
|
||||||
template <typename Context> class dynamic_format_arg_store {
|
FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
|
||||||
private:
|
private:
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ template <typename Context> class dynamic_format_arg_store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of elements in the store.
|
/// Returns the number of elements in the store.
|
||||||
size_t size() const noexcept { return data_.size(); }
|
auto size() const noexcept -> size_t { return data_.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||||
#define FMT_VERSION 110200
|
#define FMT_VERSION 120100
|
||||||
|
|
||||||
// Detect compiler versions.
|
// Detect compiler versions.
|
||||||
#if defined(__clang__) && !defined(__ibmxl__)
|
#if defined(__clang__) && !defined(__ibmxl__)
|
||||||
@ -114,7 +114,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated.
|
// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated.
|
||||||
#if !defined(__cpp_lib_is_constant_evaluated)
|
#ifdef FMT_USE_CONSTEVAL
|
||||||
|
// Use the provided definition.
|
||||||
|
#elif !defined(__cpp_lib_is_constant_evaluated)
|
||||||
# define FMT_USE_CONSTEVAL 0
|
# define FMT_USE_CONSTEVAL 0
|
||||||
#elif FMT_CPLUSPLUS < 201709L
|
#elif FMT_CPLUSPLUS < 201709L
|
||||||
# define FMT_USE_CONSTEVAL 0
|
# define FMT_USE_CONSTEVAL 0
|
||||||
@ -201,14 +203,6 @@
|
|||||||
# define FMT_NODISCARD
|
# define FMT_NODISCARD
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FMT_DEPRECATED
|
|
||||||
// Use the provided definition.
|
|
||||||
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
|
|
||||||
# define FMT_DEPRECATED [[deprecated]]
|
|
||||||
#else
|
|
||||||
# define FMT_DEPRECATED /* deprecated */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||||
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
|
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
|
||||||
#else
|
#else
|
||||||
@ -239,9 +233,9 @@
|
|||||||
FMT_PRAGMA_GCC(push_options)
|
FMT_PRAGMA_GCC(push_options)
|
||||||
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
||||||
FMT_PRAGMA_GCC(optimize("Og"))
|
FMT_PRAGMA_GCC(optimize("Og"))
|
||||||
# define FMT_GCC_OPTIMIZED
|
|
||||||
#endif
|
#endif
|
||||||
FMT_PRAGMA_CLANG(diagnostic push)
|
FMT_PRAGMA_CLANG(diagnostic push)
|
||||||
|
FMT_PRAGMA_GCC(diagnostic push)
|
||||||
|
|
||||||
#ifdef FMT_ALWAYS_INLINE
|
#ifdef FMT_ALWAYS_INLINE
|
||||||
// Use the provided definition.
|
// Use the provided definition.
|
||||||
@ -251,7 +245,7 @@ FMT_PRAGMA_CLANG(diagnostic push)
|
|||||||
# define FMT_ALWAYS_INLINE inline
|
# define FMT_ALWAYS_INLINE inline
|
||||||
#endif
|
#endif
|
||||||
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
||||||
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
|
#ifdef NDEBUG
|
||||||
# define FMT_INLINE FMT_ALWAYS_INLINE
|
# define FMT_INLINE FMT_ALWAYS_INLINE
|
||||||
#else
|
#else
|
||||||
# define FMT_INLINE inline
|
# define FMT_INLINE inline
|
||||||
@ -260,7 +254,7 @@ FMT_PRAGMA_CLANG(diagnostic push)
|
|||||||
#ifndef FMT_BEGIN_NAMESPACE
|
#ifndef FMT_BEGIN_NAMESPACE
|
||||||
# define FMT_BEGIN_NAMESPACE \
|
# define FMT_BEGIN_NAMESPACE \
|
||||||
namespace fmt { \
|
namespace fmt { \
|
||||||
inline namespace v11 {
|
inline namespace v12 {
|
||||||
# define FMT_END_NAMESPACE \
|
# define FMT_END_NAMESPACE \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
@ -356,6 +350,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
|
|||||||
return a > b ? a : b;
|
return a > b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||||
|
const char* message);
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// Suppresses "unused variable" warnings with the method described in
|
// Suppresses "unused variable" warnings with the method described in
|
||||||
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
|
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
|
||||||
@ -396,7 +393,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
|||||||
# define FMT_ASSERT(condition, message) \
|
# define FMT_ASSERT(condition, message) \
|
||||||
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
|
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
|
||||||
? (void)0 \
|
? (void)0 \
|
||||||
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
|
: ::fmt::assert_fail(__FILE__, __LINE__, (message)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FMT_USE_INT128
|
#ifdef FMT_USE_INT128
|
||||||
@ -419,8 +416,12 @@ inline auto map(int128_opt) -> monostate { return {}; }
|
|||||||
inline auto map(uint128_opt) -> monostate { return {}; }
|
inline auto map(uint128_opt) -> monostate { return {}; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_USE_BITINT
|
#ifdef FMT_USE_BITINT
|
||||||
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
|
// Use the provided definition.
|
||||||
|
#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__)
|
||||||
|
# define FMT_USE_BITINT 1
|
||||||
|
#else
|
||||||
|
# define FMT_USE_BITINT 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_BITINT
|
#if FMT_USE_BITINT
|
||||||
@ -463,12 +464,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
|
|||||||
static_assert(!FMT_UNICODE || use_utf8,
|
static_assert(!FMT_UNICODE || use_utf8,
|
||||||
"Unicode support requires compiling with /utf-8");
|
"Unicode support requires compiling with /utf-8");
|
||||||
|
|
||||||
template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
|
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
|
||||||
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
|
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
|
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
|
||||||
-> int {
|
|
||||||
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
|
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
|
||||||
for (; n != 0; ++s1, ++s2, --n) {
|
for (; n != 0; ++s1, ++s2, --n) {
|
||||||
if (*s1 < *s2) return -1;
|
if (*s1 < *s2) return -1;
|
||||||
@ -540,7 +542,7 @@ template <typename Char> class basic_string_view {
|
|||||||
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
|
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
|
||||||
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
|
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||||
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
|
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
|
||||||
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr.
|
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -616,19 +618,6 @@ template <typename Char> class basic_string_view {
|
|||||||
|
|
||||||
using string_view = basic_string_view<char>;
|
using string_view = basic_string_view<char>;
|
||||||
|
|
||||||
// DEPRECATED! Will be merged with is_char and moved to detail.
|
|
||||||
template <typename T> struct is_xchar : std::false_type {};
|
|
||||||
template <> struct is_xchar<wchar_t> : std::true_type {};
|
|
||||||
template <> struct is_xchar<char16_t> : std::true_type {};
|
|
||||||
template <> struct is_xchar<char32_t> : std::true_type {};
|
|
||||||
#ifdef __cpp_char8_t
|
|
||||||
template <> struct is_xchar<char8_t> : std::true_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Specifies if `T` is a character (code unit) type.
|
|
||||||
template <typename T> struct is_char : is_xchar<T> {};
|
|
||||||
template <> struct is_char<char> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T> class basic_appender;
|
template <typename T> class basic_appender;
|
||||||
using appender = basic_appender<char>;
|
using appender = basic_appender<char>;
|
||||||
|
|
||||||
@ -781,7 +770,7 @@ class basic_specs {
|
|||||||
(static_cast<unsigned>(p) << precision_shift);
|
(static_cast<unsigned>(p) << precision_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool dynamic() const {
|
constexpr auto dynamic() const -> bool {
|
||||||
return (data_ & (width_mask | precision_mask)) != 0;
|
return (data_ & (width_mask | precision_mask)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -921,14 +910,50 @@ template <typename Char = char> class parse_context {
|
|||||||
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
|
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef FMT_USE_LOCALE
|
||||||
|
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// A type-erased reference to std::locale to avoid the heavy <locale> include.
|
||||||
|
class locale_ref {
|
||||||
|
#if FMT_USE_LOCALE
|
||||||
|
private:
|
||||||
|
const void* locale_; // A type-erased pointer to std::locale.
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr locale_ref() : locale_(nullptr) {}
|
||||||
|
|
||||||
|
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
|
||||||
|
locale_ref(const Locale& loc) : locale_(&loc) {
|
||||||
|
// Check if std::isalpha is found via ADL to reduce the chance of misuse.
|
||||||
|
detail::ignore_unused(sizeof(isalpha('x', loc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
||||||
|
#endif // FMT_USE_LOCALE
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename Locale> auto get() const -> Locale;
|
||||||
|
};
|
||||||
|
|
||||||
FMT_END_EXPORT
|
FMT_END_EXPORT
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// Specifies if `T` is a code unit type.
|
||||||
|
template <typename T> struct is_code_unit : std::false_type {};
|
||||||
|
template <> struct is_code_unit<char> : std::true_type {};
|
||||||
|
template <> struct is_code_unit<wchar_t> : std::true_type {};
|
||||||
|
template <> struct is_code_unit<char16_t> : std::true_type {};
|
||||||
|
template <> struct is_code_unit<char32_t> : std::true_type {};
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
|
||||||
|
#endif
|
||||||
|
|
||||||
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
|
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
|
||||||
// to it, deducing Char. Explicitly convertible types such as the ones returned
|
// to it, deducing Char. Explicitly convertible types such as the ones returned
|
||||||
// from FMT_STRING are intentionally excluded.
|
// from FMT_STRING are intentionally excluded.
|
||||||
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
|
template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)>
|
||||||
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
|
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -1057,11 +1082,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
|
|||||||
return (B1 ? 1 : 0) + count<B2, Tail...>();
|
return (B1 ? 1 : 0) + count<B2, Tail...>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args> constexpr auto count_named_args() -> int {
|
template <typename... T> constexpr auto count_named_args() -> int {
|
||||||
return count<is_named_arg<Args>::value...>();
|
return count<is_named_arg<T>::value...>();
|
||||||
}
|
}
|
||||||
template <typename... Args> constexpr auto count_static_named_args() -> int {
|
template <typename... T> constexpr auto count_static_named_args() -> int {
|
||||||
return count<is_static_named_arg<Args>::value...>();
|
return count<is_static_named_arg<T>::value...>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> struct named_arg_info {
|
template <typename Char> struct named_arg_info {
|
||||||
@ -1069,7 +1094,7 @@ template <typename Char> struct named_arg_info {
|
|||||||
int id;
|
int id;
|
||||||
};
|
};
|
||||||
|
|
||||||
// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13.
|
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
|
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
|
||||||
int named_arg_index,
|
int named_arg_index,
|
||||||
@ -1173,7 +1198,7 @@ template <typename Char> struct type_mapper {
|
|||||||
static auto map(ubitint<N>)
|
static auto map(ubitint<N>)
|
||||||
-> conditional_t<N <= 64, unsigned long long, void>;
|
-> conditional_t<N <= 64, unsigned long long, void>;
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
|
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
|
||||||
static auto map(T) -> conditional_t<
|
static auto map(T) -> conditional_t<
|
||||||
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
|
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
|
||||||
|
|
||||||
@ -1679,12 +1704,12 @@ template <typename... T> struct arg_pack {};
|
|||||||
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
|
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
|
||||||
class format_string_checker {
|
class format_string_checker {
|
||||||
private:
|
private:
|
||||||
type types_[max_of(1, NUM_ARGS)];
|
type types_[max_of<size_t>(1, NUM_ARGS)];
|
||||||
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
|
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
|
||||||
compile_parse_context<Char> context_;
|
compile_parse_context<Char> context_;
|
||||||
|
|
||||||
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
|
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
|
||||||
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
|
parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
@ -1825,15 +1850,19 @@ template <typename T> class buffer {
|
|||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
|
||||||
FMT_CONSTEXPR20
|
FMT_CONSTEXPR20
|
||||||
#endif
|
#endif
|
||||||
void
|
void append(const U* begin, const U* end) {
|
||||||
append(const U* begin, const U* end) {
|
|
||||||
while (begin != end) {
|
while (begin != end) {
|
||||||
|
auto size = size_;
|
||||||
|
auto free_cap = capacity_ - size;
|
||||||
auto count = to_unsigned(end - begin);
|
auto count = to_unsigned(end - begin);
|
||||||
try_reserve(size_ + count);
|
if (free_cap < count) {
|
||||||
auto free_cap = capacity_ - size_;
|
grow_(*this, size + count);
|
||||||
if (free_cap < count) count = free_cap;
|
size = size_;
|
||||||
|
free_cap = capacity_ - size;
|
||||||
|
count = count < free_cap ? count : free_cap;
|
||||||
|
}
|
||||||
// A loop is faster than memcpy on small sizes.
|
// A loop is faster than memcpy on small sizes.
|
||||||
T* out = ptr_ + size_;
|
T* out = ptr_ + size;
|
||||||
for (size_t i = 0; i < count; ++i) out[i] = begin[i];
|
for (size_t i = 0; i < count; ++i) out[i] = begin[i];
|
||||||
size_ += count;
|
size_ += count;
|
||||||
begin += count;
|
begin += count;
|
||||||
@ -2033,6 +2062,17 @@ struct has_back_insert_iterator_container_append<
|
|||||||
.append(std::declval<InputIt>(),
|
.append(std::declval<InputIt>(),
|
||||||
std::declval<InputIt>()))>> : std::true_type {};
|
std::declval<InputIt>()))>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename InputIt, typename = void>
|
||||||
|
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename InputIt>
|
||||||
|
struct has_back_insert_iterator_container_insert_at_end<
|
||||||
|
OutputIt, InputIt,
|
||||||
|
void_t<decltype(get_container(std::declval<OutputIt>())
|
||||||
|
.insert(get_container(std::declval<OutputIt>()).end(),
|
||||||
|
std::declval<InputIt>(),
|
||||||
|
std::declval<InputIt>()))>> : std::true_type {};
|
||||||
|
|
||||||
// An optimized version of std::copy with the output value type (T).
|
// An optimized version of std::copy with the output value type (T).
|
||||||
template <typename T, typename InputIt, typename OutputIt,
|
template <typename T, typename InputIt, typename OutputIt,
|
||||||
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
|
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
|
||||||
@ -2047,6 +2087,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
|||||||
template <typename T, typename InputIt, typename OutputIt,
|
template <typename T, typename InputIt, typename OutputIt,
|
||||||
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
|
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
|
||||||
!has_back_insert_iterator_container_append<
|
!has_back_insert_iterator_container_append<
|
||||||
|
OutputIt, InputIt>::value &&
|
||||||
|
has_back_insert_iterator_container_insert_at_end<
|
||||||
OutputIt, InputIt>::value)>
|
OutputIt, InputIt>::value)>
|
||||||
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
@ -2056,7 +2098,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename InputIt, typename OutputIt,
|
template <typename T, typename InputIt, typename OutputIt,
|
||||||
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
|
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
|
||||||
|
(has_back_insert_iterator_container_append<
|
||||||
|
OutputIt, InputIt>::value ||
|
||||||
|
has_back_insert_iterator_container_insert_at_end<
|
||||||
|
OutputIt, InputIt>::value)))>
|
||||||
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
|
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
|
||||||
while (begin != end) *out++ = static_cast<T>(*begin++);
|
while (begin != end) *out++ = static_cast<T>(*begin++);
|
||||||
return out;
|
return out;
|
||||||
@ -2176,7 +2222,7 @@ template <typename Context> class value {
|
|||||||
static_assert(N <= 64, "unsupported _BitInt");
|
static_assert(N <= 64, "unsupported _BitInt");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
|
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
|
||||||
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
|
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
|
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
|
||||||
@ -2252,7 +2298,7 @@ template <typename Context> class value {
|
|||||||
custom.value = const_cast<value_type*>(&x);
|
custom.value = const_cast<value_type*>(&x);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
custom.format = format_custom<value_type, formatter<value_type, char_type>>;
|
custom.format = format_custom<value_type>;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
|
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
|
||||||
@ -2263,10 +2309,10 @@ template <typename Context> class value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Formats an argument of a custom type, such as a user-defined class.
|
// Formats an argument of a custom type, such as a user-defined class.
|
||||||
template <typename T, typename Formatter>
|
template <typename T>
|
||||||
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
|
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
|
||||||
Context& ctx) {
|
Context& ctx) {
|
||||||
auto f = Formatter();
|
auto f = formatter<T, char_type>();
|
||||||
parse_ctx.advance_to(f.parse(parse_ctx));
|
parse_ctx.advance_to(f.parse(parse_ctx));
|
||||||
using qualified_type =
|
using qualified_type =
|
||||||
conditional_t<has_formatter<const T, char_type>(), const T, T>;
|
conditional_t<has_formatter<const T, char_type>(), const T, T>;
|
||||||
@ -2293,35 +2339,14 @@ struct is_output_iterator<
|
|||||||
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
|
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
|
||||||
T>::value>> : std::true_type {};
|
T>::value>> : std::true_type {};
|
||||||
|
|
||||||
#ifndef FMT_USE_LOCALE
|
|
||||||
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
|
|
||||||
class locale_ref {
|
|
||||||
#if FMT_USE_LOCALE
|
|
||||||
private:
|
|
||||||
const void* locale_; // A type-erased pointer to std::locale.
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr locale_ref() : locale_(nullptr) {}
|
|
||||||
template <typename Locale> locale_ref(const Locale& loc);
|
|
||||||
|
|
||||||
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
|
||||||
#endif // FMT_USE_LOCALE
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename Locale> auto get() const -> Locale;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename> constexpr auto encode_types() -> unsigned long long {
|
template <typename> constexpr auto encode_types() -> unsigned long long {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Context, typename Arg, typename... Args>
|
template <typename Context, typename First, typename... T>
|
||||||
constexpr auto encode_types() -> unsigned long long {
|
constexpr auto encode_types() -> unsigned long long {
|
||||||
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
|
return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
|
||||||
(encode_types<Context, Args...>() << packed_arg_bits);
|
(encode_types<Context, T...>() << packed_arg_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
|
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
|
||||||
@ -2338,8 +2363,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
|
|||||||
unsigned long long DESC>
|
unsigned long long DESC>
|
||||||
struct named_arg_store {
|
struct named_arg_store {
|
||||||
// args_[0].named_args points to named_args to avoid bloating format_args.
|
// args_[0].named_args points to named_args to avoid bloating format_args.
|
||||||
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
|
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
|
||||||
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
|
named_arg_info<typename Context::char_type>
|
||||||
|
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
|
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
|
||||||
@ -2358,8 +2384,8 @@ struct named_arg_store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
named_arg_store(const named_arg_store& rhs) = delete;
|
named_arg_store(const named_arg_store& rhs) = delete;
|
||||||
named_arg_store& operator=(const named_arg_store& rhs) = delete;
|
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
|
||||||
named_arg_store& operator=(named_arg_store&& rhs) = delete;
|
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
|
||||||
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
|
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2372,7 +2398,7 @@ struct format_arg_store {
|
|||||||
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
using type =
|
using type =
|
||||||
conditional_t<NUM_NAMED_ARGS == 0,
|
conditional_t<NUM_NAMED_ARGS == 0,
|
||||||
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
|
arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)],
|
||||||
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
|
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
|
||||||
type args;
|
type args;
|
||||||
};
|
};
|
||||||
@ -2656,22 +2682,17 @@ class context {
|
|||||||
private:
|
private:
|
||||||
appender out_;
|
appender out_;
|
||||||
format_args args_;
|
format_args args_;
|
||||||
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
|
FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// The character type for the output.
|
using char_type = char; ///< The character type for the output.
|
||||||
using char_type = char;
|
|
||||||
|
|
||||||
using iterator = appender;
|
using iterator = appender;
|
||||||
using format_arg = basic_format_arg<context>;
|
using format_arg = basic_format_arg<context>;
|
||||||
using parse_context_type FMT_DEPRECATED = parse_context<>;
|
|
||||||
template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
|
|
||||||
enum { builtin_types = FMT_BUILTIN_TYPES };
|
enum { builtin_types = FMT_BUILTIN_TYPES };
|
||||||
|
|
||||||
/// Constructs a `context` object. References to the arguments are stored
|
/// Constructs a `context` object. References to the arguments are stored
|
||||||
/// in the object so make sure they have appropriate lifetimes.
|
/// in the object so make sure they have appropriate lifetimes.
|
||||||
FMT_CONSTEXPR context(iterator out, format_args args,
|
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
|
||||||
detail::locale_ref loc = {})
|
|
||||||
: out_(out), args_(args), loc_(loc) {}
|
: out_(out), args_(args), loc_(loc) {}
|
||||||
context(context&&) = default;
|
context(context&&) = default;
|
||||||
context(const context&) = delete;
|
context(const context&) = delete;
|
||||||
@ -2692,7 +2713,7 @@ class context {
|
|||||||
// Advances the begin iterator to `it`.
|
// Advances the begin iterator to `it`.
|
||||||
FMT_CONSTEXPR void advance_to(iterator) {}
|
FMT_CONSTEXPR void advance_to(iterator) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
|
FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char = char> struct runtime_format_string {
|
template <typename Char = char> struct runtime_format_string {
|
||||||
@ -2733,7 +2754,9 @@ template <typename... T> struct fstring {
|
|||||||
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
|
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
|
||||||
std::is_reference<T>::value)...>() == 0,
|
std::is_reference<T>::value)...>() == 0,
|
||||||
"passing views as lvalues is disallowed");
|
"passing views as lvalues is disallowed");
|
||||||
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
|
#if FMT_USE_CONSTEVAL
|
||||||
|
parse_format_string<char>(s, checker(s, arg_pack()));
|
||||||
|
#endif
|
||||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||||
static_assert(
|
static_assert(
|
||||||
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
||||||
@ -2779,9 +2802,6 @@ template <typename T, typename Char = char>
|
|||||||
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
|
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename T, typename Char>
|
|
||||||
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
|
|
||||||
|
|
||||||
// A formatter specialization for natively supported types.
|
// A formatter specialization for natively supported types.
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<T, Char,
|
struct formatter<T, Char,
|
||||||
@ -2978,9 +2998,10 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
|
|||||||
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
|
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_EXPORT
|
FMT_PRAGMA_GCC(diagnostic pop)
|
||||||
FMT_PRAGMA_CLANG(diagnostic pop)
|
FMT_PRAGMA_CLANG(diagnostic pop)
|
||||||
FMT_PRAGMA_GCC(pop_options)
|
FMT_PRAGMA_GCC(pop_options)
|
||||||
|
FMT_END_EXPORT
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#ifdef FMT_HEADER_ONLY
|
#ifdef FMT_HEADER_ONLY
|
||||||
|
|||||||
@ -38,6 +38,7 @@ FMT_BEGIN_NAMESPACE
|
|||||||
// Copyright Paul Dreik 2019
|
// Copyright Paul Dreik 2019
|
||||||
namespace safe_duration_cast {
|
namespace safe_duration_cast {
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
template <typename To, typename From,
|
template <typename To, typename From,
|
||||||
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
|
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
|
||||||
std::numeric_limits<From>::is_signed ==
|
std::numeric_limits<From>::is_signed ==
|
||||||
@ -161,17 +162,6 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
int& ec) -> To {
|
int& ec) -> To {
|
||||||
using From = std::chrono::duration<FromRep, FromPeriod>;
|
using From = std::chrono::duration<FromRep, FromPeriod>;
|
||||||
ec = 0;
|
ec = 0;
|
||||||
if (std::isnan(from.count())) {
|
|
||||||
// nan in, gives nan out. easy.
|
|
||||||
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
|
|
||||||
}
|
|
||||||
// maybe we should also check if from is denormal, and decide what to do about
|
|
||||||
// it.
|
|
||||||
|
|
||||||
// +-inf should be preserved.
|
|
||||||
if (std::isinf(from.count())) {
|
|
||||||
return To{from.count()};
|
|
||||||
}
|
|
||||||
|
|
||||||
// the basic idea is that we need to convert from count() in the from type
|
// the basic idea is that we need to convert from count() in the from type
|
||||||
// to count() in the To type, by multiplying it with this:
|
// to count() in the To type, by multiplying it with this:
|
||||||
@ -282,8 +272,6 @@ namespace detail {
|
|||||||
#define FMT_NOMACRO
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
template <typename T = void> struct null {};
|
template <typename T = void> struct null {};
|
||||||
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
|
|
||||||
inline auto localtime_s(...) -> null<> { return null<>(); }
|
|
||||||
inline auto gmtime_r(...) -> null<> { return null<>(); }
|
inline auto gmtime_r(...) -> null<> { return null<>(); }
|
||||||
inline auto gmtime_s(...) -> null<> { return null<>(); }
|
inline auto gmtime_s(...) -> null<> { return null<>(); }
|
||||||
|
|
||||||
@ -326,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename CodeUnit> struct codecvt_result {
|
template <typename CodeUnit> struct codecvt_result {
|
||||||
static constexpr const size_t max_size = 32;
|
static constexpr size_t max_size = 32;
|
||||||
CodeUnit buf[max_size];
|
CodeUnit buf[max_size];
|
||||||
CodeUnit* end;
|
CodeUnit* end;
|
||||||
};
|
};
|
||||||
@ -443,11 +431,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
|||||||
|
|
||||||
using common_rep = typename std::common_type<FromRep, typename To::rep,
|
using common_rep = typename std::common_type<FromRep, typename To::rep,
|
||||||
decltype(factor::num)>::type;
|
decltype(factor::num)>::type;
|
||||||
|
common_rep count = from.count(); // This conversion is lossless.
|
||||||
int ec = 0;
|
|
||||||
auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
|
|
||||||
from.count(), ec);
|
|
||||||
if (ec) throw_duration_error();
|
|
||||||
|
|
||||||
// Multiply from.count() by factor and check for overflow.
|
// Multiply from.count() by factor and check for overflow.
|
||||||
if (const_check(factor::num != 1)) {
|
if (const_check(factor::num != 1)) {
|
||||||
@ -458,6 +442,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
|||||||
count *= factor::num;
|
count *= factor::num;
|
||||||
}
|
}
|
||||||
if (const_check(factor::den != 1)) count /= factor::den;
|
if (const_check(factor::den != 1)) count /= factor::den;
|
||||||
|
int ec = 0;
|
||||||
auto to =
|
auto to =
|
||||||
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
|
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
|
||||||
count, ec));
|
count, ec));
|
||||||
@ -471,6 +456,8 @@ template <typename To, typename FromRep, typename FromPeriod,
|
|||||||
std::is_floating_point<typename To::rep>::value)>
|
std::is_floating_point<typename To::rep>::value)>
|
||||||
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||||
#if FMT_SAFE_DURATION_CAST
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
// Preserve infinity and NaN.
|
||||||
|
if (!isfinite(from.count())) return static_cast<To>(from.count());
|
||||||
// Throwing version of safe_duration_cast is only available for
|
// Throwing version of safe_duration_cast is only available for
|
||||||
// integer to integer or float to float casts.
|
// integer to integer or float to float casts.
|
||||||
int ec;
|
int ec;
|
||||||
@ -487,7 +474,7 @@ template <typename To, typename FromRep, typename FromPeriod,
|
|||||||
FMT_ENABLE_IF(
|
FMT_ENABLE_IF(
|
||||||
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
|
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
|
||||||
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||||
// Mixed integer <-> float cast is not supported by safe_duration_cast.
|
// Mixed integer <-> float cast is not supported by safe duration_cast.
|
||||||
return std::chrono::duration_cast<To>(from);
|
return std::chrono::duration_cast<To>(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,86 +488,10 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
|
|||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace tz {
|
|
||||||
|
|
||||||
// DEPRECATED!
|
|
||||||
struct time_zone {
|
|
||||||
template <typename Duration, typename LocalTime>
|
|
||||||
auto to_sys(LocalTime) -> sys_time<Duration> {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <typename... T> auto current_zone(T...) -> time_zone* {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T> void _tzset(T...) {}
|
|
||||||
} // namespace tz
|
|
||||||
|
|
||||||
// DEPRECATED!
|
|
||||||
inline void tzset_once() {
|
|
||||||
static bool init = []() {
|
|
||||||
using namespace tz;
|
|
||||||
_tzset();
|
|
||||||
return false;
|
|
||||||
}();
|
|
||||||
ignore_unused(init);
|
|
||||||
}
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts given time since epoch as `std::time_t` value into calendar time,
|
|
||||||
* expressed in local time. Unlike `std::localtime`, this function is
|
|
||||||
* thread-safe on most platforms.
|
|
||||||
*/
|
|
||||||
FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm {
|
|
||||||
struct dispatcher {
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
inline dispatcher(std::time_t t) : time_(t) {}
|
|
||||||
|
|
||||||
inline auto run() -> bool {
|
|
||||||
using namespace fmt::detail;
|
|
||||||
return handle(localtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
|
||||||
|
|
||||||
inline auto handle(detail::null<>) -> bool {
|
|
||||||
using namespace fmt::detail;
|
|
||||||
return fallback(localtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto fallback(int res) -> bool { return res == 0; }
|
|
||||||
|
|
||||||
#if !FMT_MSC_VERSION
|
|
||||||
inline auto fallback(detail::null<>) -> bool {
|
|
||||||
using namespace fmt::detail;
|
|
||||||
std::tm* tm = std::localtime(&time_);
|
|
||||||
if (tm) tm_ = *tm;
|
|
||||||
return tm != nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
dispatcher lt(time);
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
|
|
||||||
return lt.tm_;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_LOCAL_TIME
|
|
||||||
template <typename Duration>
|
|
||||||
FMT_DEPRECATED auto localtime(std::chrono::local_time<Duration> time)
|
|
||||||
-> std::tm {
|
|
||||||
using namespace std::chrono;
|
|
||||||
using namespace detail::tz;
|
|
||||||
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts given time since epoch as `std::time_t` value into calendar time,
|
* Converts given time since epoch as `std::time_t` value into calendar time,
|
||||||
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
|
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
|
||||||
@ -652,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
|
|||||||
// Add ASCII '0' to each digit byte and insert separators.
|
// Add ASCII '0' to each digit byte and insert separators.
|
||||||
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
|
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
|
||||||
|
|
||||||
constexpr const size_t len = 8;
|
constexpr size_t len = 8;
|
||||||
if (const_check(is_big_endian())) {
|
if (const_check(is_big_endian())) {
|
||||||
char tmp[len];
|
char tmp[len];
|
||||||
std::memcpy(tmp, &digits, len);
|
std::memcpy(tmp, &digits, len);
|
||||||
@ -1000,16 +911,16 @@ template <typename T>
|
|||||||
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
|
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
|
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
|
||||||
bool set_tm_zone(T& time, char* tz) {
|
auto set_tm_zone(T& time, char* tz) -> bool {
|
||||||
time.tm_zone = tz;
|
time.tm_zone = tz;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
|
||||||
bool set_tm_zone(T&, char*) {
|
auto set_tm_zone(T&, char*) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char* utc() {
|
inline auto utc() -> char* {
|
||||||
static char tz[] = "UTC";
|
static char tz[] = "UTC";
|
||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
@ -1683,8 +1594,13 @@ class get_locale {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
|
inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
|
||||||
if (localized)
|
if (!localized) return;
|
||||||
::new (&locale_) std::locale(loc.template get<std::locale>());
|
ignore_unused(loc);
|
||||||
|
::new (&locale_) std::locale(
|
||||||
|
#if FMT_USE_LOCALE
|
||||||
|
loc.template get<std::locale>()
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
inline ~get_locale() {
|
inline ~get_locale() {
|
||||||
if (has_locale_) locale_.~locale();
|
if (has_locale_) locale_.~locale();
|
||||||
@ -2230,7 +2146,7 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
||||||
ctx);
|
ctx);
|
||||||
|
|
||||||
auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref();
|
auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
|
||||||
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
|
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
|
||||||
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
|
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
|
||||||
loc, out, tm, subsecs);
|
loc, out, tm, subsecs);
|
||||||
|
|||||||
@ -375,19 +375,17 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
// 10 more.
|
// 10 more.
|
||||||
if (is_background) value += 10u;
|
if (is_background) value += 10u;
|
||||||
|
|
||||||
size_t index = 0;
|
buffer[size++] = static_cast<Char>('\x1b');
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[size++] = static_cast<Char>('[');
|
||||||
buffer[index++] = static_cast<Char>('[');
|
|
||||||
|
|
||||||
if (value >= 100u) {
|
if (value >= 100u) {
|
||||||
buffer[index++] = static_cast<Char>('1');
|
buffer[size++] = static_cast<Char>('1');
|
||||||
value %= 100u;
|
value %= 100u;
|
||||||
}
|
}
|
||||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
buffer[size++] = static_cast<Char>('0' + value / 10u);
|
||||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
buffer[size++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
buffer[index++] = static_cast<Char>('m');
|
buffer[size++] = static_cast<Char>('m');
|
||||||
buffer[index++] = static_cast<Char>('\0');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +396,7 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
to_esc(color.r, buffer + 7, ';');
|
to_esc(color.r, buffer + 7, ';');
|
||||||
to_esc(color.g, buffer + 11, ';');
|
to_esc(color.g, buffer + 11, ';');
|
||||||
to_esc(color.b, buffer + 15, 'm');
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
buffer[19] = static_cast<Char>(0);
|
size = 19;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
|
||||||
uint8_t em_codes[num_emphases] = {};
|
uint8_t em_codes[num_emphases] = {};
|
||||||
@ -411,26 +409,28 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||||
|
|
||||||
size_t index = 0;
|
buffer[size++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[size++] = static_cast<Char>('[');
|
||||||
|
|
||||||
for (size_t i = 0; i < num_emphases; ++i) {
|
for (size_t i = 0; i < num_emphases; ++i) {
|
||||||
if (!em_codes[i]) continue;
|
if (!em_codes[i]) continue;
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[size++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
buffer[index++] = static_cast<Char>('[');
|
buffer[size++] = static_cast<Char>(';');
|
||||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
|
||||||
buffer[index++] = static_cast<Char>('m');
|
|
||||||
}
|
}
|
||||||
buffer[index++] = static_cast<Char>(0);
|
|
||||||
|
buffer[size - 1] = static_cast<Char>('m');
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
|
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
|
||||||
|
|
||||||
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
|
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
|
||||||
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
|
FMT_CONSTEXPR auto end() const noexcept -> const Char* {
|
||||||
return buffer + basic_string_view<Char>(buffer).size();
|
return buffer + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t num_emphases = 8;
|
static constexpr size_t num_emphases = 8;
|
||||||
Char buffer[7u + 3u * num_emphases + 1u];
|
Char buffer[7u + 4u * num_emphases] = {};
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||||
char delimiter) noexcept {
|
char delimiter) noexcept {
|
||||||
|
|||||||
@ -15,15 +15,14 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
// A compile-time string which is compiled into fast formatting code.
|
// A compile-time string which is compiled into fast formatting code.
|
||||||
FMT_EXPORT class compiled_string {};
|
class compiled_string {};
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a string literal `s` into a format string that will be parsed at
|
* Converts a string literal `s` into a format string that will be parsed at
|
||||||
* compile time and converted into efficient formatting code. Requires C++17
|
* compile time and converted into efficient formatting code. Requires C++17
|
||||||
@ -41,18 +40,42 @@ namespace detail {
|
|||||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string literal into a format string that will be parsed at
|
||||||
|
* compile time and converted into efficient formatting code. Requires support
|
||||||
|
* for class types in constant template parameters (a C++20 feature).
|
||||||
|
*
|
||||||
|
* **Example**:
|
||||||
|
*
|
||||||
|
* // Converts 42 into std::string using the most efficient method and no
|
||||||
|
* // runtime format string processing.
|
||||||
|
* using namespace fmt::literals;
|
||||||
|
* std::string s = fmt::format("{}"_cf, 42);
|
||||||
|
*/
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
|
inline namespace literals {
|
||||||
|
template <detail::fixed_string Str> constexpr auto operator""_cf() {
|
||||||
|
return FMT_COMPILE(Str.data);
|
||||||
|
}
|
||||||
|
} // namespace literals
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_EXPORT
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
template <typename T, typename... Tail>
|
template <typename T, typename... Tail>
|
||||||
auto first(const T& value, const Tail&...) -> const T& {
|
constexpr auto first(const T& value, const Tail&...) -> const T& {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
template <typename... Args> struct type_list {};
|
template <typename... T> struct type_list {};
|
||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
template <int N, typename T, typename... Args>
|
template <int N, typename T, typename... Args>
|
||||||
constexpr const auto& get([[maybe_unused]] const T& first,
|
constexpr auto get([[maybe_unused]] const T& first,
|
||||||
[[maybe_unused]] const Args&... rest) {
|
[[maybe_unused]] const Args&... rest) -> const auto& {
|
||||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
@ -84,8 +107,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename... Args>
|
template <typename Char, typename... Args>
|
||||||
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
|
||||||
type_list<Args...>) {
|
type_list<Args...>) -> int {
|
||||||
return get_arg_index_by_name<Args...>(name);
|
return get_arg_index_by_name<Args...>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,8 +128,8 @@ template <typename Char> struct text {
|
|||||||
basic_string_view<Char> data;
|
basic_string_view<Char> data;
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... T>
|
||||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
|
||||||
return write<Char>(out, data);
|
return write<Char>(out, data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -115,8 +138,8 @@ template <typename Char>
|
|||||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
|
||||||
size_t size) {
|
-> text<Char> {
|
||||||
return {{&s[pos], size}};
|
return {{&s[pos], size}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,8 +147,8 @@ template <typename Char> struct code_unit {
|
|||||||
Char value;
|
Char value;
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... T>
|
||||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
|
||||||
*out++ = value;
|
*out++ = value;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@ -133,7 +156,7 @@ template <typename Char> struct code_unit {
|
|||||||
|
|
||||||
// This ensures that the argument type is convertible to `const T&`.
|
// This ensures that the argument type is convertible to `const T&`.
|
||||||
template <typename T, int N, typename... Args>
|
template <typename T, int N, typename... Args>
|
||||||
constexpr const T& get_arg_checked(const Args&... args) {
|
constexpr auto get_arg_checked(const Args&... args) -> const T& {
|
||||||
const auto& arg = detail::get<N>(args...);
|
const auto& arg = detail::get<N>(args...);
|
||||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||||
return arg.value;
|
return arg.value;
|
||||||
@ -146,13 +169,13 @@ template <typename Char>
|
|||||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||||
|
|
||||||
// A replacement field that refers to argument N.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename V, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... T>
|
||||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
|
||||||
const T& arg = get_arg_checked<T, N>(args...);
|
const V& arg = get_arg_checked<V, N>(args...);
|
||||||
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
|
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
|
||||||
auto s = basic_string_view<Char>(arg);
|
auto s = basic_string_view<Char>(arg);
|
||||||
return copy<Char>(s.begin(), s.end(), out);
|
return copy<Char>(s.begin(), s.end(), out);
|
||||||
} else {
|
} else {
|
||||||
@ -170,10 +193,10 @@ template <typename Char> struct runtime_named_field {
|
|||||||
basic_string_view<Char> name;
|
basic_string_view<Char> name;
|
||||||
|
|
||||||
template <typename OutputIt, typename T>
|
template <typename OutputIt, typename T>
|
||||||
constexpr static bool try_format_argument(
|
constexpr static auto try_format_argument(
|
||||||
OutputIt& out,
|
OutputIt& out,
|
||||||
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||||
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
|
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
|
||||||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||||
if (arg_name == arg.name) {
|
if (arg_name == arg.name) {
|
||||||
out = write<Char>(out, arg.value);
|
out = write<Char>(out, arg.value);
|
||||||
@ -183,8 +206,8 @@ template <typename Char> struct runtime_named_field {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... T>
|
||||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
|
||||||
bool found = (try_format_argument(out, name, args) || ...);
|
bool found = (try_format_argument(out, name, args) || ...);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
FMT_THROW(format_error("argument with specified name is not found"));
|
FMT_THROW(format_error("argument with specified name is not found"));
|
||||||
@ -197,17 +220,17 @@ template <typename Char>
|
|||||||
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||||
|
|
||||||
// A replacement field that refers to argument N and has format specifiers.
|
// A replacement field that refers to argument N and has format specifiers.
|
||||||
template <typename Char, typename T, int N> struct spec_field {
|
template <typename Char, typename V, int N> struct spec_field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
formatter<T, Char> fmt;
|
formatter<V, Char> fmt;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... T>
|
||||||
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
|
||||||
const Args&... args) const {
|
-> OutputIt {
|
||||||
const auto& vargs =
|
const auto& vargs =
|
||||||
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
return fmt.format(get_arg_checked<T, N>(args...), ctx);
|
return fmt.format(get_arg_checked<V, N>(args...), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -219,8 +242,8 @@ template <typename L, typename R> struct concat {
|
|||||||
R rhs;
|
R rhs;
|
||||||
using char_type = typename L::char_type;
|
using char_type = typename L::char_type;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... T>
|
||||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
|
||||||
out = lhs.format(out, args...);
|
out = lhs.format(out, args...);
|
||||||
return rhs.format(out, args...);
|
return rhs.format(out, args...);
|
||||||
}
|
}
|
||||||
@ -230,14 +253,14 @@ template <typename L, typename R>
|
|||||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||||
|
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
|
||||||
return {lhs, rhs};
|
return {lhs, rhs};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct unknown_format {};
|
struct unknown_format {};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
|
||||||
for (size_t size = str.size(); pos != size; ++pos) {
|
for (size_t size = str.size(); pos != size; ++pos) {
|
||||||
if (str[pos] == '{' || str[pos] == '}') break;
|
if (str[pos] == '{' || str[pos] == '}') break;
|
||||||
}
|
}
|
||||||
@ -270,8 +293,8 @@ template <typename T, typename Char> struct parse_specs_result {
|
|||||||
enum { manual_indexing_id = -1 };
|
enum { manual_indexing_id = -1 };
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
|
||||||
size_t pos, int next_arg_id) {
|
int next_arg_id) -> parse_specs_result<T, Char> {
|
||||||
str.remove_prefix(pos);
|
str.remove_prefix(pos);
|
||||||
auto ctx =
|
auto ctx =
|
||||||
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
|
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
|
||||||
@ -285,16 +308,16 @@ template <typename Char> struct arg_id_handler {
|
|||||||
arg_id_kind kind;
|
arg_id_kind kind;
|
||||||
arg_ref<Char> arg_id;
|
arg_ref<Char> arg_id;
|
||||||
|
|
||||||
constexpr int on_auto() {
|
constexpr auto on_auto() -> int {
|
||||||
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
constexpr int on_index(int id) {
|
constexpr auto on_index(int id) -> int {
|
||||||
kind = arg_id_kind::index;
|
kind = arg_id_kind::index;
|
||||||
arg_id = arg_ref<Char>(id);
|
arg_id = arg_ref<Char>(id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
constexpr int on_name(basic_string_view<Char> id) {
|
constexpr auto on_name(basic_string_view<Char> id) -> int {
|
||||||
kind = arg_id_kind::name;
|
kind = arg_id_kind::name;
|
||||||
arg_id = arg_ref<Char>(id);
|
arg_id = arg_ref<Char>(id);
|
||||||
return 0;
|
return 0;
|
||||||
@ -433,27 +456,28 @@ FMT_BEGIN_EXPORT
|
|||||||
|
|
||||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... T,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const T&... args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
auto s = std::basic_string<Char>();
|
auto s = std::basic_string<Char>();
|
||||||
cf.format(std::back_inserter(s), args...);
|
cf.format(std::back_inserter(s), args...);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... T,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const T&... args) -> OutputIt {
|
||||||
return cf.format(out, args...);
|
return cf.format(out, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T,
|
||||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
|
||||||
Args&&... args) {
|
-> std::basic_string<typename S::char_type> {
|
||||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||||
@ -466,72 +490,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<T...>(S());
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
detail::unknown_format>()) {
|
detail::unknown_format>()) {
|
||||||
return fmt::format(
|
return fmt::format(
|
||||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
std::forward<Args>(args)...);
|
std::forward<T>(args)...);
|
||||||
} else {
|
} else {
|
||||||
return fmt::format(compiled, std::forward<Args>(args)...);
|
return fmt::format(compiled, std::forward<T>(args)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... T,
|
||||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<T...>(S());
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
detail::unknown_format>()) {
|
detail::unknown_format>()) {
|
||||||
return fmt::format_to(
|
return fmt::format_to(
|
||||||
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
std::forward<Args>(args)...);
|
std::forward<T>(args)...);
|
||||||
} else {
|
} else {
|
||||||
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
|
return fmt::format_to(out, compiled, std::forward<T>(args)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... T,
|
||||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||||
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
|
auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
|
||||||
-> format_to_n_result<OutputIt> {
|
-> format_to_n_result<OutputIt> {
|
||||||
using traits = detail::fixed_buffer_traits;
|
using traits = detail::fixed_buffer_traits;
|
||||||
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
|
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
|
||||||
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
|
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
||||||
return {buf.out(), buf.count()};
|
return {buf.out(), buf.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T,
|
||||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||||
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
|
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
|
||||||
-> size_t {
|
|
||||||
auto buf = detail::counting_buffer<>();
|
auto buf = detail::counting_buffer<>();
|
||||||
fmt::format_to(appender(buf), fmt, args...);
|
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
||||||
return buf.count();
|
return buf.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T,
|
||||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||||
void print(std::FILE* f, const S& fmt, const Args&... args) {
|
void print(std::FILE* f, const S& fmt, T&&... args) {
|
||||||
auto buf = memory_buffer();
|
auto buf = memory_buffer();
|
||||||
fmt::format_to(appender(buf), fmt, args...);
|
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
||||||
detail::print(f, {buf.data(), buf.size()});
|
detail::print(f, {buf.data(), buf.size()});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T,
|
||||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||||
void print(const S& fmt, const Args&... args) {
|
void print(const S& fmt, T&&... args) {
|
||||||
print(stdout, fmt, args...);
|
print(stdout, fmt, std::forward<T>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
template <size_t N> class static_format_result {
|
||||||
inline namespace literals {
|
private:
|
||||||
template <detail::fixed_string Str> constexpr auto operator""_cf() {
|
char data[N];
|
||||||
return FMT_COMPILE(Str.data);
|
|
||||||
}
|
public:
|
||||||
} // namespace literals
|
template <typename S, typename... T,
|
||||||
#endif
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto str() const -> fmt::string_view { return {data, N - 1}; }
|
||||||
|
FMT_CONSTEXPR auto c_str() const -> const char* { return data; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats arguments according to the format string `fmt_str` and produces
|
||||||
|
* a string of the exact required size at compile time. Both the format string
|
||||||
|
* and the arguments must be compile-time expressions.
|
||||||
|
*
|
||||||
|
* The resulting string can be accessed as a C string via `c_str()` or as
|
||||||
|
* a `fmt::string_view` via `str()`.
|
||||||
|
*
|
||||||
|
* **Example**:
|
||||||
|
*
|
||||||
|
* // Produces the static string "42" at compile time.
|
||||||
|
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
|
||||||
|
* const char* s = result.c_str();
|
||||||
|
*/
|
||||||
|
#define FMT_STATIC_FORMAT(fmt_str, ...) \
|
||||||
|
fmt::static_format_result< \
|
||||||
|
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
|
||||||
|
FMT_COMPILE(fmt_str), __VA_ARGS__)
|
||||||
|
|
||||||
FMT_END_EXPORT
|
FMT_END_EXPORT
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
#if FMT_USE_LOCALE
|
#if FMT_USE_LOCALE && !defined(FMT_MODULE)
|
||||||
# include <locale>
|
# include <locale>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -30,15 +30,53 @@
|
|||||||
# define FMT_FUNC
|
# define FMT_FUNC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
#if defined(FMT_USE_FULL_CACHE_DRAGONBOX)
|
||||||
namespace detail {
|
// Use the provided definition.
|
||||||
|
#elif defined(__OPTIMIZE_SIZE__)
|
||||||
|
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
|
||||||
|
#else
|
||||||
|
# define FMT_USE_FULL_CACHE_DRAGONBOX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#ifndef FMT_CUSTOM_ASSERT_FAIL
|
||||||
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
||||||
// Use unchecked std::fprintf to avoid triggering another assertion when
|
// Use unchecked std::fprintf to avoid triggering another assertion when
|
||||||
// writing to stderr fails.
|
// writing to stderr fails.
|
||||||
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
|
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_LOCALE
|
||||||
|
namespace detail {
|
||||||
|
using std::locale;
|
||||||
|
using std::numpunct;
|
||||||
|
using std::use_facet;
|
||||||
|
} // namespace detail
|
||||||
|
#else
|
||||||
|
namespace detail {
|
||||||
|
struct locale {};
|
||||||
|
template <typename Char> struct numpunct {
|
||||||
|
auto grouping() const -> std::string { return "\03"; }
|
||||||
|
auto thousands_sep() const -> Char { return ','; }
|
||||||
|
auto decimal_point() const -> Char { return '.'; }
|
||||||
|
};
|
||||||
|
template <typename Facet> Facet use_facet(locale) { return {}; }
|
||||||
|
} // namespace detail
|
||||||
|
#endif // FMT_USE_LOCALE
|
||||||
|
|
||||||
|
template <typename Locale> auto locale_ref::get() const -> Locale {
|
||||||
|
using namespace detail;
|
||||||
|
static_assert(std::is_same<Locale, locale>::value, "");
|
||||||
|
#if FMT_USE_LOCALE
|
||||||
|
if (locale_) return *static_cast<const locale*>(locale_);
|
||||||
|
#endif
|
||||||
|
return locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
|
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
|
||||||
string_view message) noexcept {
|
string_view message) noexcept {
|
||||||
@ -79,33 +117,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
|
|||||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_LOCALE
|
|
||||||
using std::locale;
|
|
||||||
using std::numpunct;
|
|
||||||
using std::use_facet;
|
|
||||||
|
|
||||||
template <typename Locale>
|
|
||||||
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
|
|
||||||
static_assert(std::is_same<Locale, locale>::value, "");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
struct locale {};
|
|
||||||
template <typename Char> struct numpunct {
|
|
||||||
auto grouping() const -> std::string { return "\03"; }
|
|
||||||
auto thousands_sep() const -> Char { return ','; }
|
|
||||||
auto decimal_point() const -> Char { return '.'; }
|
|
||||||
};
|
|
||||||
template <typename Facet> Facet use_facet(locale) { return {}; }
|
|
||||||
#endif // FMT_USE_LOCALE
|
|
||||||
|
|
||||||
template <typename Locale> auto locale_ref::get() const -> Locale {
|
|
||||||
static_assert(std::is_same<Locale, locale>::value, "");
|
|
||||||
#if FMT_USE_LOCALE
|
|
||||||
if (locale_) return *static_cast<const locale*>(locale_);
|
|
||||||
#endif
|
|
||||||
return locale();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
|
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
|
||||||
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
|
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
|
||||||
@ -133,14 +144,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_FUNC void report_error(const char* message) {
|
FMT_FUNC void report_error(const char* message) {
|
||||||
#if FMT_USE_EXCEPTIONS
|
#if FMT_MSC_VERSION || defined(__NVCC__)
|
||||||
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
|
// Silence unreachable code warnings in MSVC and NVCC because these
|
||||||
// from MSVC.
|
// are nearly impossible to fix in a generic code.
|
||||||
FMT_THROW(format_error(message));
|
volatile bool b = true;
|
||||||
#else
|
if (!b) return;
|
||||||
fputs(message, stderr);
|
|
||||||
abort();
|
|
||||||
#endif
|
#endif
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale> typename Locale::id format_facet<Locale>::id;
|
template <typename Locale> typename Locale::id format_facet<Locale>::id;
|
||||||
@ -174,11 +184,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compilers should be able to optimize this into the ror instruction.
|
// Compilers should be able to optimize this into the ror instruction.
|
||||||
FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
|
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
|
||||||
r &= 31;
|
r &= 31;
|
||||||
return (n >> r) | (n << (32 - r));
|
return (n >> r) | (n << (32 - r));
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
|
FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
|
||||||
r &= 63;
|
r &= 63;
|
||||||
return (n >> r) | (n << (64 - r));
|
return (n >> r) | (n << (64 - r));
|
||||||
}
|
}
|
||||||
@ -275,7 +285,7 @@ template <> struct cache_accessor<float> {
|
|||||||
static auto get_cached_power(int k) noexcept -> uint64_t {
|
static auto get_cached_power(int k) noexcept -> uint64_t {
|
||||||
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
||||||
"k is out of range");
|
"k is out of range");
|
||||||
static constexpr const uint64_t pow10_significands[] = {
|
static constexpr uint64_t pow10_significands[] = {
|
||||||
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
|
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
|
||||||
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
|
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
|
||||||
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
|
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
|
||||||
@ -370,7 +380,7 @@ template <> struct cache_accessor<double> {
|
|||||||
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
|
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
|
||||||
"k is out of range");
|
"k is out of range");
|
||||||
|
|
||||||
static constexpr const uint128_fallback pow10_significands[] = {
|
static constexpr uint128_fallback pow10_significands[] = {
|
||||||
#if FMT_USE_FULL_CACHE_DRAGONBOX
|
#if FMT_USE_FULL_CACHE_DRAGONBOX
|
||||||
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
|
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
|
||||||
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
|
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
|
||||||
@ -1037,7 +1047,7 @@ template <> struct cache_accessor<double> {
|
|||||||
#if FMT_USE_FULL_CACHE_DRAGONBOX
|
#if FMT_USE_FULL_CACHE_DRAGONBOX
|
||||||
return pow10_significands[k - float_info<double>::min_k];
|
return pow10_significands[k - float_info<double>::min_k];
|
||||||
#else
|
#else
|
||||||
static constexpr const uint64_t powers_of_5_64[] = {
|
static constexpr uint64_t powers_of_5_64[] = {
|
||||||
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
|
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
|
||||||
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
|
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
|
||||||
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
|
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
|
||||||
@ -1149,8 +1159,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
|
|||||||
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
|
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing zeros from n and return the number of zeros removed (float)
|
// Remove trailing zeros from n and return the number of zeros removed (float).
|
||||||
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
|
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
|
||||||
FMT_ASSERT(n != 0, "");
|
FMT_ASSERT(n != 0, "");
|
||||||
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
|
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
|
||||||
constexpr uint32_t mod_inv_5 = 0xcccccccd;
|
constexpr uint32_t mod_inv_5 = 0xcccccccd;
|
||||||
@ -1170,22 +1180,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes trailing zeros and returns the number of zeros removed (double)
|
// Removes trailing zeros and returns the number of zeros removed (double).
|
||||||
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
|
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
|
||||||
FMT_ASSERT(n != 0, "");
|
FMT_ASSERT(n != 0, "");
|
||||||
|
|
||||||
// This magic number is ceil(2^90 / 10^8).
|
|
||||||
constexpr uint64_t magic_number = 12379400392853802749ull;
|
|
||||||
auto nm = umul128(n, magic_number);
|
|
||||||
|
|
||||||
// Is n is divisible by 10^8?
|
// Is n is divisible by 10^8?
|
||||||
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
|
constexpr uint32_t ten_pow_8 = 100000000u;
|
||||||
|
if ((n % ten_pow_8) == 0) {
|
||||||
// If yes, work with the quotient...
|
// If yes, work with the quotient...
|
||||||
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
|
auto n32 = static_cast<uint32_t>(n / ten_pow_8);
|
||||||
// ... and use the 32 bit variant of the function
|
// ... and use the 32 bit variant of the function
|
||||||
int s = remove_trailing_zeros(n32, 8);
|
int num_zeros = remove_trailing_zeros(n32, 8);
|
||||||
n = n32;
|
n = n32;
|
||||||
return s;
|
return num_zeros;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If n is not divisible by 10^8, work with n itself.
|
// If n is not divisible by 10^8, work with n itself.
|
||||||
@ -1210,7 +1217,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
|
|||||||
|
|
||||||
// The main algorithm for shorter interval case
|
// The main algorithm for shorter interval case
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
|
FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> {
|
||||||
decimal_fp<T> ret_value;
|
decimal_fp<T> ret_value;
|
||||||
// Compute k and beta
|
// Compute k and beta
|
||||||
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
|
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
|
||||||
@ -1454,8 +1461,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
|
|||||||
auto out = appender(buf);
|
auto out = appender(buf);
|
||||||
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
|
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
|
||||||
return args.get(0).visit(default_arg_formatter<char>{out});
|
return args.get(0).visit(default_arg_formatter<char>{out});
|
||||||
parse_format_string(
|
parse_format_string(fmt,
|
||||||
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
|
format_handler<>{parse_context<>(fmt), {out, args, loc}});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> struct span {
|
template <typename T> struct span {
|
||||||
@ -1546,10 +1553,11 @@ template <typename F> class glibc_file : public file_base<F> {
|
|||||||
|
|
||||||
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
|
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
|
||||||
|
|
||||||
bool needs_flush() const {
|
auto needs_flush() const -> bool {
|
||||||
if ((this->file_->_flags & line_buffered) == 0) return false;
|
if ((this->file_->_flags & line_buffered) == 0) return false;
|
||||||
char* end = this->file_->_IO_write_end;
|
char* end = this->file_->_IO_write_end;
|
||||||
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
|
auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0);
|
||||||
|
return memchr(end, '\n', static_cast<size_t>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() { fflush_unlocked(this->file_); }
|
void flush() { fflush_unlocked(this->file_); }
|
||||||
@ -1573,7 +1581,7 @@ template <typename F> class apple_file : public file_base<F> {
|
|||||||
void init_buffer() {
|
void init_buffer() {
|
||||||
if (this->file_->_p) return;
|
if (this->file_->_p) return;
|
||||||
// Force buffer initialization by placing and removing a char in a buffer.
|
// Force buffer initialization by placing and removing a char in a buffer.
|
||||||
putc_unlocked(0, this->file_);
|
if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_);
|
||||||
--this->file_->_p;
|
--this->file_->_p;
|
||||||
++this->file_->_w;
|
++this->file_->_w;
|
||||||
}
|
}
|
||||||
@ -1594,7 +1602,7 @@ template <typename F> class apple_file : public file_base<F> {
|
|||||||
this->file_->_w -= size;
|
this->file_->_w -= size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needs_flush() const {
|
auto needs_flush() const -> bool {
|
||||||
if ((this->file_->_flags & line_buffered) == 0) return false;
|
if ((this->file_->_flags & line_buffered) == 0) return false;
|
||||||
return memchr(this->file_->_p + this->file_->_w, '\n',
|
return memchr(this->file_->_p + this->file_->_w, '\n',
|
||||||
to_unsigned(-this->file_->_w));
|
to_unsigned(-this->file_->_w));
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,8 @@
|
|||||||
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
defined(__linux__)) && \
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || \
|
(!defined(WINAPI_FAMILY) || \
|
||||||
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
|
||||||
|
!defined(__wasm__)
|
||||||
# include <fcntl.h> // for O_RDONLY
|
# include <fcntl.h> // for O_RDONLY
|
||||||
# define FMT_USE_FCNTL 1
|
# define FMT_USE_FCNTL 1
|
||||||
# else
|
# else
|
||||||
@ -135,10 +136,9 @@ FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
|
|||||||
* **Example**:
|
* **Example**:
|
||||||
*
|
*
|
||||||
* // This throws a system_error with the description
|
* // This throws a system_error with the description
|
||||||
* // cannot open file 'madeup': The system cannot find the file
|
* // cannot open file 'foo': The system cannot find the file specified.
|
||||||
* specified.
|
* // or similar (system message may vary) if the file doesn't exist.
|
||||||
* // or similar (system message may vary).
|
* const char *filename = "foo";
|
||||||
* const char *filename = "madeup";
|
|
||||||
* LPOFSTRUCT of = LPOFSTRUCT();
|
* LPOFSTRUCT of = LPOFSTRUCT();
|
||||||
* HFILE file = OpenFile(filename, &of, OF_READ);
|
* HFILE file = OpenFile(filename, &of, OF_READ);
|
||||||
* if (file == HFILE_ERROR) {
|
* if (file == HFILE_ERROR) {
|
||||||
@ -161,14 +161,6 @@ inline auto system_category() noexcept -> const std::error_category& {
|
|||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
// std::system is not available on some platforms such as iOS (#2248).
|
|
||||||
#ifdef __OSX__
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
void say(const S& fmt, Args&&... args) {
|
|
||||||
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class buffered_file {
|
class buffered_file {
|
||||||
private:
|
private:
|
||||||
@ -364,17 +356,17 @@ FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
|
|||||||
|
|
||||||
/// A fast buffered output stream for writing from a single thread. Writing from
|
/// A fast buffered output stream for writing from a single thread. Writing from
|
||||||
/// multiple threads without external synchronization may result in a data race.
|
/// multiple threads without external synchronization may result in a data race.
|
||||||
class FMT_API ostream : private detail::buffer<char> {
|
class ostream : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
ostream(cstring_view path, const detail::ostream_params& params);
|
FMT_API ostream(cstring_view path, const detail::ostream_params& params);
|
||||||
|
|
||||||
static void grow(buffer<char>& buf, size_t);
|
FMT_API static void grow(buffer<char>& buf, size_t);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ostream(ostream&& other) noexcept;
|
FMT_API ostream(ostream&& other) noexcept;
|
||||||
~ostream();
|
FMT_API ~ostream();
|
||||||
|
|
||||||
operator writer() {
|
operator writer() {
|
||||||
detail::buffer<char>& buf = *this;
|
detail::buffer<char>& buf = *this;
|
||||||
|
|||||||
@ -33,12 +33,12 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// Generate a unique explicit instantion in every translation unit using a tag
|
// Generate a unique explicit instantiation in every translation unit using a
|
||||||
// type in an anonymous namespace.
|
// tag type in an anonymous namespace.
|
||||||
namespace {
|
namespace {
|
||||||
struct file_access_tag {};
|
struct file_access_tag {};
|
||||||
} // namespace
|
} // namespace
|
||||||
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
|
template <typename Tag, typename BufType, FILE* BufType::* FileMemberPtr>
|
||||||
class file_access {
|
class file_access {
|
||||||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#define FMT_PRINTF_H_
|
#define FMT_PRINTF_H_
|
||||||
|
|
||||||
#ifndef FMT_MODULE
|
#ifndef FMT_MODULE
|
||||||
# include <algorithm> // std::max
|
# include <algorithm> // std::find
|
||||||
# include <limits> // std::numeric_limits
|
# include <limits> // std::numeric_limits
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -18,10 +18,6 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
template <typename T> struct printf_formatter {
|
|
||||||
printf_formatter() = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> class basic_printf_context {
|
template <typename Char> class basic_printf_context {
|
||||||
private:
|
private:
|
||||||
basic_appender<Char> out_;
|
basic_appender<Char> out_;
|
||||||
@ -33,8 +29,6 @@ template <typename Char> class basic_printf_context {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
using parse_context_type = parse_context<Char>;
|
|
||||||
template <typename T> using formatter_type = printf_formatter<T>;
|
|
||||||
enum { builtin_types = 1 };
|
enum { builtin_types = 1 };
|
||||||
|
|
||||||
/// Constructs a `printf_context` object. References to the arguments are
|
/// Constructs a `printf_context` object. References to the arguments are
|
||||||
@ -46,7 +40,7 @@ template <typename Char> class basic_printf_context {
|
|||||||
auto out() -> basic_appender<Char> { return out_; }
|
auto out() -> basic_appender<Char> { return out_; }
|
||||||
void advance_to(basic_appender<Char>) {}
|
void advance_to(basic_appender<Char>) {}
|
||||||
|
|
||||||
auto locale() -> detail::locale_ref { return {}; }
|
auto locale() -> locale_ref { return {}; }
|
||||||
|
|
||||||
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
|
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
|
||||||
return args_.get(id);
|
return args_.get(id);
|
||||||
@ -74,10 +68,9 @@ inline auto find<false, char>(const char* first, const char* last, char value,
|
|||||||
|
|
||||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
// signed and unsigned integers.
|
// signed and unsigned integers.
|
||||||
template <bool IsSigned> struct int_checker {
|
template <bool IS_SIGNED> struct int_checker {
|
||||||
template <typename T> static auto fits_in_int(T value) -> bool {
|
template <typename T> static auto fits_in_int(T value) -> bool {
|
||||||
unsigned max = to_unsigned(max_value<int>());
|
return value <= to_unsigned(max_value<int>());
|
||||||
return value <= max;
|
|
||||||
}
|
}
|
||||||
inline static auto fits_in_int(bool) -> bool { return true; }
|
inline static auto fits_in_int(bool) -> bool { return true; }
|
||||||
};
|
};
|
||||||
@ -95,7 +88,7 @@ struct printf_precision_handler {
|
|||||||
auto operator()(T value) -> int {
|
auto operator()(T value) -> int {
|
||||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
report_error("number is too big");
|
report_error("number is too big");
|
||||||
return (std::max)(static_cast<int>(value), 0);
|
return max_of(static_cast<int>(value), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
@ -410,7 +403,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
arg_index = parse_ctx.next_arg_id();
|
arg_index = parse_ctx.next_arg_id();
|
||||||
else
|
else
|
||||||
parse_ctx.check_arg_id(--arg_index);
|
parse_ctx.check_arg_id(--arg_index);
|
||||||
return detail::get_arg(context, arg_index);
|
auto arg = context.arg(arg_index);
|
||||||
|
if (!arg) report_error("argument not found");
|
||||||
|
return arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Char* start = parse_ctx.begin();
|
const Char* start = parse_ctx.begin();
|
||||||
@ -571,14 +566,18 @@ inline auto vsprintf(basic_string_view<Char> fmt,
|
|||||||
*
|
*
|
||||||
* std::string message = fmt::sprintf("The answer is %d", 42);
|
* std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... T, typename Char = detail::char_t<S>>
|
template <typename... T>
|
||||||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
inline auto sprintf(string_view fmt, const T&... args) -> std::string {
|
||||||
return vsprintf(detail::to_string_view(fmt),
|
return vsprintf(fmt, make_printf_args(args...));
|
||||||
fmt::make_format_args<basic_printf_context<Char>>(args...));
|
}
|
||||||
|
template <typename... T>
|
||||||
|
FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
|
||||||
|
-> std::wstring {
|
||||||
|
return vsprintf(fmt, make_printf_args<wchar_t>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
|
auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
|
||||||
typename vprintf_args<Char>::type args) -> int {
|
typename vprintf_args<Char>::type args) -> int {
|
||||||
auto buf = basic_memory_buffer<Char>();
|
auto buf = basic_memory_buffer<Char>();
|
||||||
detail::vprintf(buf, fmt, args);
|
detail::vprintf(buf, fmt, args);
|
||||||
@ -596,17 +595,14 @@ inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
|
|||||||
*
|
*
|
||||||
* fmt::fprintf(stderr, "Don't %s!", "panic");
|
* fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... T, typename Char = detail::char_t<S>>
|
template <typename... T>
|
||||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
|
||||||
return vfprintf(f, detail::to_string_view(fmt),
|
return vfprintf(f, fmt, make_printf_args(args...));
|
||||||
make_printf_args<Char>(args...));
|
|
||||||
}
|
}
|
||||||
|
template <typename... T>
|
||||||
template <typename Char>
|
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
|
||||||
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
|
const T&... args) -> int {
|
||||||
typename vprintf_args<Char>::type args)
|
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
|
||||||
-> int {
|
|
||||||
return vfprintf(stdout, fmt, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -621,11 +617,6 @@ template <typename... T>
|
|||||||
inline auto printf(string_view fmt, const T&... args) -> int {
|
inline auto printf(string_view fmt, const T&... args) -> int {
|
||||||
return vfprintf(stdout, fmt, make_printf_args(args...));
|
return vfprintf(stdout, fmt, make_printf_args(args...));
|
||||||
}
|
}
|
||||||
template <typename... T>
|
|
||||||
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
|
|
||||||
const T&... args) -> int {
|
|
||||||
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_EXPORT
|
FMT_END_EXPORT
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
#ifndef FMT_MODULE
|
#ifndef FMT_MODULE
|
||||||
# include <initializer_list>
|
# include <initializer_list>
|
||||||
# include <iterator>
|
# include <iterator>
|
||||||
# include <string>
|
|
||||||
# include <tuple>
|
# include <tuple>
|
||||||
# include <type_traits>
|
# include <type_traits>
|
||||||
# include <utility>
|
# include <utility>
|
||||||
@ -19,6 +18,13 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#if FMT_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
|
||||||
|
# define FMT_LIFETIMEBOUND [[clang::lifetimebound]]
|
||||||
|
#else
|
||||||
|
# define FMT_LIFETIMEBOUND
|
||||||
|
#endif
|
||||||
|
FMT_PRAGMA_CLANG(diagnostic error "-Wreturn-stack-address")
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
FMT_EXPORT
|
FMT_EXPORT
|
||||||
@ -31,7 +37,7 @@ template <typename T> class is_map {
|
|||||||
template <typename> static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,17 +46,16 @@ template <typename T> class is_set {
|
|||||||
template <typename> static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// C array overload
|
// C array overload
|
||||||
template <typename T, std::size_t N>
|
template <typename T, size_t N>
|
||||||
auto range_begin(const T (&arr)[N]) -> const T* {
|
auto range_begin(const T (&arr)[N]) -> const T* {
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
template <typename T, std::size_t N>
|
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
|
||||||
auto range_end(const T (&arr)[N]) -> const T* {
|
|
||||||
return arr + N;
|
return arr + N;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +125,7 @@ template <typename T> class is_tuple_like_ {
|
|||||||
template <typename> static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -154,7 +159,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
|
|||||||
template <typename T, typename C, bool = is_tuple_like_<T>::value>
|
template <typename T, typename C, bool = is_tuple_like_<T>::value>
|
||||||
class is_tuple_formattable_ {
|
class is_tuple_formattable_ {
|
||||||
public:
|
public:
|
||||||
static constexpr const bool value = false;
|
static constexpr bool value = false;
|
||||||
};
|
};
|
||||||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
||||||
template <size_t... Is>
|
template <size_t... Is>
|
||||||
@ -170,7 +175,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
|||||||
C>::value)...>{}));
|
C>::value)...>{}));
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
decltype(check(tuple_index_sequence<T>{}))::value;
|
decltype(check(tuple_index_sequence<T>{}))::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -208,7 +213,7 @@ template <typename Char, typename... T>
|
|||||||
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
|
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
|
||||||
|
|
||||||
using std::get;
|
using std::get;
|
||||||
template <typename Tuple, typename Char, std::size_t... Is>
|
template <typename Tuple, typename Char, size_t... Is>
|
||||||
auto get_formatters(index_sequence<Is...>)
|
auto get_formatters(index_sequence<Is...>)
|
||||||
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
|
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
|
||||||
} // namespace tuple
|
} // namespace tuple
|
||||||
@ -219,7 +224,7 @@ template <typename R> struct range_reference_type_impl {
|
|||||||
using type = decltype(*detail::range_begin(std::declval<R&>()));
|
using type = decltype(*detail::range_begin(std::declval<R&>()));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
|
template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
|
||||||
using type = T&;
|
using type = T&;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -236,14 +241,6 @@ using range_reference_type =
|
|||||||
template <typename Range>
|
template <typename Range>
|
||||||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||||
|
|
||||||
template <typename Formatter>
|
|
||||||
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
|
|
||||||
-> decltype(f.set_debug_format(set)) {
|
|
||||||
f.set_debug_format(set);
|
|
||||||
}
|
|
||||||
template <typename Formatter>
|
|
||||||
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct range_format_kind_
|
struct range_format_kind_
|
||||||
: std::integral_constant<range_format,
|
: std::integral_constant<range_format,
|
||||||
@ -281,14 +278,15 @@ template <typename FormatContext> struct format_tuple_element {
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
template <typename T> struct is_tuple_like {
|
template <typename T> struct is_tuple_like {
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
template <typename T, typename C> struct is_tuple_formattable {
|
template <typename T, typename C> struct is_tuple_formattable {
|
||||||
static constexpr const bool value =
|
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
|
||||||
detail::is_tuple_formattable_<T, C>::value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Tuple, typename Char>
|
template <typename Tuple, typename Char>
|
||||||
@ -343,8 +341,9 @@ struct formatter<Tuple, Char,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
|
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -368,6 +367,7 @@ template <typename P1, typename... Pn>
|
|||||||
struct conjunction<P1, Pn...>
|
struct conjunction<P1, Pn...>
|
||||||
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
|
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
template <typename T, typename Char, typename Enable = void>
|
template <typename T, typename Char, typename Enable = void>
|
||||||
struct range_formatter;
|
struct range_formatter;
|
||||||
|
|
||||||
@ -670,7 +670,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
|
FMT_EXPORT
|
||||||
|
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
|
||||||
const Tuple& tuple;
|
const Tuple& tuple;
|
||||||
basic_string_view<Char> sep;
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
@ -685,15 +686,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
|
|||||||
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Char, typename Tuple>
|
template <typename Tuple, typename Char>
|
||||||
struct formatter<tuple_join_view<Char, Tuple>, Char,
|
struct formatter<tuple_join_view<Tuple, Char>, Char,
|
||||||
enable_if_t<is_tuple_like<Tuple>::value>> {
|
enable_if_t<is_tuple_like<Tuple>::value>> {
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
return do_parse(ctx, std::tuple_size<Tuple>());
|
return do_parse(ctx, std::tuple_size<Tuple>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const tuple_join_view<Char, Tuple>& value,
|
auto format(const tuple_join_view<Tuple, Char>& value,
|
||||||
FormatContext& ctx) const -> typename FormatContext::iterator {
|
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||||
return do_format(value, ctx, std::tuple_size<Tuple>());
|
return do_format(value, ctx, std::tuple_size<Tuple>());
|
||||||
}
|
}
|
||||||
@ -725,14 +726,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
|
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
|
||||||
std::integral_constant<size_t, 0>) const ->
|
std::integral_constant<size_t, 0>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, size_t N>
|
template <typename FormatContext, size_t N>
|
||||||
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
|
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
|
||||||
std::integral_constant<size_t, N>) const ->
|
std::integral_constant<size_t, N>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
using std::get;
|
using std::get;
|
||||||
@ -754,7 +755,7 @@ template <typename T> class is_container_adaptor_like {
|
|||||||
template <typename> static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr const bool value =
|
static constexpr bool value =
|
||||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -819,13 +820,13 @@ auto join(Range&& r, string_view sep)
|
|||||||
*
|
*
|
||||||
* **Example**:
|
* **Example**:
|
||||||
*
|
*
|
||||||
* auto t = std::tuple<int, char>{1, 'a'};
|
* auto t = std::tuple<int, char>(1, 'a');
|
||||||
* fmt::print("{}", fmt::join(t, ", "));
|
* fmt::print("{}", fmt::join(t, ", "));
|
||||||
* // Output: 1, a
|
* // Output: 1, a
|
||||||
*/
|
*/
|
||||||
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
|
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
|
||||||
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
|
FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep)
|
||||||
-> tuple_join_view<char, Tuple> {
|
-> tuple_join_view<Tuple, char> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,15 +15,13 @@
|
|||||||
# include <atomic>
|
# include <atomic>
|
||||||
# include <bitset>
|
# include <bitset>
|
||||||
# include <complex>
|
# include <complex>
|
||||||
# include <cstdlib>
|
|
||||||
# include <exception>
|
# include <exception>
|
||||||
# include <functional>
|
# include <functional> // std::reference_wrapper
|
||||||
# include <memory>
|
# include <memory>
|
||||||
# include <thread>
|
# include <thread>
|
||||||
# include <type_traits>
|
# include <type_traits>
|
||||||
# include <typeinfo>
|
# include <typeinfo> // std::type_info
|
||||||
# include <utility>
|
# include <utility> // std::make_index_sequence
|
||||||
# include <vector>
|
|
||||||
|
|
||||||
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
|
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
|
||||||
# if FMT_CPLUSPLUS >= 201703L
|
# if FMT_CPLUSPLUS >= 201703L
|
||||||
@ -62,35 +60,36 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
|
#ifdef FMT_CPP_LIB_FILESYSTEM
|
||||||
#ifndef FMT_CPP_LIB_FILESYSTEM
|
// Use the provided definition.
|
||||||
# ifdef __cpp_lib_filesystem
|
#elif defined(__cpp_lib_filesystem)
|
||||||
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
|
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
|
||||||
# else
|
#else
|
||||||
# define FMT_CPP_LIB_FILESYSTEM 0
|
# define FMT_CPP_LIB_FILESYSTEM 0
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_CPP_LIB_VARIANT
|
#ifdef FMT_CPP_LIB_VARIANT
|
||||||
# ifdef __cpp_lib_variant
|
// Use the provided definition.
|
||||||
|
#elif defined(__cpp_lib_variant)
|
||||||
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
|
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
|
||||||
# else
|
#else
|
||||||
# define FMT_CPP_LIB_VARIANT 0
|
# define FMT_CPP_LIB_VARIANT 0
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
#if FMT_CPP_LIB_FILESYSTEM
|
#if FMT_CPP_LIB_FILESYSTEM
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename Char, typename PathChar>
|
template <typename Char, typename PathChar>
|
||||||
auto get_path_string(const std::filesystem::path& p,
|
auto get_path_string(const std::filesystem::path& p,
|
||||||
const std::basic_string<PathChar>& native) {
|
const std::basic_string<PathChar>& native) {
|
||||||
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
|
if constexpr (std::is_same_v<Char, char> &&
|
||||||
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
|
std::is_same_v<PathChar, wchar_t>) {
|
||||||
else
|
return to_utf8<wchar_t>(native, to_utf8_error_policy::wtf);
|
||||||
|
} else {
|
||||||
return p.string<Char>();
|
return p.string<Char>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename PathChar>
|
template <typename Char, typename PathChar>
|
||||||
@ -111,8 +110,180 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // FMT_CPP_LIB_FILESYSTEM
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename T, typename FormatContext>
|
||||||
|
auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
|
||||||
|
-> OutputIt {
|
||||||
|
if constexpr (has_to_string_view<T>::value)
|
||||||
|
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
||||||
|
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
|
||||||
|
|
||||||
|
formatter<std::remove_cv_t<T>, Char> underlying;
|
||||||
|
maybe_set_debug_format(underlying, true);
|
||||||
|
return underlying.format(v, ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_CPP_LIB_VARIANT
|
||||||
|
|
||||||
|
template <typename> struct is_variant_like_ : std::false_type {};
|
||||||
|
template <typename... Types>
|
||||||
|
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename Variant, typename Char> class is_variant_formattable {
|
||||||
|
template <size_t... Is>
|
||||||
|
static auto check(std::index_sequence<Is...>) -> std::conjunction<
|
||||||
|
is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value = decltype(check(
|
||||||
|
std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FMT_CPP_LIB_VARIANT
|
||||||
|
|
||||||
|
#if FMT_USE_RTTI
|
||||||
|
inline auto normalize_libcxx_inline_namespaces(string_view demangled_name_view,
|
||||||
|
char* begin) -> string_view {
|
||||||
|
// Normalization of stdlib inline namespace names.
|
||||||
|
// libc++ inline namespaces.
|
||||||
|
// std::__1::* -> std::*
|
||||||
|
// std::__1::__fs::* -> std::*
|
||||||
|
// libstdc++ inline namespaces.
|
||||||
|
// std::__cxx11::* -> std::*
|
||||||
|
// std::filesystem::__cxx11::* -> std::filesystem::*
|
||||||
|
if (demangled_name_view.starts_with("std::")) {
|
||||||
|
char* to = begin + 5; // std::
|
||||||
|
for (const char *from = to, *end = begin + demangled_name_view.size();
|
||||||
|
from < end;) {
|
||||||
|
// This is safe, because demangled_name is NUL-terminated.
|
||||||
|
if (from[0] == '_' && from[1] == '_') {
|
||||||
|
const char* next = from + 1;
|
||||||
|
while (next < end && *next != ':') next++;
|
||||||
|
if (next[0] == ':' && next[1] == ':') {
|
||||||
|
from = next + 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*to++ = *from++;
|
||||||
|
}
|
||||||
|
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
|
||||||
|
}
|
||||||
|
return demangled_name_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class OutputIt>
|
||||||
|
auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out)
|
||||||
|
-> OutputIt {
|
||||||
|
const string_view demangled_name(abi_name_view);
|
||||||
|
for (size_t i = 0; i < demangled_name.size(); ++i) {
|
||||||
|
auto sub = demangled_name;
|
||||||
|
sub.remove_prefix(i);
|
||||||
|
if (sub.starts_with("enum ")) {
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sub.starts_with("class ") || sub.starts_with("union ")) {
|
||||||
|
i += 5;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sub.starts_with("struct ")) {
|
||||||
|
i += 6;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*sub.begin() != ' ') *out++ = *sub.begin();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
|
||||||
|
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
|
||||||
|
int status = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
|
||||||
|
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &free);
|
||||||
|
|
||||||
|
string_view demangled_name_view;
|
||||||
|
if (demangled_name_ptr) {
|
||||||
|
demangled_name_view = normalize_libcxx_inline_namespaces(
|
||||||
|
demangled_name_ptr.get(), demangled_name_ptr.get());
|
||||||
|
} else {
|
||||||
|
demangled_name_view = string_view(ti.name());
|
||||||
|
}
|
||||||
|
return detail::write_bytes<char>(out, demangled_name_view);
|
||||||
|
# elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE)
|
||||||
|
return normalize_msvc_abi_name(ti.name(), out);
|
||||||
|
# elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION)
|
||||||
|
const string_view demangled_name = ti.name();
|
||||||
|
std::string name_copy(demangled_name.size(), '\0');
|
||||||
|
// normalize_msvc_abi_name removes class, struct, union etc that MSVC has in
|
||||||
|
// front of types
|
||||||
|
name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()),
|
||||||
|
name_copy.end());
|
||||||
|
// normalize_libcxx_inline_namespaces removes the inline __1, __2, etc
|
||||||
|
// namespaces libc++ uses for ABI versioning On MSVC ABI + libc++
|
||||||
|
// environments, we need to eliminate both of them.
|
||||||
|
const string_view normalized_name =
|
||||||
|
normalize_libcxx_inline_namespaces(name_copy, name_copy.data());
|
||||||
|
return detail::write_bytes<char>(out, normalized_name);
|
||||||
|
# else
|
||||||
|
return detail::write_bytes<char>(out, string_view(ti.name()));
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_RTTI
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_flip : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> struct is_bit_reference_like {
|
||||||
|
static constexpr bool value = std::is_convertible<T, bool>::value &&
|
||||||
|
std::is_nothrow_assignable<T, bool>::value &&
|
||||||
|
has_flip<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Workaround for libc++ incompatibility with C++ standard.
|
||||||
|
// According to the Standard, `bitset::operator[] const` returns bool.
|
||||||
|
#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
|
||||||
|
template <typename C>
|
||||||
|
struct is_bit_reference_like<std::__bit_const_reference<C>> {
|
||||||
|
static constexpr bool value = true;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_format_as : std::false_type {};
|
||||||
|
template <typename T>
|
||||||
|
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_format_as_member : std::false_type {};
|
||||||
|
template <typename T>
|
||||||
|
struct has_format_as_member<
|
||||||
|
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename Deleter>
|
||||||
|
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
|
||||||
|
return p.get();
|
||||||
|
}
|
||||||
|
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
|
||||||
|
return p.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_CPP_LIB_FILESYSTEM
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::filesystem::path, Char> {
|
template <typename Char> struct formatter<std::filesystem::path, Char> {
|
||||||
private:
|
private:
|
||||||
format_specs specs_;
|
format_specs specs_;
|
||||||
@ -177,24 +348,20 @@ class path : public std::filesystem::path {
|
|||||||
auto generic_system_string() const -> std::string { return generic_string(); }
|
auto generic_system_string() const -> std::string { return generic_string(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
#endif // FMT_CPP_LIB_FILESYSTEM
|
#endif // FMT_CPP_LIB_FILESYSTEM
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
template <size_t N, typename Char>
|
||||||
template <std::size_t N, typename Char>
|
|
||||||
struct formatter<std::bitset<N>, Char>
|
struct formatter<std::bitset<N>, Char>
|
||||||
: nested_formatter<basic_string_view<Char>, Char> {
|
: nested_formatter<basic_string_view<Char>, Char> {
|
||||||
private:
|
private:
|
||||||
// Functor because C++11 doesn't support generic lambdas.
|
// This is a functor because C++11 doesn't support generic lambdas.
|
||||||
struct writer {
|
struct writer {
|
||||||
const std::bitset<N>& bs;
|
const std::bitset<N>& bs;
|
||||||
|
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
|
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
|
||||||
for (auto pos = N; pos > 0; --pos) {
|
for (auto pos = N; pos > 0; --pos)
|
||||||
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
|
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -209,33 +376,22 @@ struct formatter<std::bitset<N>, Char>
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_optional
|
#ifdef __cpp_lib_optional
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<std::optional<T>, Char,
|
struct formatter<std::optional<T>, Char,
|
||||||
std::enable_if_t<is_formattable<T, Char>::value>> {
|
std::enable_if_t<is_formattable<T, Char>::value>> {
|
||||||
private:
|
private:
|
||||||
formatter<T, Char> underlying_;
|
formatter<std::remove_cv_t<T>, Char> underlying_;
|
||||||
static constexpr basic_string_view<Char> optional =
|
static constexpr basic_string_view<Char> optional =
|
||||||
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
|
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
|
||||||
'('>{};
|
'('>{};
|
||||||
static constexpr basic_string_view<Char> none =
|
static constexpr basic_string_view<Char> none =
|
||||||
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
|
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
|
||||||
|
|
||||||
template <class U>
|
|
||||||
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
|
|
||||||
-> decltype(u.set_debug_format(set)) {
|
|
||||||
u.set_debug_format(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U>
|
|
||||||
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
|
||||||
maybe_set_debug_format(underlying_, true);
|
detail::maybe_set_debug_format(underlying_, true);
|
||||||
return underlying_.parse(ctx);
|
return underlying_.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,30 +407,9 @@ struct formatter<std::optional<T>, Char,
|
|||||||
return detail::write(out, ')');
|
return detail::write(out, ')');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
|
||||||
#endif // __cpp_lib_optional
|
#endif // __cpp_lib_optional
|
||||||
|
|
||||||
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt, typename T>
|
|
||||||
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
|
|
||||||
if constexpr (has_to_string_view<T>::value)
|
|
||||||
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
|
||||||
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
|
|
||||||
return write<Char>(out, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_expected
|
#ifdef __cpp_lib_expected
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
template <typename T, typename E, typename Char>
|
template <typename T, typename E, typename Char>
|
||||||
struct formatter<std::expected<T, E>, Char,
|
struct formatter<std::expected<T, E>, Char,
|
||||||
std::enable_if_t<(std::is_void<T>::value ||
|
std::enable_if_t<(std::is_void<T>::value ||
|
||||||
@ -292,20 +427,18 @@ struct formatter<std::expected<T, E>, Char,
|
|||||||
if (value.has_value()) {
|
if (value.has_value()) {
|
||||||
out = detail::write<Char>(out, "expected(");
|
out = detail::write<Char>(out, "expected(");
|
||||||
if constexpr (!std::is_void<T>::value)
|
if constexpr (!std::is_void<T>::value)
|
||||||
out = detail::write_escaped_alternative<Char>(out, *value);
|
out = detail::write_escaped_alternative<Char>(out, *value, ctx);
|
||||||
} else {
|
} else {
|
||||||
out = detail::write<Char>(out, "unexpected(");
|
out = detail::write<Char>(out, "unexpected(");
|
||||||
out = detail::write_escaped_alternative<Char>(out, value.error());
|
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
|
||||||
}
|
}
|
||||||
*out++ = ')';
|
*out++ = ')';
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
|
||||||
#endif // __cpp_lib_expected
|
#endif // __cpp_lib_expected
|
||||||
|
|
||||||
#ifdef __cpp_lib_source_location
|
#ifdef __cpp_lib_source_location
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
template <> struct formatter<std::source_location> {
|
template <> struct formatter<std::source_location> {
|
||||||
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
|
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
@ -323,42 +456,12 @@ template <> struct formatter<std::source_location> {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_CPP_LIB_VARIANT
|
#if FMT_CPP_LIB_VARIANT
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using variant_index_sequence =
|
|
||||||
std::make_index_sequence<std::variant_size<T>::value>;
|
|
||||||
|
|
||||||
template <typename> struct is_variant_like_ : std::false_type {};
|
|
||||||
template <typename... Types>
|
|
||||||
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
|
|
||||||
|
|
||||||
// formattable element check.
|
|
||||||
template <typename T, typename C> class is_variant_formattable_ {
|
|
||||||
template <std::size_t... Is>
|
|
||||||
static std::conjunction<
|
|
||||||
is_formattable<std::variant_alternative_t<Is, T>, C>...>
|
|
||||||
check(std::index_sequence<Is...>);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr const bool value =
|
|
||||||
decltype(check(variant_index_sequence<T>{}))::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename T> struct is_variant_like {
|
template <typename T> struct is_variant_like {
|
||||||
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
static constexpr bool value = detail::is_variant_like_<T>::value;
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename C> struct is_variant_formattable {
|
|
||||||
static constexpr const bool value =
|
|
||||||
detail::is_variant_formattable_<T, C>::value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::monostate, Char> {
|
template <typename Char> struct formatter<std::monostate, Char> {
|
||||||
@ -374,10 +477,10 @@ template <typename Char> struct formatter<std::monostate, Char> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Variant, typename Char>
|
template <typename Variant, typename Char>
|
||||||
struct formatter<
|
struct formatter<Variant, Char,
|
||||||
Variant, Char,
|
|
||||||
std::enable_if_t<std::conjunction_v<
|
std::enable_if_t<std::conjunction_v<
|
||||||
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
|
is_variant_like<Variant>,
|
||||||
|
detail::is_variant_formattable<Variant, Char>>>> {
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
@ -391,7 +494,7 @@ struct formatter<
|
|||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
std::visit(
|
std::visit(
|
||||||
[&](const auto& v) {
|
[&](const auto& v) {
|
||||||
out = detail::write_escaped_alternative<Char>(out, v);
|
out = detail::write_escaped_alternative<Char>(out, v, ctx);
|
||||||
},
|
},
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
@ -402,10 +505,9 @@ struct formatter<
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
|
||||||
#endif // FMT_CPP_LIB_VARIANT
|
#endif // FMT_CPP_LIB_VARIANT
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
template <> struct formatter<std::error_code> {
|
template <> struct formatter<std::error_code> {
|
||||||
private:
|
private:
|
||||||
format_specs specs_;
|
format_specs specs_;
|
||||||
@ -413,6 +515,8 @@ template <> struct formatter<std::error_code> {
|
|||||||
bool debug_ = false;
|
bool debug_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
|
||||||
|
|
||||||
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
|
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
|
||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it == end) return it;
|
if (it == end) return it;
|
||||||
@ -459,101 +563,29 @@ template <> struct formatter<std::error_code> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#if FMT_USE_RTTI
|
#if FMT_USE_RTTI
|
||||||
namespace detail {
|
template <> struct formatter<std::type_info> {
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
|
|
||||||
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
|
|
||||||
int status = 0;
|
|
||||||
std::size_t size = 0;
|
|
||||||
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
|
|
||||||
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
|
|
||||||
|
|
||||||
string_view demangled_name_view;
|
|
||||||
if (demangled_name_ptr) {
|
|
||||||
demangled_name_view = demangled_name_ptr.get();
|
|
||||||
|
|
||||||
// Normalization of stdlib inline namespace names.
|
|
||||||
// libc++ inline namespaces.
|
|
||||||
// std::__1::* -> std::*
|
|
||||||
// std::__1::__fs::* -> std::*
|
|
||||||
// libstdc++ inline namespaces.
|
|
||||||
// std::__cxx11::* -> std::*
|
|
||||||
// std::filesystem::__cxx11::* -> std::filesystem::*
|
|
||||||
if (demangled_name_view.starts_with("std::")) {
|
|
||||||
char* begin = demangled_name_ptr.get();
|
|
||||||
char* to = begin + 5; // std::
|
|
||||||
for (char *from = to, *end = begin + demangled_name_view.size();
|
|
||||||
from < end;) {
|
|
||||||
// This is safe, because demangled_name is NUL-terminated.
|
|
||||||
if (from[0] == '_' && from[1] == '_') {
|
|
||||||
char* next = from + 1;
|
|
||||||
while (next < end && *next != ':') next++;
|
|
||||||
if (next[0] == ':' && next[1] == ':') {
|
|
||||||
from = next + 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*to++ = *from++;
|
|
||||||
}
|
|
||||||
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
demangled_name_view = string_view(ti.name());
|
|
||||||
}
|
|
||||||
return detail::write_bytes<Char>(out, demangled_name_view);
|
|
||||||
# elif FMT_MSC_VERSION
|
|
||||||
const string_view demangled_name(ti.name());
|
|
||||||
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
|
|
||||||
auto sub = demangled_name;
|
|
||||||
sub.remove_prefix(i);
|
|
||||||
if (sub.starts_with("enum ")) {
|
|
||||||
i += 4;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sub.starts_with("class ") || sub.starts_with("union ")) {
|
|
||||||
i += 5;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sub.starts_with("struct ")) {
|
|
||||||
i += 6;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (*sub.begin() != ' ') *out++ = *sub.begin();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
# else
|
|
||||||
return detail::write_bytes<Char>(out, string_view(ti.name()));
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
|
|
||||||
> {
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Context>
|
||||||
auto format(const std::type_info& ti, Context& ctx) const
|
auto format(const std::type_info& ti, Context& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
return detail::write_demangled_name<Char>(ctx.out(), ti);
|
return detail::write_demangled_name(ctx.out(), ti);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif // FMT_USE_RTTI
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T>
|
||||||
struct formatter<
|
struct formatter<
|
||||||
T, Char, // DEPRECATED! Mixing code unit types.
|
T, char,
|
||||||
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
|
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
|
||||||
private:
|
private:
|
||||||
bool with_typename_ = false;
|
bool with_typename_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
|
||||||
auto it = ctx.begin();
|
auto it = ctx.begin();
|
||||||
auto end = ctx.end();
|
auto end = ctx.end();
|
||||||
if (it == end || *it == '}') return it;
|
if (it == end || *it == '}') return it;
|
||||||
@ -570,43 +602,15 @@ struct formatter<
|
|||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
#if FMT_USE_RTTI
|
#if FMT_USE_RTTI
|
||||||
if (with_typename_) {
|
if (with_typename_) {
|
||||||
out = detail::write_demangled_name<Char>(out, typeid(ex));
|
out = detail::write_demangled_name(out, typeid(ex));
|
||||||
*out++ = ':';
|
*out++ = ':';
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return detail::write_bytes<Char>(out, string_view(ex.what()));
|
return detail::write_bytes<char>(out, string_view(ex.what()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename T, typename Enable = void>
|
|
||||||
struct has_flip : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
|
|
||||||
: std::true_type {};
|
|
||||||
|
|
||||||
template <typename T> struct is_bit_reference_like {
|
|
||||||
static constexpr const bool value =
|
|
||||||
std::is_convertible<T, bool>::value &&
|
|
||||||
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef _LIBCPP_VERSION
|
|
||||||
|
|
||||||
// Workaround for libc++ incompatibility with C++ standard.
|
|
||||||
// According to the Standard, `bitset::operator[] const` returns bool.
|
|
||||||
template <typename C>
|
|
||||||
struct is_bit_reference_like<std::__bit_const_reference<C>> {
|
|
||||||
static constexpr const bool value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// We can't use std::vector<bool, Allocator>::reference and
|
// We can't use std::vector<bool, Allocator>::reference and
|
||||||
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
|
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
|
||||||
// in partial specialization.
|
// in partial specialization.
|
||||||
@ -621,14 +625,6 @@ struct formatter<BitRef, Char,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Deleter>
|
|
||||||
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
|
|
||||||
return p.get();
|
|
||||||
}
|
|
||||||
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
|
|
||||||
return p.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<std::atomic<T>, Char,
|
struct formatter<std::atomic<T>, Char,
|
||||||
enable_if_t<is_formattable<T, Char>::value>>
|
enable_if_t<is_formattable<T, Char>::value>>
|
||||||
@ -651,6 +647,11 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
|
|||||||
};
|
};
|
||||||
#endif // __cpp_lib_atomic_flag_test
|
#endif // __cpp_lib_atomic_flag_test
|
||||||
|
|
||||||
|
template <typename T> struct is_tuple_like;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_tuple_like<std::complex<T>> : std::false_type {};
|
||||||
|
|
||||||
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
||||||
private:
|
private:
|
||||||
detail::dynamic_format_specs<Char> specs_;
|
detail::dynamic_format_specs<Char> specs_;
|
||||||
@ -715,7 +716,11 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
|||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<std::reference_wrapper<T>, Char,
|
struct formatter<std::reference_wrapper<T>, Char,
|
||||||
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
|
// Guard against format_as because reference_wrapper is
|
||||||
|
// implicitly convertible to T&.
|
||||||
|
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
|
||||||
|
!detail::has_format_as<T>::value &&
|
||||||
|
!detail::has_format_as_member<T>::value>>
|
||||||
: formatter<remove_cvref_t<T>, Char> {
|
: formatter<remove_cvref_t<T>, Char> {
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
|
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
|
||||||
@ -725,4 +730,5 @@ struct formatter<std::reference_wrapper<T>, Char,
|
|||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_STD_H_
|
#endif // FMT_STD_H_
|
||||||
|
|||||||
@ -55,6 +55,16 @@ inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
|
|||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
|
||||||
|
basic_format_args<buffered_context<Char>> args,
|
||||||
|
locale_ref loc = {}) {
|
||||||
|
static_assert(!std::is_same<Char, char>::value, "");
|
||||||
|
auto out = basic_appender<Char>(buf);
|
||||||
|
parse_format_string(
|
||||||
|
fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
|
||||||
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
@ -112,10 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
|
|||||||
return {{s}};
|
return {{s}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cpp_char8_t
|
|
||||||
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
constexpr auto make_wformat_args(T&... args)
|
constexpr auto make_wformat_args(T&... args)
|
||||||
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
|
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
|
||||||
@ -151,13 +157,13 @@ auto join(std::initializer_list<T> list, wstring_view sep)
|
|||||||
|
|
||||||
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
|
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
|
||||||
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
|
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
|
||||||
-> tuple_join_view<wchar_t, Tuple> {
|
-> tuple_join_view<Tuple, wchar_t> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
auto vformat(basic_string_view<Char> fmt,
|
auto vformat(basic_string_view<Char> fmt,
|
||||||
typename detail::vformat_args<Char>::type args)
|
basic_format_args<buffered_context<Char>> args)
|
||||||
-> std::basic_string<Char> {
|
-> std::basic_string<Char> {
|
||||||
auto buf = basic_memory_buffer<Char>();
|
auto buf = basic_memory_buffer<Char>();
|
||||||
detail::vformat_to(buf, fmt, args);
|
detail::vformat_to(buf, fmt, args);
|
||||||
@ -187,24 +193,20 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
|
|||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename S,
|
template <typename S, typename Char = detail::format_string_char_t<S>,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
inline auto vformat(locale_ref loc, const S& fmt,
|
||||||
detail::is_exotic_char<Char>::value)>
|
basic_format_args<buffered_context<Char>> args)
|
||||||
inline auto vformat(const Locale& loc, const S& fmt,
|
|
||||||
typename detail::vformat_args<Char>::type args)
|
|
||||||
-> std::basic_string<Char> {
|
-> std::basic_string<Char> {
|
||||||
auto buf = basic_memory_buffer<Char>();
|
auto buf = basic_memory_buffer<Char>();
|
||||||
detail::vformat_to(buf, detail::to_string_view(fmt), args,
|
detail::vformat_to(buf, detail::to_string_view(fmt), args, loc);
|
||||||
detail::locale_ref(loc));
|
|
||||||
return {buf.data(), buf.size()};
|
return {buf.data(), buf.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename S, typename... T,
|
template <typename S, typename... T,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
typename Char = detail::format_string_char_t<S>,
|
||||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||||
detail::is_exotic_char<Char>::value)>
|
inline auto format(locale_ref loc, const S& fmt, T&&... args)
|
||||||
inline auto format(const Locale& loc, const S& fmt, T&&... args)
|
|
||||||
-> std::basic_string<Char> {
|
-> std::basic_string<Char> {
|
||||||
return vformat(loc, detail::to_string_view(fmt),
|
return vformat(loc, detail::to_string_view(fmt),
|
||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
@ -215,7 +217,7 @@ template <typename OutputIt, typename S,
|
|||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
detail::is_exotic_char<Char>::value)>
|
detail::is_exotic_char<Char>::value)>
|
||||||
auto vformat_to(OutputIt out, const S& fmt,
|
auto vformat_to(OutputIt out, const S& fmt,
|
||||||
typename detail::vformat_args<Char>::type args) -> OutputIt {
|
basic_format_args<buffered_context<Char>> args) -> OutputIt {
|
||||||
auto&& buf = detail::get_buffer<Char>(out);
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
detail::vformat_to(buf, detail::to_string_view(fmt), args);
|
detail::vformat_to(buf, detail::to_string_view(fmt), args);
|
||||||
return detail::get_iterator(buf, out);
|
return detail::get_iterator(buf, out);
|
||||||
@ -231,27 +233,24 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
|
|||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
typename Char = detail::format_string_char_t<S>,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
detail::is_locale<Locale>::value&&
|
|
||||||
detail::is_exotic_char<Char>::value)>
|
detail::is_exotic_char<Char>::value)>
|
||||||
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
|
inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt,
|
||||||
typename detail::vformat_args<Char>::type args)
|
basic_format_args<buffered_context<Char>> args)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
auto&& buf = detail::get_buffer<Char>(out);
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
|
vformat_to(buf, detail::to_string_view(fmt), args, loc);
|
||||||
return detail::get_iterator(buf, out);
|
return detail::get_iterator(buf, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename OutputIt, typename S, typename... T,
|
template <typename OutputIt, typename S, typename... T,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
typename Char = detail::format_string_char_t<S>,
|
||||||
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
|
||||||
detail::is_locale<Locale>::value &&
|
|
||||||
detail::is_exotic_char<Char>::value>
|
detail::is_exotic_char<Char>::value>
|
||||||
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
|
inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args)
|
||||||
T&&... args) ->
|
-> typename std::enable_if<enable, OutputIt>::type {
|
||||||
typename std::enable_if<enable, OutputIt>::type {
|
|
||||||
return vformat_to(out, loc, detail::to_string_view(fmt),
|
return vformat_to(out, loc, detail::to_string_view(fmt),
|
||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
}
|
}
|
||||||
@ -260,7 +259,7 @@ template <typename OutputIt, typename Char, typename... Args,
|
|||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
detail::is_exotic_char<Char>::value)>
|
detail::is_exotic_char<Char>::value)>
|
||||||
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
|
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
|
||||||
typename detail::vformat_args<Char>::type args)
|
basic_format_args<buffered_context<Char>> args)
|
||||||
-> format_to_n_result<OutputIt> {
|
-> format_to_n_result<OutputIt> {
|
||||||
using traits = detail::fixed_buffer_traits;
|
using traits = detail::fixed_buffer_traits;
|
||||||
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
|
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
|
||||||
@ -331,18 +330,6 @@ inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
|
|||||||
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
|
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
|
|
||||||
const T&... args) {
|
|
||||||
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
|
|
||||||
const T&... args) {
|
|
||||||
return print(stdout, ts, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
|
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
|
||||||
auto buffer = basic_memory_buffer<wchar_t>();
|
auto buffer = basic_memory_buffer<wchar_t>();
|
||||||
detail::vformat_to(buffer, fmt, args);
|
detail::vformat_to(buffer, fmt, args);
|
||||||
|
|||||||
@ -50,6 +50,8 @@ module;
|
|||||||
# include <limits.h>
|
# include <limits.h>
|
||||||
# include <stdint.h>
|
# include <stdint.h>
|
||||||
# include <stdio.h>
|
# include <stdio.h>
|
||||||
|
# include <stdlib.h>
|
||||||
|
# include <string.h>
|
||||||
# include <time.h>
|
# include <time.h>
|
||||||
#endif
|
#endif
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|||||||
@ -8,6 +8,12 @@
|
|||||||
#include "fmt/format-inl.h"
|
#include "fmt/format-inl.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#if FMT_USE_LOCALE
|
||||||
|
template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED!
|
||||||
|
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template FMT_API auto dragonbox::to_decimal(float x) noexcept
|
template FMT_API auto dragonbox::to_decimal(float x) noexcept
|
||||||
@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept
|
|||||||
template FMT_API auto dragonbox::to_decimal(double x) noexcept
|
template FMT_API auto dragonbox::to_decimal(double x) noexcept
|
||||||
-> dragonbox::decimal_fp<double>;
|
-> dragonbox::decimal_fp<double>;
|
||||||
|
|
||||||
#if FMT_USE_LOCALE
|
|
||||||
// DEPRECATED! locale_ref in the detail namespace
|
|
||||||
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
|
||||||
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||||
@ -30,16 +30,13 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
|||||||
// DEPRECATED!
|
// DEPRECATED!
|
||||||
template FMT_API void buffer<char>::append(const char*, const char*);
|
template FMT_API void buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
// DEPRECATED!
|
|
||||||
template FMT_API void vformat_to(buffer<char>&, string_view,
|
|
||||||
typename vformat_args<>::type, locale_ref);
|
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||||
-> thousands_sep_result<wchar_t>;
|
-> thousands_sep_result<wchar_t>;
|
||||||
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
20
src/os.cc
20
src/os.cc
@ -66,14 +66,14 @@ using rwresult = int;
|
|||||||
|
|
||||||
// On Windows the count argument to read and write is unsigned, so convert
|
// On Windows the count argument to read and write is unsigned, so convert
|
||||||
// it from size_t preventing integer overflow.
|
// it from size_t preventing integer overflow.
|
||||||
inline unsigned convert_rwcount(std::size_t count) {
|
inline unsigned convert_rwcount(size_t count) {
|
||||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||||
}
|
}
|
||||||
#elif FMT_USE_FCNTL
|
#elif FMT_USE_FCNTL
|
||||||
// Return type of read and write functions.
|
// Return type of read and write functions.
|
||||||
using rwresult = ssize_t;
|
using rwresult = ssize_t;
|
||||||
|
|
||||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
inline auto convert_rwcount(size_t count) -> size_t { return count; }
|
||||||
#endif
|
#endif
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ void buffered_file::close() {
|
|||||||
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
|
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
|
||||||
}
|
}
|
||||||
|
|
||||||
int buffered_file::descriptor() const {
|
auto buffered_file::descriptor() const -> int {
|
||||||
#ifdef FMT_HAS_SYSTEM
|
#ifdef FMT_HAS_SYSTEM
|
||||||
// fileno is a macro on OpenBSD.
|
// fileno is a macro on OpenBSD.
|
||||||
# ifdef fileno
|
# ifdef fileno
|
||||||
@ -240,7 +240,7 @@ void file::close() {
|
|||||||
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
|
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
|
||||||
}
|
}
|
||||||
|
|
||||||
long long file::size() const {
|
auto file::size() const -> long long {
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||||
@ -251,7 +251,7 @@ long long file::size() const {
|
|||||||
if (size_lower == INVALID_FILE_SIZE) {
|
if (size_lower == INVALID_FILE_SIZE) {
|
||||||
DWORD error = GetLastError();
|
DWORD error = GetLastError();
|
||||||
if (error != NO_ERROR)
|
if (error != NO_ERROR)
|
||||||
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
FMT_THROW(windows_error(error, "cannot get file size"));
|
||||||
}
|
}
|
||||||
unsigned long long long_size = size_upper;
|
unsigned long long long_size = size_upper;
|
||||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||||
@ -266,7 +266,7 @@ long long file::size() const {
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t file::read(void* buffer, std::size_t count) {
|
auto file::read(void* buffer, size_t count) -> size_t {
|
||||||
rwresult result = 0;
|
rwresult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
@ -274,7 +274,7 @@ std::size_t file::read(void* buffer, std::size_t count) {
|
|||||||
return detail::to_unsigned(result);
|
return detail::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t file::write(const void* buffer, std::size_t count) {
|
auto file::write(const void* buffer, size_t count) -> size_t {
|
||||||
rwresult result = 0;
|
rwresult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
@ -282,7 +282,7 @@ std::size_t file::write(const void* buffer, std::size_t count) {
|
|||||||
return detail::to_unsigned(result);
|
return detail::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
file file::dup(int fd) {
|
auto file::dup(int fd) -> file {
|
||||||
// Don't retry as dup doesn't return EINTR.
|
// Don't retry as dup doesn't return EINTR.
|
||||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||||
@ -308,7 +308,7 @@ void file::dup2(int fd, std::error_code& ec) noexcept {
|
|||||||
if (result == -1) ec = std::error_code(errno, std::generic_category());
|
if (result == -1) ec = std::error_code(errno, std::generic_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
buffered_file file::fdopen(const char* mode) {
|
auto file::fdopen(const char* mode) -> buffered_file {
|
||||||
// Don't retry as fdopen doesn't return EINTR.
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
# if defined(__MINGW32__) && defined(_POSIX_)
|
# if defined(__MINGW32__) && defined(_POSIX_)
|
||||||
FILE* f = ::fdopen(fd_, mode);
|
FILE* f = ::fdopen(fd_, mode);
|
||||||
@ -355,7 +355,7 @@ pipe::pipe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# if !defined(__MSDOS__)
|
# if !defined(__MSDOS__)
|
||||||
long getpagesize() {
|
auto getpagesize() -> long {
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
GetSystemInfo(&si);
|
GetSystemInfo(&si);
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
8.1.1
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "fmt",
|
|
||||||
srcs = [
|
|
||||||
#"src/fmt.cc", # No C++ module support, yet in Bazel (https://github.com/bazelbuild/bazel/pull/19940)
|
|
||||||
"src/format.cc",
|
|
||||||
"src/os.cc",
|
|
||||||
],
|
|
||||||
hdrs = glob([
|
|
||||||
"include/fmt/*.h",
|
|
||||||
]),
|
|
||||||
copts = select({
|
|
||||||
"@platforms//os:windows": ["-utf-8"],
|
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
includes = [
|
|
||||||
"include",
|
|
||||||
],
|
|
||||||
strip_include_prefix = "include",
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
)
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module(
|
|
||||||
name = "fmt",
|
|
||||||
compatibility_level = 10,
|
|
||||||
)
|
|
||||||
|
|
||||||
bazel_dep(name = "platforms", version = "0.0.11")
|
|
||||||
bazel_dep(name = "rules_cc", version = "0.1.1")
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
# Bazel support
|
|
||||||
|
|
||||||
To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`,
|
|
||||||
`MODULE.bazel`, `WORKSPACE.bazel`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project.
|
|
||||||
This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}).
|
|
||||||
|
|
||||||
## Using {fmt} as a dependency
|
|
||||||
|
|
||||||
### Using Bzlmod
|
|
||||||
|
|
||||||
The [Bazel Central Registry](https://github.com/bazelbuild/bazel-central-registry/tree/main/modules/fmt) provides support for {fmt}.
|
|
||||||
|
|
||||||
For instance, to use {fmt} add to your `MODULE.bazel` file:
|
|
||||||
|
|
||||||
```
|
|
||||||
bazel_dep(name = "fmt", version = "11.1.4")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Live at head
|
|
||||||
|
|
||||||
For a live-at-head approach, you can copy the contents of this repository and move the Bazel-related build files to the root folder of this project as described above and make use of `local_path_override`, e.g.:
|
|
||||||
|
|
||||||
```
|
|
||||||
local_path_override(
|
|
||||||
module_name = "fmt",
|
|
||||||
path = "../third_party/fmt",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
# WORKSPACE marker file needed by Bazel
|
|
||||||
|
|
||||||
@ -2,6 +2,10 @@
|
|||||||
# A script to invoke mkdocs with the correct environment.
|
# A script to invoke mkdocs with the correct environment.
|
||||||
# Additionally supports deploying via mike:
|
# Additionally supports deploying via mike:
|
||||||
# ./mkdocs deploy [mike-deploy-options]
|
# ./mkdocs deploy [mike-deploy-options]
|
||||||
|
# For example:
|
||||||
|
# ./mkdocs deploy <version>
|
||||||
|
# This will checkout the website to fmt/build/fmt.dev and deploy documentation
|
||||||
|
# <version> there.
|
||||||
|
|
||||||
import errno, os, shutil, sys
|
import errno, os, shutil, sys
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
@ -40,7 +44,7 @@ config_path = os.path.join(support_dir, 'mkdocs.yml')
|
|||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
command = args[0]
|
command = args[0]
|
||||||
if command == 'deploy':
|
if command == 'deploy' or command == 'set-default':
|
||||||
git_url = 'https://github.com/' if 'CI' in os.environ else 'git@github.com:'
|
git_url = 'https://github.com/' if 'CI' in os.environ else 'git@github.com:'
|
||||||
site_repo = git_url + 'fmtlib/fmt.dev.git'
|
site_repo = git_url + 'fmtlib/fmt.dev.git'
|
||||||
|
|
||||||
@ -63,7 +67,13 @@ if len(args) > 0:
|
|||||||
'--branch', 'master'], cwd=site_dir, env=env)
|
'--branch', 'master'], cwd=site_dir, env=env)
|
||||||
if ret != 0 or version == 'dev':
|
if ret != 0 or version == 'dev':
|
||||||
sys.exit(ret)
|
sys.exit(ret)
|
||||||
redirect_page_path = os.path.join(site_dir, version, 'api.html')
|
current_doc_path = os.path.join(site_dir, version)
|
||||||
|
# mike stages files added by deploy for deletion for unclear reason,
|
||||||
|
# undo it.
|
||||||
|
ret = call(['git', 'reset', '--hard'], cwd=site_dir)
|
||||||
|
if False:
|
||||||
|
os.makedirs(current_doc_path, exist_ok=True)
|
||||||
|
redirect_page_path = os.path.join(current_doc_path, 'api.html')
|
||||||
with open(redirect_page_path, "w") as file:
|
with open(redirect_page_path, "w") as file:
|
||||||
file.write(redirect_page)
|
file.write(redirect_page)
|
||||||
ret = call(['git', 'add', redirect_page_path], cwd=site_dir)
|
ret = call(['git', 'add', redirect_page_path], cwd=site_dir)
|
||||||
|
|||||||
@ -34,8 +34,8 @@ tag_map = {
|
|||||||
'emphasis': 'em',
|
'emphasis': 'em',
|
||||||
'computeroutput': 'code',
|
'computeroutput': 'code',
|
||||||
'para': 'p',
|
'para': 'p',
|
||||||
'programlisting': 'pre',
|
'itemizedlist': 'ul',
|
||||||
'verbatim': 'pre'
|
'listitem': 'li'
|
||||||
}
|
}
|
||||||
|
|
||||||
# A map from Doxygen tags to text.
|
# A map from Doxygen tags to text.
|
||||||
@ -50,21 +50,37 @@ def escape_html(s: str) -> str:
|
|||||||
return s.replace("<", "<")
|
return s.replace("<", "<")
|
||||||
|
|
||||||
|
|
||||||
|
# Converts a node from doxygen to HTML format.
|
||||||
|
def convert_node(node: ElementTree.Element, tag: str, attrs: dict = {}):
|
||||||
|
out = '<' + tag
|
||||||
|
for key, value in attrs.items():
|
||||||
|
out += ' ' + key + '="' + value + '"'
|
||||||
|
out += '>'
|
||||||
|
if node.text:
|
||||||
|
out += escape_html(node.text)
|
||||||
|
out += doxyxml2html(list(node))
|
||||||
|
out += '</' + tag + '>'
|
||||||
|
if node.tail:
|
||||||
|
out += node.tail
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def doxyxml2html(nodes: List[ElementTree.Element]):
|
def doxyxml2html(nodes: List[ElementTree.Element]):
|
||||||
out = ''
|
out = ''
|
||||||
for n in nodes:
|
for n in nodes:
|
||||||
tag = tag_map.get(n.tag)
|
tag = tag_map.get(n.tag)
|
||||||
if not tag:
|
if tag:
|
||||||
|
out += convert_node(n, tag)
|
||||||
|
continue
|
||||||
|
if n.tag == 'programlisting' or n.tag == 'verbatim':
|
||||||
|
out += '<pre>'
|
||||||
|
out += convert_node(n, 'code', {'class': 'language-cpp'})
|
||||||
|
out += '</pre>'
|
||||||
|
continue
|
||||||
|
if n.tag == 'ulink':
|
||||||
|
out += convert_node(n, 'a', {'href': n.attrib['url']})
|
||||||
|
continue
|
||||||
out += tag_text_map[n.tag]
|
out += tag_text_map[n.tag]
|
||||||
out += '<' + tag + '>' if tag else ''
|
|
||||||
out += '<code class="language-cpp">' if tag == 'pre' else ''
|
|
||||||
if n.text:
|
|
||||||
out += escape_html(n.text)
|
|
||||||
out += doxyxml2html(list(n))
|
|
||||||
out += '</code>' if tag == 'pre' else ''
|
|
||||||
out += '</' + tag + '>' if tag else ''
|
|
||||||
if n.tail:
|
|
||||||
out += n.tail
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -62,11 +62,6 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
|||||||
endif ()
|
endif ()
|
||||||
add_fmt_test(ostream-test)
|
add_fmt_test(ostream-test)
|
||||||
add_fmt_test(compile-test)
|
add_fmt_test(compile-test)
|
||||||
add_fmt_test(compile-fp-test)
|
|
||||||
if (MSVC)
|
|
||||||
# Without this option, MSVC returns 199711L for the __cplusplus macro.
|
|
||||||
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
|
|
||||||
endif()
|
|
||||||
add_fmt_test(printf-test)
|
add_fmt_test(printf-test)
|
||||||
add_fmt_test(ranges-test ranges-odr-test.cc)
|
add_fmt_test(ranges-test ranges-odr-test.cc)
|
||||||
add_fmt_test(no-builtin-types-test HEADER_ONLY)
|
add_fmt_test(no-builtin-types-test HEADER_ONLY)
|
||||||
|
|||||||
@ -64,7 +64,7 @@ TEST(args_test, custom_format) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct to_stringable {
|
struct to_stringable {
|
||||||
friend fmt::string_view to_string_view(to_stringable) { return {}; }
|
friend auto to_string_view(to_stringable) -> fmt::string_view { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|||||||
@ -5,14 +5,16 @@
|
|||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
// Turn assertion failures into exceptions for testing.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include "test-assert.h"
|
#include "test-assert.h"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#include "fmt/base.h"
|
#include "fmt/base.h"
|
||||||
|
|
||||||
#include <climits> // INT_MAX
|
#include <limits.h> // INT_MAX
|
||||||
#include <cstring> // std::strlen
|
#include <string.h> // strlen
|
||||||
|
|
||||||
#include <functional> // std::equal_to
|
#include <functional> // std::equal_to
|
||||||
#include <iterator> // std::back_insert_iterator, std::distance
|
#include <iterator> // std::back_insert_iterator, std::distance
|
||||||
#include <limits> // std::numeric_limits
|
#include <limits> // std::numeric_limits
|
||||||
@ -21,39 +23,36 @@
|
|||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
using fmt::string_view;
|
#ifdef FMT_FORMAT_H_
|
||||||
using fmt::detail::buffer;
|
# error base-test includes format.h
|
||||||
|
#endif
|
||||||
|
|
||||||
using testing::_;
|
using testing::_;
|
||||||
using testing::Invoke;
|
using testing::Invoke;
|
||||||
using testing::Return;
|
using testing::Return;
|
||||||
|
|
||||||
#ifdef FMT_FORMAT_H_
|
auto copy(fmt::string_view s, fmt::appender out) -> fmt::appender {
|
||||||
# error core-test includes format.h
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fmt::appender copy(fmt::string_view s, fmt::appender out) {
|
|
||||||
for (char c : s) *out++ = c;
|
for (char c : s) *out++ = c;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(string_view_test, value_type) {
|
TEST(string_view_test, value_type) {
|
||||||
static_assert(std::is_same<string_view::value_type, char>::value, "");
|
static_assert(std::is_same<fmt::string_view::value_type, char>::value, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(string_view_test, ctor) {
|
TEST(string_view_test, ctor) {
|
||||||
EXPECT_STREQ("abc", fmt::string_view("abc").data());
|
EXPECT_STREQ(fmt::string_view("abc").data(), "abc");
|
||||||
EXPECT_EQ(3u, fmt::string_view("abc").size());
|
EXPECT_EQ(fmt::string_view("abc").size(), 3u);
|
||||||
|
|
||||||
EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data());
|
EXPECT_STREQ(fmt::string_view(std::string("defg")).data(), "defg");
|
||||||
EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size());
|
EXPECT_EQ(fmt::string_view(std::string("defg")).size(), 4u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(string_view_test, length) {
|
TEST(string_view_test, length) {
|
||||||
// Test that string_view::size() returns string length, not buffer size.
|
// Test that string_view::size() returns string length, not buffer size.
|
||||||
char str[100] = "some string";
|
char str[100] = "some string";
|
||||||
EXPECT_EQ(std::strlen(str), string_view(str).size());
|
EXPECT_EQ(fmt::string_view(str).size(), strlen(str));
|
||||||
EXPECT_LT(std::strlen(str), sizeof(str));
|
EXPECT_LT(strlen(str), sizeof(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check string_view's comparison operator.
|
// Check string_view's comparison operator.
|
||||||
@ -62,13 +61,16 @@ template <template <typename> class Op> void check_op() {
|
|||||||
size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
|
size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
|
||||||
for (size_t i = 0; i < num_inputs; ++i) {
|
for (size_t i = 0; i < num_inputs; ++i) {
|
||||||
for (size_t j = 0; j < num_inputs; ++j) {
|
for (size_t j = 0; j < num_inputs; ++j) {
|
||||||
string_view lhs(inputs[i]), rhs(inputs[j]);
|
fmt::string_view lhs(inputs[i]), rhs(inputs[j]);
|
||||||
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
|
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0),
|
||||||
|
Op<fmt::string_view>()(lhs, rhs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(string_view_test, compare) {
|
TEST(string_view_test, compare) {
|
||||||
|
using fmt::string_view;
|
||||||
|
|
||||||
EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
|
EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
|
||||||
EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
|
EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
|
||||||
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
|
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
|
||||||
@ -93,77 +95,48 @@ TEST(string_view_test, compare) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_CONSTEVAL
|
#if FMT_USE_CONSTEVAL
|
||||||
template <size_t N> struct fixed_string {
|
|
||||||
char data[N] = {};
|
|
||||||
|
|
||||||
constexpr fixed_string(const char (&m)[N]) {
|
|
||||||
for (size_t i = 0; i != N; ++i) data[i] = m[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(string_view_test, from_constexpr_fixed_string) {
|
TEST(string_view_test, from_constexpr_fixed_string) {
|
||||||
static constexpr auto fs = fixed_string<4>("foo");
|
constexpr int size = 4;
|
||||||
|
|
||||||
|
struct fixed_string {
|
||||||
|
char data[size] = {};
|
||||||
|
|
||||||
|
constexpr fixed_string(const char (&m)[size]) {
|
||||||
|
for (size_t i = 0; i != size; ++i) data[i] = m[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto fs = fixed_string("foo");
|
||||||
static constexpr auto sv = fmt::string_view(fs.data);
|
static constexpr auto sv = fmt::string_view(fs.data);
|
||||||
EXPECT_EQ(sv, "foo");
|
EXPECT_EQ(sv, "foo");
|
||||||
}
|
}
|
||||||
#endif // FMT_USE_CONSTEVAL
|
#endif // FMT_USE_CONSTEVAL
|
||||||
|
|
||||||
TEST(base_test, is_locking) {
|
|
||||||
EXPECT_FALSE(fmt::detail::is_locking<const char(&)[3]>());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base_test, is_output_iterator) {
|
|
||||||
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
|
|
||||||
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
|
|
||||||
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value));
|
|
||||||
EXPECT_TRUE(
|
|
||||||
(fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
|
|
||||||
char>::value));
|
|
||||||
EXPECT_TRUE(
|
|
||||||
(fmt::detail::is_output_iterator<std::string::iterator, char>::value));
|
|
||||||
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,
|
|
||||||
char>::value));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base_test, is_back_insert_iterator) {
|
|
||||||
EXPECT_TRUE(fmt::detail::is_back_insert_iterator<
|
|
||||||
std::back_insert_iterator<std::string>>::value);
|
|
||||||
EXPECT_FALSE(fmt::detail::is_back_insert_iterator<
|
|
||||||
std::front_insert_iterator<std::string>>::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
|
|
||||||
TEST(buffer_test, noncopyable) {
|
TEST(buffer_test, noncopyable) {
|
||||||
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
|
EXPECT_FALSE(std::is_copy_constructible<fmt::detail::buffer<char>>::value);
|
||||||
# if !FMT_MSC_VERSION
|
EXPECT_FALSE(std::is_copy_assignable<fmt::detail::buffer<char>>::value);
|
||||||
// std::is_copy_assignable is broken in MSVC2013.
|
|
||||||
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
|
|
||||||
# endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(buffer_test, nonmoveable) {
|
TEST(buffer_test, nonmoveable) {
|
||||||
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
|
EXPECT_FALSE(std::is_move_constructible<fmt::detail::buffer<char>>::value);
|
||||||
# if !FMT_MSC_VERSION
|
EXPECT_FALSE(std::is_move_assignable<fmt::detail::buffer<char>>::value);
|
||||||
// std::is_move_assignable is broken in MSVC2013.
|
|
||||||
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
|
|
||||||
# endif
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(buffer_test, indestructible) {
|
TEST(buffer_test, indestructible) {
|
||||||
static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
|
static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
|
||||||
"buffer's destructor is protected");
|
"buffer's destructor is protected");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> struct mock_buffer final : buffer<T> {
|
template <typename T> struct mock_buffer final : fmt::detail::buffer<T> {
|
||||||
MOCK_METHOD(size_t, do_grow, (size_t));
|
MOCK_METHOD(size_t, do_grow, (size_t));
|
||||||
|
|
||||||
static void grow(buffer<T>& buf, size_t capacity) {
|
static void grow(fmt::detail::buffer<T>& buf, size_t capacity) {
|
||||||
auto& self = static_cast<mock_buffer&>(buf);
|
auto& self = static_cast<mock_buffer&>(buf);
|
||||||
self.set(buf.data(), self.do_grow(capacity));
|
self.set(buf.data(), self.do_grow(capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_buffer(T* data = nullptr, size_t buf_capacity = 0) : buffer<T>(grow) {
|
mock_buffer(T* data = nullptr, size_t buf_capacity = 0)
|
||||||
|
: fmt::detail::buffer<T>(grow) {
|
||||||
this->set(data, buf_capacity);
|
this->set(data, buf_capacity);
|
||||||
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
|
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
|
||||||
return capacity;
|
return capacity;
|
||||||
@ -174,24 +147,24 @@ template <typename T> struct mock_buffer final : buffer<T> {
|
|||||||
TEST(buffer_test, ctor) {
|
TEST(buffer_test, ctor) {
|
||||||
{
|
{
|
||||||
mock_buffer<int> buffer;
|
mock_buffer<int> buffer;
|
||||||
EXPECT_EQ(nullptr, buffer.data());
|
EXPECT_EQ(buffer.data(), nullptr);
|
||||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
EXPECT_EQ(buffer.size(), 0u);
|
||||||
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
EXPECT_EQ(buffer.capacity(), 0u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int dummy;
|
int data;
|
||||||
mock_buffer<int> buffer(&dummy);
|
mock_buffer<int> buffer(&data);
|
||||||
EXPECT_EQ(&dummy, &buffer[0]);
|
EXPECT_EQ(&buffer[0], &data);
|
||||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
EXPECT_EQ(buffer.size(), 0u);
|
||||||
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
EXPECT_EQ(buffer.capacity(), 0u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int dummy;
|
int data;
|
||||||
size_t capacity = std::numeric_limits<size_t>::max();
|
size_t capacity = std::numeric_limits<size_t>::max();
|
||||||
mock_buffer<int> buffer(&dummy, capacity);
|
mock_buffer<int> buffer(&data, capacity);
|
||||||
EXPECT_EQ(&dummy, &buffer[0]);
|
EXPECT_EQ(&buffer[0], &data);
|
||||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
EXPECT_EQ(buffer.size(), 0u);
|
||||||
EXPECT_EQ(capacity, buffer.capacity());
|
EXPECT_EQ(buffer.capacity(), capacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,26 +172,26 @@ TEST(buffer_test, access) {
|
|||||||
char data[10];
|
char data[10];
|
||||||
mock_buffer<char> buffer(data, sizeof(data));
|
mock_buffer<char> buffer(data, sizeof(data));
|
||||||
buffer[0] = 11;
|
buffer[0] = 11;
|
||||||
EXPECT_EQ(11, buffer[0]);
|
EXPECT_EQ(buffer[0], 11);
|
||||||
buffer[3] = 42;
|
buffer[3] = 42;
|
||||||
EXPECT_EQ(42, *(&buffer[0] + 3));
|
EXPECT_EQ(*(&buffer[0] + 3), 42);
|
||||||
const fmt::detail::buffer<char>& const_buffer = buffer;
|
const fmt::detail::buffer<char>& const_buffer = buffer;
|
||||||
EXPECT_EQ(42, const_buffer[3]);
|
EXPECT_EQ(const_buffer[3], 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(buffer_test, try_resize) {
|
TEST(buffer_test, try_resize) {
|
||||||
char data[123];
|
char data[123];
|
||||||
mock_buffer<char> buffer(data, sizeof(data));
|
mock_buffer<char> buffer(data, sizeof(data));
|
||||||
buffer[10] = 42;
|
buffer[10] = 42;
|
||||||
EXPECT_EQ(42, buffer[10]);
|
EXPECT_EQ(buffer[10], 42);
|
||||||
buffer.try_resize(20);
|
buffer.try_resize(20);
|
||||||
EXPECT_EQ(20u, buffer.size());
|
EXPECT_EQ(buffer.size(), 20u);
|
||||||
EXPECT_EQ(123u, buffer.capacity());
|
EXPECT_EQ(buffer.capacity(), 123u);
|
||||||
EXPECT_EQ(42, buffer[10]);
|
EXPECT_EQ(buffer[10], 42);
|
||||||
buffer.try_resize(5);
|
buffer.try_resize(5);
|
||||||
EXPECT_EQ(5u, buffer.size());
|
EXPECT_EQ(buffer.size(), 5u);
|
||||||
EXPECT_EQ(123u, buffer.capacity());
|
EXPECT_EQ(buffer.capacity(), 123u);
|
||||||
EXPECT_EQ(42, buffer[10]);
|
EXPECT_EQ(buffer[10], 42);
|
||||||
// Check if try_resize calls grow.
|
// Check if try_resize calls grow.
|
||||||
EXPECT_CALL(buffer, do_grow(124));
|
EXPECT_CALL(buffer, do_grow(124));
|
||||||
buffer.try_resize(124);
|
buffer.try_resize(124);
|
||||||
@ -240,8 +213,8 @@ TEST(buffer_test, clear) {
|
|||||||
EXPECT_CALL(buffer, do_grow(20));
|
EXPECT_CALL(buffer, do_grow(20));
|
||||||
buffer.try_resize(20);
|
buffer.try_resize(20);
|
||||||
buffer.try_resize(0);
|
buffer.try_resize(0);
|
||||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
EXPECT_EQ(buffer.size(), 0u);
|
||||||
EXPECT_EQ(20u, buffer.capacity());
|
EXPECT_EQ(buffer.capacity(), 20u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(buffer_test, append) {
|
TEST(buffer_test, append) {
|
||||||
@ -249,14 +222,14 @@ TEST(buffer_test, append) {
|
|||||||
mock_buffer<char> buffer(data, 10);
|
mock_buffer<char> buffer(data, 10);
|
||||||
auto test = "test";
|
auto test = "test";
|
||||||
buffer.append(test, test + 5);
|
buffer.append(test, test + 5);
|
||||||
EXPECT_STREQ(test, &buffer[0]);
|
EXPECT_STREQ(&buffer[0], test);
|
||||||
EXPECT_EQ(5u, buffer.size());
|
EXPECT_EQ(buffer.size(), 5u);
|
||||||
buffer.try_resize(10);
|
buffer.try_resize(10);
|
||||||
EXPECT_CALL(buffer, do_grow(12));
|
EXPECT_CALL(buffer, do_grow(12));
|
||||||
buffer.append(test, test + 2);
|
buffer.append(test, test + 2);
|
||||||
EXPECT_EQ('t', buffer[10]);
|
EXPECT_EQ(buffer[10], 't');
|
||||||
EXPECT_EQ('e', buffer[11]);
|
EXPECT_EQ(buffer[11], 'e');
|
||||||
EXPECT_EQ(12u, buffer.size());
|
EXPECT_EQ(buffer.size(), 12u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(buffer_test, append_partial) {
|
TEST(buffer_test, append_partial) {
|
||||||
@ -282,6 +255,41 @@ TEST(buffer_test, append_allocates_enough_storage) {
|
|||||||
buffer.append(test, test + 9);
|
buffer.append(test, test + 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(base_test, is_locking) {
|
||||||
|
EXPECT_FALSE(fmt::detail::is_locking<const char(&)[3]>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base_test, is_output_iterator) {
|
||||||
|
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
|
||||||
|
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
|
||||||
|
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
(fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
|
||||||
|
char>::value));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
(fmt::detail::is_output_iterator<std::string::iterator, char>::value));
|
||||||
|
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,
|
||||||
|
char>::value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base_test, is_back_insert_iterator) {
|
||||||
|
EXPECT_TRUE(fmt::detail::is_back_insert_iterator<
|
||||||
|
std::back_insert_iterator<std::string>>::value);
|
||||||
|
EXPECT_FALSE(fmt::detail::is_back_insert_iterator<
|
||||||
|
std::front_insert_iterator<std::string>>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct minimal_container {
|
||||||
|
using value_type = char;
|
||||||
|
void push_back(char) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(base_test, copy) {
|
||||||
|
minimal_container c;
|
||||||
|
static constexpr char str[] = "a";
|
||||||
|
fmt::detail::copy<char>(str, str + 1, std::back_inserter(c));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(base_test, get_buffer) {
|
TEST(base_test, get_buffer) {
|
||||||
mock_buffer<char> buffer;
|
mock_buffer<char> buffer;
|
||||||
void* buffer_ptr = &buffer;
|
void* buffer_ptr = &buffer;
|
||||||
@ -306,11 +314,6 @@ template <typename Char> struct formatter<test_struct, Char> {
|
|||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(arg_test, format_args) {
|
|
||||||
auto args = fmt::format_args();
|
|
||||||
EXPECT_FALSE(args.get(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a unique result type to make sure that there are no undesirable
|
// Use a unique result type to make sure that there are no undesirable
|
||||||
// conversions.
|
// conversions.
|
||||||
struct test_result {};
|
struct test_result {};
|
||||||
@ -376,33 +379,9 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
|||||||
CHECK_ARG(expected, value) \
|
CHECK_ARG(expected, value) \
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> class numeric_arg_test : public testing::Test {};
|
TEST(arg_test, format_args) {
|
||||||
|
auto args = fmt::format_args();
|
||||||
#if FMT_BUILTIN_TYPES
|
EXPECT_FALSE(args.get(1));
|
||||||
using test_types =
|
|
||||||
testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
|
|
||||||
unsigned, long, unsigned long, long long, unsigned long long,
|
|
||||||
float, double, long double>;
|
|
||||||
#else
|
|
||||||
using test_types = testing::Types<int>;
|
|
||||||
#endif
|
|
||||||
TYPED_TEST_SUITE(numeric_arg_test, test_types);
|
|
||||||
|
|
||||||
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
|
|
||||||
auto test_value() -> T {
|
|
||||||
return static_cast<T>(42);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T,
|
|
||||||
fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
|
|
||||||
auto test_value() -> T {
|
|
||||||
return static_cast<T>(4.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
TYPED_TEST(numeric_arg_test, make_and_visit) {
|
|
||||||
CHECK_ARG_SIMPLE(test_value<TypeParam>());
|
|
||||||
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min());
|
|
||||||
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(arg_test, char_arg) { CHECK_ARG('a', 'a'); }
|
TEST(arg_test, char_arg) { CHECK_ARG('a', 'a'); }
|
||||||
@ -444,7 +423,7 @@ struct check_custom {
|
|||||||
auto parse_ctx = fmt::format_parse_context("");
|
auto parse_ctx = fmt::format_parse_context("");
|
||||||
auto ctx = fmt::format_context(fmt::appender(buffer), fmt::format_args());
|
auto ctx = fmt::format_context(fmt::appender(buffer), fmt::format_args());
|
||||||
h.format(parse_ctx, ctx);
|
h.format(parse_ctx, ctx);
|
||||||
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
|
EXPECT_EQ(std::string(buffer.data, buffer.size()), "test");
|
||||||
return test_result();
|
return test_result();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -464,27 +443,57 @@ TEST(arg_test, visit_invalid_arg) {
|
|||||||
fmt::basic_format_arg<fmt::format_context>().visit(visitor);
|
fmt::basic_format_arg<fmt::format_context>().visit(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> class numeric_arg_test : public testing::Test {};
|
||||||
|
|
||||||
|
#if FMT_BUILTIN_TYPES
|
||||||
|
using test_types =
|
||||||
|
testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
|
||||||
|
unsigned, long, unsigned long, long long, unsigned long long,
|
||||||
|
float, double, long double>;
|
||||||
|
#else
|
||||||
|
using test_types = testing::Types<int>;
|
||||||
|
#endif
|
||||||
|
TYPED_TEST_SUITE(numeric_arg_test, test_types);
|
||||||
|
|
||||||
|
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||||
|
auto test_value() -> T {
|
||||||
|
return static_cast<T>(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
|
||||||
|
auto test_value() -> T {
|
||||||
|
return static_cast<T>(4.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST(numeric_arg_test, make_and_visit) {
|
||||||
|
CHECK_ARG_SIMPLE(test_value<TypeParam>());
|
||||||
|
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min());
|
||||||
|
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
|
||||||
|
}
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
#if FMT_USE_CONSTEXPR
|
||||||
|
|
||||||
enum class arg_id_result { none, index, name };
|
enum class arg_id_result { none, index, name };
|
||||||
|
|
||||||
struct test_arg_id_handler {
|
struct test_arg_id_handler {
|
||||||
arg_id_result res = arg_id_result::none;
|
arg_id_result res = arg_id_result::none;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
string_view name;
|
fmt::string_view name;
|
||||||
|
|
||||||
constexpr void on_index(int i) {
|
constexpr void on_index(int i) {
|
||||||
res = arg_id_result::index;
|
res = arg_id_result::index;
|
||||||
index = i;
|
index = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void on_name(string_view n) {
|
constexpr void on_name(fmt::string_view n) {
|
||||||
res = arg_id_result::name;
|
res = arg_id_result::name;
|
||||||
name = n;
|
name = n;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
|
constexpr auto parse_arg_id(const char (&s)[N]) -> test_arg_id_handler {
|
||||||
auto h = test_arg_id_handler();
|
auto h = test_arg_id_handler();
|
||||||
fmt::detail::parse_arg_id(s, s + N, h);
|
fmt::detail::parse_arg_id(s, s + N, h);
|
||||||
return h;
|
return h;
|
||||||
@ -542,7 +551,7 @@ struct test_format_string_handler {
|
|||||||
bool error = false;
|
bool error = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
|
template <size_t N> constexpr auto parse_string(const char (&s)[N]) -> bool {
|
||||||
auto h = test_format_string_handler();
|
auto h = test_format_string_handler();
|
||||||
fmt::detail::parse_format_string(fmt::string_view(s, N - 1), h);
|
fmt::detail::parse_format_string(fmt::string_view(s, N - 1), h);
|
||||||
return !h.error;
|
return !h.error;
|
||||||
@ -556,6 +565,7 @@ TEST(base_test, constexpr_parse_format_string) {
|
|||||||
static_assert(parse_string("{foo}"), "");
|
static_assert(parse_string("{foo}"), "");
|
||||||
static_assert(parse_string("{:}"), "");
|
static_assert(parse_string("{:}"), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_CONSTEXPR
|
#endif // FMT_USE_CONSTEXPR
|
||||||
|
|
||||||
struct enabled_formatter {};
|
struct enabled_formatter {};
|
||||||
@ -700,46 +710,46 @@ TEST(base_test, format_to) {
|
|||||||
TEST(base_test, format_to_array) {
|
TEST(base_test, format_to_array) {
|
||||||
char buffer[4];
|
char buffer[4];
|
||||||
auto result = fmt::format_to(buffer, "{}", 12345);
|
auto result = fmt::format_to(buffer, "{}", 12345);
|
||||||
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
|
EXPECT_EQ(std::distance(&buffer[0], result.out), 4);
|
||||||
EXPECT_TRUE(result.truncated);
|
EXPECT_TRUE(result.truncated);
|
||||||
EXPECT_EQ(buffer + 4, result.out);
|
EXPECT_EQ(result.out, buffer + 4);
|
||||||
EXPECT_EQ("1234", fmt::string_view(buffer, 4));
|
EXPECT_EQ(fmt::string_view(buffer, 4), "1234");
|
||||||
|
|
||||||
char* out = nullptr;
|
char* out = nullptr;
|
||||||
EXPECT_THROW(out = result, std::runtime_error);
|
EXPECT_THROW(out = result, std::runtime_error);
|
||||||
(void)out;
|
(void)out;
|
||||||
|
|
||||||
result = fmt::format_to(buffer, "{:s}", "foobar");
|
result = fmt::format_to(buffer, "{:s}", "foobar");
|
||||||
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
|
EXPECT_EQ(std::distance(&buffer[0], result.out), 4);
|
||||||
EXPECT_TRUE(result.truncated);
|
EXPECT_TRUE(result.truncated);
|
||||||
EXPECT_EQ(buffer + 4, result.out);
|
EXPECT_EQ(result.out, buffer + 4);
|
||||||
EXPECT_EQ("foob", fmt::string_view(buffer, 4));
|
EXPECT_EQ(fmt::string_view(buffer, 4), "foob");
|
||||||
|
|
||||||
buffer[0] = 'x';
|
buffer[0] = 'x';
|
||||||
buffer[1] = 'x';
|
buffer[1] = 'x';
|
||||||
buffer[2] = 'x';
|
buffer[2] = 'x';
|
||||||
buffer[3] = 'x';
|
buffer[3] = 'x';
|
||||||
result = fmt::format_to(buffer, "{}", 'A');
|
result = fmt::format_to(buffer, "{}", 'A');
|
||||||
EXPECT_EQ(1, std::distance(&buffer[0], result.out));
|
EXPECT_EQ(std::distance(&buffer[0], result.out), 1);
|
||||||
EXPECT_FALSE(result.truncated);
|
EXPECT_FALSE(result.truncated);
|
||||||
EXPECT_EQ(buffer + 1, result.out);
|
EXPECT_EQ(result.out, buffer + 1);
|
||||||
EXPECT_EQ("Axxx", fmt::string_view(buffer, 4));
|
EXPECT_EQ(fmt::string_view(buffer, 4), "Axxx");
|
||||||
|
|
||||||
result = fmt::format_to(buffer, "{}{} ", 'B', 'C');
|
result = fmt::format_to(buffer, "{}{} ", 'B', 'C');
|
||||||
EXPECT_EQ(3, std::distance(&buffer[0], result.out));
|
EXPECT_EQ(std::distance(&buffer[0], result.out), 3);
|
||||||
EXPECT_FALSE(result.truncated);
|
EXPECT_FALSE(result.truncated);
|
||||||
EXPECT_EQ(buffer + 3, result.out);
|
EXPECT_EQ(result.out, buffer + 3);
|
||||||
EXPECT_EQ("BC x", fmt::string_view(buffer, 4));
|
EXPECT_EQ(fmt::string_view(buffer, 4), "BC x");
|
||||||
|
|
||||||
result = fmt::format_to(buffer, "{}", "ABCDE");
|
result = fmt::format_to(buffer, "{}", "ABCDE");
|
||||||
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
|
EXPECT_EQ(std::distance(&buffer[0], result.out), 4);
|
||||||
EXPECT_TRUE(result.truncated);
|
EXPECT_TRUE(result.truncated);
|
||||||
EXPECT_EQ("ABCD", fmt::string_view(buffer, 4));
|
EXPECT_EQ(fmt::string_view(buffer, 4), "ABCD");
|
||||||
|
|
||||||
result = fmt::format_to(buffer, "{}", std::string(1000, '*').c_str());
|
result = fmt::format_to(buffer, "{}", std::string(1000, '*').c_str());
|
||||||
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
|
EXPECT_EQ(std::distance(&buffer[0], result.out), 4);
|
||||||
EXPECT_TRUE(result.truncated);
|
EXPECT_TRUE(result.truncated);
|
||||||
EXPECT_EQ("****", fmt::string_view(buffer, 4));
|
EXPECT_EQ(fmt::string_view(buffer, 4), "****");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that check is not found by ADL.
|
// Test that check is not found by ADL.
|
||||||
@ -806,7 +816,7 @@ TEST(base_test, format_nonconst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(base_test, throw_in_buffer_dtor) {
|
TEST(base_test, throw_in_buffer_dtor) {
|
||||||
enum { buffer_size = 256 };
|
constexpr int buffer_size = 256;
|
||||||
|
|
||||||
struct throwing_iterator {
|
struct throwing_iterator {
|
||||||
int& count;
|
int& count;
|
||||||
@ -828,7 +838,7 @@ TEST(base_test, throw_in_buffer_dtor) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct its_a_trap {
|
struct convertible_to_any_type_with_member_x {
|
||||||
template <typename T> operator T() const {
|
template <typename T> operator T() const {
|
||||||
auto v = T();
|
auto v = T();
|
||||||
v.x = 42;
|
v.x = 42;
|
||||||
@ -837,12 +847,12 @@ struct its_a_trap {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
template <> struct formatter<its_a_trap> {
|
template <> struct formatter<convertible_to_any_type_with_member_x> {
|
||||||
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto format(its_a_trap, format_context& ctx) const
|
auto format(convertible_to_any_type_with_member_x, format_context& ctx) const
|
||||||
-> decltype(ctx.out()) const {
|
-> decltype(ctx.out()) const {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
*out++ = 'x';
|
*out++ = 'x';
|
||||||
@ -851,9 +861,10 @@ template <> struct formatter<its_a_trap> {
|
|||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(base_test, trappy_conversion) {
|
TEST(base_test, promiscuous_conversions) {
|
||||||
auto s = std::string();
|
auto s = std::string();
|
||||||
fmt::format_to(std::back_inserter(s), "{}", its_a_trap());
|
fmt::format_to(std::back_inserter(s), "{}",
|
||||||
|
convertible_to_any_type_with_member_x());
|
||||||
EXPECT_EQ(s, "x");
|
EXPECT_EQ(s, "x");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,11 +873,11 @@ struct custom_container {
|
|||||||
|
|
||||||
using value_type = char;
|
using value_type = char;
|
||||||
|
|
||||||
size_t size() const { return 0; }
|
auto size() const -> size_t { return 0; }
|
||||||
void resize(size_t) {}
|
void resize(size_t) {}
|
||||||
|
|
||||||
void push_back(char) {}
|
void push_back(char) {}
|
||||||
char& operator[](size_t) { return data; }
|
auto operator[](size_t) -> char& { return data; }
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
@ -878,25 +889,23 @@ TEST(base_test, format_to_custom_container) {
|
|||||||
fmt::format_to(std::back_inserter(c), "");
|
fmt::format_to(std::back_inserter(c), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nondeterministic_format_string {
|
|
||||||
mutable int i = 0;
|
|
||||||
FMT_CONSTEXPR operator string_view() const {
|
|
||||||
return string_view("{}", i++ != 0 ? 2 : 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(base_test, no_repeated_format_string_conversions) {
|
TEST(base_test, no_repeated_format_string_conversions) {
|
||||||
#if !FMT_GCC_VERSION
|
struct nondeterministic_format_string {
|
||||||
|
mutable int i = 0;
|
||||||
|
FMT_CONSTEXPR operator fmt::string_view() const {
|
||||||
|
return {"{}", i++ != 0 ? 2u : 0u};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200
|
||||||
char buf[10];
|
char buf[10];
|
||||||
fmt::format_to(buf, nondeterministic_format_string());
|
fmt::format_to(buf, nondeterministic_format_string());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(base_test, format_context_accessors) {
|
TEST(base_test, format_context_accessors) {
|
||||||
class copier {
|
auto copy = [](fmt::appender app, const fmt::format_context& ctx) {
|
||||||
static fmt::format_context copy(fmt::appender app,
|
return fmt::format_context(app, ctx.args(), ctx.locale());
|
||||||
const fmt::format_context& ctx) {
|
|
||||||
return fmt::format_context(std::move(app), ctx.args(), ctx.locale());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
fmt::detail::ignore_unused(copy);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,8 +55,8 @@ auto make_second(int s) -> std::tm {
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string system_strftime(const std::string& format, const std::tm* timeptr,
|
auto system_strftime(const std::string& format, const std::tm* timeptr,
|
||||||
std::locale* locptr = nullptr) {
|
std::locale* locptr = nullptr) -> std::string {
|
||||||
auto loc = locptr ? *locptr : std::locale::classic();
|
auto loc = locptr ? *locptr : std::locale::classic();
|
||||||
auto& facet = std::use_facet<std::time_put<char>>(loc);
|
auto& facet = std::use_facet<std::time_put<char>>(loc);
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
@ -73,8 +73,8 @@ std::string system_strftime(const std::string& format, const std::tm* timeptr,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min,
|
FMT_CONSTEXPR auto make_tm(int year, int mon, int mday, int hour, int min,
|
||||||
int sec) {
|
int sec) -> std::tm {
|
||||||
auto tm = std::tm();
|
auto tm = std::tm();
|
||||||
tm.tm_sec = sec;
|
tm.tm_sec = sec;
|
||||||
tm.tm_min = min;
|
tm.tm_min = min;
|
||||||
@ -336,12 +336,12 @@ TEST(chrono_test, local_time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(fmt::detail::has_tm_gmtoff<T>::value)>
|
template <typename T, FMT_ENABLE_IF(fmt::detail::has_tm_gmtoff<T>::value)>
|
||||||
bool set_tm_gmtoff(T& time, long offset) {
|
auto set_tm_gmtoff(T& time, long offset) -> bool {
|
||||||
time.tm_gmtoff = offset;
|
time.tm_gmtoff = offset;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
template <typename T, FMT_ENABLE_IF(!fmt::detail::has_tm_gmtoff<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!fmt::detail::has_tm_gmtoff<T>::value)>
|
||||||
bool set_tm_gmtoff(T&, long) {
|
auto set_tm_gmtoff(T&, long) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -113,6 +113,15 @@ TEST(color_test, format) {
|
|||||||
EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) |
|
EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) |
|
||||||
fmt::emphasis::underline)),
|
fmt::emphasis::underline)),
|
||||||
"\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m");
|
"\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m");
|
||||||
|
EXPECT_EQ(
|
||||||
|
fmt::format(
|
||||||
|
"{}", fmt::styled(
|
||||||
|
"all", fmt::emphasis::bold | fmt::emphasis::faint |
|
||||||
|
fmt::emphasis::italic |
|
||||||
|
fmt::emphasis::underline | fmt::emphasis::blink |
|
||||||
|
fmt::emphasis::reverse | fmt::emphasis::conceal |
|
||||||
|
fmt::emphasis::strikethrough)),
|
||||||
|
"\x1b[1;2;3;4;5;7;8;9mall\x1b[0m");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(color_test, format_to) {
|
TEST(color_test, format_to) {
|
||||||
|
|||||||
@ -1,61 +0,0 @@
|
|||||||
// Formatting library for C++ - formatting library tests
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#include "fmt/compile.h"
|
|
||||||
#include "gmock/gmock.h"
|
|
||||||
|
|
||||||
#if FMT_USE_CONSTEVAL
|
|
||||||
|
|
||||||
template <size_t max_string_length, typename Char = char> struct test_string {
|
|
||||||
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
|
|
||||||
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
|
|
||||||
}
|
|
||||||
Char buffer[max_string_length]{};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t max_string_length, typename Char = char, typename... Args>
|
|
||||||
consteval auto test_format(auto format, const Args&... args) {
|
|
||||||
test_string<max_string_length, Char> string{};
|
|
||||||
fmt::format_to(string.buffer, format, args...);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(compile_time_formatting_test, floating_point) {
|
|
||||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
|
|
||||||
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
|
|
||||||
|
|
||||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
|
|
||||||
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
|
|
||||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
|
|
||||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
|
|
||||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
|
|
||||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
|
|
||||||
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
|
|
||||||
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
|
|
||||||
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
|
|
||||||
|
|
||||||
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
|
|
||||||
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
|
|
||||||
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
|
|
||||||
EXPECT_EQ("9223372036854775808.000000",
|
|
||||||
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
|
|
||||||
|
|
||||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
|
||||||
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
|
|
||||||
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
|
|
||||||
if (std::signbit(-nan))
|
|
||||||
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
|
|
||||||
else
|
|
||||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
|
||||||
|
|
||||||
constexpr double inf = std::numeric_limits<double>::infinity();
|
|
||||||
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
|
|
||||||
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
|
|
||||||
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FMT_USE_CONSTEVAL
|
|
||||||
@ -90,9 +90,6 @@ TEST(compile_test, format_escape) {
|
|||||||
EXPECT_EQ("\"abc\" ", fmt::format(FMT_COMPILE("{0:<7?}"), "abc"));
|
EXPECT_EQ("\"abc\" ", fmt::format(FMT_COMPILE("{0:<7?}"), "abc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(compile_test, format_wide_string) {
|
|
||||||
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(compile_test, format_specs) {
|
TEST(compile_test, format_specs) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
||||||
@ -124,7 +121,6 @@ TEST(compile_test, manual_ordering) {
|
|||||||
"true 42 42 foo 0x1234 foo",
|
"true 42 42 foo 0x1234 foo",
|
||||||
fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,
|
fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,
|
||||||
"foo", reinterpret_cast<void*>(0x1234), test_formattable()));
|
"foo", reinterpret_cast<void*>(0x1234), test_formattable()));
|
||||||
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(compile_test, named) {
|
TEST(compile_test, named) {
|
||||||
@ -133,10 +129,6 @@ TEST(compile_test, named) {
|
|||||||
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
||||||
fmt::detail::runtime_named_field<char>>);
|
fmt::detail::runtime_named_field<char>>);
|
||||||
|
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), fmt::arg("arg", 42)));
|
|
||||||
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{} {}"), fmt::arg("arg", 41),
|
|
||||||
fmt::arg("arg", 43)));
|
|
||||||
|
|
||||||
EXPECT_EQ("foobar",
|
EXPECT_EQ("foobar",
|
||||||
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"),
|
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"),
|
||||||
fmt::arg("a1", "bar")));
|
fmt::arg("a1", "bar")));
|
||||||
@ -236,6 +228,12 @@ TEST(compile_test, constexpr_formatted_size) {
|
|||||||
fmt::formatted_size(FMT_COMPILE("{:s}"), "abc");
|
fmt::formatted_size(FMT_COMPILE("{:s}"), "abc");
|
||||||
EXPECT_EQ(str_size, 3);
|
EXPECT_EQ(str_size, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(compile_test, static_format) {
|
||||||
|
constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
|
||||||
|
EXPECT_STREQ(result.c_str(), "42");
|
||||||
|
EXPECT_EQ(result.str(), "42");
|
||||||
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
TEST(compile_test, text_and_arg) {
|
TEST(compile_test, text_and_arg) {
|
||||||
@ -312,7 +310,6 @@ TEST(compile_test, compile_format_string_literal) {
|
|||||||
using namespace fmt::literals;
|
using namespace fmt::literals;
|
||||||
EXPECT_EQ("", fmt::format(""_cf));
|
EXPECT_EQ("", fmt::format(""_cf));
|
||||||
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
||||||
EXPECT_EQ(L"42", fmt::format(L"{}"_cf, 42));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -420,4 +417,53 @@ TEST(compile_time_formatting_test, custom_type) {
|
|||||||
TEST(compile_time_formatting_test, multibyte_fill) {
|
TEST(compile_time_formatting_test, multibyte_fill) {
|
||||||
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
|
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(compile_time_formatting_test, floating_point) {
|
||||||
|
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
|
||||||
|
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
|
||||||
|
|
||||||
|
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
|
||||||
|
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
|
||||||
|
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
|
||||||
|
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
|
||||||
|
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
|
||||||
|
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
|
||||||
|
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
|
||||||
|
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
|
||||||
|
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
|
||||||
|
|
||||||
|
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
|
||||||
|
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
|
||||||
|
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
|
||||||
|
EXPECT_EQ("9223372036854775808.000000",
|
||||||
|
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
|
||||||
|
|
||||||
|
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
|
||||||
|
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
|
||||||
|
if (std::signbit(-nan))
|
||||||
|
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
|
||||||
|
else
|
||||||
|
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||||
|
|
||||||
|
constexpr double inf = std::numeric_limits<double>::infinity();
|
||||||
|
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
|
||||||
|
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
|
||||||
|
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_CONSTEXPR_STRING
|
||||||
|
TEST(compile_test, constexpr_string_format) {
|
||||||
|
constexpr auto result = []() {
|
||||||
|
return fmt::format(FMT_COMPILE("{}"), 42) == "42";
|
||||||
|
}();
|
||||||
|
EXPECT_TRUE(result);
|
||||||
|
|
||||||
|
// Test with a larger string to avoid small string optimization.
|
||||||
|
constexpr auto big = []() {
|
||||||
|
return fmt::format(FMT_COMPILE("{:100}"), ' ') == std::string(100, ' ');
|
||||||
|
}();
|
||||||
|
EXPECT_TRUE(big);
|
||||||
|
}
|
||||||
|
#endif // FMT_USE_CONSTEXPR_STRING
|
||||||
|
|||||||
@ -292,7 +292,7 @@ struct double_double {
|
|||||||
|
|
||||||
auto format_as(double_double d) -> double { return d; }
|
auto format_as(double_double d) -> double { return d; }
|
||||||
|
|
||||||
bool operator>=(const double_double& lhs, const double_double& rhs) {
|
auto operator>=(const double_double& lhs, const double_double& rhs) -> bool {
|
||||||
return lhs.a + lhs.b >= rhs.a + rhs.b;
|
return lhs.a + lhs.b >= rhs.a + rhs.b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,11 +356,11 @@ TEST(format_impl_test, write_console_signature) {
|
|||||||
|
|
||||||
// A public domain branchless UTF-8 decoder by Christopher Wellons:
|
// A public domain branchless UTF-8 decoder by Christopher Wellons:
|
||||||
// https://github.com/skeeto/branchless-utf8
|
// https://github.com/skeeto/branchless-utf8
|
||||||
constexpr bool unicode_is_surrogate(uint32_t c) {
|
constexpr auto unicode_is_surrogate(uint32_t c) -> bool {
|
||||||
return c >= 0xD800U && c <= 0xDFFFU;
|
return c >= 0xD800U && c <= 0xDFFFU;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR char* utf8_encode(char* s, uint32_t c) {
|
FMT_CONSTEXPR auto utf8_encode(char* s, uint32_t c) -> char* {
|
||||||
if (c >= (1UL << 16)) {
|
if (c >= (1UL << 16)) {
|
||||||
s[0] = static_cast<char>(0xf0 | (c >> 18));
|
s[0] = static_cast<char>(0xf0 | (c >> 18));
|
||||||
s[1] = static_cast<char>(0x80 | ((c >> 12) & 0x3f));
|
s[1] = static_cast<char>(0x80 | ((c >> 12) & 0x3f));
|
||||||
|
|||||||
@ -206,10 +206,6 @@ TEST(util_test, parse_nonnegative_int) {
|
|||||||
EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1);
|
EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(format_impl_test, compute_width) {
|
|
||||||
EXPECT_EQ(fmt::detail::compute_width("вожык"), 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(util_test, utf8_to_utf16) {
|
TEST(util_test, utf8_to_utf16) {
|
||||||
auto u = fmt::detail::utf8_to_utf16("лошадка");
|
auto u = fmt::detail::utf8_to_utf16("лошадка");
|
||||||
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
||||||
@ -319,18 +315,17 @@ TEST(memory_buffer_test, move_ctor_inline_buffer) {
|
|||||||
std::allocator<char>* alloc = buffer.get_allocator().get();
|
std::allocator<char>* alloc = buffer.get_allocator().get();
|
||||||
basic_memory_buffer<char, 5, std_allocator> buffer2(std::move(buffer));
|
basic_memory_buffer<char, 5, std_allocator> buffer2(std::move(buffer));
|
||||||
// Move shouldn't destroy the inline content of the first buffer.
|
// Move shouldn't destroy the inline content of the first buffer.
|
||||||
EXPECT_EQ(str, std::string(&buffer[0], buffer.size()));
|
EXPECT_EQ(std::string(buffer.data(), buffer.size()), str);
|
||||||
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
|
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), str);
|
||||||
EXPECT_EQ(5u, buffer2.capacity());
|
EXPECT_EQ(buffer2.capacity(), 5u);
|
||||||
// Move should transfer allocator.
|
// Move should transfer allocator.
|
||||||
EXPECT_EQ(nullptr, buffer.get_allocator().get());
|
EXPECT_EQ(buffer.get_allocator().get(), nullptr);
|
||||||
EXPECT_EQ(alloc, buffer2.get_allocator().get());
|
EXPECT_EQ(buffer2.get_allocator().get(), alloc);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto alloc = std::allocator<char>();
|
auto alloc = std::allocator<char>();
|
||||||
basic_memory_buffer<char, 5, std_allocator> buffer((std_allocator(&alloc)));
|
basic_memory_buffer<char, 5, std_allocator> buffer((std_allocator(&alloc)));
|
||||||
const char test[] = "test";
|
buffer.append(string_view("test"));
|
||||||
buffer.append(string_view(test, 4));
|
|
||||||
check_move_buffer("test", buffer);
|
check_move_buffer("test", buffer);
|
||||||
// Adding one more character fills the inline buffer, but doesn't cause
|
// Adding one more character fills the inline buffer, but doesn't cause
|
||||||
// dynamic allocation.
|
// dynamic allocation.
|
||||||
@ -355,14 +350,63 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) {
|
|||||||
EXPECT_GT(buffer2.capacity(), 4u);
|
EXPECT_GT(buffer2.capacity(), 4u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using std_allocator_noprop = allocator_ref<std::allocator<char>, false>;
|
||||||
|
|
||||||
|
TEST(memory_buffer_test, move_ctor_inline_buffer_non_propagating) {
|
||||||
|
auto check_move_buffer =
|
||||||
|
[](const char* str,
|
||||||
|
basic_memory_buffer<char, 5, std_allocator_noprop>& buffer) {
|
||||||
|
std::allocator<char>* original_alloc_ptr = buffer.get_allocator().get();
|
||||||
|
const char* original_data_ptr = &buffer[0];
|
||||||
|
basic_memory_buffer<char, 5, std_allocator_noprop> buffer2(
|
||||||
|
std::move(buffer));
|
||||||
|
const char* new_data_ptr = &buffer2[0];
|
||||||
|
EXPECT_NE(new_data_ptr, original_data_ptr);
|
||||||
|
EXPECT_EQ(std::string(buffer.data(), buffer.size()), str);
|
||||||
|
EXPECT_EQ(std::string(buffer2.data(), buffer2.size()), str);
|
||||||
|
EXPECT_EQ(buffer2.capacity(), 5u);
|
||||||
|
// Allocators should NOT be transferred; they remain distinct instances.
|
||||||
|
// The original buffer's allocator pointer should still be valid (not
|
||||||
|
// nullptr).
|
||||||
|
EXPECT_EQ(buffer.get_allocator().get(), original_alloc_ptr);
|
||||||
|
EXPECT_NE(buffer2.get_allocator().get(), original_alloc_ptr);
|
||||||
|
};
|
||||||
|
auto alloc = std::allocator<char>();
|
||||||
|
basic_memory_buffer<char, 5, std_allocator_noprop> buffer(
|
||||||
|
(std_allocator_noprop(&alloc)));
|
||||||
|
buffer.append(string_view("test", 4));
|
||||||
|
check_move_buffer("test", buffer);
|
||||||
|
buffer.push_back('a');
|
||||||
|
check_move_buffer("testa", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(memory_buffer_test, move_ctor_dynamic_buffer_non_propagating) {
|
||||||
|
auto alloc = std::allocator<char>();
|
||||||
|
basic_memory_buffer<char, 4, std_allocator_noprop> buffer(
|
||||||
|
(std_allocator_noprop(&alloc)));
|
||||||
|
const char test[] = "test";
|
||||||
|
buffer.append(test, test + 4);
|
||||||
|
const char* inline_buffer_ptr = &buffer[0];
|
||||||
|
buffer.push_back('a');
|
||||||
|
EXPECT_NE(buffer.data(), inline_buffer_ptr);
|
||||||
|
std::allocator<char>* original_alloc_ptr = buffer.get_allocator().get();
|
||||||
|
basic_memory_buffer<char, 4, std_allocator_noprop> buffer2;
|
||||||
|
buffer2 = std::move(buffer);
|
||||||
|
EXPECT_EQ(std::string(buffer2.data(), buffer2.size()), "testa");
|
||||||
|
EXPECT_GT(buffer2.capacity(), 4u);
|
||||||
|
EXPECT_NE(buffer2.data(), inline_buffer_ptr);
|
||||||
|
EXPECT_EQ(buffer.get_allocator().get(), original_alloc_ptr);
|
||||||
|
EXPECT_NE(buffer2.get_allocator().get(), original_alloc_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
void check_move_assign_buffer(const char* str,
|
void check_move_assign_buffer(const char* str,
|
||||||
basic_memory_buffer<char, 5>& buffer) {
|
basic_memory_buffer<char, 5>& buffer) {
|
||||||
basic_memory_buffer<char, 5> buffer2;
|
basic_memory_buffer<char, 5> buffer2;
|
||||||
buffer2 = std::move(buffer);
|
buffer2 = std::move(buffer);
|
||||||
// Move shouldn't destroy the inline content of the first buffer.
|
// Move shouldn't destroy the inline content of the first buffer.
|
||||||
EXPECT_EQ(str, std::string(&buffer[0], buffer.size()));
|
EXPECT_EQ(std::string(&buffer[0], buffer.size()), str);
|
||||||
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
|
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), str);
|
||||||
EXPECT_EQ(5u, buffer2.capacity());
|
EXPECT_EQ(buffer2.capacity(), 5u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(memory_buffer_test, move_assignment) {
|
TEST(memory_buffer_test, move_assignment) {
|
||||||
@ -381,8 +425,8 @@ TEST(memory_buffer_test, move_assignment) {
|
|||||||
basic_memory_buffer<char, 5> buffer2;
|
basic_memory_buffer<char, 5> buffer2;
|
||||||
buffer2 = std::move(buffer);
|
buffer2 = std::move(buffer);
|
||||||
// Move should rip the guts of the first buffer.
|
// Move should rip the guts of the first buffer.
|
||||||
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
|
EXPECT_EQ(buffer.data(), inline_buffer_ptr);
|
||||||
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
|
EXPECT_EQ(std::string(buffer2.data(), buffer2.size()), "testab");
|
||||||
EXPECT_GT(buffer2.capacity(), 5u);
|
EXPECT_GT(buffer2.capacity(), 5u);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,6 +525,12 @@ TEST(memory_buffer_test, max_size_allocator_overflow) {
|
|||||||
EXPECT_THROW(buffer.resize(161), std::exception);
|
EXPECT_THROW(buffer.resize(161), std::exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(memory_buffer_test, back_insert_iterator) {
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
using iterator = decltype(std::back_inserter(buf));
|
||||||
|
EXPECT_TRUE(fmt::detail::is_back_insert_iterator<iterator>::value);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(format_test, digits2_alignment) {
|
TEST(format_test, digits2_alignment) {
|
||||||
auto p =
|
auto p =
|
||||||
fmt::detail::bit_cast<fmt::detail::uintptr_t>(fmt::detail::digits2(0));
|
fmt::detail::bit_cast<fmt::detail::uintptr_t>(fmt::detail::digits2(0));
|
||||||
@ -552,6 +602,10 @@ TEST(format_test, arg_errors) {
|
|||||||
format_error, "argument not found");
|
format_error, "argument not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(format_test, display_width_precision) {
|
||||||
|
EXPECT_EQ(fmt::format("{:.5}", "🐱🐱🐱"), "🐱🐱");
|
||||||
|
}
|
||||||
|
|
||||||
template <int N> struct test_format {
|
template <int N> struct test_format {
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static auto format(fmt::string_view fmt, const T&... args) -> std::string {
|
static auto format(fmt::string_view fmt, const T&... args) -> std::string {
|
||||||
@ -877,6 +931,7 @@ TEST(format_test, width) {
|
|||||||
" 0xcafe");
|
" 0xcafe");
|
||||||
EXPECT_EQ(fmt::format("{:11}", 'x'), "x ");
|
EXPECT_EQ(fmt::format("{:11}", 'x'), "x ");
|
||||||
EXPECT_EQ(fmt::format("{:12}", "str"), "str ");
|
EXPECT_EQ(fmt::format("{:12}", "str"), "str ");
|
||||||
|
EXPECT_EQ(fmt::format("{:*^5}", "🤡"), "*🤡**");
|
||||||
EXPECT_EQ(fmt::format("{:*^6}", "🤡"), "**🤡**");
|
EXPECT_EQ(fmt::format("{:*^6}", "🤡"), "**🤡**");
|
||||||
EXPECT_EQ(fmt::format("{:*^8}", "你好"), "**你好**");
|
EXPECT_EQ(fmt::format("{:*^8}", "你好"), "**你好**");
|
||||||
EXPECT_EQ(fmt::format("{:#6}", 42.0), " 42.");
|
EXPECT_EQ(fmt::format("{:#6}", 42.0), " 42.");
|
||||||
@ -884,6 +939,31 @@ TEST(format_test, width) {
|
|||||||
EXPECT_EQ(fmt::format("{:>06.0f}", 0.00884311), " 0");
|
EXPECT_EQ(fmt::format("{:>06.0f}", 0.00884311), " 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(format_test, debug_presentation) {
|
||||||
|
EXPECT_EQ(fmt::format("{:?}", ""), R"("")");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.0?}", "\n"), R"(*****)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.1?}", "\n"), R"("****)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.2?}", "\n"), R"("\***)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.3?}", "\n"), R"("\n**)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.4?}", "\n"), R"("\n"*)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.1?}", "Σ"), R"("****)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.2?}", "Σ"), R"("Σ***)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.3?}", "Σ"), R"("Σ"**)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.1?}", "笑"), R"("****)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.2?}", "笑"), R"("****)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.3?}", "笑"), R"("笑**)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*<5.4?}", "笑"), R"("笑"*)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:*<8?}", "туда"), R"("туда"**)");
|
||||||
|
EXPECT_EQ(fmt::format("{:*>8?}", "сюда"), R"(**"сюда")");
|
||||||
|
EXPECT_EQ(fmt::format("{:*^8?}", "中心"), R"(*"中心"*)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:*^14?}", "A\t👈🤯ы猫"), R"(*"A\t👈🤯ы猫"*)");
|
||||||
|
}
|
||||||
|
|
||||||
auto bad_dynamic_spec_msg = FMT_BUILTIN_TYPES
|
auto bad_dynamic_spec_msg = FMT_BUILTIN_TYPES
|
||||||
? "width/precision is out of range"
|
? "width/precision is out of range"
|
||||||
: "width/precision is not integer";
|
: "width/precision is not integer";
|
||||||
@ -1085,9 +1165,6 @@ TEST(format_test, precision) {
|
|||||||
EXPECT_THROW_MSG(
|
EXPECT_THROW_MSG(
|
||||||
(void)fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)),
|
(void)fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)),
|
||||||
format_error, "invalid format specifier");
|
format_error, "invalid format specifier");
|
||||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{}e}"), 42.0,
|
|
||||||
fmt::detail::max_value<int>()),
|
|
||||||
format_error, "number is too big");
|
|
||||||
EXPECT_THROW_MSG(
|
EXPECT_THROW_MSG(
|
||||||
(void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
|
(void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
|
||||||
format_error, "number is too big");
|
format_error, "number is too big");
|
||||||
@ -1099,9 +1176,34 @@ TEST(format_test, precision) {
|
|||||||
EXPECT_EQ(fmt::format("{0:.6}", "123456\xad"), "123456");
|
EXPECT_EQ(fmt::format("{0:.6}", "123456\xad"), "123456");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(format_test, large_precision) {
|
||||||
|
// Iterator used to abort the actual output.
|
||||||
|
struct throwing_iterator {
|
||||||
|
auto operator=(char) -> throwing_iterator& {
|
||||||
|
throw std::runtime_error("aborted");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
auto operator*() -> throwing_iterator& { return *this; }
|
||||||
|
auto operator++() -> throwing_iterator& { return *this; }
|
||||||
|
auto operator++(int) -> throwing_iterator { return *this; }
|
||||||
|
};
|
||||||
|
auto it = throwing_iterator();
|
||||||
|
|
||||||
|
EXPECT_THROW_MSG(fmt::format_to(it, fmt::runtime("{:#.{}}"), 1.0,
|
||||||
|
fmt::detail::max_value<int>()),
|
||||||
|
std::runtime_error, "aborted");
|
||||||
|
|
||||||
|
EXPECT_THROW_MSG(fmt::format_to(it, fmt::runtime("{:#.{}e}"), 1.0,
|
||||||
|
fmt::detail::max_value<int>() - 1),
|
||||||
|
std::runtime_error, "aborted");
|
||||||
|
|
||||||
|
EXPECT_THROW_MSG((void)fmt::format(fmt::runtime("{:.{}e}"), 42.0,
|
||||||
|
fmt::detail::max_value<int>()),
|
||||||
|
format_error, "number is too big");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(format_test, utf8_precision) {
|
TEST(format_test, utf8_precision) {
|
||||||
auto result = fmt::format("{:.4}", "caf\u00e9s"); // cafés
|
auto result = fmt::format("{:.4}", "caf\u00e9s"); // cafés
|
||||||
EXPECT_EQ(fmt::detail::compute_width(result), 4);
|
|
||||||
EXPECT_EQ(result, "caf\u00e9");
|
EXPECT_EQ(result, "caf\u00e9");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1755,19 +1857,15 @@ TEST(format_test, format_examples) {
|
|||||||
fmt::format_to(std::back_inserter(out), "The answer is {}.", 42);
|
fmt::format_to(std::back_inserter(out), "The answer is {}.", 42);
|
||||||
EXPECT_EQ("The answer is 42.", to_string(out));
|
EXPECT_EQ("The answer is 42.", to_string(out));
|
||||||
|
|
||||||
const char* filename = "nonexistent";
|
EXPECT_THROW(
|
||||||
FILE* ftest = safe_fopen(filename, "r");
|
|
||||||
if (ftest) fclose(ftest);
|
|
||||||
int error_code = errno;
|
|
||||||
EXPECT_TRUE(ftest == nullptr);
|
|
||||||
EXPECT_SYSTEM_ERROR(
|
|
||||||
{
|
{
|
||||||
FILE* f = safe_fopen(filename, "r");
|
const char* filename = "madeup";
|
||||||
if (!f)
|
FILE* file = fopen(filename, "r");
|
||||||
throw fmt::system_error(errno, "Cannot open file '{}'", filename);
|
if (!file)
|
||||||
fclose(f);
|
throw fmt::system_error(errno, "cannot open file '{}'", filename);
|
||||||
|
fclose(file);
|
||||||
},
|
},
|
||||||
error_code, "Cannot open file 'nonexistent'");
|
std::system_error);
|
||||||
|
|
||||||
EXPECT_EQ("First, thou shalt count to three",
|
EXPECT_EQ("First, thou shalt count to three",
|
||||||
fmt::format("First, thou shalt count to {0}", "three"));
|
fmt::format("First, thou shalt count to {0}", "three"));
|
||||||
@ -1938,11 +2036,6 @@ TEST(format_test, unpacked_args) {
|
|||||||
6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g'));
|
6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g'));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char with_null[3] = {'{', '}', '\0'};
|
|
||||||
constexpr char no_null[2] = {'{', '}'};
|
|
||||||
static constexpr const char static_with_null[3] = {'{', '}', '\0'};
|
|
||||||
static constexpr const char static_no_null[2] = {'{', '}'};
|
|
||||||
|
|
||||||
TEST(format_test, compile_time_string) {
|
TEST(format_test, compile_time_string) {
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo");
|
EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo");
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42");
|
EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42");
|
||||||
@ -1957,19 +2050,12 @@ TEST(format_test, compile_time_string) {
|
|||||||
EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2");
|
EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void)static_with_null;
|
static constexpr char format_str[3] = {'{', '}', '\0'};
|
||||||
(void)static_no_null;
|
(void)format_str;
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(static_with_null), 42), "42");
|
EXPECT_EQ(fmt::format(FMT_STRING(format_str), 42), "42");
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(static_no_null), 42), "42");
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void)with_null;
|
|
||||||
(void)no_null;
|
|
||||||
#if FMT_CPLUSPLUS >= 201703L
|
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(with_null), 42), "42");
|
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(no_null), 42), "42");
|
|
||||||
#endif
|
|
||||||
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42");
|
EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42");
|
||||||
#endif
|
#endif
|
||||||
@ -2499,6 +2585,20 @@ TEST(format_test, writer) {
|
|||||||
EXPECT_EQ(s.str(), "foo");
|
EXPECT_EQ(s.str(), "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_FCNTL && !defined(_WIN32)
|
||||||
|
TEST(format_test, invalid_glibc_buffer) {
|
||||||
|
auto pipe = fmt::pipe();
|
||||||
|
auto write_end = pipe.write_end.fdopen("w");
|
||||||
|
auto file = write_end.get();
|
||||||
|
|
||||||
|
// This results in _IO_write_ptr < _IO_write_end.
|
||||||
|
fprintf(file, "111\n");
|
||||||
|
setvbuf(file, nullptr, _IOLBF, 0);
|
||||||
|
|
||||||
|
fmt::print(file, "------\n");
|
||||||
|
}
|
||||||
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
#if FMT_USE_BITINT
|
#if FMT_USE_BITINT
|
||||||
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
|
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
|
||||||
|
|
||||||
|
|||||||
@ -12,10 +12,10 @@ void invoke_inner(fmt::string_view format_str, Rep rep) {
|
|||||||
auto value = std::chrono::duration<Rep, Period>(rep);
|
auto value = std::chrono::duration<Rep, Period>(rep);
|
||||||
try {
|
try {
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||||
std::string message = fmt::format(format_str, value);
|
std::string message = fmt::format(fmt::runtime(format_str), value);
|
||||||
#else
|
#else
|
||||||
auto buf = fmt::memory_buffer();
|
auto buf = fmt::memory_buffer();
|
||||||
fmt::format_to(std::back_inserter(buf), format_str, value);
|
fmt::format_to(std::back_inserter(buf), fmt::runtime(format_str), value);
|
||||||
#endif
|
#endif
|
||||||
} catch (std::exception&) {
|
} catch (std::exception&) {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
||||||
|
|
||||||
// The size of the largest possible type in use.
|
// The size of the largest possible type in use.
|
||||||
// To let the the fuzzer mutation be efficient at cross pollinating between
|
// To let the fuzzer mutation be efficient at cross pollinating between
|
||||||
// different types, use a fixed size format. The same bit pattern, interpreted
|
// different types, use a fixed size format. The same bit pattern, interpreted
|
||||||
// as another type, is likely interesting.
|
// as another type, is likely interesting.
|
||||||
constexpr auto fixed_size = 16;
|
constexpr auto fixed_size = 16;
|
||||||
|
|||||||
@ -15,6 +15,10 @@
|
|||||||
#include "fmt/os.h"
|
#include "fmt/os.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# include <crtdbg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
||||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||||
|
|||||||
@ -37,7 +37,7 @@ template <typename T> class mock_allocator {
|
|||||||
MOCK_METHOD(void, deallocate, (T*, size_t));
|
MOCK_METHOD(void, deallocate, (T*, size_t));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Allocator> class allocator_ref {
|
template <typename Allocator, bool PropagateOnMove = true> class allocator_ref {
|
||||||
private:
|
private:
|
||||||
Allocator* alloc_;
|
Allocator* alloc_;
|
||||||
|
|
||||||
@ -48,6 +48,8 @@ template <typename Allocator> class allocator_ref {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = typename Allocator::value_type;
|
using value_type = typename Allocator::value_type;
|
||||||
|
using propagate_on_container_move_assignment =
|
||||||
|
fmt::bool_constant<PropagateOnMove>;
|
||||||
|
|
||||||
explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {}
|
explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {}
|
||||||
|
|
||||||
@ -66,12 +68,21 @@ template <typename Allocator> class allocator_ref {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Allocator* get() const { return alloc_; }
|
auto get() const -> Allocator* { return alloc_; }
|
||||||
|
|
||||||
value_type* allocate(size_t n) {
|
auto allocate(size_t n) -> value_type* {
|
||||||
return std::allocator_traits<Allocator>::allocate(*alloc_, n);
|
return std::allocator_traits<Allocator>::allocate(*alloc_, n);
|
||||||
}
|
}
|
||||||
void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); }
|
void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); }
|
||||||
|
|
||||||
|
friend auto operator==(allocator_ref a, allocator_ref b) noexcept -> bool {
|
||||||
|
if (a.alloc_ == b.alloc_) return true;
|
||||||
|
return a.alloc_ && b.alloc_ && *a.alloc_ == *b.alloc_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend auto operator!=(allocator_ref a, allocator_ref b) noexcept -> bool {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FMT_MOCK_ALLOCATOR_H_
|
#endif // FMT_MOCK_ALLOCATOR_H_
|
||||||
|
|||||||
@ -19,10 +19,21 @@ using fmt::buffered_file;
|
|||||||
using testing::HasSubstr;
|
using testing::HasSubstr;
|
||||||
using wstring_view = fmt::basic_string_view<wchar_t>;
|
using wstring_view = fmt::basic_string_view<wchar_t>;
|
||||||
|
|
||||||
static std::string uniq_file_name(unsigned line_number) {
|
static auto uniq_file_name(unsigned line_number) -> std::string {
|
||||||
return "test-file" + std::to_string(line_number);
|
return "test-file" + std::to_string(line_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto safe_fopen(const char* filename, const char* mode) -> FILE* {
|
||||||
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
// Fix MSVC warning about "unsafe" fopen.
|
||||||
|
FILE* f = nullptr;
|
||||||
|
errno = fopen_s(&f, filename, mode);
|
||||||
|
return f;
|
||||||
|
#else
|
||||||
|
return std::fopen(filename, mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@ -237,8 +248,7 @@ TEST(buffered_file_test, descriptor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ostream_test, move) {
|
TEST(ostream_test, move) {
|
||||||
auto test_file = uniq_file_name(__LINE__);
|
fmt::ostream out = fmt::output_file(uniq_file_name(__LINE__));
|
||||||
fmt::ostream out = fmt::output_file(test_file);
|
|
||||||
fmt::ostream moved(std::move(out));
|
fmt::ostream moved(std::move(out));
|
||||||
moved.print("hello");
|
moved.print("hello");
|
||||||
}
|
}
|
||||||
@ -430,8 +440,7 @@ TEST(file_test, read) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(file_test, read_error) {
|
TEST(file_test, read_error) {
|
||||||
auto test_file = uniq_file_name(__LINE__);
|
file f(uniq_file_name(__LINE__), file::WRONLY | file::CREATE);
|
||||||
file f(test_file, file::WRONLY | file::CREATE);
|
|
||||||
char buf;
|
char buf;
|
||||||
// We intentionally read from a file opened in the write-only mode to
|
// We intentionally read from a file opened in the write-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
@ -440,15 +449,13 @@ TEST(file_test, read_error) {
|
|||||||
|
|
||||||
TEST(file_test, write) {
|
TEST(file_test, write) {
|
||||||
auto pipe = fmt::pipe();
|
auto pipe = fmt::pipe();
|
||||||
auto test_file = uniq_file_name(__LINE__);
|
write(pipe.write_end, "test");
|
||||||
write(pipe.write_end, test_file);
|
|
||||||
pipe.write_end.close();
|
pipe.write_end.close();
|
||||||
EXPECT_READ(pipe.read_end, test_file);
|
EXPECT_READ(pipe.read_end, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(file_test, write_error) {
|
TEST(file_test, write_error) {
|
||||||
auto test_file = uniq_file_name(__LINE__);
|
file f(uniq_file_name(__LINE__), file::RDONLY | file::CREATE);
|
||||||
file f(test_file, file::RDONLY | file::CREATE);
|
|
||||||
// We intentionally write to a file opened in the read-only mode to
|
// We intentionally write to a file opened in the read-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
||||||
|
|||||||
@ -265,7 +265,7 @@ template <> struct formatter<abstract> : ostream_formatter {};
|
|||||||
} // namespace fmt
|
} // namespace fmt
|
||||||
|
|
||||||
void format_abstract_compiles(const abstract& a) {
|
void format_abstract_compiles(const abstract& a) {
|
||||||
fmt::format(FMT_COMPILE("{}"), a);
|
(void)fmt::format(FMT_COMPILE("{}"), a);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ostream_test, is_formattable) {
|
TEST(ostream_test, is_formattable) {
|
||||||
|
|||||||
@ -6,16 +6,12 @@
|
|||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#include "fmt/printf.h"
|
#include "fmt/printf.h"
|
||||||
// include <format> if possible for https://github.com/fmtlib/fmt/pull/4042
|
|
||||||
#if FMT_HAS_INCLUDE(<format>) && FMT_CPLUSPLUS > 201703L
|
|
||||||
# include <format>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "fmt/xchar.h"
|
#include "fmt/xchar.h" // DEPRECATED!
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@ -26,27 +22,21 @@ using fmt::detail::max_value;
|
|||||||
const unsigned big_num = INT_MAX + 1u;
|
const unsigned big_num = INT_MAX + 1u;
|
||||||
|
|
||||||
// Makes format string argument positional.
|
// Makes format string argument positional.
|
||||||
static std::string make_positional(fmt::string_view format) {
|
static auto make_positional(fmt::string_view format) -> std::string {
|
||||||
std::string s(format.data(), format.size());
|
std::string s(format.data(), format.size());
|
||||||
s.replace(s.find('%'), 1, "%1$");
|
s.replace(s.find('%'), 1, "%1$");
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::wstring make_positional(fmt::basic_string_view<wchar_t> format) {
|
|
||||||
std::wstring s(format.data(), format.size());
|
|
||||||
s.replace(s.find(L'%'), 1, L"%1$");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A wrapper around fmt::sprintf to workaround bogus warnings about invalid
|
// A wrapper around fmt::sprintf to workaround bogus warnings about invalid
|
||||||
// format strings in MSVC.
|
// format strings in MSVC.
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
std::string test_sprintf(fmt::string_view format, const Args&... args) {
|
auto test_sprintf(fmt::string_view format, const Args&... args) -> std::string {
|
||||||
return fmt::sprintf(format, args...);
|
return fmt::sprintf(format, args...);
|
||||||
}
|
}
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
std::wstring test_sprintf(fmt::basic_string_view<wchar_t> format,
|
auto test_sprintf(fmt::basic_string_view<wchar_t> format, const Args&... args)
|
||||||
const Args&... args) {
|
-> std::wstring {
|
||||||
return fmt::sprintf(format, args...);
|
return fmt::sprintf(format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,10 +45,7 @@ std::wstring test_sprintf(fmt::basic_string_view<wchar_t> format,
|
|||||||
<< "format: " << format; \
|
<< "format: " << format; \
|
||||||
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
|
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
|
||||||
|
|
||||||
TEST(printf_test, no_args) {
|
TEST(printf_test, no_args) { EXPECT_EQ("test", test_sprintf("test")); }
|
||||||
EXPECT_EQ("test", test_sprintf("test"));
|
|
||||||
EXPECT_EQ(L"test", fmt::sprintf(L"test"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(printf_test, escape) {
|
TEST(printf_test, escape) {
|
||||||
EXPECT_EQ("%", test_sprintf("%%"));
|
EXPECT_EQ("%", test_sprintf("%%"));
|
||||||
@ -66,11 +53,6 @@ TEST(printf_test, escape) {
|
|||||||
EXPECT_EQ("% after", test_sprintf("%% after"));
|
EXPECT_EQ("% after", test_sprintf("%% after"));
|
||||||
EXPECT_EQ("before % after", test_sprintf("before %% after"));
|
EXPECT_EQ("before % after", test_sprintf("before %% after"));
|
||||||
EXPECT_EQ("%s", test_sprintf("%%s"));
|
EXPECT_EQ("%s", test_sprintf("%%s"));
|
||||||
EXPECT_EQ(L"%", fmt::sprintf(L"%%"));
|
|
||||||
EXPECT_EQ(L"before %", fmt::sprintf(L"before %%"));
|
|
||||||
EXPECT_EQ(L"% after", fmt::sprintf(L"%% after"));
|
|
||||||
EXPECT_EQ(L"before % after", fmt::sprintf(L"before %% after"));
|
|
||||||
EXPECT_EQ(L"%s", fmt::sprintf(L"%%s"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(printf_test, positional_args) {
|
TEST(printf_test, positional_args) {
|
||||||
@ -467,9 +449,6 @@ TEST(printf_test, char) {
|
|||||||
EXPECT_PRINTF("x", "%c", 'x');
|
EXPECT_PRINTF("x", "%c", 'x');
|
||||||
int max = max_value<int>();
|
int max = max_value<int>();
|
||||||
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
||||||
// EXPECT_PRINTF("x", "%lc", L'x');
|
|
||||||
EXPECT_PRINTF(L"x", L"%c", L'x');
|
|
||||||
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(printf_test, string) {
|
TEST(printf_test, string) {
|
||||||
@ -477,10 +456,6 @@ TEST(printf_test, string) {
|
|||||||
const char* null_str = nullptr;
|
const char* null_str = nullptr;
|
||||||
EXPECT_PRINTF("(null)", "%s", null_str);
|
EXPECT_PRINTF("(null)", "%s", null_str);
|
||||||
EXPECT_PRINTF(" (null)", "%10s", null_str);
|
EXPECT_PRINTF(" (null)", "%10s", null_str);
|
||||||
EXPECT_PRINTF(L"abc", L"%s", L"abc");
|
|
||||||
const wchar_t* null_wstr = nullptr;
|
|
||||||
EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
|
|
||||||
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(printf_test, pointer) {
|
TEST(printf_test, pointer) {
|
||||||
@ -494,16 +469,6 @@ TEST(printf_test, pointer) {
|
|||||||
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
|
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
|
||||||
const char* null_str = nullptr;
|
const char* null_str = nullptr;
|
||||||
EXPECT_PRINTF("(nil)", "%p", null_str);
|
EXPECT_PRINTF("(nil)", "%p", null_str);
|
||||||
|
|
||||||
p = &n;
|
|
||||||
EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p);
|
|
||||||
p = nullptr;
|
|
||||||
EXPECT_PRINTF(L"(nil)", L"%p", p);
|
|
||||||
EXPECT_PRINTF(L" (nil)", L"%10p", p);
|
|
||||||
const wchar_t* w = L"test";
|
|
||||||
EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w);
|
|
||||||
const wchar_t* null_wstr = nullptr;
|
|
||||||
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum test_enum { answer = 42 };
|
enum test_enum { answer = 42 };
|
||||||
@ -531,10 +496,6 @@ TEST(printf_test, printf_error) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(printf_test, wide_string) {
|
|
||||||
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(printf_test, vprintf) {
|
TEST(printf_test, vprintf) {
|
||||||
int n = 42;
|
int n = 42;
|
||||||
auto store = fmt::make_format_args<fmt::printf_context>(n);
|
auto store = fmt::make_format_args<fmt::printf_context>(n);
|
||||||
|
|||||||
53
test/scan.h
53
test/scan.h
@ -229,6 +229,8 @@ enum class scan_type {
|
|||||||
uint_type,
|
uint_type,
|
||||||
long_long_type,
|
long_long_type,
|
||||||
ulong_long_type,
|
ulong_long_type,
|
||||||
|
double_type,
|
||||||
|
float_type,
|
||||||
string_type,
|
string_type,
|
||||||
string_view_type,
|
string_view_type,
|
||||||
custom_type
|
custom_type
|
||||||
@ -251,6 +253,8 @@ template <typename Context> class basic_scan_arg {
|
|||||||
unsigned* uint_value_;
|
unsigned* uint_value_;
|
||||||
long long* long_long_value_;
|
long long* long_long_value_;
|
||||||
unsigned long long* ulong_long_value_;
|
unsigned long long* ulong_long_value_;
|
||||||
|
double* double_value_;
|
||||||
|
float* float_value_;
|
||||||
std::string* string_;
|
std::string* string_;
|
||||||
string_view* string_view_;
|
string_view* string_view_;
|
||||||
detail::custom_scan_arg<Context> custom_;
|
detail::custom_scan_arg<Context> custom_;
|
||||||
@ -276,6 +280,10 @@ template <typename Context> class basic_scan_arg {
|
|||||||
: type_(scan_type::long_long_type), long_long_value_(&value) {}
|
: type_(scan_type::long_long_type), long_long_value_(&value) {}
|
||||||
FMT_CONSTEXPR basic_scan_arg(unsigned long long& value)
|
FMT_CONSTEXPR basic_scan_arg(unsigned long long& value)
|
||||||
: type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
|
: type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
|
||||||
|
FMT_CONSTEXPR basic_scan_arg(double& value)
|
||||||
|
: type_(scan_type::double_type), double_value_(&value) {}
|
||||||
|
FMT_CONSTEXPR basic_scan_arg(float& value)
|
||||||
|
: type_(scan_type::float_type), float_value_(&value) {}
|
||||||
FMT_CONSTEXPR basic_scan_arg(std::string& value)
|
FMT_CONSTEXPR basic_scan_arg(std::string& value)
|
||||||
: type_(scan_type::string_type), string_(&value) {}
|
: type_(scan_type::string_type), string_(&value) {}
|
||||||
FMT_CONSTEXPR basic_scan_arg(string_view& value)
|
FMT_CONSTEXPR basic_scan_arg(string_view& value)
|
||||||
@ -305,6 +313,10 @@ template <typename Context> class basic_scan_arg {
|
|||||||
return vis(*long_long_value_);
|
return vis(*long_long_value_);
|
||||||
case scan_type::ulong_long_type:
|
case scan_type::ulong_long_type:
|
||||||
return vis(*ulong_long_value_);
|
return vis(*ulong_long_value_);
|
||||||
|
case scan_type::double_type:
|
||||||
|
return vis(*double_value_);
|
||||||
|
case scan_type::float_type:
|
||||||
|
return vis(*float_value_);
|
||||||
case scan_type::string_type:
|
case scan_type::string_type:
|
||||||
return vis(*string_);
|
return vis(*string_);
|
||||||
case scan_type::string_view_type:
|
case scan_type::string_view_type:
|
||||||
@ -457,6 +469,47 @@ auto read(scan_iterator it, T& value, const format_specs& specs = {})
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto read(scan_iterator it, double& value, const format_specs& = {})
|
||||||
|
-> scan_iterator {
|
||||||
|
if (it == scan_sentinel()) return it;
|
||||||
|
|
||||||
|
// Simple floating-point parsing
|
||||||
|
bool negative = *it == '-';
|
||||||
|
if (negative) {
|
||||||
|
++it;
|
||||||
|
if (it == scan_sentinel()) report_error("invalid input");
|
||||||
|
}
|
||||||
|
|
||||||
|
double result = 0.0;
|
||||||
|
// Parse integer part
|
||||||
|
while (it != scan_sentinel() && *it >= '0' && *it <= '9') {
|
||||||
|
result = result * 10.0 + (*it - '0');
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decimal part if present
|
||||||
|
if (it != scan_sentinel() && *it == '.') {
|
||||||
|
++it;
|
||||||
|
double fraction = 0.1;
|
||||||
|
while (it != scan_sentinel() && *it >= '0' && *it <= '9') {
|
||||||
|
result += (*it - '0') * fraction;
|
||||||
|
fraction *= 0.1;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = negative ? -result : result;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto read(scan_iterator it, float& value, const format_specs& specs = {})
|
||||||
|
-> scan_iterator {
|
||||||
|
double temp;
|
||||||
|
it = read(it, temp, specs);
|
||||||
|
value = static_cast<float>(temp);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
auto read(scan_iterator it, std::string& value, const format_specs& = {})
|
auto read(scan_iterator it, std::string& value, const format_specs& = {})
|
||||||
-> scan_iterator {
|
-> scan_iterator {
|
||||||
while (it != scan_sentinel() && *it != ' ') value.push_back(*it++);
|
while (it != scan_sentinel() && *it != ' ') value.push_back(*it++);
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "fmt/os.h" // fmt::system_category
|
#include "fmt/os.h" // fmt::system_category
|
||||||
|
#include "fmt/ranges.h"
|
||||||
#include "gtest-extra.h" // StartsWith
|
#include "gtest-extra.h" // StartsWith
|
||||||
|
|
||||||
#ifdef __cpp_lib_filesystem
|
#ifdef __cpp_lib_filesystem
|
||||||
@ -38,13 +39,12 @@ TEST(std_test, path) {
|
|||||||
EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
||||||
L"\x0447\x044B\x043D\x0430")),
|
L"\x0447\x044B\x043D\x0430")),
|
||||||
"Шчучыншчына");
|
"Шчучыншчына");
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "<EFBFBD>");
|
EXPECT_EQ(fmt::format("{}", path(L"\xD800")), "\xED\xA0\x80");
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xd800 TAIL")), "HEAD <20> TAIL");
|
EXPECT_EQ(fmt::format("{}", path(L"[\xD800]")), "[\xED\xA0\x80]");
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xDE00 TAIL")),
|
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xDE00]")), "[\xF0\x9F\x98\x80]");
|
||||||
"HEAD \xF0\x9F\x98\x80 TAIL");
|
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xD83D\xDE00]")),
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xD83D\xDE00 TAIL")),
|
"[\xED\xA0\xBD\xF0\x9F\x98\x80]");
|
||||||
"HEAD <20>\xF0\x9F\x98\x80 TAIL");
|
EXPECT_EQ(fmt::format("{:?}", path(L"\xD800")), "\"\\ud800\"");
|
||||||
EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\"");
|
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +145,7 @@ TEST(std_test, optional) {
|
|||||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||||
EXPECT_FALSE((fmt::is_formattable<std::optional<unformattable>>::value));
|
EXPECT_FALSE((fmt::is_formattable<std::optional<unformattable>>::value));
|
||||||
EXPECT_TRUE((fmt::is_formattable<std::optional<int>>::value));
|
EXPECT_TRUE((fmt::is_formattable<std::optional<int>>::value));
|
||||||
|
EXPECT_TRUE((fmt::is_formattable<std::optional<const int>>::value));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +197,33 @@ class my_class {
|
|||||||
return fmt::to_string(elm.av);
|
return fmt::to_string(elm.av);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class my_class_int {
|
||||||
|
public:
|
||||||
|
int av;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend auto format_as(const my_class_int& elm) -> int { return elm.av; }
|
||||||
|
};
|
||||||
} // namespace my_nso
|
} // namespace my_nso
|
||||||
|
|
||||||
|
TEST(std_test, expected_format_as) {
|
||||||
|
#ifdef __cpp_lib_expected
|
||||||
|
EXPECT_EQ(
|
||||||
|
fmt::format(
|
||||||
|
"{}", std::expected<my_nso::my_number, int>{my_nso::my_number::one}),
|
||||||
|
"expected(\"first\")");
|
||||||
|
EXPECT_EQ(
|
||||||
|
fmt::format("{}",
|
||||||
|
std::expected<my_nso::my_class, int>{my_nso::my_class{7}}),
|
||||||
|
"expected(\"7\")");
|
||||||
|
EXPECT_EQ(fmt::format("{}",
|
||||||
|
std::expected<my_nso::my_class_int, int>{
|
||||||
|
my_nso::my_class_int{8}}),
|
||||||
|
"expected(8)");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
TEST(std_test, optional_format_as) {
|
TEST(std_test, optional_format_as) {
|
||||||
#ifdef __cpp_lib_optional
|
#ifdef __cpp_lib_optional
|
||||||
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_number>{}), "none");
|
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_number>{}), "none");
|
||||||
@ -205,6 +232,8 @@ TEST(std_test, optional_format_as) {
|
|||||||
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_class>{}), "none");
|
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_class>{}), "none");
|
||||||
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
|
||||||
"optional(\"7\")");
|
"optional(\"7\")");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class_int{8}}),
|
||||||
|
"optional(8)");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +303,24 @@ TEST(std_test, variant) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(std_test, variant_format_as) {
|
||||||
|
#ifdef __cpp_lib_variant
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::variant<my_nso::my_number>{}),
|
||||||
|
"variant(\"first\")");
|
||||||
|
EXPECT_EQ(fmt::format(
|
||||||
|
"{}", std::variant<my_nso::my_number>{my_nso::my_number::one}),
|
||||||
|
"variant(\"first\")");
|
||||||
|
EXPECT_EQ(
|
||||||
|
fmt::format("{}", std::variant<my_nso::my_class>{my_nso::my_class{7}}),
|
||||||
|
"variant(\"7\")");
|
||||||
|
EXPECT_EQ(
|
||||||
|
fmt::format("{}",
|
||||||
|
std::variant<my_nso::my_class_int>{my_nso::my_class_int{8}}),
|
||||||
|
"variant(8)");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
TEST(std_test, error_code) {
|
TEST(std_test, error_code) {
|
||||||
auto& generic = std::generic_category();
|
auto& generic = std::generic_category();
|
||||||
EXPECT_EQ(fmt::format("{}", std::error_code(42, generic)), "generic:42");
|
EXPECT_EQ(fmt::format("{}", std::error_code(42, generic)), "generic:42");
|
||||||
@ -288,6 +335,10 @@ TEST(std_test, error_code) {
|
|||||||
EXPECT_EQ(fmt::format("{:s}", ec), ec.message());
|
EXPECT_EQ(fmt::format("{:s}", ec), ec.message());
|
||||||
EXPECT_EQ(fmt::format("{:?}", std::error_code(42, generic)),
|
EXPECT_EQ(fmt::format("{:?}", std::error_code(42, generic)),
|
||||||
"\"generic:42\"");
|
"\"generic:42\"");
|
||||||
|
EXPECT_EQ(fmt::format("{}",
|
||||||
|
std::map<std::error_code, int>{
|
||||||
|
{std::error_code(42, generic), 0}}),
|
||||||
|
"{\"generic:42\": 0}");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Catch> void exception_test() {
|
template <typename Catch> void exception_test() {
|
||||||
@ -413,5 +464,31 @@ TEST(std_test, format_shared_ptr) {
|
|||||||
|
|
||||||
TEST(std_test, format_reference_wrapper) {
|
TEST(std_test, format_reference_wrapper) {
|
||||||
int num = 35;
|
int num = 35;
|
||||||
EXPECT_EQ("35", fmt::to_string(std::cref(num)));
|
EXPECT_EQ(fmt::to_string(std::cref(num)), "35");
|
||||||
|
EXPECT_EQ(fmt::to_string(std::ref(num)), "35");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::cref(num)), "35");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::ref(num)), "35");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/fmtlib/fmt/issues/4424.
|
||||||
|
struct type_with_format_as {};
|
||||||
|
int format_as(type_with_format_as) { return 20; }
|
||||||
|
|
||||||
|
TEST(std_test, format_reference_wrapper_with_format_as) {
|
||||||
|
type_with_format_as t;
|
||||||
|
EXPECT_EQ(fmt::to_string(std::cref(t)), "20");
|
||||||
|
EXPECT_EQ(fmt::to_string(std::ref(t)), "20");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::cref(t)), "20");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::ref(t)), "20");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct type_with_format_as_string {};
|
||||||
|
std::string format_as(type_with_format_as_string) { return "foo"; }
|
||||||
|
|
||||||
|
TEST(std_test, format_reference_wrapper_with_format_as_string) {
|
||||||
|
type_with_format_as_string t;
|
||||||
|
EXPECT_EQ(fmt::to_string(std::cref(t)), "foo");
|
||||||
|
EXPECT_EQ(fmt::to_string(std::ref(t)), "foo");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::cref(t)), "foo");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::ref(t)), "foo");
|
||||||
}
|
}
|
||||||
|
|||||||
11
test/util.h
11
test/util.h
@ -31,17 +31,6 @@ extern const char* const file_content;
|
|||||||
// Opens a buffered file for reading.
|
// Opens a buffered file for reading.
|
||||||
auto open_buffered_file(FILE** fp = nullptr) -> fmt::buffered_file;
|
auto open_buffered_file(FILE** fp = nullptr) -> fmt::buffered_file;
|
||||||
|
|
||||||
inline auto safe_fopen(const char* filename, const char* mode) -> FILE* {
|
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
|
||||||
// Fix MSVC warning about "unsafe" fopen.
|
|
||||||
FILE* f = nullptr;
|
|
||||||
errno = fopen_s(&f, filename, mode);
|
|
||||||
return f;
|
|
||||||
#else
|
|
||||||
return std::fopen(filename, mode);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char> class basic_test_string {
|
template <typename Char> class basic_test_string {
|
||||||
private:
|
private:
|
||||||
std::basic_string<Char> value_;
|
std::basic_string<Char> value_;
|
||||||
|
|||||||
@ -74,6 +74,7 @@ TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
|
|||||||
TEST(xchar_test, format) {
|
TEST(xchar_test, format) {
|
||||||
EXPECT_EQ(fmt::format(L"{}", 42), L"42");
|
EXPECT_EQ(fmt::format(L"{}", 42), L"42");
|
||||||
EXPECT_EQ(fmt::format(L"{}", 4.2), L"4.2");
|
EXPECT_EQ(fmt::format(L"{}", 4.2), L"4.2");
|
||||||
|
EXPECT_EQ(fmt::format(L"{}", 1e100), L"1e+100");
|
||||||
EXPECT_EQ(fmt::format(L"{}", L"abc"), L"abc");
|
EXPECT_EQ(fmt::format(L"{}", L"abc"), L"abc");
|
||||||
EXPECT_EQ(fmt::format(L"{}", L'z'), L"z");
|
EXPECT_EQ(fmt::format(L"{}", L'z'), L"z");
|
||||||
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
|
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
|
||||||
@ -171,6 +172,13 @@ TEST(xchar_test, join) {
|
|||||||
EXPECT_EQ(fmt::format(L"({})", fmt::join(t, L", ")), L"(a, 1, 2)");
|
EXPECT_EQ(fmt::format(L"({})", fmt::join(t, L", ")), L"(a, 1, 2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_byte
|
||||||
|
TEST(xchar_test, join_bytes) {
|
||||||
|
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)};
|
||||||
|
EXPECT_EQ(fmt::format(L"{}", fmt::join(v, L", ")), L"1, 2, 3");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
enum streamable_enum {};
|
enum streamable_enum {};
|
||||||
|
|
||||||
std::wostream& operator<<(std::wostream& os, streamable_enum) {
|
std::wostream& operator<<(std::wostream& os, streamable_enum) {
|
||||||
@ -371,7 +379,7 @@ TEST(locale_test, int_formatter) {
|
|||||||
f.parse(parse_ctx);
|
f.parse(parse_ctx);
|
||||||
auto buf = fmt::memory_buffer();
|
auto buf = fmt::memory_buffer();
|
||||||
fmt::basic_format_context<fmt::appender, char> format_ctx(
|
fmt::basic_format_context<fmt::appender, char> format_ctx(
|
||||||
fmt::appender(buf), {}, fmt::detail::locale_ref(loc));
|
fmt::appender(buf), {}, fmt::locale_ref(loc));
|
||||||
f.format(12345, format_ctx);
|
f.format(12345, format_ctx);
|
||||||
EXPECT_EQ(fmt::to_string(buf), "12,345");
|
EXPECT_EQ(fmt::to_string(buf), "12,345");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user