Merge branch 'main' into libcxx-fix

This commit is contained in:
mccakit 2026-07-21 16:58:27 +03:00 committed by GitHub
commit 6a0e898ae0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
81 changed files with 2184 additions and 1393 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @vitaut

8
.github/SECURITY.md vendored Normal file
View File

@ -0,0 +1,8 @@
# Security Policy
To report a security issue, please disclose it at [security
advisory](https://github.com/fmtlib/fmt/security/advisories/new).
This project is maintained by a team of volunteers on a
reasonable-effort basis. As such, please give us at least 90 days to
work on a fix before public disclosure.

33
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: codeql
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v3.29.5
with:
languages: c-cpp
- name: Build
run: |
cmake -B build -DFMT_DOC=OFF -DFMT_TEST=OFF
cmake --build build --parallel
- uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v3.29.5
with:
category: /language:c-cpp

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Add Ubuntu mirrors
run: |
@ -24,7 +24,7 @@ jobs:
run: |
sudo apt update
sudo apt install doxygen
pip install mkdocs-material==9.7.0 mkdocstrings==1.0.0 mike==2.1.3 typing_extensions==4.15.0
pip install --require-hashes -r support/doc-requirements.txt
cmake -E make_directory ${{runner.workspace}}/build
# Workaround https://github.com/actions/checkout/issues/13:
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
@ -37,6 +37,6 @@ jobs:
- name: Deploy
env:
KEY: "${{secrets.KEY}}"
if: env.KEY != '' && github.ref == 'refs/heads/master'
if: env.KEY != '' && github.ref == 'refs/heads/main'
working-directory: ${{runner.workspace}}/fmt/build/fmt.dev
run: git push https://$KEY@github.com/fmtlib/fmt.dev.git

View File

@ -1,11 +1,11 @@
name: CIFuzz
name: fuzz
on: [pull_request]
permissions:
contents: read
jobs:
Fuzzing:
fuzz:
runs-on: ubuntu-latest
steps:
- name: Build fuzzers

View File

@ -2,11 +2,6 @@ name: lint
on:
pull_request:
paths:
- '**.h'
- '**.cc'
- '**.cmake'
- '**/CMakeLists.txt'
permissions:
contents: read
@ -15,13 +10,19 @@ jobs:
clang-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install clang-format
run: |
wget https://apt.llvm.org/llvm.sh
sudo bash ./llvm.sh 21
sudo apt install clang-format-21
sudo install -D -m 0644 support/llvm-snapshot.gpg.key \
/etc/apt/keyrings/apt.llvm.org.asc
codename=$(lsb_release -cs)
echo "deb [signed-by=/etc/apt/keyrings/apt.llvm.org.asc]" \
"https://apt.llvm.org/$codename/" \
"llvm-toolchain-$codename-21 main" \
| sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update
sudo apt-get install -y clang-format-21
- name: Run clang-format
run: |
@ -32,10 +33,10 @@ jobs:
cmake-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install cmake-format
run: pip install cmakelang
run: pip install --require-hashes -r support/cmake-format-requirements.txt
- name: Run cmake-format
run: |

View File

@ -7,7 +7,12 @@ permissions:
jobs:
build:
runs-on: ubuntu-22.04
name: >-
${{matrix.cxx}} c++${{matrix.std}} ${{matrix.build_type}}
${{matrix.gen}} ${{matrix.fuzz && 'Fuzz' || ''}}
${{matrix.shared && 'Shared' || ''}}
${{matrix.cxxflags_extra && 'Sanitize' || ''}}
runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
strategy:
matrix:
cxx: [g++-4.9, g++-11, clang++-3.6, clang++-11]
@ -16,7 +21,6 @@ jobs:
shared: [""]
include:
- cxx: g++-4.9
- cxx: clang++-3.6
- cxx: g++-11
build_type: Debug
std: 14
@ -31,8 +35,14 @@ jobs:
- cxx: g++-13
build_type: Release
std: 23
install: sudo apt install g++-13
os: ubuntu-24.04
shared: -DBUILD_SHARED_LIBS=ON
- cxx: g++-14
build_type: Release
std: 23
os: ubuntu-24.04
gen: Ninja
- cxx: clang++-3.6
- cxx: clang++-11
build_type: Debug
std: 17
@ -48,14 +58,22 @@ jobs:
- cxx: clang++-14
build_type: Debug
std: 20
cxxflags: -fsanitize=address
cxxflags_extra: -fno-sanitize-recover=all -fno-omit-frame-pointer
- cxx: clang++-14
build_type: Debug
std: 20
cxxflags: -stdlib=libc++
install: sudo apt install libc++-14-dev libc++abi-14-dev
- cxx: clang++-20
build_type: Debug
std: 20
cxxflags: -stdlib=libc++
install: sudo apt install clang-20 libc++-20-dev libc++abi-20-dev
gen: Ninja
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set timezone
run: sudo timedatectl set-timezone 'Europe/Kyiv'
@ -126,10 +144,16 @@ jobs:
clang-3.6_3.6.2-3ubuntu2_amd64.deb
if: ${{ matrix.cxx == 'clang++-3.6' }}
- name: Add repositories for newer GCC
- name: Install LLVM-20
run: |
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
if: ${{ matrix.cxx == 'g++-13' }}
sudo install -D -m 0644 support/llvm-snapshot.gpg.key \
/etc/apt/keyrings/apt.llvm.org.asc
codename=$(lsb_release -cs)
echo "deb [signed-by=/etc/apt/keyrings/apt.llvm.org.asc]" \
"https://apt.llvm.org/$codename/" \
"llvm-toolchain-$codename-20 main" \
| sudo tee /etc/apt/sources.list.d/llvm.list
if: ${{ matrix.cxx == 'clang++-20' }}
- name: Add Ubuntu mirrors
run: |
@ -154,14 +178,18 @@ jobs:
working-directory: ${{runner.workspace}}/build
env:
CXX: ${{matrix.cxx}}
CXXFLAGS: ${{matrix.cxxflags}}
CXXFLAGS: ${{matrix.cxxflags}} ${{matrix.cxxflags_extra}}
run: |
cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
cmake ${{matrix.gen && '-G' || ''}} ${{matrix.gen}} \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
-DCMAKE_CXX_VISIBILITY_PRESET=hidden \
-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \
-DFMT_DOC=OFF -DFMT_PEDANTIC=ON -DFMT_WERROR=ON \
${{matrix.fuzz}} ${{matrix.shared}} $GITHUB_WORKSPACE
-DFMT_DOC=OFF -DFMT_PEDANTIC=ON \
-DFMT_WERROR=ON \
${{matrix.fuzz}} \
${{matrix.shared}} \
$GITHUB_WORKSPACE
- name: Build
working-directory: ${{runner.workspace}}/build

View File

@ -7,6 +7,9 @@ permissions:
jobs:
build:
name: >-
c++${{matrix.std }} ${{ matrix.build_type}}
${{matrix.shared && 'Shared' || ''}}
strategy:
matrix:
os: [macos-14]
@ -22,7 +25,7 @@ jobs:
runs-on: '${{ matrix.os }}'
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set timezone
run: sudo systemsetup -settimezone 'Europe/Minsk'

143
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,143 @@
# Builds the release source package in CI for a draft release (typically
# created via support/release.py), uploads the zip to that release, and
# attaches a SLSA v1.0 provenance attestation generated by the OpenSSF
# slsa-github-generator. The maintainer reviews the draft (which by then has
# both the zip and *.intoto.jsonl attached) and clicks Publish to finalize.
#
# The source package includes the pre-rendered HTML documentation (doc-html/),
# so this workflow builds the docs and stages them into the source tree before
# running CPack's package_source target.
#
# This makes the provenance attest to the actual build that produced the
# artifact, rather than just attesting to a hash observed after the fact.
#
# GitHub does not fire `release: created` for draft releases, so release.py
# triggers this workflow explicitly via workflow_dispatch, passing the tag to
# attach to and the ref to build from. The `release: created` trigger is kept
# for non-draft releases created directly through the GitHub UI.
name: release
on:
release:
types: [created]
workflow_dispatch:
inputs:
tag_name:
description: "Release tag to attach the artifacts to (e.g. 12.2.0)"
required: true
type: string
ref:
description: "Git ref to build the source package from"
required: false
default: release
type: string
permissions: read-all
jobs:
build:
name: Build source package
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
package: ${{ steps.build.outputs.package }}
tag: ${{ steps.vars.outputs.tag }}
steps:
- name: Resolve tag and ref for both event types
id: vars
run: |
echo "tag=${{ github.event.release.tag_name || inputs.tag_name }}" >> "$GITHUB_OUTPUT"
echo "ref=${{ github.event.release.target_commitish || inputs.ref }}" >> "$GITHUB_OUTPUT"
- name: Checkout the release ref
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ steps.vars.outputs.ref }}
persist-credentials: false
- name: Install the documentation toolchain
# The source zip ships the pre-rendered HTML docs (doc-html/), so the
# docs must be built before packaging. doxygen feeds the API reference;
# mkdocs plus the pinned, hash-verified deps render the site. The venv
# lives outside the checkout so it is not swept into the source zip, and
# is added to PATH so CMake's find_program(MKDOCS) and the doxygen call
# in the mkdocstrings handler both resolve in later steps.
run: |
sudo apt-get update
sudo apt-get install -y doxygen
python3 -m venv "$RUNNER_TEMP/doc-venv"
"$RUNNER_TEMP/doc-venv/bin/pip" install --quiet --upgrade pip
"$RUNNER_TEMP/doc-venv/bin/pip" install --quiet --require-hashes \
-r support/doc-requirements.txt
echo "$RUNNER_TEMP/doc-venv/bin" >> "$GITHUB_PATH"
- name: Build source zip via CPack
id: build
# Build the docs and stage the rendered site at the top level so the
# CPack source package includes doc-html/. The build/ tree itself is
# excluded by CPACK_SOURCE_IGNORE_FILES.
run: |
cmake -B build .
cmake --build build --target doc
cp -r build/doc-html doc-html
cmake --build build --target package_source
pkg=$(ls build/fmt-*.zip)
test -f "$pkg"
echo "package=$pkg" >> "$GITHUB_OUTPUT"
- name: Compute base64-encoded SHA-256 subjects
id: hash
run: |
file="${{ steps.build.outputs.package }}"
subjects=$(cd "$(dirname "$file")" && sha256sum "$(basename "$file")")
echo "hashes=$(printf '%s' "$subjects" | base64 -w0)" >> "$GITHUB_OUTPUT"
- name: Upload zip to the release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ steps.vars.outputs.tag }}" \
"${{ steps.build.outputs.package }}" \
--repo "${{ github.repository }}" --clobber
# Generate the provenance only; do not let the generator upload it. With
# upload-assets, the generator creates and publishes its own release (and
# tag) to host the attestation, which duplicates our draft. Instead we
# download the attestation artifact and attach it to the draft below.
provenance:
needs: [build]
permissions:
actions: read
id-token: write
# contents: write is required because the generator's (skipped)
# upload-assets job declares it, and a reusable workflow's job
# permissions may not exceed the caller's, or the run fails at startup.
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
with:
base64-subjects: ${{ needs.build.outputs.hashes }}
provenance-name: "fmt-${{ needs.build.outputs.tag }}.intoto.jsonl"
upload-assets: false
attach-provenance:
name: Attach provenance to the release
needs: [build, provenance]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download the provenance attestation
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ needs.provenance.outputs.provenance-name }}
- name: Attach provenance to the draft release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ needs.build.outputs.tag }}" \
"${{ needs.provenance.outputs.provenance-name }}" \
--repo "${{ github.repository }}" --clobber

View File

@ -1,9 +1,6 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
name: supply-chain
on:
workflow_dispatch:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
@ -12,14 +9,13 @@ on:
schedule:
- cron: '26 14 * * 5'
push:
branches: [ "master" ]
branches: [main]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
scorecard:
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
@ -28,20 +24,15 @@ jobs:
id-token: write
steps:
- name: "Checkout code"
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: "Run analysis"
- name: Run analysis
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
@ -49,17 +40,8 @@ jobs:
# - See https://github.com/ossf/scorecard-action#publishing-results.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3.29.5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v3.29.5
with:
sarif_file: results.sarif

View File

@ -7,6 +7,9 @@ permissions:
jobs:
build:
name: >-
${{matrix.platform}} ${{matrix.toolset}} c++${{matrix.standard}}
${{matrix.build_type}} ${{matrix.shared && 'Shared' || ''}}
runs-on: ${{matrix.os}}
strategy:
matrix:
@ -32,7 +35,7 @@ jobs:
standard: 20
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set timezone
run: tzutil /s "FLE Standard Time"
@ -79,7 +82,7 @@ jobs:
release: false
msystem: ${{matrix.sys}}
pacboy: cc:p cmake:p ninja:p lld:p
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
env: { LDFLAGS: -fuse-ld=lld }

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.so*
*.xcodeproj
*~
*.gradle/
.vscode/
.cache/
.vs/

View File

@ -63,7 +63,7 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND CMAKE_CXX_STANDARD
OUTPUT_VARIABLE NINJA_VERSION)
if (NINJA_VERSION VERSION_GREATER_EQUAL 1.11)
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION
GREATER_EQUAL 14)
GREATER_EQUAL 15)
OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
AND CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 16)
OR (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION
@ -256,11 +256,13 @@ function (setup_target target kind)
target_compile_definitions(${target} ${kind} FMT_UNICODE=0)
endif ()
set_target_properties(
${target}
PROPERTIES VERSION ${FMT_VERSION}
SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
if (NOT "${kind}" STREQUAL "INTERFACE")
set_target_properties(
${target}
PROPERTIES VERSION ${FMT_VERSION}
SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
endif ()
endfunction ()
set(FMT_HEADERS)
@ -417,8 +419,19 @@ target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
setup_target(fmt-header-only INTERFACE)
add_library(fmt-c STATIC src/fmt-c.cc)
add_library(fmt-c src/fmt-c.cc)
target_compile_features(fmt-c INTERFACE c_std_11)
if (BUILD_SHARED_LIBS)
target_compile_definitions(
fmt-c
PRIVATE FMT_LIB_EXPORT
INTERFACE FMT_SHARED)
endif ()
set_target_properties(
fmt-c
PROPERTIES VERSION ${FMT_VERSION}
SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
if (MSVC)
target_compile_options(fmt-c PUBLIC /Zc:preprocessor)
endif ()
@ -428,6 +441,16 @@ add_library(fmt::fmt-c ALIAS fmt-c)
set_target_properties(fmt-c PROPERTIES PUBLIC_HEADER include/fmt/fmt-c.h)
# Install targets.
set(targets_export_name fmt-targets)
set(INSTALL_TARGETS fmt fmt-header-only fmt-c)
if (FMT_MODULE)
list(APPEND INSTALL_TARGETS fmt-module)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION ${FMT_INC_DIR}/fmt)
endif ()
endif ()
if (FMT_INSTALL)
include(CMakePackageConfigHelpers)
set_verbose(
@ -441,7 +464,6 @@ if (FMT_INSTALL)
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets)
set_verbose(
FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
@ -472,15 +494,6 @@ if (FMT_INSTALL)
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in ${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only fmt-c)
if (FMT_MODULE)
list(APPEND INSTALL_TARGETS fmt-module)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION ${FMT_INC_DIR}/fmt)
endif ()
endif ()
# Install the library and headers.
install(
TARGETS ${INSTALL_TARGETS}
@ -491,13 +504,6 @@ if (FMT_INSTALL)
PUBLIC_HEADER DESTINATION ${FMT_INC_DIR}/fmt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ${INSTALL_FILE_SET})
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(
TARGETS ${INSTALL_TARGETS}
NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(
FILES ${project_config} ${version_config}
@ -515,6 +521,15 @@ if (FMT_INSTALL)
COMPONENT fmt_core)
endif ()
# Generate an export file in the build tree so that fmt can be used by
# add_subdirectory() or FetchContent without being installed. Use a
# namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(
TARGETS ${INSTALL_TARGETS}
NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
function (add_doc_target)
find_program(DOXYGEN doxygen PATHS "$ENV{ProgramFiles}/doxygen/bin"
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")
@ -582,7 +597,9 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
string(REPLACE "*" ".*" line "${line}")
set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach ()
set(ignored_files ${ignored_files} /.git /build/doxyxml .vagrant)
# Exclude the whole build tree: the .gitignore-derived `build/` pattern does
# not actually prune the directory or its contents from the source package.
set(ignored_files ${ignored_files} /.git "/build($|/)" .vagrant)
set(CPACK_SOURCE_GENERATOR ZIP)
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})

View File

@ -1,3 +1,226 @@
# 12.2.0 - 2026-06-16
- Added a C11 API that brings fast, type-safe formatting to C. The new
`fmt-c` library and `fmt/fmt-c.h` header use `_Generic` to dispatch on
argument types and outperform `printf`/`sprintf`. For example:
```c++
#include <fmt/fmt-c.h>
fmt_print(stdout, "The answer is {}.\n", 42);
```
(https://github.com/fmtlib/fmt/issues/4663,
https://github.com/fmtlib/fmt/pull/4671,
https://github.com/fmtlib/fmt/pull/4696,
https://github.com/fmtlib/fmt/issues/4693,
https://github.com/fmtlib/fmt/pull/4694,
https://github.com/fmtlib/fmt/pull/4712,
https://github.com/fmtlib/fmt/pull/4789).
Thanks @Soumik15630m, @Ferdi265 and @localspook.
- Added a separate `fmt::fmt-module` CMake target for C++20 modules and a
CI workflow that exercises module-based builds
(https://github.com/fmtlib/fmt/issues/4684,
https://github.com/fmtlib/fmt/pull/4685,
https://github.com/fmtlib/fmt/issues/4707,
https://github.com/fmtlib/fmt/pull/4708,
https://github.com/fmtlib/fmt/pull/4702,
https://github.com/fmtlib/fmt/pull/4709).
Thanks @MathewBensonCode.
- Enabled the full Dragonbox lookup cache by default for floating-point
formatting unless optimizing for binary size (`__OPTIMIZE_SIZE__`),
giving a ~1025% speedup. Thanks Matthias Kretz for the suggestion.
Average time per `double` on Apple M1 Pro (clang 17, random digits,
smaller is better) measured with
[dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark):
| Method | Time (ns) |
| ----------------------- | --------: |
| fmt (full) | 22.07 |
| fmt (compact) | 29.55 |
| ryu | 35.21 |
| double-conversion | 81.81 |
| `sprintf` | 726.27 |
| `ostringstream` | 864.34 |
- Improved integer formatting performance by ~3%
(https://github.com/fmtlib/fmt/pull/4630). Thanks @user202729.
- Optimized formatting into back-insert iterators by using bulk container
append/insert methods (e.g. on `std::vector<char>` and custom string
types) (https://github.com/fmtlib/fmt/pull/4679). Thanks @user202729.
- Reduced binary size of debug builds (~200k to ~85k in the bloat test) and
improved compile speed when `consteval` is unavailable.
- Made path formatting lossless, preserving ill-formed UTF-16
sequences when converting `std::filesystem::path` to a narrow string.
- Added support for formatting `std::unexpected`
(https://github.com/fmtlib/fmt/pull/4675). Thanks @17steen.
- Added overloads of `fmt::println` that take a `fmt::text_style`
(https://github.com/fmtlib/fmt/pull/4782). Thanks @ahoarau.
- Added support for positional arguments as width and precision specifiers
in `fmt::printf` (https://github.com/fmtlib/fmt/pull/4643).
Thanks @KareemOtoum.
- Made `FMT_STRING` a no-op when `FMT_USE_CONSTEVAL` is enabled, since the
consteval format-string constructor already provides compile-time
validation
(https://github.com/fmtlib/fmt/issues/4611,
https://github.com/fmtlib/fmt/pull/4612). Thanks @friedkeenan.
- Promoted `fmt::detail::named_arg` to the public API as `fmt::named_arg` and
deprecated the detail alias
(https://github.com/fmtlib/fmt/issues/4683,
https://github.com/fmtlib/fmt/pull/4687). Thanks @TPPPP72.
- Moved the `std::byte` formatter from `fmt/format.h` to `fmt/std.h`.
- Provided a default definition for `fmt::is_contiguous`
(https://github.com/fmtlib/fmt/pull/4731,
https://github.com/fmtlib/fmt/pull/4770). Thanks @user202729 and @phprus.
- Added the `FMT_USE_FLOCKFILE` macro to disable the use of `flockfile`
(https://github.com/fmtlib/fmt/issues/4646,
https://github.com/fmtlib/fmt/pull/4666). Thanks @mvastola.
- Added `include_guard(GLOBAL)` so that {fmt} can be used in multiple
submodules of the same project
(https://github.com/fmtlib/fmt/pull/4672). Thanks @torsten48.
- Improved `constexpr` support
(https://github.com/fmtlib/fmt/pull/4659,
https://github.com/fmtlib/fmt/pull/4591).
Thanks @elbeno and @17steen.
- Deprecated the implicit conversion from `fmt::format_string` and
`fmt::basic_fstring` to `string_view` to align with `std::format_string`;
use `format_string::get()` instead.
- Opted out `std::complex` from tuple formatting so that the dedicated
`std::complex` formatter is always used.
- Removed the `fmt::say` function.
- Deprecated the `std::initializer_list` overload of `fmt::join` and the array
overload of `fmt::vformat_to`.
- Made the `<fmt/core.h>` header equivalent to `<fmt/base.h>` by
default. Code that relied on `<fmt/core.h>` pulling in `<fmt/format.h>`
must now either include `<fmt/format.h>` directly or define
`FMT_DEPRECATED_HEAVY_CORE` to opt back in.
- Improved `wchar_t` support: `fmt::join` now accepts `wchar_t` and other
non-`char` separators, and `fmt::format_to_n` now works with `fmt::runtime`
on `wchar_t`
(https://github.com/fmtlib/fmt/pull/4686,
https://github.com/fmtlib/fmt/issues/4714,
https://github.com/fmtlib/fmt/pull/4715).
Thanks @Yancey2023 and @sunmy2019.
- Fixed formatting of `std::tm` with a null `tm_zone`
(https://github.com/fmtlib/fmt/pull/4790). Thanks @Carmel0.
- Fixed compile-time formatting in `fmt/ranges.h`, `fmt/style.h` and
`fmt/std.h` (https://github.com/fmtlib/fmt/pull/4759). Thanks @j4niwzis.
- Fixed an ambiguity between `formatter<std::optional<T>>` in `fmt/std.h`
and `fmt/ranges.h` on C++26 (P3168R2)
(https://github.com/fmtlib/fmt/pull/4761). Thanks @phprus.
- Fixed a GCC PCH breakage triggered by a scoped `#pragma GCC optimize`.
- Fixed a TSAN false positive in the locale handling code
(https://github.com/fmtlib/fmt/issues/4755).
- Fixed compile-time format string checks truncating string literals at
an embedded null byte
(https://github.com/fmtlib/fmt/pull/4732). Thanks @user202729.
- Fixed out-of-bounds reads in `printf` formatting
(https://github.com/fmtlib/fmt/issues/4741,
https://github.com/fmtlib/fmt/pull/4742,
https://github.com/fmtlib/fmt/pull/4800).
Thanks @Algunenano and @aizu-m.
- Fixed the return type of the `f(un)lockfile` wrappers on Windows
(https://github.com/fmtlib/fmt/pull/4739). Thanks @mvastola.
- Worked around a CUDA issue when handling UTF-32 literals
(https://github.com/fmtlib/fmt/pull/4719). Thanks @Cazadorro.
- Fixed missing named-argument validation for compiled format strings
(https://github.com/fmtlib/fmt/pull/4638). Thanks @JaeheonShim.
- Fixed `fmt::format_to_n` in `<fmt/compile.h>` failing to compile when
`<iterator>` is not transitively included
(https://github.com/fmtlib/fmt/issues/4615).
- Fixed handling of pointers in format string compilation with
`FMT_BUILTIN_TYPES=0`.
- Stopped assuming nul termination of the format string in `fmt::printf`.
Thanks @ZUENS2020 for reporting.
- Fixed a build error when locale support is disabled
(https://github.com/fmtlib/fmt/pull/4627). Thanks @marcel-behlau-elfin.
- Fixed a fallback range formatter for types with a `container_type` member
(https://github.com/fmtlib/fmt/issues/4123,
https://github.com/fmtlib/fmt/pull/4660). Thanks @Soumik15630m.
- Fixed C++20 concept detection
(https://github.com/fmtlib/fmt/pull/4653). Thanks @tearfur.
- Fixed a clang compilation failure
(https://github.com/fmtlib/fmt/pull/4718). Thanks @mccakit.
- Fixed various MSVC warnings, including C4305 and conversion warnings on
x86 (https://github.com/fmtlib/fmt/pull/4668,
https://github.com/fmtlib/fmt/pull/4594).
Thanks @kanren3 and @blizzard4591.
- Updated the Android Gradle Plugin to 9.x
(https://github.com/fmtlib/fmt/issues/4651,
https://github.com/fmtlib/fmt/pull/4658). Thanks @Soumik15630m.
- Made various code, build and test improvements
(https://github.com/fmtlib/fmt/pull/4625,
https://github.com/fmtlib/fmt/pull/4639,
https://github.com/fmtlib/fmt/pull/4644,
https://github.com/fmtlib/fmt/pull/4656,
https://github.com/fmtlib/fmt/pull/4680,
https://github.com/fmtlib/fmt/pull/4681,
https://github.com/fmtlib/fmt/pull/4704,
https://github.com/fmtlib/fmt/pull/4710,
https://github.com/fmtlib/fmt/pull/4713,
https://github.com/fmtlib/fmt/pull/4729,
https://github.com/fmtlib/fmt/pull/4751,
https://github.com/fmtlib/fmt/pull/4758,
https://github.com/fmtlib/fmt/pull/4799).
Thanks @ZephyrLykos, @togunchan, @kagancansit, @BerndPetrovitsch,
@Skylion007, @st0rmbtw, @localspook and @EXtremeExploit.
- Improved documentation, including a rewrite of the format string syntax,
better handling of doxygen tags, documenting `output_file`, fixing CSS so
that whitespace is displayed properly, and various smaller fixes
(https://github.com/fmtlib/fmt/pull/4622,
https://github.com/fmtlib/fmt/pull/4626,
https://github.com/fmtlib/fmt/pull/4631,
https://github.com/fmtlib/fmt/pull/4667,
https://github.com/fmtlib/fmt/pull/4616,
https://github.com/fmtlib/fmt/pull/4748).
Thanks @heavywatal, @ZephyrLykos, @user202729, @ssszcmawo, @bigmoonbit
and @Powerbyte7.
- Added building of release artifacts and SLSA provenance in CI, added a
CodeQL workflow, and added the security policy in `.github/SECURITY.md`.
# 12.1.0 - 2025-10-29
- Optimized `buffer::append`, resulting in up to ~16% improvement on spdlog

139
README.md
View File

@ -1,19 +1,25 @@
<img src="https://user-images.githubusercontent.com/576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png" alt="{fmt}" width="25%"/>
[![image](https://github.com/fmtlib/fmt/actions/workflows/linux.yml/badge.svg?branch=master)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
[![image](https://github.com/fmtlib/fmt/actions/workflows/macos.yml/badge.svg?branch=master)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![image](https://github.com/fmtlib/fmt/actions/workflows/linux.yml/badge.svg?branch=master)](
https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
[![image](https://github.com/fmtlib/fmt/actions/workflows/macos.yml/badge.svg?branch=master)](
https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/actions/workflows/windows.yml/badge.svg?branch=master)](
https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](
https://issues.oss-fuzz.com/issues?q=title:fmt%20cc:victor.zverovich@gmail.com)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](
https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](
https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](
https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![Support Ukraine](
https://img.shields.io/badge/Support-Ukraine-005BBB?labelColor=FFD500)](https://novaukraine.org/)
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to one of the funds
that help victims of the war in Ukraine: <https://u24.gov.ua/>.
[Documentation](https://fmt.dev)
[Cheat Sheets](https://hackingcpp.com/cpp/libs/fmt.html)
@ -38,7 +44,7 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v).
[Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm
- Portable Unicode support
- Safe [printf
implementation](https://fmt.dev/latest/api/#printf-formatting)
implementation](https://fmt.dev/latest/api/#printf-api)
including the POSIX extension for positional arguments
- Extensibility: [support for user-defined
types](https://fmt.dev/latest/api/#formatting-user-defined-types)
@ -46,7 +52,7 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v).
implementations of `(s)printf`, iostreams, `to_string` and
`to_chars`, see [Speed tests](#speed-tests) and [Converting a
hundred million integers to strings per
second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html)
second](https://vitaut.net/posts/2020/fast-int-to-string-revisited/)
- Small code size both in terms of source code with the minimum
configuration consisting of just three files, `base.h`, `format.h`
and `format-inl.h`, and compiled code; see [Compile time and code
@ -151,7 +157,7 @@ int main() {
```
This can be [up to 9 times faster than `fprintf`](
http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
https://vitaut.net/posts/2020/optimal-file-buffer-size/).
**Print with colors and text styles**
@ -170,7 +176,7 @@ int main() {
Output on a modern terminal with Unicode support:
![image](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7)
![image](https://github.com/fmtlib/fmt/assets/576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7)
# Benchmarks
@ -345,7 +351,7 @@ converts to `std::print`.)
cluster proxy
- [redpanda](https://vectorized.io/redpanda): a 10x faster Kafka®
replacement for mission-critical systems written in C++
- [rpclib](http://rpclib.net/): a modern C++ msgpack-RPC server and
- [rpclib](https://github.com/rpclib/rpclib): a modern C++ msgpack-RPC server and
client library
- [Salesforce Analytics
Cloud](https://www.salesforce.com/analytics-cloud/overview/):
@ -353,7 +359,7 @@ converts to `std::print`.)
- [Scylla](https://www.scylladb.com/): a Cassandra-compatible NoSQL
data store that can handle 1 million transactions per second on a
single server
- [Seastar](http://www.seastar-project.org/): an advanced, open-source
- [Seastar](https://seastar.io/): an advanced, open-source
C++ framework for high-performance server applications on modern
hardware
- [spdlog](https://github.com/gabime/spdlog): super fast C++ logging
@ -374,98 +380,6 @@ If you are aware of other projects using this library, please let me
know by [email](mailto:victor.zverovich@gmail.com) or by submitting an
[issue](https://github.com/fmtlib/fmt/issues).
# Motivation
So why yet another formatting library?
There are plenty of methods for doing this task, from standard ones like
the printf family of function and iostreams to Boost Format and
FastFormat libraries. The reason for creating a new library is that
every existing solution that I found either had serious issues or
didn\'t provide all the features I needed.
## printf
The good thing about `printf` is that it is pretty fast and readily
available being a part of the C standard library. The main drawback is
that it doesn\'t support user-defined types. `printf` also has safety
issues although they are somewhat mitigated with [\_\_attribute\_\_
((format (printf,
\...))](https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html) in
GCC. There is a POSIX extension that adds positional arguments required
for
[i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization)
to `printf` but it is not a part of C99 and may not be available on some
platforms.
## iostreams
The main issue with iostreams is best illustrated with an example:
``` c++
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
```
which is a lot of typing compared to printf:
``` c++
printf("%.2f\n", 1.23456);
```
Matthew Wilson, the author of FastFormat, called this \"chevron hell\".
iostreams don\'t support positional arguments by design.
The good part is that iostreams support user-defined types and are safe
although error handling is awkward.
## Boost Format
This is a very powerful library that supports both `printf`-like format
strings and positional arguments. Its main drawback is performance.
According to various benchmarks, it is much slower than other methods
considered here. Boost Format also has excessive build times and severe
code bloat issues (see [Benchmarks](#benchmarks)).
## FastFormat
This is an interesting library that is fast, safe and has positional
arguments. However, it has significant limitations, citing its author:
> Three features that have no hope of being accommodated within the
> current design are:
>
> - Leading zeros (or any other non-space padding)
> - Octal/hexadecimal encoding
> - Runtime width/alignment specification
It is also quite big and has a heavy dependency, on STLSoft, which might be
too restrictive for use in some projects.
## Boost Spirit.Karma
This is not a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing
verbatim text with arguments. The library is pretty fast, but slower on
integer formatting than `fmt::format_to` with format string compilation
on Karma\'s own benchmark, see [Converting a hundred million integers to
strings per
second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html).
# License
{fmt} is distributed under the MIT
[license](https://github.com/fmtlib/fmt/blob/master/LICENSE).
# Documentation License
The [Format String Syntax](https://fmt.dev/latest/syntax/) section
in the documentation is based on the one from Python [string module
documentation](https://docs.python.org/3/library/string.html#module-string).
For this reason, the documentation is distributed under the Python
Software Foundation license available in
[doc/python-license.txt](https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt).
It only applies if you distribute the documentation of {fmt}.
# Maintainers
The {fmt} library is maintained by Victor Zverovich
@ -475,12 +389,3 @@ people. See
[Releases](https://github.com/fmtlib/fmt/releases) for some of the
names. Let us know if your contribution is not listed or mentioned
incorrectly and we\'ll make it right.
# Security Policy
To report a security issue, please disclose it at [security
advisory](https://github.com/fmtlib/fmt/security/advisories/new).
This project is maintained by a team of volunteers on a
reasonable-effort basis. As such, please give us at least *90* days to
work on a fix before public exposure.

View File

@ -130,7 +130,6 @@ hide:
Where possible, the output of formatting functions is <b>consistent across
platforms</b>.
</p>
</p>
</div>
<div class="feature">

View File

@ -1,290 +0,0 @@
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others.
In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Digital Creations (now Zope
Corporation, see http://www.zope.com). In 2001, the Python Software
Foundation (PSF, see http://www.python.org/psf/) was formed, a
non-profit organization created specifically to own Python-related
Intellectual Property. Zope Corporation is a sponsoring member of
the PSF.
All Python releases are Open Source (see http://www.opensource.org for
the Open Source Definition). Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.
Release Derived Year Owner GPL-
from compatible? (1)
0.9.0 thru 1.2 1991-1995 CWI yes
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
1.6 1.5.2 2000 CNRI no
2.0 1.6 2000 BeOpen.com no
1.6.1 1.6 2001 CNRI yes (2)
2.1 2.0+1.6.1 2001 PSF no
2.0.1 2.0+1.6.1 2001 PSF yes
2.1.1 2.1+2.0.1 2001 PSF yes
2.2 2.1.1 2001 PSF yes
2.1.2 2.1.1 2002 PSF yes
2.1.3 2.1.2 2002 PSF yes
2.2.1 2.2 2002 PSF yes
2.2.2 2.2.1 2002 PSF yes
2.2.3 2.2.2 2003 PSF yes
2.3 2.2.2 2002-2003 PSF yes
2.3.1 2.3 2002-2003 PSF yes
2.3.2 2.3.1 2002-2003 PSF yes
2.3.3 2.3.2 2002-2003 PSF yes
2.3.4 2.3.3 2004 PSF yes
2.3.5 2.3.4 2005 PSF yes
2.4 2.3 2004 PSF yes
2.4.1 2.4 2005 PSF yes
2.4.2 2.4.1 2005 PSF yes
2.4.3 2.4.2 2006 PSF yes
2.4.4 2.4.3 2006 PSF yes
2.5 2.4 2006 PSF yes
2.5.1 2.5 2007 PSF yes
2.5.2 2.5.1 2008 PSF yes
2.5.3 2.5.2 2008 PSF yes
2.6 2.5 2008 PSF yes
2.6.1 2.6 2008 PSF yes
2.6.2 2.6.1 2009 PSF yes
2.6.3 2.6.2 2009 PSF yes
2.6.4 2.6.3 2009 PSF yes
2.6.5 2.6.4 2010 PSF yes
3.0 2.6 2008 PSF yes
3.0.1 3.0 2009 PSF yes
3.1 3.0.1 2009 PSF yes
3.1.1 3.1 2009 PSF yes
3.1.2 3.1.1 2010 PSF yes
3.1.3 3.1.2 2010 PSF yes
3.1.4 3.1.3 2011 PSF yes
3.2 3.1 2011 PSF yes
3.2.1 3.2 2011 PSF yes
3.2.2 3.2.1 2011 PSF yes
3.2.3 3.2.2 2012 PSF yes
3.3.0 3.2 2012 PSF yes
Footnotes:
(1) GPL-compatible doesn't mean that we're distributing Python under
the GPL. All Python licenses, unlike the GPL, let you distribute
a modified version without making your changes open source. The
GPL-compatible licenses make it possible to combine Python with
other software that is released under the GPL; the others don't.
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
because its license has a choice of law clause. According to
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
is "not incompatible" with the GPL.
Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012 Python Software Foundation; All Rights Reserved" are retained in Python
alone or in any derivative version prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the Internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the Internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,14 +1,15 @@
# Format String Syntax
Formatting functions such as [`fmt::format`](api.md#format) and [`fmt::print`](
api.md#print) use the same format string syntax described in this section.
The formatting functions in this library — most notably
[`fmt::format`](api.md#format) and [`fmt::print`](api.md#print) — accept
format strings written in the syntax described here.
Format strings contain "replacement fields" surrounded by curly braces `{}`.
Anything that is not contained in braces is considered literal text, which is
copied unchanged to the output. If you need to include a brace character in
the literal text, it can be escaped by doubling: `{{` and `}}`.
A format string is plain text with embedded *replacement fields* delimited by
the braces `{` and `}`. Characters outside of any replacement field are
copied to the output unchanged. To emit a literal brace, double it: `{{`
yields a single `{` in the output, and `}}` yields a single `}`.
The grammar for a replacement field is as follows:
A replacement field is described by the grammar below.
<a id="replacement-field"></a>
<pre><code class="language-json"
@ -22,55 +23,43 @@ id_start ::= "a"..."z" | "A"..."Z" | "_"
id_continue ::= id_start | digit</code>
</pre>
In less formal terms, the replacement field can start with an *arg_id* that
specifies the argument whose value is to be formatted and inserted into the
output instead of the replacement field. The *arg_id* is optionally followed
by a *format_spec*, which is preceded by a colon `':'`. These specify a
non-default format for the replacement value.
An *arg_id* selects which argument to format. It may be a non-negative
integer (positional reference) or an identifier matching the name of an
argument passed via [`fmt::arg`](api.md#arg) (named reference). When *arg_id*
is omitted, arguments are consumed in left-to-right order; this *automatic*
indexing must be used uniformly throughout the format string — mixing
automatic and explicit numeric ids is a compile-time error (or a
`format_error` at runtime).
See also the [Format Specification
Mini-Language](#format-specification-mini-language) section.
A *format_spec*, introduced by `:`, describes how the value should be
rendered. Its grammar is type-dependent; the form used by the standard
built-in types is documented in the next section.
If the numerical arg_ids in a format string are 0, 1, 2, ... in sequence,
they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be
automatically inserted in that order.
Named arguments can be referred to by their names or indices.
Some simple format string examples:
For example:
```c++
"First, thou shalt count to {0}" // References the first argument
"Bring me a {}" // Implicitly references the first argument
"From {} to {}" // Same as "From {0} to {1}"
fmt::format("hello, {}", "world");
// Result: "hello, world"
fmt::format("{1}, {0}!", "world", "hello");
// Result: "hello, world!"
fmt::format("{greeting}, {name}!",
fmt::arg("greeting", "hi"), fmt::arg("name", "fmt"));
// Result: "hi, fmt!"
```
The *format_spec* field contains a specification of how the value should
be presented, including such details as field width, alignment, padding,
decimal precision and so on. Each value type can define its own
"formatting mini-language" or interpretation of the *format_spec*.
A *width* or *precision* inside a *format_spec* may itself be written as a
nested replacement field — `{}` or `{arg_id}` — in which case it takes its
value from an integer argument at runtime. Nested fields accept only an
*arg_id*; they cannot themselves contain a *format_spec*.
Most built-in types support a common formatting mini-language, which is
described in the next section.
## Format Specification
A *format_spec* field can also include nested replacement fields in
certain positions within it. These nested replacement fields can contain
only an argument id; format specifications are not allowed. This allows
the formatting of a value to be dynamically specified.
See the [Format Examples](#format-examples) section for some examples.
## Format Specification Mini-Language
"Format specifications" are used within replacement fields contained within a
format string to define how individual values are presented. Each formattable
type may define how the format specification is to be interpreted.
Most built-in types implement the following options for format
specifications, although some of the formatting options are only
supported by the numeric types.
The general form of a *standard format specifier* is:
The grammar below describes the *format_spec* shared by the built-in
types — integers, floating-point values, characters, strings, booleans, and
pointers — as well as by any user-defined type whose `formatter` reuses
fmt's parser.
<a id="format-spec"></a>
<pre><code class="language-json"
@ -86,318 +75,221 @@ type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" |
"g" | "G" | "o" | "p" | "s" | "x" | "X" | "?"</code>
</pre>
The *fill* character can be any Unicode code point other than `'{'` or `'}'`.
The presence of a fill character is signaled by the character following it,
which must be one of the alignment options. If the second character of
*format_spec* is not a valid alignment option, then it is assumed that both
the fill character and the alignment option are absent.
Whether a particular option is meaningful depends on the value being
formatted; options that do not apply to a value's type are diagnosed at
compile time when possible and otherwise raise a `format_error`.
The meaning of the various alignment options is as follows:
### Fill and alignment
<table>
<tr>
<th>Option</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'<'</code></td>
<td>
Forces the field to be left-aligned within the available space (this is the
default for most objects).
</td>
</tr>
<tr>
<td><code>'>'</code></td>
<td>
Forces the field to be right-aligned within the available space (this is
the default for numbers).
</td>
</tr>
<tr>
<td><code>'^'</code></td>
<td>Forces the field to be centered within the available space.</td>
</tr>
</table>
The *align* field selects where padding is placed when *width* makes the
field wider than the value's natural rendering.
Note that unless a minimum field width is defined, the field width will
always be the same size as the data to fill it, so that the alignment
option has no meaning in this case.
| Option | Effect |
|--------|-------------------------------------------------------------------|
| `<` | Left-align; pad on the right. Default for non-numeric types. |
| `>` | Right-align; pad on the left. Default for numeric types. |
| `^` | Center the value; if the padding cannot be split evenly, the extra padding character goes on the right. |
The *sign* option is only valid for floating point and signed integer types,
and can be one of the following:
The *fill* character is any single Unicode code point other than `{` or `}`,
encoded the same way as the format string. It supplies the padding character
in place of the default space. A fill character is recognized only when it is
immediately followed by an *align* character — otherwise it would be
indistinguishable from an option in another position — so to use a custom
fill you must also specify an alignment.
<table>
<tr>
<th>Option</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'+'</code></td>
<td>
Indicates that a sign should be used for both nonnegative as well as
negative numbers.
</td>
</tr>
<tr>
<td><code>'-'</code></td>
<td>
Indicates that a sign should be used only for negative numbers (this is the
default behavior).
</td>
</tr>
<tr>
<td>space</td>
<td>
Indicates that a leading space should be used on nonnegative numbers, and a
minus sign on negative numbers.
</td>
</tr>
</table>
Alignment has no observable effect when the value's natural rendering is
already at least as wide as *width*; the value is never truncated to fit.
The `'#'` option causes the "alternate form" to be used for the
conversion. The alternate form is defined differently for different
types. This option is only valid for integer and floating-point types.
For integers, when binary, octal, or hexadecimal output is used, this
option adds the prefix respective `"0b"` (`"0B"`), `"0"`, or `"0x"`
(`"0X"`) to the output value. Whether the prefix is lower-case or
upper-case is determined by the case of the type specifier, for example,
the prefix `"0x"` is used for the type `'x'` and `"0X"` is used for
`'X'`. For floating-point numbers the alternate form causes the result
of the conversion to always contain a decimal-point character, even if
no digits follow it. Normally, a decimal-point character appears in the
result of these conversions only if a digit follows it. In addition, for
`'g'` and `'G'` conversions, trailing zeros are not removed from the
result.
```c++
fmt::format("[{:<10}]", "42"); // Result: "[42 ]"
fmt::format("[{:>10}]", "42"); // Result: "[ 42]"
fmt::format("[{:^10}]", "42"); // Result: "[ 42 ]"
fmt::format("[{:*^10}]", "42"); // Result: "[****42****]" - '*' as fill
```
*width* is a decimal integer defining the minimum field width. If not
specified, then the field width will be determined by the content.
### Sign
Preceding the *width* field by a zero (`'0'`) character enables
sign-aware zero-padding for numeric types. It forces the padding to be
placed after the sign or base (if any) but before the digits. This is
used for printing fields in the form "+000000120". This option is only
valid for numeric types and it has no effect on formatting of infinity
and NaN. This option is ignored when any alignment specifier is present.
The *sign* field controls how the sign of a numeric value is emitted. It
applies to signed integer and floating-point types only.
The *precision* is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted
with `'f'` and `'F'`, or before and after the decimal point for a
floating-point value formatted with `'g'` or `'G'`. For non-number types
the field indicates the maximum field size - in other words, how many
characters will be used from the field content. The *precision* is not
allowed for integer, character, Boolean, and pointer values. Note that a
C string must be null-terminated even if precision is specified.
| Option | Effect |
|--------|-------------------------------------------------------------------|
| `+` | Always emit a sign (`+` for nonnegative values, `-` for negative).|
| `-` | Emit `-` only for negative values. This is the default. |
| space | Emit a leading space for nonnegative values and `-` for negative ones; useful for aligning columns of signed numbers. |
The `'L'` option uses the current locale setting to insert the appropriate
number separator characters. This option is only valid for numeric types.
The sign of `-0.0` is preserved in floating-point output.
Finally, the *type* determines how the data should be presented.
```c++
fmt::format("{:+d} {:+d}", 7, -7); // Result: "+7 -7"
fmt::format("{: d} {: d}", 7, -7); // Result: " 7 -7"
```
The available string presentation types are:
### Alternate form (`#`)
<table>
<tr>
<th>Type</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'s'</code></td>
<td>
String format. This is the default type for strings and may be omitted.
</td>
</tr>
<tr>
<td><code>'?'</code></td>
<td>Debug format. The string is quoted and special characters escaped.</td>
</tr>
<tr>
<td>none</td>
<td>The same as <code>'s'</code>.</td>
</tr>
</table>
The `#` flag selects an *alternate form* whose exact meaning depends on the
presentation type:
The available character presentation types are:
- For integers rendered in binary, octal, or hexadecimal, it prepends the
appropriate base prefix (`0b`/`0B`, `0`, or `0x`/`0X`). The case of the
prefix follows the case of the type specifier — `0x` for `x`, `0X` for
`X`, and so on.
- For floating-point values, it forces the decimal point to appear in the
output even if no fractional digits would otherwise be emitted, and
prevents the `g`/`G` presentation types from removing trailing zeros from
the significand.
<table>
<tr>
<th>Type</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'c'</code></td>
<td>
Character format. This is the default type for characters and may be
omitted.
</td>
</tr>
<tr>
<td><code>'?'</code></td>
<td>Debug format. The character is quoted and special characters escaped.</td>
</tr>
<tr>
<td>none</td>
<td>The same as <code>'c'</code>.</td>
</tr>
</table>
The `#` flag is not accepted by non-numeric types.
The available integer presentation types are:
### Zero padding (`0`)
<table>
<tr>
<th>Type</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'b'</code></td>
<td>
Binary format. Outputs the number in base 2. Using the <code>'#'</code>
option with this type adds the prefix <code>"0b"</code> to the output value.
</td>
</tr>
<tr>
<td><code>'B'</code></td>
<td>
Binary format. Outputs the number in base 2. Using the <code>'#'</code>
option with this type adds the prefix <code>"0B"</code> to the output value.
</td>
</tr>
<tr>
<td><code>'c'</code></td>
<td>Character format. Outputs the number as a character.</td>
</tr>
<tr>
<td><code>'d'</code></td>
<td>Decimal integer. Outputs the number in base 10.</td>
</tr>
<tr>
<td><code>'o'</code></td>
<td>Octal format. Outputs the number in base 8.</td>
</tr>
<tr>
<td><code>'x'</code></td>
<td>
Hex format. Outputs the number in base 16, using lower-case letters for the
digits above 9. Using the <code>'#'</code> option with this type adds the
prefix <code>"0x"</code> to the output value.
</td>
</tr>
<tr>
<td><code>'X'</code></td>
<td>
Hex format. Outputs the number in base 16, using upper-case letters for the
digits above 9. Using the <code>'#'</code> option with this type adds the
prefix <code>"0X"</code> to the output value.
</td>
</tr>
<tr>
<td>none</td>
<td>The same as <code>'d'</code>.</td>
</tr>
</table>
A `0` placed immediately before *width* enables sign-aware zero padding for
numeric types. Zeros are inserted between the sign (or base prefix, if any)
and the most significant digit, so that a sign or `0x` prefix stays adjacent
to the digits rather than being separated by spaces. For example, `{:+08d}`
applied to `120` produces `+0000120`.
Integer presentation types can also be used with character and Boolean values
with the only exception that `'c'` cannot be used with `bool`. Boolean values
are formatted using textual representation, either `true` or `false`, if the
presentation type is not specified.
Zero padding:
The available presentation types for floating-point values are:
- applies only to numeric types;
- has no effect on `inf` or `nan`;
- is ignored when an explicit *align* is also present.
<table>
<tr>
<th>Type</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'a'</code></td>
<td>
Hexadecimal floating point format. Prints the number in base 16 with
prefix <code>"0x"</code> and lower-case letters for digits above 9.
Uses <code>'p'</code> to indicate the exponent.
</td>
</tr>
<tr>
<td><code>'A'</code></td>
<td>
Same as <code>'a'</code> except it uses upper-case letters for the
prefix, digits above 9 and to indicate the exponent.
</td>
</tr>
<tr>
<td><code>'e'</code></td>
<td>
Exponent notation. Prints the number in scientific notation using
the letter 'e' to indicate the exponent.
</td>
</tr>
<tr>
<td><code>'E'</code></td>
<td>
Exponent notation. Same as <code>'e'</code> except it uses an
upper-case <code>'E'</code> as the separator character.
</td>
</tr>
<tr>
<td><code>'f'</code></td>
<td>Fixed point. Displays the number as a fixed-point number.</td>
</tr>
<tr>
<td><code>'F'</code></td>
<td>
Fixed point. Same as <code>'f'</code>, but converts <code>nan</code>
to <code>NAN</code> and <code>inf</code> to <code>INF</code>.
</td>
</tr>
<tr>
<td><code>'g'</code></td>
<td>
<p>General format. For a given precision <code>p &gt;= 1</code>,
this rounds the number to <code>p</code> significant digits and then
formats the result in either fixed-point format or in scientific
notation, depending on its magnitude.</p>
<p>A precision of <code>0</code> is treated as equivalent to a precision
of <code>1</code>.</p>
</td>
</tr>
<tr>
<td><code>'G'</code></td>
<td>
General format. Same as <code>'g'</code> except switches to
<code>'E'</code> if the number gets too large. The representations of
infinity and NaN are uppercased, too.
</td>
</tr>
<tr>
<td>none</td>
<td>
Similar to <code>'g'</code>, except that the default precision is as
high as needed to represent the particular value.
</td>
</tr>
</table>
### Width
The available presentation types for pointers are:
*width* is a non-negative decimal integer giving the minimum number of
characters that the field should occupy. If the formatted value is shorter
than *width*, it is padded according to *align* and *fill*; if it is longer,
the value is written in full. *width* never causes the value to be
truncated.
<table>
<tr>
<th>Type</th>
<th>Meaning</th>
</tr>
<tr>
<td><code>'p'</code></td>
<td>
Pointer format. This is the default type for pointers and may be omitted.
</td>
</tr>
<tr>
<td>none</td>
<td>The same as <code>'p'</code>.</td>
</tr>
</table>
To supply *width* at runtime, write the field as `{}` to consume the next
argument, or as `{arg_id}` to reference an integer argument by position or
by name.
## Chrono Format Specifications
When formatting strings, "width" is measured in display columns using a
Unicode-aware estimate (East Asian wide and fullwidth characters, plus
common emoji ranges, count as two columns; everything else counts as one).
This keeps fixed *width* values visually consistent in monospace renderings
that combine Latin and CJK text.
Format specifications for chrono duration and time point types as well as
`std::tm` have the following syntax:
```c++
fmt::format("[{:6}]", 42);
// Result: "[ 42]" - right-aligned by default
fmt::format("[{:6}]", "hi");
// Result: "[hi ]" - left-aligned by default
fmt::format("[{:{}}]", 42, 6);
// Result: "[ 42]" - width from an argument
```
### Precision
*precision* is a non-negative decimal integer (introduced by `.`) whose
meaning depends on the value being formatted. As with *width*, it may be
supplied as a nested replacement field for runtime evaluation.
| Type | Meaning of `.precision` |
|-------------------------------|-----------------------------------------------|
| `e`, `E`, `f`, `F` | Digits emitted after the decimal point. |
| `g`, `G` | Total number of significant digits. |
| `a`, `A` | Digits after the decimal point in the hexadecimal significand. If omitted, just enough digits are emitted to round-trip the value exactly. |
| Strings (`s`, `?`, or default) | Upper bound on the number of code points copied from the value. |
A *precision* is not accepted for integer, character, boolean, or pointer
types. When a *precision* limits the number of characters taken from a C
string, the string must still be null-terminated.
```c++
fmt::format("{:.2f}", 3.14159); // Result: "3.14"
fmt::format("{:.3g}", 3.14159); // Result: "3.14"
fmt::format("{:.4}", "hello, world"); // Result: "hell"
fmt::format("{:.{}f}", 3.14159, 4);
// Result: "3.1416" - precision from an argument
```
### Locale (`L`)
The `L` flag selects locale-sensitive formatting for numeric types. The
formatter inspects the C++ locale supplied to the formatting function (or
the global locale, if none was passed) and inserts the locale's digit
grouping characters and — for floating-point values — its decimal point.
The flag has no effect on non-numeric types.
```c++
auto loc = std::locale("en_US.UTF-8");
fmt::format(loc, "{:L}", 1234567890); // Result: "1,234,567,890"
fmt::format(loc, "{:.2Lf}", 1234567.89); // Result: "1,234,567.89"
```
### Presentation type
The *type* field chooses the representation for the value. Specifiers are
grouped below by the value categories they apply to.
**Integers, booleans, and characters:**
| Type | Effect |
|------|---------------------------------------------------------------------|
| `b` | Base 2. The `#` flag adds a `0b` prefix. |
| `B` | Base 2. The `#` flag adds a `0B` prefix. |
| `c` | Render the integer as the character with that code point. Not allowed for `bool`. |
| `d` | Base 10. The default for integer types. |
| `o` | Base 8. |
| `x` | Base 16, lower-case digits. The `#` flag adds a `0x` prefix. |
| `X` | Base 16, upper-case digits. The `#` flag adds a `0X` prefix. |
| none | Same as `d` for integers, `c` for characters, and the textual form (`true`/`false`) for `bool`. |
```c++
fmt::format("{:d} {:#x} {:#o} {:#b}", 42, 42, 42, 42);
// Result: "42 0x2a 052 0b101010"
fmt::format("{:#06x}", 0xfe); // # adds the prefix, 06 zero-pads to width 6
// Result: "0x00fe"
```
**Floating-point values:**
| Type | Effect |
|------|---------------------------------------------------------------------|
| `a` | Hexadecimal-significand form (e.g. `0x1.8p+1`). Lower-case digits and a lower-case `p` for the binary exponent. The `0x` prefix is always emitted, matching printf's `%a`. The `#` flag forces a decimal point (e.g. `0x1.p+1`).
| `A` | Same as `a`, but upper-case throughout. |
| `e` | Scientific notation with a lower-case `e` for the decimal exponent. |
| `E` | Scientific notation with an upper-case `E`. |
| `f` | Fixed-point notation. |
| `F` | Same as `f`, but renders `nan` as `NAN` and `inf` as `INF`. |
| `g` | General form: scientific notation when the exponent would be less than &minus;4 or not less than the precision, otherwise fixed-point; trailing zeros are removed from the fractional part unless `#` is set. A precision of `0` is interpreted as `1`. |
| `G` | Same as `g`, but uses `E` for the exponent and upper-case `INF`/`NAN`. |
| none | Shortest round-trip representation: the formatted value, when parsed back into the same floating-point type, reproduces the input bit for bit. |
**Strings and characters:**
| Type | Effect |
|------|---------------------------------------------------------------------|
| `s` | Plain string output. Default for string types and for `bool` (which is rendered as `true` or `false`). |
| `c` | Character output. Default for character types. Not allowed for `bool`. |
| `?` | Debug output: the value is wrapped in single quotes (characters) or double quotes (strings), and non-printable, non-ASCII, and special characters are escaped using C-style escape sequences such as `\n`, `\t`, `\"`, and `\u{...}`. |
| none | Same as `s` for strings and `bool`, and as `c` for characters. |
```c++
fmt::format("{}", "tab\there"); // Result contains a literal tab character.
fmt::format("{:?}", "tab\there"); // Result: "\"tab\\there\""
```
**Pointers:**
| Type | Effect |
|------|---------------------------------------------------------------------|
| `p` | Hexadecimal address prefixed by `0x`. Default for pointer types. |
| none | Same as `p`. |
A C string (`char*` or `const char*`) accepts both the string presentation
types and `p`, so the same value can be formatted as either text or an
address.
## Chrono Format Specification
The format specification for chrono duration and time point types as well
as `std::tm` has the following syntax:
<a id="chrono-format-spec"></a>
<pre><code class="language-json"
@ -713,9 +605,25 @@ The available padding modifiers (*padding_modifier*) are:
These modifiers are only supported for the `'H'`, `'I'`, `'M'`, `'S'`, `'U'`,
`'V'`, `'W'`, `'Y'`, `'d'`, `'j'` and `'m'` presentation types.
## Range Format Specifications
Example:
Format specifications for range types have the following syntax:
```c++
#include <fmt/chrono.h>
auto t = std::tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 7;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58
```
## Range Format Specification
The format specification for range types has the following syntax:
<pre><code class="language-json"
>range_format_spec ::= ["n"][range_type][":" range_underlying_spec]</code>
@ -765,87 +673,11 @@ fmt::print("{:n:f}", std::array{std::numbers::pi, std::numbers::e});
// Output: 3.141593, 2.718282
```
## Format Examples
## A Combined Example
This section contains examples of the format syntax and comparison with
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
example, `"%03.2f"` can be translated to `"{:03.2f}"`.
The new format syntax also supports new and different options, shown in
the following examples.
Accessing arguments by position:
```c++
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c"
fmt::format("{}, {}, {}", 'a', 'b', 'c');
// Result: "a, b, c"
fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
// Result: "c, b, a"
fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
// Result: "abracadabra"
```
Aligning the text and specifying a width:
```c++
fmt::format("{:<30}", "left aligned");
// Result: "left aligned "
fmt::format("{:>30}", "right aligned");
// Result: " right aligned"
fmt::format("{:^30}", "centered");
// Result: " centered "
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********"
```
Dynamic width:
```c++
fmt::format("{:<{}}", "left aligned", 30);
// Result: "left aligned "
```
Dynamic precision:
```c++
fmt::format("{:.{}f}", 3.14, 1);
// Result: "3.1"
```
Replacing `%+f`, `%-f`, and `% f` and specifying a sign:
```c++
fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
// Result: "+3.140000; -3.140000"
fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
// Result: " 3.140000; -3.140000"
fmt::format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
// Result: "3.140000; -3.140000"
```
Replacing `%x` and `%o` and converting the value to different bases:
```c++
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
// with 0x or 0 or 0b as prefix:
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
```
Padded hex byte with prefix and always prints both hex characters:
```c++
fmt::format("{:#04x}", 0);
// Result: "0x00"
```
Box drawing using Unicode fill:
The example below ties together several elements introduced above — nested
replacement fields, fill characters, and centering — to draw a fixed-width
box around a message:
```c++
fmt::print(
@ -854,35 +686,10 @@ fmt::print(
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
```
prints:
Output:
```
┌────────────────────┐
│ Hello, world! │
└────────────────────┘
```
Using type-specific formatting:
```c++
#include <fmt/chrono.h>
auto t = tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 7;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58
```
Using the comma as a thousands separator:
```c++
#include <fmt/format.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"
```

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - the base API for char/UTF-8
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 120101
#define FMT_VERSION 120200
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@ -223,13 +223,26 @@
#else
# define FMT_PRAGMA_CLANG(x)
#endif
#if FMT_MSC_VERSION
# define FMT_PRAGMA_MSVC(x) __pragma(x)
#else
# define FMT_PRAGMA_MSVC(x)
#endif
#ifndef FMT_USE_OPTIMIZE_PRAGMA
# define FMT_USE_OPTIMIZE_PRAGMA 1
#endif
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
#if FMT_USE_OPTIMIZE_PRAGMA && !defined(__OPTIMIZE__) && \
!defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
#endif
FMT_PRAGMA_MSVC(warning(push))
FMT_PRAGMA_MSVC(warning(disable : 4702))
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
@ -484,6 +497,15 @@ inline FMT_CONSTEXPR auto get_container(OutputIt it) ->
};
return *accessor(it).container;
}
template <typename T, typename Enable = void>
struct is_contiguous : std::false_type {};
template <typename T>
struct is_contiguous<T,
void_t<decltype(std::declval<T&>().data()),
decltype(std::declval<T&>().size()),
decltype(std::declval<T&>().operator[](size_t()))>>
: std::true_type {};
} // namespace detail
// Parsing-related public API and forward declarations.
@ -587,9 +609,6 @@ using string_view = basic_string_view<char>;
template <typename T> class basic_appender;
using appender = basic_appender<char>;
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
class context;
template <typename OutputIt, typename Char> class generic_context;
template <typename Char> class parse_context;
@ -608,6 +627,8 @@ using buffered_context =
conditional_t<std::is_same<Char, char>::value, context,
generic_context<basic_appender<Char>, Char>>;
template <typename T> struct is_contiguous : detail::is_contiguous<T> {};
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
@ -1858,6 +1879,54 @@ class fixed_buffer_traits {
}
};
template <typename OutputIt, typename InputIt, typename = void>
struct has_append : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_append<OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>>
: std::true_type {};
template <typename OutputIt, typename T, typename = void>
struct has_insert : std::false_type {};
template <typename OutputIt, typename T>
struct has_insert<
OutputIt, T,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert({}, std::declval<T>(), std::declval<T>()))>>
: std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>() &&
has_append<OutputIt, InputIt>())>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
get_container(out).append(begin, end);
return out;
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>() &&
!has_append<OutputIt, InputIt>() &&
has_insert<OutputIt, InputIt>())>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
auto& c = get_container(out);
c.insert(c.end(), begin, end);
return out;
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>() ||
!(has_append<OutputIt, InputIt>() ||
has_insert<OutputIt, InputIt>()))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
}
// A buffer that writes to an output iterator when flushed.
template <typename OutputIt, typename T, typename Traits = buffer_traits>
class iterator_buffer : public Traits, public buffer<T> {
@ -1875,7 +1944,7 @@ class iterator_buffer : public Traits, public buffer<T> {
this->clear();
const T* begin = data_;
const T* end = begin + this->limit(size);
while (begin != end) *out_++ = *begin++;
out_ = copy<T>(begin, end, out_);
}
public:
@ -2533,7 +2602,7 @@ template <typename Context> class basic_format_args {
FMT_CONSTEXPR auto get(int id) const -> format_arg {
auto arg = format_arg();
if (!is_packed()) {
if (id < max_size()) arg = args_[id];
if (unsigned(id) < unsigned(max_size())) arg = args_[id];
return arg;
}
if (unsigned(id) >= detail::max_packed_args) return arg;
@ -2644,16 +2713,16 @@ template <typename... T> struct fstring {
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
std::is_reference<T>::value)...>() == 0,
"passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
if (FMT_USE_CONSTEVAL)
parse_format_string<char>(str, checker(str, arg_pack()));
constexpr bool unused = detail::enforce_compile_checks<sizeof(s) != 0>();
(void)unused;
}
template <typename S,
FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
auto sv = string_view(str);
if (FMT_USE_CONSTEVAL)
detail::parse_format_string<char>(sv, checker(sv, arg_pack()));
detail::parse_format_string<char>(str, checker(str, arg_pack()));
constexpr bool unused = detail::enforce_compile_checks<sizeof(s) != 0>();
(void)unused;
}
@ -2877,6 +2946,7 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
}
FMT_PRAGMA_GCC(pop_options)
FMT_PRAGMA_MSVC(warning(pop))
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - chrono support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -934,10 +934,9 @@ inline auto to_nonnegative_int(T value, Int upper) -> Int {
}
template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
inline auto to_nonnegative_int(T value, Int upper) -> Int {
auto int_value = static_cast<Int>(value);
if (int_value < 0 || value > static_cast<T>(upper))
if (value < 0 || value >= static_cast<T>(upper) + 1)
FMT_THROW(format_error("invalid value"));
return int_value;
return static_cast<Int>(value);
}
constexpr auto pow10(std::uint32_t n) -> long long {
@ -1192,6 +1191,7 @@ class tm_writer {
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
void format_tz_name(const T& tm) {
if (!tm.tm_zone) FMT_THROW(format_error("no timezone"));
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// Copyright (c) 2018 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -471,7 +471,7 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
FMT_CONSTEXPR styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
@ -528,6 +528,42 @@ void print(text_style ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
inline void vprintln(FILE* f, text_style ts, string_view fmt,
format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
buf.push_back('\n');
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
* Formats a string and prints it to the specified file stream followed by a
* newline, using ANSI escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::println(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void println(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
vprintln(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
* Formats a string and prints it to stdout followed by a newline, using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::println(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void println(text_style ts, format_string<T...> fmt, T&&... args) {
return println(stdout, ts, fmt, std::forward<T>(args)...);
}
inline auto vformat(text_style ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
@ -583,8 +619,8 @@ inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(const detail::styled_arg<T>& arg,
FormatContext& ctx) const -> decltype(ctx.out()) {
const auto& ts = arg.style;
auto out = ctx.out();

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -178,6 +178,10 @@ template <typename Char, typename V, int N> struct field {
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else if constexpr (use_format_as<V>::value) {
return write<Char>(out, format_as(arg));
} else if constexpr (use_format_as_member<V>::value) {
return write<Char>(out, formatter<V>::format_as(arg));
} else {
return write<Char>(out, arg);
}

View File

@ -1,3 +1,10 @@
// Formatting library for C++ - core API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "base.h"
// Using fmt::format via fmt/core.h has been deprecated since version 11

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - the C API
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -10,6 +10,24 @@
#include <stdbool.h> // bool
#include <stddef.h> // size_t
#include <stdio.h> // FILE
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# if defined(FMT_LIB_EXPORT)
# define FMT_CAPI __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_CAPI __declspec(dllimport)
# endif
#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
# ifdef __GNUC__
# define FMT_CAPI __attribute__((visibility("default")))
# else
# define FMT_CAPI
# endif
#endif
#ifndef FMT_CAPI
# define FMT_CAPI
#endif
#ifdef __cplusplus
extern "C" {
@ -44,8 +62,10 @@ typedef struct {
enum { fmt_error = -1, fmt_error_invalid_arg = -2 };
int fmt_vformat(char* buffer, size_t size, const char* fmt, const fmt_arg* args,
size_t num_args);
int FMT_CAPI fmt_vformat(char* buffer, size_t size, const char* fmt,
const fmt_arg* args, size_t num_args);
int FMT_CAPI fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args,
size_t num_args);
#ifdef __cplusplus
}
@ -89,7 +109,7 @@ static inline fmt_arg fmt_from_ptr(const void* x) {
return (fmt_arg){.type = fmt_pointer, .value.pointer = x};
}
void fmt_unsupported_type(void);
void FMT_CAPI fmt_unsupported_type(void);
# if !defined(_MSC_VER) || defined(__clang__)
typedef signed char fmt_signed_char;
@ -181,12 +201,17 @@ typedef enum {} fmt_signed_char;
(fmt_arg[]) { FMT_MAP(FMT_MAKE_ARG, ##__VA_ARGS__) }
# define FMT_EXPAND(v) v
# define fmt_format(buffer, size, fmt, ...) \
fmt_vformat( \
(buffer), (size), (fmt), \
FMT_EXPAND(FMT_VA_SELECT( \
FMT_MAKE_NULL, FMT_MAKE_ARGLIST, ##__VA_ARGS__)(__VA_ARGS__)), \
FMT_NARG(, ##__VA_ARGS__))
# define FMT_FORMAT_ARGS(fmt, ...) \
(fmt), \
FMT_EXPAND(FMT_VA_SELECT(FMT_MAKE_NULL, FMT_MAKE_ARGLIST, \
##__VA_ARGS__)(__VA_ARGS__)), \
FMT_NARG(, ##__VA_ARGS__)
# define fmt_format(buffer, size, fmt, ...) \
fmt_vformat((buffer), (size), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__))
# define fmt_print(stream, fmt, ...) \
fmt_vprint((stream), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__))
#endif // __cplusplus

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -8,15 +8,17 @@
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#ifdef __SANITIZE_THREAD__
extern "C" void __tsan_acquire(void*);
extern "C" void __tsan_release(void*);
#endif
#ifndef FMT_MODULE
# include <stddef.h> // ptrdiff_t
# include <algorithm>
# include <cerrno> // errno
# include <climits>
# include <cmath>
# include <exception>
# include <new> // std::bad_alloc
# include <new> // std::bad_alloc
#endif
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
@ -1477,10 +1479,10 @@ template <typename T> struct span {
};
template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
_lock_file(f);
return _lock_file(f);
}
template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
_unlock_file(f);
return _unlock_file(f);
}
#ifndef getc_unlocked
@ -1524,7 +1526,10 @@ template <typename F> class file_base {
FMT_THROW(system_error(errno, FMT_STRING("ungetc failed")));
}
void flush() { fflush(this->file_); }
void flush() {
if (fflush(this->file_) != 0)
FMT_THROW(system_error(errno, FMT_STRING("fflush failed")));
}
};
// A FILE wrapper for glibc.
@ -1570,7 +1575,10 @@ template <typename F> class glibc_file : public file_base<F> {
return memchr(end, '\n', static_cast<size_t>(size));
}
void flush() { fflush_unlocked(this->file_); }
void flush() {
if (fflush_unlocked(this->file_) != 0)
FMT_THROW(system_error(errno, FMT_STRING("fflush failed")));
}
};
// A FILE wrapper for Apple's libc.
@ -1696,6 +1704,9 @@ class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
public:
explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
flockfile(f);
#ifdef __SANITIZE_THREAD__
__tsan_acquire(f);
#endif
file_.init_buffer();
auto buf = file_.get_write_buffer();
set(buf.data, buf.size);
@ -1703,7 +1714,10 @@ class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
~file_print_buffer() {
file_.advance_write_buffer(size());
bool flush = file_.needs_flush();
F* f = file_; // Make funlockfile depend on the template parameter F
F* f = file_; // Make funlockfile depend on the template parameter F.
#ifdef __SANITIZE_THREAD__
__tsan_release(f);
#endif
funlockfile(f); // for the system API detection to work.
if (flush) fflush(file_);
}

View File

@ -1,7 +1,7 @@
/*
Formatting library for C++
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@ -326,6 +326,9 @@ class uint128 {
-> uint128 {
return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
}
friend constexpr auto operator~(const uint128& n) -> uint128 {
return {~n.hi_, ~n.lo_};
}
friend FMT_CONSTEXPR auto operator+(const uint128& lhs, const uint128& rhs)
-> uint128 {
auto result = uint128(lhs);
@ -548,60 +551,6 @@ FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
return out + count;
}
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_append : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_append<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert(get_container(std::declval<OutputIt>()).end(),
std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
has_back_insert_iterator_container_append<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
get_container(out).append(begin, end);
return out;
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append<
OutputIt, InputIt>::value &&
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
auto& c = get_container(out);
c.insert(c.end(), begin, end);
return out;
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
(has_back_insert_iterator_container_append<
OutputIt, InputIt>::value ||
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
}
template <typename T, typename V, typename OutputIt>
FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
return copy<T>(s.begin(), s.end(), out);
@ -1115,12 +1064,6 @@ template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
count += 4;
}
}
#if FMT_USE_INT128
FMT_CONSTEXPR inline auto count_digits(native_uint128 n) -> int {
return count_digits_fallback(n);
}
#endif
#ifdef FMT_BUILTIN_CLZLL
// It is a separate function rather than a part of count_digits to workaround
// the lack of static constexpr in constexpr functions.
@ -1151,6 +1094,20 @@ FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
return count_digits_fallback(n);
}
#if FMT_USE_INT128
FMT_CONSTEXPR20 inline auto count_digits(native_uint128 n) -> int {
if (is_constant_evaluated()) return count_digits_fallback(n);
// 128-bit division is a slow library call, so reduce to 64-bit chunks via
// 10^19 (the largest power of 10 fitting in uint64_t).
const uint64_t pow10_19 = 10000000000000000000ULL;
if ((n >> 64) == 0) return count_digits(static_cast<uint64_t>(n));
n /= pow10_19;
if ((n >> 64) == 0) return count_digits(static_cast<uint64_t>(n)) + 19;
n /= pow10_19;
return count_digits(static_cast<uint64_t>(n)) + 38;
}
#endif
// Counts the number of digits in n. BITS = log2(radix).
template <int BITS, typename UInt>
FMT_CONSTEXPR auto count_digits(UInt n) -> int {
@ -1282,10 +1239,30 @@ template <typename Char, typename UInt>
FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size)
-> Char* {
FMT_ASSERT(size >= count_digits(value), "invalid digit count");
#if FMT_USE_INT128
// Avoid slow 128-bit division by formatting 64-bit chunks of 19 digits each.
if (!is_constant_evaluated() && num_bits<UInt>() == 128) {
const uint64_t pow10_19 = 10000000000000000000ULL;
auto v = static_cast<native_uint128>(value);
unsigned pos = to_unsigned(size);
while ((v >> 64) != 0) {
auto chunk = static_cast<uint64_t>(v % pow10_19);
v /= pow10_19;
pos -= 19;
fill_n(out + pos, 19, Char('0'));
do_format_decimal(out + pos, chunk, 19);
}
auto top = static_cast<uint64_t>(v);
int top_digits = count_digits(top);
pos -= to_unsigned(top_digits);
do_format_decimal(out + pos, top, top_digits);
return out + pos;
}
#endif // FMT_USE_INT128
unsigned n = to_unsigned(size);
while (value >= 100) {
n -= 2;
if (!is_constant_evaluated() && sizeof(UInt) == 4) {
if (!is_constant_evaluated() && num_bits<UInt>() == 32) {
auto p = value * static_cast<uint64_t>((1ull << 39) / 100 + 1);
write2digits_i(out + n, p >> (39 - 7) & ((1 << 7) - 1));
value = static_cast<UInt>(p >> 39) +
@ -2012,6 +1989,15 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
}
// Writes an integer as a character, treating chars as unsigned.
template <typename Char, typename OutputIt, typename UInt>
FMT_CONSTEXPR auto write_int_chr(OutputIt out, UInt abs_value, bool negative,
const format_specs& specs) -> OutputIt {
if (negative || abs_value > max_value<make_unsigned_t<Char>>())
report_error("character value out of range");
return write_char<Char>(out, static_cast<Char>(abs_value), specs);
}
// Writes a decimal integer with digit grouping.
template <typename OutputIt, typename UInt, typename Char>
auto write_int(OutputIt out, UInt value, unsigned prefix,
@ -2048,7 +2034,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
format_base2e<char>(1, appender(buffer), value, num_digits);
break;
case presentation_type::chr:
return write_char<Char>(out, static_cast<Char>(value), specs);
return write_int_chr<Char>(out, value, (prefix & 0xff) == '-', specs);
}
unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) +
@ -2175,7 +2161,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
break;
case presentation_type::chr:
return write_char<Char>(out, static_cast<Char>(abs_value), specs);
return write_int_chr<Char>(out, abs_value, (prefix & 0xff) == '-', specs);
}
// Write an integer in the format
@ -2208,29 +2194,19 @@ FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out,
return write_int<Char>(out, arg, specs);
}
template <typename Char, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value &&
!std::is_same<T, Char>::value)>
FMT_CONSTEXPR FMT_INLINE auto write(basic_appender<Char> out, T value,
const format_specs& specs, locale_ref loc)
-> basic_appender<Char> {
if (specs.localized() && write_loc(out, value, specs, loc)) return out;
return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign()),
specs);
}
// An inlined version of write used in format string compilation.
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value &&
!std::is_same<T, Char>::value &&
!std::is_same<OutputIt, basic_appender<Char>>::value)>
!std::is_same<T, Char>::value)>
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const format_specs& specs, locale_ref loc)
-> OutputIt {
if (specs.localized() && write_loc(out, value, specs, loc)) return out;
return write_int<Char>(out, make_write_int_arg(value, specs.sign()), specs);
auto arg = make_write_int_arg(value, specs.sign());
// Out of line for appenders to avoid bloat; inlined for compiled formatting.
if FMT_CONSTEXPR20 (std::is_same<OutputIt, basic_appender<Char>>::value)
return write_int_noinline<Char>(out, arg, specs);
return write_int<Char>(out, arg, specs);
}
template <typename Char, typename OutputIt>
@ -3215,8 +3191,9 @@ constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t {
// It is equal to ceil(2^31 + 2^32/10^(k + 1)).
// These are stored in a string literal because we cannot have static arrays
// in constexpr functions and non-static ones are poorly optimized.
return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7"
U"\x800001ae\x8000002b"[index];
return uint32_t(u"\x9999\x828f\x8041\x8006\x8000\x8000\x8000\x8000"[index])
<< 16u |
uint32_t(u"\x999a\x5c29\x8938\x8db9\xa7c6\x10c7\x01ae\x002b"[index]);
}
template <typename Float>
@ -3941,7 +3918,7 @@ template <typename OutputIt, typename Char> class generic_context {
constexpr auto out() const -> iterator { return out_; }
void advance_to(iterator it) {
FMT_CONSTEXPR void advance_to(iterator it) {
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -330,6 +330,7 @@ template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
if (it == end) return arg_index;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
@ -437,6 +438,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
if (it == end) report_error("invalid format string");
auto specs = format_specs();
specs.set_align(align::right);

View File

@ -69,11 +69,12 @@ struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
FMT_CONSTEXPR auto range_begin(T&& rng)
-> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T>
auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
FMT_CONSTEXPR auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
@ -129,6 +130,13 @@ template <typename T> class is_tuple_like_ {
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T, typename _ = void>
struct is_optional_like_ : std::false_type {};
template <typename T>
struct is_optional_like_<T, void_t<decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().value())>>
: std::true_type {};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
@ -343,8 +351,9 @@ struct formatter<Tuple, Char,
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
static constexpr bool value = detail::is_range_<T>::value &&
!detail::is_optional_like_<T>::value &&
!detail::has_to_string_view<T>::value;
};
namespace detail {
@ -460,7 +469,8 @@ struct range_formatter<
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(R&& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
@ -516,7 +526,7 @@ struct formatter<
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
FMT_CONSTEXPR auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
@ -625,7 +635,7 @@ struct join_view : detail::view {
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
FMT_CONSTEXPR join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
@ -652,7 +662,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(view& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
@ -675,7 +686,7 @@ template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
FMT_CONSTEXPR tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
@ -694,8 +705,9 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
}
template <typename FormatContext>
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
FMT_CONSTEXPR auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const ->
typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@ -726,15 +738,17 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>&,
FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
auto out =
@ -813,7 +827,7 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
* // Output: 01, 02, 03
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
FMT_CONSTEXPR auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -133,8 +133,8 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
#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 {
FMT_CONSTEXPR 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);
@ -508,7 +508,7 @@ template <typename Char> struct formatter<std::monostate, Char> {
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
FMT_CONSTEXPR auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
@ -524,7 +524,7 @@ struct formatter<Variant, Char,
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
FMT_CONSTEXPR20 auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
@ -637,15 +637,52 @@ struct formatter<
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
return write(ctx.out(), ex);
}
private:
template <typename OutputIt>
auto write(OutputIt out, const std::exception& ex) const -> OutputIt {
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<char>(out, string_view(ex.what()));
#endif // FMT_USE_RTTI
out = detail::write_bytes<char>(out, string_view(ex.what()));
#if FMT_USE_RTTI
// If the exception carries a nested exception (e.g. via
// std::throw_with_nested), format the whole chain.
if (auto* nested = dynamic_cast<const std::nested_exception*>(&ex)) {
if (auto ep = nested->nested_ptr()) {
out = detail::write(out, string_view(": "));
try {
std::rethrow_exception(ep);
} catch (const std::exception& nested_ex) {
out = write(out, nested_ex);
} catch (...) {
out = detail::write(out, string_view("unknown exception"));
}
}
}
#endif // FMT_USE_RTTI
return out;
}
};
template <> struct formatter<std::exception_ptr> : formatter<std::exception> {
template <typename FormatContext>
auto format(const std::exception_ptr& ep, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!ep) return detail::write(ctx.out(), string_view("none"));
try {
std::rethrow_exception(ep);
} catch (const std::exception& e) {
return formatter<std::exception>::format(e, ctx);
} catch (...) {
return detail::write(ctx.out(), string_view("unknown exception"));
}
}
};
@ -690,11 +727,12 @@ struct formatter<BitRef, Char,
#ifdef __cpp_lib_byte
template <typename Char>
struct formatter<std::byte, Char> : formatter<unsigned, Char> {
static auto format_as(std::byte b) -> unsigned char {
FMT_CONSTEXPR static auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
template <typename Context>
auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(std::byte b, Context& ctx) const
-> decltype(ctx.out()) {
return formatter<unsigned, Char>::format(format_as(b), ctx);
}
};

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -279,10 +279,18 @@ inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, wchar_t>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, wformat_string<T...> fmt,
T&&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt.get(), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - the C API
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -9,12 +9,13 @@
#include <fmt/base.h>
extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt,
const fmt_arg* args, size_t num_args) {
constexpr size_t max_args = 16;
if (num_args > max_args) return fmt_error_invalid_arg;
constexpr size_t max_c_format_args = 16;
static int convert_c_format_args(
fmt::basic_format_arg<fmt::format_context>* format_args,
const fmt_arg* args, size_t num_args) {
if (num_args > max_c_format_args) return fmt_error_invalid_arg;
fmt::basic_format_arg<fmt::format_context> format_args[max_args];
for (size_t i = 0; i < num_args; ++i) {
switch (args[i].type) {
case fmt_int: format_args[i] = args[i].value.int_value; break;
@ -31,12 +32,36 @@ extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt,
default: return fmt_error_invalid_arg;
}
}
try {
return 0;
}
extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt,
const fmt_arg* args, size_t num_args) {
fmt::basic_format_arg<fmt::format_context> format_args[max_c_format_args];
int error = convert_c_format_args(format_args, args, num_args);
if (error != 0) return error;
FMT_TRY {
auto result = fmt::vformat_to_n(
buffer, size, fmt,
fmt::format_args(format_args, static_cast<int>(num_args)));
return static_cast<int>(result.size);
} catch (...) {
}
FMT_CATCH(...) {}
return fmt_error;
}
extern "C" int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args,
size_t num_args) {
fmt::basic_format_arg<fmt::format_context> format_args[max_c_format_args];
int error = convert_c_format_args(format_args, args, num_args);
if (error != 0) return error;
FMT_TRY {
fmt::vprint(stream, fmt,
fmt::format_args(format_args, static_cast<int>(num_args)));
return 0;
}
FMT_CATCH(...) {}
return fmt_error;
}

View File

@ -1,3 +1,10 @@
// Formatting library for C++ - C++20 module
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
module;
#define FMT_MODULE

View File

@ -1,6 +1,6 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -13,7 +13,6 @@
#include "fmt/os.h"
#ifndef FMT_MODULE
# include <climits>
# if FMT_USE_FCNTL
# include <sys/stat.h>
@ -35,6 +34,8 @@
# ifdef _WIN32
# include <windows.h>
# include <climits> // CHAR_BIT
# endif
#endif

View File

@ -0,0 +1,6 @@
cmakelang==0.6.13 \
--hash=sha256:764b9467195c7c36453d60a829f30229720d26c7dffd41cb516b99bd9c7daf4e \
--hash=sha256:03982e87b00654d024d73ef972d9d9bb0e5726cdb6b8a424a15661fb6278e67f
six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81

View File

@ -0,0 +1,4 @@
mkdocs-material==9.7.0
mkdocstrings==1.0.0
mike==2.1.3
typing_extensions==4.15.0

View File

@ -0,0 +1,502 @@
# Pinned, hash-verified Python dependencies for building the docs.
# To regenerate, run from the repository root:
# uv pip compile support/doc-requirements.in --generate-hashes \
# --python-version 3.10 --python-platform x86_64-unknown-linux-gnu \
# --output-file support/doc-requirements.txt
babel==2.18.0 \
--hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \
--hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35
# via mkdocs-material
backrefs==7.0 \
--hash=sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82 \
--hash=sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475 \
--hash=sha256:a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9 \
--hash=sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9 \
--hash=sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12 \
--hash=sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a
# via mkdocs-material
certifi==2026.5.20 \
--hash=sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897 \
--hash=sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d
# via requests
charset-normalizer==3.4.7 \
--hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \
--hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \
--hash=sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67 \
--hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \
--hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \
--hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \
--hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \
--hash=sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444 \
--hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \
--hash=sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9 \
--hash=sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01 \
--hash=sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217 \
--hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \
--hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \
--hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \
--hash=sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83 \
--hash=sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5 \
--hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \
--hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \
--hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \
--hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \
--hash=sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42 \
--hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \
--hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \
--hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \
--hash=sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207 \
--hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \
--hash=sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734 \
--hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \
--hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \
--hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \
--hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \
--hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \
--hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \
--hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \
--hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \
--hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \
--hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \
--hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \
--hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \
--hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \
--hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \
--hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \
--hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \
--hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \
--hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \
--hash=sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776 \
--hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \
--hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \
--hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \
--hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \
--hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \
--hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \
--hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \
--hash=sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5 \
--hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \
--hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \
--hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \
--hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \
--hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \
--hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \
--hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \
--hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \
--hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \
--hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \
--hash=sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4 \
--hash=sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545 \
--hash=sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706 \
--hash=sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366 \
--hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \
--hash=sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a \
--hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \
--hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \
--hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \
--hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \
--hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \
--hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \
--hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \
--hash=sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319 \
--hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \
--hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \
--hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \
--hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \
--hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \
--hash=sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0 \
--hash=sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686 \
--hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \
--hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \
--hash=sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c \
--hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \
--hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \
--hash=sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60 \
--hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \
--hash=sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274 \
--hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \
--hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \
--hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \
--hash=sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f \
--hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \
--hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \
--hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \
--hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \
--hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \
--hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \
--hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \
--hash=sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00 \
--hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \
--hash=sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3 \
--hash=sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7 \
--hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \
--hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \
--hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \
--hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \
--hash=sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259 \
--hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \
--hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \
--hash=sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30 \
--hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \
--hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \
--hash=sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24 \
--hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \
--hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \
--hash=sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc \
--hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \
--hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \
--hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \
--hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \
--hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \
--hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464
# via requests
click==8.4.1 \
--hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \
--hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96
# via mkdocs
colorama==0.4.6 \
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
# via mkdocs-material
ghp-import==2.1.0 \
--hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \
--hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343
# via mkdocs
idna==3.17 \
--hash=sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c \
--hash=sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f
# via requests
importlib-metadata==9.0.0 \
--hash=sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7 \
--hash=sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc
# via mike
importlib-resources==7.1.0 \
--hash=sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708 \
--hash=sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1
# via mike
jinja2==3.1.6 \
--hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \
--hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
# via
# mike
# mkdocs
# mkdocs-material
# mkdocstrings
markdown==3.10.2 \
--hash=sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950 \
--hash=sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36
# via
# mkdocs
# mkdocs-autorefs
# mkdocs-material
# mkdocstrings
# pymdown-extensions
markupsafe==3.0.3 \
--hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \
--hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \
--hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \
--hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \
--hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \
--hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \
--hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \
--hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \
--hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \
--hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \
--hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \
--hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \
--hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \
--hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \
--hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \
--hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \
--hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \
--hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \
--hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \
--hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \
--hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \
--hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \
--hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \
--hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \
--hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \
--hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \
--hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \
--hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \
--hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \
--hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \
--hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \
--hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \
--hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \
--hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \
--hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \
--hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \
--hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \
--hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \
--hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \
--hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \
--hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \
--hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \
--hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \
--hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \
--hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \
--hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \
--hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \
--hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \
--hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \
--hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \
--hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \
--hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \
--hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \
--hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \
--hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \
--hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \
--hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \
--hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \
--hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \
--hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \
--hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \
--hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \
--hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \
--hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \
--hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \
--hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \
--hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \
--hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \
--hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \
--hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \
--hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \
--hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \
--hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \
--hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \
--hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \
--hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \
--hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \
--hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \
--hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \
--hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \
--hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \
--hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \
--hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \
--hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \
--hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \
--hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \
--hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \
--hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \
--hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50
# via
# jinja2
# mkdocs
# mkdocs-autorefs
# mkdocstrings
mergedeep==1.3.4 \
--hash=sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8 \
--hash=sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307
# via
# mkdocs
# mkdocs-get-deps
mike==2.1.3 \
--hash=sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810 \
--hash=sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a
# via -r /tmp/doc-requirements.in
mkdocs==1.6.1 \
--hash=sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2 \
--hash=sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e
# via
# mike
# mkdocs-autorefs
# mkdocs-material
# mkdocstrings
mkdocs-autorefs==1.4.4 \
--hash=sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089 \
--hash=sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197
# via mkdocstrings
mkdocs-get-deps==0.2.2 \
--hash=sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1 \
--hash=sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650
# via mkdocs
mkdocs-material==9.7.0 \
--hash=sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec \
--hash=sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887
# via -r /tmp/doc-requirements.in
mkdocs-material-extensions==1.3.1 \
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
--hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31
# via mkdocs-material
mkdocstrings==1.0.0 \
--hash=sha256:351a006dbb27aefce241ade110d3cd040c1145b7a3eb5fd5ac23f03ed67f401a \
--hash=sha256:4c50eb960bff6e05dfc631f6bc00dfabffbcb29c5ff25f676d64daae05ed82fa
# via -r /tmp/doc-requirements.in
packaging==26.2 \
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
# via mkdocs
paginate==0.5.7 \
--hash=sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945 \
--hash=sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591
# via mkdocs-material
pathspec==1.1.1 \
--hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \
--hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189
# via mkdocs
platformdirs==4.10.0 \
--hash=sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7 \
--hash=sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a
# via mkdocs-get-deps
pygments==2.20.0 \
--hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
--hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
# via mkdocs-material
pymdown-extensions==10.21.3 \
--hash=sha256:72cfcf55f07aea0d4af2c4f11dd4e52466ddfb1bb819673146398e0bd3a77354 \
--hash=sha256:d7a5d08014fc571e80ca21dd6f854e31f94c489800350564d55d15b3c41e76b6
# via
# mkdocs-material
# mkdocstrings
pyparsing==3.3.2 \
--hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \
--hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc
# via mike
python-dateutil==2.9.0.post0 \
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
# via ghp-import
pyyaml==6.0.3 \
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
--hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \
--hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
--hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \
--hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \
--hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \
--hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \
--hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \
--hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \
--hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \
--hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \
--hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \
--hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \
--hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \
--hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \
--hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \
--hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \
--hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \
--hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \
--hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \
--hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \
--hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \
--hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \
--hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \
--hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \
--hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \
--hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \
--hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \
--hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \
--hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \
--hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \
--hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \
--hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \
--hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \
--hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \
--hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \
--hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \
--hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \
--hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \
--hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \
--hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \
--hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \
--hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \
--hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \
--hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \
--hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \
--hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \
--hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \
--hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \
--hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \
--hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \
--hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \
--hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \
--hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \
--hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \
--hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \
--hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \
--hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \
--hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \
--hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \
--hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \
--hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \
--hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \
--hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \
--hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \
--hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
--hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \
--hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
--hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
--hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \
--hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
--hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
# via
# mike
# mkdocs
# mkdocs-get-deps
# pymdown-extensions
# pyyaml-env-tag
pyyaml-env-tag==1.1 \
--hash=sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04 \
--hash=sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff
# via
# mike
# mkdocs
requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via mkdocs-material
six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
typing-extensions==4.15.0 \
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
# via -r /tmp/doc-requirements.in
urllib3==2.7.0 \
--hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \
--hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897
# via requests
verspec==0.1.0 \
--hash=sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31 \
--hash=sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e
# via mike
watchdog==6.0.0 \
--hash=sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a \
--hash=sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 \
--hash=sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f \
--hash=sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c \
--hash=sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c \
--hash=sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c \
--hash=sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0 \
--hash=sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13 \
--hash=sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134 \
--hash=sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa \
--hash=sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e \
--hash=sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379 \
--hash=sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a \
--hash=sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11 \
--hash=sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282 \
--hash=sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b \
--hash=sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f \
--hash=sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c \
--hash=sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112 \
--hash=sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948 \
--hash=sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881 \
--hash=sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860 \
--hash=sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3 \
--hash=sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680 \
--hash=sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26 \
--hash=sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26 \
--hash=sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e \
--hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \
--hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \
--hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2
# via mkdocs
zipp==4.1.0 \
--hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \
--hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602
# via importlib-metadata

View File

@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM
EZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM
R18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2
B5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY
Wh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT
DhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1
G88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/
ZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU
cs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq
7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc
Ha8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB
tD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz
dHJlQGRlYmlhbi5vcmc+iQJMBBMBCgA2AhsDAh4BAheABAsJCAcEFQoJCAUWAgMB
ABYhBGCE88+BS1fBzxLv1RXPTRivT3QhBQJpl4k7AAoJEBXPTRivT3QhivEP/2Ix
/Pi72KhD47zbxLecS0VLB/gURPC2h5AgWgW5P/GSQe0QXHAkqCWFd9y1Y5PI8e3v
6RZFDCPJrfVKfHzIx4nciUHr0qlJqGaplvf6AZvgvF8Xrua9VZaHnZJO+GmK+WtH
/bCgtzk91O08w8wcZ/4wmopQjcSu69OcpqpPBVClhIxaIIFFlATdUAKNVAzXDQDf
WfWT2N3ghsbac1MRQKKzddzXgvVw5mRAw0NCKUJ2DPSohfRsS1LjVG4wThkLWZJb
tPx6+meunBspvvROthh+HEEjiB5o4i7230AOhyYAOjgZj3wQq7A/2O0Ro1A2zl5L
vB17a6WLnEdUzLgj0NYtE1x4W0fNAspEQpIXvVRkZzHA+GH31a2Fs/S2kqsuF/OY
hsD35d/6lJtxbNY2sYsprDxT2tfdnUG9KCJSI3p/vjGIezFgrfBB7u6lUHx/GAQM
nwpiXKeRRS4fT03aZ79FR3xVE7pLOHRq4tUVEH1lvHQB8aU07NHK61BR8NXS++cg
LhveD+TwCKNQ9WaDdNNDl0jGbOBzcou+KguZD8wfoCj/Ko0CdCUFdO+H+TPsIH4F
nRovkQ5bN7Y7iTHiV96mDOtnE2kd4ctbioEl8/4zroKCZTQEzJRQ/Zz+NdtRa/ev
n2jUyoHWs2Vc5bYLyvwjk6CCurIumdT6JAdEUMF6uQINBFE9lCwBEADiPFiZtMfC
YyOpKLOUwlg24Y6kiJ0R3H3NDmDM6vjfX9V9arhL5WEwOGzXmP2XKaXTLiiTbXlV
EXCsBEQODpQ8yvtqMuYFAZq7QVxu9kn3SXg2NenmnQ0QT+o5v1QQdPsQaQSFpnV6
SRYVNYKCzLutFd9kvEHqyTunMsIOyHJbfOMhqRYCelDmPmhuIPLvTylF4C4YH3g1
LJvUehb/EawaC+8LDivTDfE9K3AXU+GZJwTo1MEh8uWJ5remXy+Eo+UKHngeTsjx
AaOjUROVE/hCWXVnEcny7bl/+1pERLYDDweLuPQRVxnoxP0lZMjzfJRydSVk9lLY
fJ5ZxSP7ydQLxbg3wCf5o2mz2X/bWKciXZNsajdifUlAucDVgKgbALfiDxBlMmES
URAE3Rfb1rJZbo50XHwwO+WVsFXKaYqraUrVTnmZBN8OWLtLe98bP0an+hDBnxtC
k55SRzmFtmzNjA/hFf7rzEM9+f4z252X6pb3C74vJ49tbodf3QuaSE2SRjZnNQ9E
8MdkA1Tlu1o9dzYft51fjLiZpBrF6eoPSUaH0sbP+amzgcRm0IjkJjyjS4IwXeVP
Rd9eG4uSyvs/G/nvK57wstMi04l30ebOn0b3tJFhh6pT7yXI1e1TY92ssurtAeTO
uyWgbvyUKlBxqUcYAInOuVRZU/983ce3eQARAQABiQI2BBgBCgAgAhsMFiEEYITz
z4FLV8HPEu/VFc9NGK9PdCEFAmmXid0ACgkQFc9NGK9PdCF/PxAAlMhoX2YtQ2vc
aPt+6hZ3q/8y+xht/WK7YmPm76b7tmRvahZIIfr9Criu9HyL4nFXlVnYot63qIqa
ebu4GUr7Dau+oimJ2olLXMbSjhg7R/Q9ESqG/6pvBl6gwgKqxQAh8lVgK4KevK++
nxdPLLNe9F1kkwnLKCUvYElEYEUMrqI4GMHfgPw84vCCWmR8CSZ+AJ95tft5Xzxf
7uENFADYkDkLiA4giHSasMOhiVxhZWSI+UVJ2vTGuscma509kqs+CjzXOzCKymQ4
qVCjKLaSkQK4rUV125AU0WfRFpaKtiHWmLic5opBzCGhvJ7HI8iOxAFq3bHB1WN1
Ba7VHOCbSKAFIw+7n+onEmKPpiZe55pc684wHy1RD3YnimdJWyHxMXSGuhggv3W1
CWxE1U2fmnMzXbn3T1VXk5/q/5X4wtirzWobZIzVcg5WcoSx0w0LOqIgf6C1z0jq
FLloorOrbdfYOg6GEhaoiiYiwnWwPiKHNqONpMqXitwh/lCK3SomtdsACy1FlVP7
HvM41+jf7qZ9WqCvUZKc+11XAUdOaqAoxQU/T1I1gPGTTJ3uYvAsOqQc9SZAt3Io
8QkYjS9LQvxtZqAdWUALaiQIYKf3uJE5eWP3sF6uEJA3LTyIZsMd7MO25UEPLKCw
O9BuqGhXJW1iyMZ/Smhp993FdBBf4FA=
=/NY6
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -71,15 +71,18 @@ if len(args) > 0:
# 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:
file.write(redirect_page)
ret = call(['git', 'add', redirect_page_path], cwd=site_dir)
if ret != 0:
sys.exit(ret)
ret = call(['git', 'commit', '--amend', '--no-edit'], cwd=site_dir)
if ret != 0:
sys.exit(ret)
# Generate a redirect page from the old Sphinx api.html to the new api/
# page, preserving the URL hash so links such as api.html#udt keep working.
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:
file.write(redirect_page)
ret = call(['git', 'add', redirect_page_path], cwd=site_dir)
if ret != 0:
sys.exit(ret)
ret = call(['git', 'commit', '--amend', '--no-edit'], cwd=site_dir)
sys.exit(ret)
elif not command.startswith('-'):
args += ['-f', config_path]

View File

@ -35,6 +35,9 @@ class Git:
def clone(self, *args):
return self.call('clone', list(args) + [self.dir])
def fetch(self, *args):
return self.call('fetch', args, cwd=self.dir)
def commit(self, *args):
return self.call('commit', args, cwd=self.dir)
@ -56,16 +59,22 @@ class Git:
def clean_checkout(repo, branch):
repo.clean('-f', '-d')
repo.reset('--hard')
repo.fetch('origin')
repo.checkout(branch)
# Hard-reset to the remote so a reused clone picks up new commits
# instead of building from stale local state.
repo.reset('--hard', 'origin/' + branch)
class Runner:
def __init__(self, cwd):
def __init__(self, cwd, env=None):
self.cwd = cwd
self.env = env
def __call__(self, *args, **kwargs):
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
if self.env is not None:
kwargs['env'] = kwargs.get('env', self.env)
check_call(args, **kwargs)
@ -80,6 +89,28 @@ def create_build_env():
return env
def create_doc_env(env, fmt_repo):
"""Create a virtualenv with the pinned documentation dependencies and
return an environment dict with it prepended to PATH. This ensures the
docs are built with the exact mkdocs/mkdocstrings versions required by the
custom handler, regardless of what is installed system-wide."""
# Use an absolute path so the venv resolves on PATH regardless of the
# working directory the build steps run in.
venv_dir = os.path.abspath(os.path.join(env.build_dir, 'venv'))
shutil.rmtree(venv_dir, ignore_errors=True)
check_call([sys.executable, '-m', 'venv', venv_dir])
venv_bin = os.path.join(venv_dir, 'bin')
pip = os.path.join(venv_bin, 'pip')
check_call([pip, 'install', '--quiet', '--upgrade', 'pip'])
requirements = os.path.join(
fmt_repo.dir, 'support', 'doc-requirements.txt')
check_call(
[pip, 'install', '--quiet', '--require-hashes', '-r', requirements])
doc_env = os.environ.copy()
doc_env['PATH'] = venv_bin + os.pathsep + doc_env.get('PATH', '')
return doc_env
if __name__ == '__main__':
args = docopt.docopt(__doc__)
env = create_build_env()
@ -87,7 +118,7 @@ if __name__ == '__main__':
branch = args.get('<branch>')
if branch is None:
branch = 'master'
branch = 'main'
if not fmt_repo.update('-b', branch, 'git@github.com:fmtlib/fmt'):
clean_checkout(fmt_repo, branch)
@ -151,12 +182,21 @@ if __name__ == '__main__':
fmt_repo.add(changelog)
fmt_repo.commit('-m', 'Update version')
# Build the docs and package.
run = Runner(fmt_repo.dir)
# Build the docs locally in a virtualenv with the pinned doc dependencies;
# the source zip is now built and attached to the release in CI by
# .github/workflows/release.yml, which also generates a SLSA provenance
# attestation for it. The venv is prepended to PATH so that CMake's
# find_program(MKDOCS mkdocs) and the ./mkdocs deploy step below both pick
# up the correct mkdocs/mkdocstrings versions.
doc_env = create_doc_env(env, fmt_repo)
run = Runner(fmt_repo.dir, env=doc_env)
run('cmake', '.')
run('make', 'doc', 'package_source')
run('make', 'doc')
# Create a release on GitHub.
# Create a draft release on GitHub, then trigger the release workflow to
# build the source zip from the `release` branch and attach the zip plus
# *.intoto.jsonl provenance to this draft. After reviewing the draft, the
# maintainer clicks Publish to finalize.
fmt_repo.push('origin', 'release')
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
req = urllib.request.Request(
@ -169,20 +209,21 @@ if __name__ == '__main__':
if response.status != 201:
raise Exception(f'Failed to create a release ' +
'{response.status} {response.reason}')
response_data = json.loads(response.read().decode('utf-8'))
id = response_data['id']
# Upload the package.
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
package = 'fmt-{}.zip'.format(version)
req = urllib.request.Request(
f'{uploads_url}/{id}/assets?name={package}',
headers={'Content-Type': 'application/zip'} | auth_headers,
data=open('build/fmt/' + package, 'rb').read(), method='POST')
with urllib.request.urlopen(req) as response:
if response.status != 201:
raise Exception(f'Failed to upload an asset '
'{response.status} {response.reason}')
# Draft releases do not fire `release: created`, so explicitly dispatch the
# release workflow. It runs from the default branch (`branch`) but builds
# and uploads artifacts from the `release` branch for tag `version`.
dispatch_req = urllib.request.Request(
'https://api.github.com/repos/fmtlib/fmt/actions/workflows/'
'release.yml/dispatches',
data=json.dumps({'ref': branch,
'inputs': {'tag_name': version,
'ref': 'release'}}).encode('utf-8'),
headers=auth_headers, method='POST')
with urllib.request.urlopen(dispatch_req) as response:
if response.status != 204:
raise Exception('Failed to dispatch the release workflow ' +
f'{response.status} {response.reason}')
short_version = '.'.join(version.split('.')[:-1])
check_call(['./mkdocs', 'deploy', short_version])
check_call(['./mkdocs', 'deploy', short_version], env=doc_env)

View File

@ -19,7 +19,7 @@ function (add_fmt_test name)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables)
endif ()
elseif (ADD_FMT_TEST_MODULE)
set(libs test-main)
set(libs test-main fmt-module)
else ()
set(libs test-main fmt)
endif ()
@ -81,9 +81,7 @@ add_executable(perf-sanity perf-sanity.cc)
target_link_libraries(perf-sanity fmt::fmt)
if (FMT_MODULE)
# Module tests are currently disabled.
# add_fmt_test(module-test MODULE test-main.cc)
# target_link_libraries(module-test fmt-module)
add_fmt_test(module-test MODULE)
endif ()
if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC)

View File

@ -15,3 +15,12 @@ if (TARGET fmt::fmt-header-only)
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_link_libraries(header-only-test fmt::fmt-header-only)
endif ()
# Test that a target publicly depending on fmt can itself be exported even
# though fmt is added via add_subdirectory and FMT_INSTALL is off (#4806).
add_library(export-test STATIC export-lib.cc)
target_link_libraries(export-test PUBLIC fmt::fmt)
export(
TARGETS export-test
NAMESPACE test::
FILE ${CMAKE_CURRENT_BINARY_DIR}/export-test-targets.cmake)

View File

@ -0,0 +1,3 @@
#include "fmt/base.h"
void greet(const char* name) { fmt::print("Hello, {}!\n", name); }

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - dynamic argument store tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -4,7 +4,7 @@
// which are slow on some platforms. In other tests FMT_ASSERT is made to throw
// an exception which is much faster and easier to check.
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - core tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -21,8 +21,10 @@
#include <functional> // std::equal_to
#include <iterator> // std::back_insert_iterator, std::distance
#include <limits> // std::numeric_limits
#include <list> // std::list
#include <string> // std::string
#include <type_traits> // std::is_same
#include <vector> // std::vector
#include "gmock/gmock.h"
@ -811,10 +813,10 @@ TEST(base_test, throw_in_buffer_dtor) {
constexpr int buffer_size = 256;
struct throwing_iterator {
int& count;
int* count;
auto operator=(char) -> throwing_iterator& {
if (++count > buffer_size) throw std::exception();
if (++*count > buffer_size) throw std::exception();
return *this;
}
auto operator*() -> throwing_iterator& { return *this; }
@ -824,7 +826,7 @@ TEST(base_test, throw_in_buffer_dtor) {
try {
int count = 0;
fmt::format_to(throwing_iterator{count}, fmt::runtime("{:{}}{"), "",
fmt::format_to(throwing_iterator{&count}, fmt::runtime("{:{}}{"), "",
buffer_size + 1);
} catch (const std::exception&) {
}
@ -872,10 +874,32 @@ struct custom_container {
auto operator[](size_t) -> char& { return data; }
};
struct phantom_subscript_container {
using value_type = char;
auto data() -> char*;
auto size() const -> size_t;
operator const char*() const;
};
struct real_subscript_container : phantom_subscript_container {
auto operator[](size_t) -> char&;
};
FMT_BEGIN_NAMESPACE
template <> struct is_contiguous<custom_container> : std::true_type {};
FMT_END_NAMESPACE
TEST(base_test, is_contiguous) {
EXPECT_TRUE((fmt::is_contiguous<custom_container>::value));
EXPECT_TRUE((fmt::is_contiguous<std::string>::value));
EXPECT_TRUE((fmt::is_contiguous<fmt::string_view>::value));
EXPECT_TRUE((fmt::is_contiguous<std::vector<char>>::value));
EXPECT_FALSE((fmt::is_contiguous<std::list<char>>::value));
EXPECT_FALSE((fmt::is_contiguous<phantom_subscript_container>::value));
EXPECT_TRUE((fmt::is_contiguous<real_subscript_container>::value));
}
TEST(base_test, format_to_custom_container) {
auto c = custom_container();
fmt::format_to(std::back_inserter(c), "");

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - the C API tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -83,10 +83,30 @@ void test_buffer_size_query(void) {
ASSERT_INT_EQ(size, 15);
}
void test_print(void) {
FILE* file = tmpfile();
if (!file) {
fprintf(stderr, "\nFailed to create temporary file\n");
exit(1);
}
int ret = fmt_print(file, "{} and {}", 42, "foo");
ASSERT_INT_EQ(ret, 0);
rewind(file);
char buf[100];
size_t n = fread(buf, 1, sizeof(buf) - 1, file);
buf[n] = '\0';
ASSERT_STR_EQ(buf, "42 and foo");
fclose(file);
}
int main(void) {
printf("Running C API tests\n");
test_types();
test_zero_arguments();
test_buffer_size_query();
test_print();
printf("C API tests passed\n");
}

View File

@ -1,13 +1,12 @@
// Formatting library for C++ - time formatting tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/chrono.h"
#include <algorithm>
#include <ctime>
#include <vector>
@ -359,6 +358,9 @@ TEST(chrono_test, tm) {
char tz[] = "EET";
if (fmt::detail::set_tm_zone(time, tz)) {
EXPECT_EQ(fmt::format(fmt::runtime("{:%Z}"), time), "EET");
fmt::detail::set_tm_zone(time, nullptr);
EXPECT_THROW_MSG((void)fmt::format(fmt::runtime("{:%Z}"), time),
fmt::format_error, "no timezone");
} else {
EXPECT_THROW_MSG((void)fmt::format(fmt::runtime("{:%Z}"), time),
fmt::format_error, "no timezone");
@ -987,6 +989,9 @@ TEST(chrono_test, glibc_extensions) {
TEST(chrono_test, out_of_range) {
auto d = std::chrono::duration<unsigned long, std::giga>(538976288);
EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error);
// A floating-point day count that doesn't fit in int.
auto fd = std::chrono::duration<double>(1e300);
EXPECT_THROW((void)fmt::format("{:%j}", fd), fmt::format_error);
}
TEST(chrono_test, year_month_day) {

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - color tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -136,3 +136,15 @@ TEST(color_test, print) {
EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
}
TEST(color_test, println) {
EXPECT_WRITE(stdout,
fmt::println(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m\n");
EXPECT_WRITE(stdout, fmt::println(fmt::emphasis::bold, "bold"),
"\x1b[1mbold\x1b[0m\n");
EXPECT_WRITE(
stdout,
fmt::println(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m\n");
}

View File

@ -239,6 +239,16 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
#endif
"
ERROR)
expect_compile(
format-string-embedded-nul-error
"
#if FMT_USE_CONSTEVAL
fmt::format(\"a\\0{}\");
#else
#error
#endif
"
ERROR)
# Compile-time argument name check
expect_compile(

View File

@ -1,19 +1,22 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/compile.h"
#include <array>
#include <iterator>
#include <list>
#include <type_traits>
#include <vector>
#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/ranges.h"
#include "fmt/std.h"
#include "gmock/gmock.h"
#include "gtest-extra.h"
@ -87,7 +90,6 @@ TEST(compile_test, format_escape) {
EXPECT_EQ("\"abc\" ", fmt::format(FMT_COMPILE("{0:<7?}"), "abc"));
}
TEST(compile_test, format_specs) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
EXPECT_EQ("1.2 ms ",
@ -224,6 +226,19 @@ TEST(compile_test, constexpr_formatted_size) {
FMT_CONSTEXPR20 size_t str_size =
fmt::formatted_size(FMT_COMPILE("{:s}"), "abc");
EXPECT_EQ(str_size, 3);
FMT_CONSTEXPR20 size_t tuple_size = fmt::formatted_size(
FMT_COMPILE("{}"), fmt::join(std::tuple(1, 2, 3), ","));
EXPECT_EQ(tuple_size, 5);
FMT_CONSTEXPR20 size_t array_size = fmt::formatted_size(
FMT_COMPILE("{}"), fmt::join(std::array<int, 3>{1, 2, 3}, ","));
EXPECT_EQ(array_size, 5);
FMT_CONSTEXPR20 size_t styled_size = fmt::formatted_size(
FMT_COMPILE("{}"),
fmt::styled(std::array{1, 2, 3}, fmt::bg(fmt::color::green)));
EXPECT_EQ(styled_size, 32);
FMT_CONSTEXPR20 size_t variant_size = fmt::formatted_size(
FMT_COMPILE("{}"), std::variant<std::monostate, char>{});
EXPECT_EQ(variant_size, 18);
}
TEST(compile_test, static_format) {
@ -464,3 +479,13 @@ TEST(compile_test, constexpr_string_format) {
EXPECT_TRUE(big);
}
#endif // FMT_USE_CONSTEXPR_STRING
namespace {
struct compile_format_as_type {
int value;
};
int format_as(compile_format_as_type f) { return f.value; }
}
TEST(compile_test, format_as) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), compile_format_as_type{42}));
}

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - tests of formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,11 +1,10 @@
// Formatting library for C++ - formatting library implementation tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include <algorithm>
#include <cstring>
// clang-format off
@ -14,7 +13,6 @@
#include "fmt/format.h"
#include "gmock/gmock.h"
#include "util.h"
using fmt::detail::bigint;
using fmt::detail::fp;

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -15,17 +15,17 @@
#include <stdint.h> // uint32_t
#include <cfenv> // fegetexceptflag and FE_ALL_EXCEPT
#include <climits> // INT_MAX
#include <cmath> // std::signbit
#include <condition_variable> // std::condition_variable
#include <cstring> // std::strlen
#include <iterator> // std::back_inserter
#include <list> // std::list
#include <mutex> // std::mutex
#include <string> // std::string
#include <thread> // std::thread
#include <type_traits> // std::is_default_constructible
#include <cfenv> // fegetexceptflag and FE_ALL_EXCEPT
#include <climits> // INT_MAX
#include <cmath> // std::signbit
#include <csignal> // std::signal, SIGPIPE
#include <cstring> // std::strlen
#include <iterator> // std::back_inserter
#include <list> // std::list
#include <mutex> // std::mutex
#include <string> // std::string
#include <thread> // std::thread
#include <type_traits> // std::is_default_constructible
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
@ -82,6 +82,11 @@ TEST(uint128_test, minus) {
EXPECT_EQ(n - 2, 40);
}
TEST(uint128_test, bitwise_not) {
auto n = ~uint128(0x123456789abcdef0, 0x0fedcba987654321);
EXPECT_EQ(n, uint128(0xedcba9876543210f, 0xf0123456789abcde));
}
TEST(uint128_test, plus_assign) {
auto n = uint128(32);
n += uint128(10);
@ -268,22 +273,6 @@ TEST(util_test, format_system_error) {
fmt::format_system_error(message, EDOM, "test");
auto ec = std::error_code(EDOM, std::generic_category());
EXPECT_EQ(to_string(message), std::system_error(ec, "test").what());
message = fmt::memory_buffer();
// Check if std::allocator throws on allocating max size_t / 2 chars.
size_t max_size = max_value<size_t>() / 2;
bool throws_on_alloc = false;
try {
auto alloc = std::allocator<char>();
alloc.deallocate(alloc.allocate(max_size), max_size);
} catch (const std::bad_alloc&) {
throws_on_alloc = true;
}
if (!throws_on_alloc) {
fmt::print(stderr, "warning: std::allocator allocates {} chars\n",
max_size);
return;
}
}
TEST(util_test, system_error) {
@ -1345,6 +1334,16 @@ TEST(format_test, format_int) {
"invalid format specifier");
check_unknown_types(42, "bBdoxXnLc", "integer");
EXPECT_EQ(fmt::format("{:c}", static_cast<int>('x')), "x");
// The 'c' type treats all character types as unsigned for portability, so the
// representable range for char is [0, 255] and out-of-range values are
// reported as an error.
EXPECT_EQ(fmt::format("{:c}", 200), std::string(1, static_cast<char>(200)));
EXPECT_EQ(fmt::format("{:c}", 255), std::string(1, static_cast<char>(255)));
const char* msg = "character value out of range";
EXPECT_THROW_MSG((void)fmt::format("{:c}", -1), format_error, msg);
EXPECT_THROW_MSG((void)fmt::format("{:c}", -104), format_error, msg);
EXPECT_THROW_MSG((void)fmt::format("{:c}", 256), format_error, msg);
EXPECT_THROW_MSG((void)fmt::format("{:c}", 400u), format_error, msg);
}
TEST(format_test, format_bin) {
@ -2609,6 +2608,24 @@ TEST(format_test, invalid_glibc_buffer) {
fmt::print(file, "------\n");
}
TEST(format_test, print_to_broken_pipe) {
// Ignore SIGPIPE so that a failing write reports EPIPE instead of
// terminating the test process. It must stay ignored until the file is
// closed below because closing also flushes the remaining buffered data.
auto old_handler = std::signal(SIGPIPE, SIG_IGN);
{
auto pipe = fmt::pipe();
pipe.read_end.close();
auto write_end = pipe.write_end.fdopen("w");
// The data must exceed the file's buffer to force a flush during
// formatting, whose underlying write() then fails with EPIPE.
auto data = std::string(1024 * 1024, 'x');
EXPECT_THROW(fmt::print(write_end.get(), "{}", data), std::system_error);
}
std::signal(SIGPIPE, old_handler);
}
#endif // FMT_USE_FCNTL
// Only defined after the test case.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - tests of custom Google Test assertions
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -9,7 +9,6 @@
#include <gtest/gtest-spi.h>
#include <cstring>
#include <memory>
#include <stdexcept>

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - custom Google Test assertions
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - custom Google Test assertions
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - mock allocator
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,19 +1,9 @@
// Formatting library for C++ - module tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2021 - present, Daniela Engert
// All Rights Reserved
// {fmt} module.
#ifdef _MSC_FULL_VER
// hide some implementation bugs in msvc
// that are not essential to users of the module.
# define FMT_HIDE_MODULE_BUGS
#endif
#include <bit>
#include <chrono>
@ -25,6 +15,7 @@
#include <string>
#include <string_view>
#include <system_error>
#include <gmock/gmock.h>
#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
@ -42,14 +33,6 @@
import fmt;
// check for macros leaking from BMI
static bool macro_leaked =
#if defined(FMT_CORE_H_) || defined(FMT_FORMAT_H_)
true;
#else
false;
#endif
#define FMT_OS_H_ // don't pull in os.h, neither directly nor indirectly
#include "gtest-extra.h"
@ -60,42 +43,11 @@ TEST(module_test, namespace) {
ASSERT_TRUE(true);
}
namespace detail {
bool oops_detail_namespace_is_visible;
}
namespace fmt {
bool namespace_detail_invisible() {
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
_MSC_FULL_VER <= 193700000
// bug in msvc up to at least 17.7:
// the namespace is visible even when it is neither
// implicitly nor explicitly exported
return true;
#else
using namespace detail;
// this fails to compile if fmt::detail is visible
return !oops_detail_namespace_is_visible;
#endif
}
} // namespace fmt
// the non-exported namespace 'detail' must be invisible [module.interface]/2
TEST(module_test, detail_namespace) {
EXPECT_TRUE(fmt::namespace_detail_invisible());
}
// macros must not be imported from a *named* module [cpp.import]/5.1
// Macros must not be imported from a named module [cpp.import]/5.1.
TEST(module_test, macros) {
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
_MSC_FULL_VER <= 192930130
// bug in msvc up to 16.11-pre2:
// include-guard macros leak from BMI
// and even worse: they cannot be #undef-ined
macro_leaked = false;
#if defined(FMT_BASE_H_) || defined(FMT_FORMAT_H_)
FAIL() << "Macros are leaking from a named module";
#endif
EXPECT_FALSE(macro_leaked);
}
// The following is less about functional testing (that's done elsewhere)
@ -167,78 +119,33 @@ TEST(module_test, format_to_n) {
}
TEST(module_test, format_args) {
auto no_args = fmt::format_args();
EXPECT_FALSE(no_args.get(1));
fmt::basic_format_args args = fmt::make_format_args(42);
EXPECT_TRUE(args.max_size() > 0);
auto arg0 = args.get(0);
EXPECT_TRUE(arg0);
decltype(arg0) arg_none;
EXPECT_FALSE(arg_none);
EXPECT_TRUE(arg0.type() != arg_none.type());
}
TEST(module_test, wformat_args) {
auto no_args = fmt::wformat_args();
EXPECT_FALSE(no_args.get(1));
fmt::basic_format_args args = fmt::make_wformat_args(42);
int n = 42;
auto store = fmt::make_format_args(n);
fmt::format_args args = store;
EXPECT_TRUE(args.get(0));
}
TEST(module_test, dynamic_format_args) {
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
dyn_store.push_back(fmt::arg("a42", 42));
fmt::basic_format_args args = dyn_store;
EXPECT_FALSE(args.get(3));
EXPECT_TRUE(args.get(fmt::string_view("a42")));
fmt::dynamic_format_arg_store<fmt::wformat_context> wdyn_store;
wdyn_store.push_back(fmt::arg(L"a42", 42));
fmt::basic_format_args wargs = wdyn_store;
EXPECT_FALSE(wargs.get(3));
EXPECT_TRUE(wargs.get(fmt::wstring_view(L"a42")));
EXPECT_FALSE(args.get(1));
}
TEST(module_test, vformat) {
EXPECT_EQ("42", fmt::vformat("{}", fmt::make_format_args(42)));
EXPECT_EQ(L"42",
fmt::vformat(fmt::wstring_view(L"{}"), fmt::make_wformat_args(42)));
int n = 42;
EXPECT_EQ("42", fmt::vformat("{}", fmt::make_format_args(n)));
}
TEST(module_test, vformat_to) {
auto store = fmt::make_format_args(42);
int n = 42;
auto store = fmt::make_format_args(n);
std::string s;
fmt::vformat_to(std::back_inserter(s), "{}", store);
EXPECT_EQ("42", s);
char buffer[4] = {0};
fmt::vformat_to(buffer, "{:}", store);
EXPECT_EQ("42", std::string_view(buffer));
auto wstore = fmt::make_wformat_args(42);
std::wstring w;
fmt::vformat_to(std::back_inserter(w), L"{}", wstore);
EXPECT_EQ(L"42", w);
wchar_t wbuffer[4] = {0};
fmt::vformat_to(wbuffer, L"{:}", wstore);
EXPECT_EQ(L"42", std::wstring_view(wbuffer));
}
TEST(module_test, vformat_to_n) {
auto store = fmt::make_format_args(12345);
int n = 12345;
auto store = fmt::make_format_args(n);
std::string s;
auto result = fmt::vformat_to_n(std::back_inserter(s), 1, "{}", store);
fmt::vformat_to_n(std::back_inserter(s), 1, "{}", store);
char buffer[4] = {0};
fmt::vformat_to_n(buffer, 3, "{:}", store);
auto wstore = fmt::make_wformat_args(12345);
std::wstring w;
auto wresult = fmt::vformat_to_n(std::back_inserter(w), 1,
fmt::wstring_view(L"{}"), wstore);
wchar_t wbuffer[4] = {0};
fmt::vformat_to_n(wbuffer, 3, fmt::wstring_view(L"{:}"), wstore);
}
std::string as_string(std::wstring_view text) {
@ -249,18 +156,14 @@ std::string as_string(std::wstring_view text) {
TEST(module_test, print) {
EXPECT_WRITE(stdout, fmt::print("{}µ", 42), "42µ");
EXPECT_WRITE(stderr, fmt::print(stderr, "{}µ", 4.2), "4.2µ");
EXPECT_WRITE(stdout, fmt::print(L"{}µ", 42), as_string(L"42µ"));
EXPECT_WRITE(stderr, fmt::print(stderr, L"{}µ", 4.2), as_string(L"4.2µ"));
}
TEST(module_test, vprint) {
EXPECT_WRITE(stdout, fmt::vprint("{:}µ", fmt::make_format_args(42)), "42µ");
EXPECT_WRITE(stderr, fmt::vprint(stderr, "{}", fmt::make_format_args(4.2)),
int n = 42;
double m = 4.2;
EXPECT_WRITE(stdout, fmt::vprint("{:}µ", fmt::make_format_args(n)), "42µ");
EXPECT_WRITE(stderr, fmt::vprint(stderr, "{}", fmt::make_format_args(m)),
"4.2");
EXPECT_WRITE(stdout, fmt::vprint(L"{:}µ", fmt::make_wformat_args(42)),
as_string(L"42µ"));
EXPECT_WRITE(stderr, fmt::vprint(stderr, L"{}", fmt::make_wformat_args(42)),
as_string(L"42"));
}
TEST(module_test, named_args) {
@ -275,7 +178,8 @@ TEST(module_test, literals) {
}
TEST(module_test, locale) {
auto store = fmt::make_format_args(4.2);
const double m = 4.2;
auto store = fmt::make_format_args(m);
const auto classic = std::locale::classic();
EXPECT_EQ("4.2", fmt::format(classic, "{:L}", 4.2));
EXPECT_EQ("4.2", fmt::vformat(classic, "{:L}", store));
@ -283,14 +187,6 @@ TEST(module_test, locale) {
fmt::vformat_to(std::back_inserter(s), classic, "{:L}", store);
EXPECT_EQ("4.2", s);
EXPECT_EQ("4.2", fmt::format("{:L}", 4.2));
auto wstore = fmt::make_wformat_args(4.2);
EXPECT_EQ(L"4.2", fmt::format(classic, L"{:L}", 4.2));
EXPECT_EQ(L"4.2", fmt::vformat(classic, L"{:L}", wstore));
std::wstring w;
fmt::vformat_to(std::back_inserter(w), classic, L"{:L}", wstore);
EXPECT_EQ(L"4.2", w);
EXPECT_EQ(L"4.2", fmt::format(L"{:L}", 4.2));
}
TEST(module_test, string_view) {
@ -313,34 +209,17 @@ TEST(module_test, memory_buffer) {
EXPECT_EQ("42", to_string(buffer));
nbuffer.clear();
EXPECT_EQ(0u, to_string(nbuffer).size());
fmt::wmemory_buffer wbuffer;
EXPECT_EQ(0u, to_string(wbuffer).size());
}
TEST(module_test, is_char) {
EXPECT_TRUE(fmt::is_char<char>());
EXPECT_TRUE(fmt::is_char<wchar_t>());
EXPECT_TRUE(fmt::is_char<char8_t>());
EXPECT_TRUE(fmt::is_char<char16_t>());
EXPECT_TRUE(fmt::is_char<char32_t>());
EXPECT_FALSE(fmt::is_char<signed char>());
}
TEST(module_test, ptr) {
uintptr_t answer = 42;
auto p = std::bit_cast<int*>(answer);
EXPECT_EQ("0x2a", fmt::to_string(fmt::ptr(p)));
std::unique_ptr<int> up(p);
EXPECT_EQ("0x2a", fmt::to_string(fmt::ptr(up)));
up.release();
auto sp = std::make_shared<int>(0);
p = sp.get();
EXPECT_EQ(fmt::to_string(fmt::ptr(p)), fmt::to_string(fmt::ptr(sp)));
}
TEST(module_test, errors) {
auto store = fmt::make_format_args(42);
int n = 42;
auto store = fmt::make_format_args(n);
EXPECT_THROW(throw fmt::format_error("oops"), std::exception);
EXPECT_THROW(throw fmt::vsystem_error(0, "{}", store), std::system_error);
EXPECT_THROW(throw fmt::system_error(0, "{}", 42), std::system_error);
@ -365,8 +244,6 @@ TEST(module_test, error_code) {
fmt::format("{0}", std::error_code(42, std::generic_category())));
EXPECT_EQ("system:42",
fmt::format("{0}", std::error_code(42, fmt::system_category())));
EXPECT_EQ(L"generic:42",
fmt::format(L"{0}", std::error_code(42, std::generic_category())));
}
TEST(module_test, format_int) {
@ -385,23 +262,19 @@ TEST(module_test, formatter) { EXPECT_TRUE(test_formatter{}.check()); }
TEST(module_test, join) {
int arr[3] = {1, 2, 3};
std::vector<double> vec{1.0, 2.0, 3.0};
std::initializer_list<int> il{1, 2, 3};
auto sep = fmt::string_view(", ");
EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr + 0, arr + 3, sep)));
EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr, sep)));
EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec.begin(), vec.end(), sep)));
EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec, sep)));
EXPECT_EQ("1, 2, 3", to_string(fmt::join(il, sep)));
auto wsep = fmt::wstring_view(L", ");
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr + 0, arr + 3, wsep)));
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr, wsep)));
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(il, wsep)));
}
TEST(module_test, time) {
auto time_now = std::time(nullptr);
EXPECT_TRUE(fmt::localtime(time_now).tm_year > 120);
EXPECT_TRUE(fmt::gmtime(time_now).tm_year > 120);
auto chrono_now = std::chrono::system_clock::now();
EXPECT_TRUE(fmt::gmtime(chrono_now).tm_year > 120);
@ -433,42 +306,17 @@ TEST(module_test, weekday) {
TEST(module_test, printf) {
EXPECT_WRITE(stdout, fmt::printf("%f", 42.123456), "42.123456");
EXPECT_WRITE(stdout, fmt::printf("%d", 42), "42");
EXPECT_WRITE(stdout, fmt::printf(L"%f", 42.123456), as_string(L"42.123456"));
EXPECT_WRITE(stdout, fmt::printf(L"%d", 42), as_string(L"42"));
}
TEST(module_test, fprintf) {
EXPECT_WRITE(stderr, fmt::fprintf(stderr, "%d", 42), "42");
EXPECT_WRITE(stderr, fmt::fprintf(stderr, L"%d", 42), as_string(L"42"));
}
TEST(module_test, sprintf) {
EXPECT_EQ("42", fmt::sprintf("%d", 42));
EXPECT_EQ(L"42", fmt::sprintf(L"%d", 42));
}
TEST(module_test, vprintf) {
EXPECT_WRITE(stdout, fmt::vprintf("%d", fmt::make_printf_args(42)), "42");
EXPECT_WRITE(stdout, fmt::vprintf(L"%d", fmt::make_wprintf_args(42)),
as_string(L"42"));
}
TEST(module_test, vfprintf) {
auto args = fmt::make_printf_args(42);
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, "%d", args), "42");
auto wargs = fmt::make_wprintf_args(42);
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, L"%d", wargs), as_string(L"42"));
}
TEST(module_test, vsprintf) {
EXPECT_EQ("42", fmt::vsprintf("%d", fmt::make_printf_args(42)));
EXPECT_EQ(L"42", fmt::vsprintf(L"%d", fmt::make_wprintf_args(42)));
}
TEST(module_test, color) {
auto fg_check = fg(fmt::rgb(255, 200, 30));
auto bg_check = bg(fmt::color::dark_slate_gray) | fmt::emphasis::italic;
auto emphasis_check = fmt::emphasis::underline | fmt::emphasis::bold;
EXPECT_EQ("\x1B[30m42\x1B[0m",
fmt::format(fg(fmt::terminal_color::black), "{}", 42));
EXPECT_EQ(L"\x1B[30m42\x1B[0m",

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - Noexception tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,13 +1,12 @@
// Formatting library for C++ - tests of the OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/os.h"
#include <cstdlib> // std::exit
#include <cstring>
#include <memory>
#include <thread>

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - std::ostream support tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Tests of the C++ interface to POSIX functions that require mocks
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -15,19 +15,19 @@
#include <errno.h>
#include <fcntl.h>
#include <climits>
#include <memory>
#include "../src/os.cc"
#ifdef _WIN32
# include <io.h>
# include <climits> // UINT_MAX
# undef max
#endif
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
using fmt::buffered_file;

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - mocks of POSIX functions
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - printf tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -9,7 +9,6 @@
#include <cctype>
#include <climits>
#include <cstring>
#include "fmt/xchar.h" // DEPRECATED!
#include "gtest-extra.h"
@ -47,6 +46,14 @@ auto test_sprintf(fmt::basic_string_view<wchar_t> format, const Args&... args)
TEST(printf_test, no_args) { EXPECT_EQ("test", test_sprintf("test")); }
TEST(printf_test, trailing_percent) {
EXPECT_THROW_MSG(test_sprintf("%"), format_error, "invalid format string");
EXPECT_THROW_MSG(test_sprintf("hello%"), format_error,
"invalid format string");
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
"invalid format string");
}
TEST(printf_test, escape) {
EXPECT_EQ("%", test_sprintf("%%"));
EXPECT_EQ("before %", test_sprintf("before %%"));
@ -77,8 +84,6 @@ TEST(printf_test, number_is_too_big_in_arg_index) {
}
TEST(printf_test, switch_arg_indexing) {
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", big_num), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
@ -111,6 +116,17 @@ TEST(printf_test, invalid_arg_index) {
"argument not found");
}
TEST(printf_test, zero_positional_width_precision) {
// A '0' positional index for a '*' width or precision must be rejected. Use
// enough arguments to exercise the unpacked argument storage path.
EXPECT_THROW_MSG(test_sprintf("%*0$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16),
format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf("%.*0$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16),
format_error, "argument not found");
}
TEST(printf_test, default_align_right) {
EXPECT_PRINTF(" 42", "%5d", 42);
EXPECT_PRINTF(" abc", "%5s", "abc");
@ -313,7 +329,8 @@ TEST(printf_test, positional_precision) {
EXPECT_EQ("Hell", test_sprintf("%2$.*1$s", 4, "Hello"));
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", 5.0, 42), format_error,
"precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d"), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d"), format_error,
"argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", big_num, 42), format_error,
"number is too big");
}
@ -322,7 +339,8 @@ TEST(printf_test, positional_width_and_precision) {
EXPECT_EQ(" 00042", test_sprintf("%3$*1$.*2$d", 7, 5, 42));
EXPECT_EQ(" ab", test_sprintf("%3$*1$.*2$s", 7, 2, "abcdef"));
EXPECT_EQ(" 00042", test_sprintf("%3$*1$.*2$x", 7, 5, 0x42));
EXPECT_EQ("100.4400000", test_sprintf("%6$-*5$.*4$f%3$s%2$s%1$s", "", "", "", 7, 4, 100.44));
EXPECT_EQ("100.4400000",
test_sprintf("%6$-*5$.*4$f%3$s%2$s%1$s", "", "", "", 7, 4, 100.44));
}
template <typename T> struct make_signed {
@ -555,3 +573,8 @@ TEST(printf_test, make_printf_args) {
fmt::vsprintf(fmt::basic_string_view<wchar_t>(L"[%d] %s happened"),
{fmt::make_printf_args<wchar_t>(n, L"something")}));
}
TEST(printf_test, trailing_percent_non_nul_terminated) {
auto p = std::unique_ptr<char>(new char('%'));
EXPECT_THROW(fmt::sprintf(fmt::string_view(p.get(), 1)), format_error);
}

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - the core API
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -10,7 +10,6 @@
#include <array>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <stack>
#include <string>
@ -272,6 +271,23 @@ TEST(ranges_test, disabled_range_formatting_of_path) {
fmt::range_format::disabled);
}
template <typename T> struct optional_like {
auto begin() const -> const T*;
auto end() const -> const T*;
bool has_value() const noexcept;
T& value() &;
const T& value() const&;
T&& value() &&;
const T&& value() const&&;
};
TEST(ranges_test, disabled_range_formatting_of_optional) {
// (C++26) Has a range support for std::optional.
EXPECT_EQ((fmt::range_format_kind<optional_like<int>, char>::value),
fmt::range_format::disabled);
}
struct vector_string : std::vector<char> {
using base = std::vector<char>;
using base::base;
@ -710,10 +726,17 @@ struct codec_mask {
int except = 0;
};
// A named functor instead of a lambda to avoid -Wsubobject-linkage: a lambda
// gives the filter_view type internal linkage, which propagates to the
// formatter base class via format_as.
struct not_equal {
int value;
bool operator()(int c) const { return c != value; }
};
auto format_as(codec_mask mask) {
// Careful not to capture param by reference here, it will dangle.
return codec_mask::codecs |
std::views::filter([mask](auto c) { return c != mask.except; });
std::views::filter(not_equal{mask.except});
}
} // namespace views_filter_view_test

View File

@ -1,14 +1,12 @@
// Formatting library for C++ - scanning API test
//
// Copyright (c) 2019 - present, Victor Zverovich
// Copyright (c) 2019 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "scan.h"
#include <time.h>
#include <climits>
#include <thread>

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - scanning API proof of concept
//
// Copyright (c) 2019 - present, Victor Zverovich
// Copyright (c) 2019 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - tests of formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -402,6 +402,67 @@ TEST(std_test, exception) {
StartsWith("std::filesystem::filesystem_error: "));
}
#endif
#if FMT_USE_RTTI
// Nested exceptions (e.g. from std::throw_with_nested) are unwound.
try {
try {
throw std::runtime_error("inner");
} catch (...) {
std::throw_with_nested(std::runtime_error("outer"));
}
} catch (const std::exception& ex) {
EXPECT_EQ("outer: inner", fmt::format("{}", ex));
}
// Multiple levels of nesting.
try {
try {
try {
throw std::runtime_error("level 3");
} catch (...) {
std::throw_with_nested(std::runtime_error("level 2"));
}
} catch (...) {
std::throw_with_nested(std::runtime_error("level 1"));
}
} catch (const std::exception& ex) {
EXPECT_EQ("level 1: level 2: level 3", fmt::format("{}", ex));
}
#endif // FMT_USE_RTTI
}
TEST(std_test, exception_ptr) {
std::exception_ptr p1 = nullptr;
std::exception_ptr p2;
try {
using namespace my_ns1::my_ns2;
throw my_exception("My Exception");
} catch (...) {
p2 = std::current_exception();
}
EXPECT_EQ(fmt::format("{}", p1), "none");
EXPECT_EQ(fmt::format("{}", p2), "My Exception");
#if FMT_USE_RTTI
EXPECT_EQ(fmt::format("{:t}", p2),
"my_ns1::my_ns2::my_exception: My Exception");
// Nested exceptions are unwound through an exception_ptr too.
std::exception_ptr p3;
try {
try {
throw std::runtime_error("inner");
} catch (...) {
std::throw_with_nested(std::runtime_error("outer"));
}
} catch (...) {
p3 = std::current_exception();
}
EXPECT_EQ(fmt::format("{}", p3), "outer: inner");
#endif // FMT_USE_RTTI
}
#if FMT_USE_RTTI
@ -409,7 +470,7 @@ TEST(std_test, type_info) {
EXPECT_EQ(fmt::format("{}", typeid(std::runtime_error)),
"std::runtime_error");
}
#endif
#endif // FMT_USE_RTTI
#if FMT_USE_BITINT
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - test version of FMT_ASSERT
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - test main function.
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - Unicode tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - test utilities
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - test utilities
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@ -1,15 +1,13 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/xchar.h"
#include <algorithm>
#include <complex>
#include <cwchar>
#include <vector>
#include "fmt/chrono.h"
@ -146,6 +144,26 @@ TEST(format_test, wide_format_to_n) {
EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4));
}
TEST(format_test, wide_format_to_n_runtime) {
wchar_t buffer[4];
buffer[3] = L'x';
auto result = fmt::format_to_n(buffer, 3, fmt::runtime(L"{}"), 12345);
EXPECT_EQ(5u, result.size);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4));
buffer[0] = L'x';
buffer[1] = L'x';
buffer[2] = L'x';
result = fmt::format_to_n(buffer, 3, fmt::runtime(L"{}"), L'A');
EXPECT_EQ(1u, result.size);
EXPECT_EQ(buffer + 1, result.out);
EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4));
result = fmt::format_to_n(buffer, 3, fmt::runtime(L"{}{} "), L'B', L'C');
EXPECT_EQ(3u, result.size);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4));
}
TEST(xchar_test, named_arg_udl) {
using namespace fmt::literals;
auto udl_a =