diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..2145fe5f
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @vitaut
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 00000000..1b6fba6b
--- /dev/null
+++ b/.github/SECURITY.md
@@ -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.
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000..8d7becf5
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -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
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index 7aa2c011..1e2f69b3 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -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
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/fuzz.yml
similarity index 97%
rename from .github/workflows/cifuzz.yml
rename to .github/workflows/fuzz.yml
index d6b94918..660e8c49 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -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
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 87900ac9..55879a25 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -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: |
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 65a07306..70260dd9 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -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
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index ef7964ac..87fb8414 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -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'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..30ae7b22
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -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
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/supply-chain.yml
similarity index 50%
rename from .github/workflows/scorecard.yml
rename to .github/workflows/supply-chain.yml
index 7d389b2a..0c30e0f8 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/supply-chain.yml
@@ -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
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index cabd2833..b439bcaa 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -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 }
diff --git a/.gitignore b/.gitignore
index 25924b1e..235a2f56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
*.so*
*.xcodeproj
*~
+*.gradle/
.vscode/
.cache/
.vs/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cc47c96e..9fd7662d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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})
diff --git a/ChangeLog.md b/ChangeLog.md
index e96f9df8..d132266d 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -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_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 ~10–25% 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` 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 `` header equivalent to `` by
+ default. Code that relied on `` pulling in ``
+ must now either include `` 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>` 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 `` failing to compile when
+ `` 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
diff --git a/README.md b/README.md
index 040d17cb..d2c1af9a 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,25 @@
-[](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
-[](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
-[](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
-[](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
-[](https://www.bestpractices.dev/projects/8880)
-[](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
-[](https://stackoverflow.com/questions/tagged/fmt)
+[](
+https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
+[](
+https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
+[](
+https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
+[](
+https://issues.oss-fuzz.com/issues?q=title:fmt%20cc:victor.zverovich@gmail.com)
+[](
+https://www.bestpractices.dev/projects/8880)
+[](
+https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
+[](https://stackoverflow.com/questions/tagged/fmt)
+[](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: .
-
[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:
-
+
# 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.
diff --git a/doc/index.md b/doc/index.md
index e312c398..217ade6b 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -130,7 +130,6 @@ hide:
Where possible, the output of formatting functions is consistent across
platforms.
-
diff --git a/doc/python-license.txt b/doc/python-license.txt
deleted file mode 100644
index 88eed1f9..00000000
--- a/doc/python-license.txt
+++ /dev/null
@@ -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.
diff --git a/doc/syntax.md b/doc/syntax.md
index ac512823..9df3cd93 100644
--- a/doc/syntax.md
+++ b/doc/syntax.md
@@ -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.
-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.
-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
-
-
- | Option |
- Meaning |
-
-
- '<' |
-
- Forces the field to be left-aligned within the available space (this is the
- default for most objects).
- |
-
-
- '>' |
-
- Forces the field to be right-aligned within the available space (this is
- the default for numbers).
- |
-
-
- '^' |
- Forces the field to be centered within the available space. |
-
-
+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.
-
-
- | Option |
- Meaning |
-
-
- '+' |
-
- Indicates that a sign should be used for both nonnegative as well as
- negative numbers.
- |
-
-
- '-' |
-
- Indicates that a sign should be used only for negative numbers (this is the
- default behavior).
- |
-
-
- | space |
-
- Indicates that a leading space should be used on nonnegative numbers, and a
- minus sign on negative numbers.
- |
-
-
+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 (`#`)
-
-
- | Type |
- Meaning |
-
-
- 's' |
-
- String format. This is the default type for strings and may be omitted.
- |
-
-
- '?' |
- Debug format. The string is quoted and special characters escaped. |
-
-
- | none |
- The same as 's'. |
-
-
+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.
-
-
- | Type |
- Meaning |
-
-
- 'c' |
-
- Character format. This is the default type for characters and may be
- omitted.
- |
-
-
- '?' |
- Debug format. The character is quoted and special characters escaped. |
-
-
- | none |
- The same as 'c'. |
-
-
+The `#` flag is not accepted by non-numeric types.
-The available integer presentation types are:
+### Zero padding (`0`)
-
-
- | Type |
- Meaning |
-
-
- 'b' |
-
- Binary format. Outputs the number in base 2. Using the '#'
- option with this type adds the prefix "0b" to the output value.
- |
-
-
- 'B' |
-
- Binary format. Outputs the number in base 2. Using the '#'
- option with this type adds the prefix "0B" to the output value.
- |
-
-
- 'c' |
- Character format. Outputs the number as a character. |
-
-
- 'd' |
- Decimal integer. Outputs the number in base 10. |
-
-
- 'o' |
- Octal format. Outputs the number in base 8. |
-
-
- 'x' |
-
- Hex format. Outputs the number in base 16, using lower-case letters for the
- digits above 9. Using the '#' option with this type adds the
- prefix "0x" to the output value.
- |
-
-
- 'X' |
-
- Hex format. Outputs the number in base 16, using upper-case letters for the
- digits above 9. Using the '#' option with this type adds the
- prefix "0X" to the output value.
- |
-
-
- | none |
- The same as 'd'. |
-
-
+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.
-
-
- | Type |
- Meaning |
-
-
- 'a' |
-
- Hexadecimal floating point format. Prints the number in base 16 with
- prefix "0x" and lower-case letters for digits above 9.
- Uses 'p' to indicate the exponent.
- |
-
-
- 'A' |
-
- Same as 'a' except it uses upper-case letters for the
- prefix, digits above 9 and to indicate the exponent.
- |
-
-
- 'e' |
-
- Exponent notation. Prints the number in scientific notation using
- the letter 'e' to indicate the exponent.
- |
-
-
- 'E' |
-
- Exponent notation. Same as 'e' except it uses an
- upper-case 'E' as the separator character.
- |
-
-
- 'f' |
- Fixed point. Displays the number as a fixed-point number. |
-
-
- 'F' |
-
- Fixed point. Same as 'f', but converts nan
- to NAN and inf to INF.
- |
-
-
- 'g' |
-
- General format. For a given precision p >= 1,
- this rounds the number to p significant digits and then
- formats the result in either fixed-point format or in scientific
- notation, depending on its magnitude.
- A precision of 0 is treated as equivalent to a precision
- of 1.
- |
-
-
- 'G' |
-
- General format. Same as 'g' except switches to
- 'E' if the number gets too large. The representations of
- infinity and NaN are uppercased, too.
- |
-
-
- | none |
-
- Similar to 'g', except that the default precision is as
- high as needed to represent the particular value.
- |
-
-
+### 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.
-
-
- | Type |
- Meaning |
-
-
- 'p' |
-
- Pointer format. This is the default type for pointers and may be omitted.
- |
-
-
- | none |
- The same as 'p'. |
-
-
+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 −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:
+
+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:
range_format_spec ::= ["n"][range_type][":" range_underlying_spec]
@@ -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
-
-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
-
-auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
-// s == "1,234,567,890"
-```
diff --git a/include/fmt/args.h b/include/fmt/args.h
index 7b04ef0d..3283e6fa 100644
--- a/include/fmt/args.h
+++ b/include/fmt/args.h
@@ -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.
diff --git a/include/fmt/base.h b/include/fmt/base.h
index 48adb794..8f9955bf 100644
--- a/include/fmt/base.h
+++ b/include/fmt/base.h
@@ -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
+struct is_contiguous : std::false_type {};
+template
+struct is_contiguous().data()),
+ decltype(std::declval().size()),
+ decltype(std::declval().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;
template class basic_appender;
using appender = basic_appender;
-// Checks whether T is a container with contiguous storage.
-template struct is_contiguous : std::false_type {};
-
class context;
template class generic_context;
template class parse_context;
@@ -608,6 +627,8 @@ using buffered_context =
conditional_t::value, context,
generic_context, Char>>;
+template struct is_contiguous : detail::is_contiguous {};
+
template class basic_format_arg;
template class basic_format_args;
@@ -1858,6 +1879,54 @@ class fixed_buffer_traits {
}
};
+template
+struct has_append : std::false_type {};
+
+template
+struct has_append())
+ .append(std::declval(),
+ std::declval()))>>
+ : std::true_type {};
+
+template
+struct has_insert : std::false_type {};
+
+template
+struct has_insert<
+ OutputIt, T,
+ void_t())
+ .insert({}, std::declval(), std::declval()))>>
+ : std::true_type {};
+
+// An optimized version of std::copy with the output value type (T).
+template () &&
+ has_append())>
+FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
+ get_container(out).append(begin, end);
+ return out;
+}
+
+template () &&
+ !has_append() &&
+ has_insert())>
+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 () ||
+ !(has_append() ||
+ has_insert()))>
+FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
+ while (begin != end) *out++ = static_cast(*begin++);
+ return out;
+}
+
// A buffer that writes to an output iterator when flushed.
template
class iterator_buffer : public Traits, public buffer {
@@ -1875,7 +1944,7 @@ class iterator_buffer : public Traits, public buffer {
this->clear();
const T* begin = data_;
const T* end = begin + this->limit(size);
- while (begin != end) *out_++ = *begin++;
+ out_ = copy(begin, end, out_);
}
public:
@@ -2533,7 +2602,7 @@ template 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 struct fstring {
static_assert(count<(is_view>::value &&
std::is_reference::value)...>() == 0,
"passing views as lvalues is disallowed");
- if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack()));
+ if (FMT_USE_CONSTEVAL)
+ parse_format_string(str, checker(str, arg_pack()));
constexpr bool unused = detail::enforce_compile_checks();
(void)unused;
}
template ::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(sv, checker(sv, arg_pack()));
+ detail::parse_format_string(str, checker(str, arg_pack()));
constexpr bool unused = detail::enforce_compile_checks();
(void)unused;
}
@@ -2877,6 +2946,7 @@ FMT_INLINE void println(format_string fmt, T&&... args) {
}
FMT_PRAGMA_GCC(pop_options)
+FMT_PRAGMA_MSVC(warning(pop))
FMT_END_EXPORT
FMT_END_NAMESPACE
diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h
index 752b9699..46590074 100644
--- a/include/fmt/chrono.h
+++ b/include/fmt/chrono.h
@@ -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 ::value)>
inline auto to_nonnegative_int(T value, Int upper) -> Int {
- auto int_value = static_cast(value);
- if (int_value < 0 || value > static_cast(upper))
+ if (value < 0 || value >= static_cast(upper) + 1)
FMT_THROW(format_error("invalid value"));
- return int_value;
+ return static_cast(value);
}
constexpr auto pow10(std::uint32_t n) -> long long {
@@ -1192,6 +1191,7 @@ class tm_writer {
template ::value)>
void format_tz_name(const T& tm) {
+ if (!tm.tm_zone) FMT_THROW(format_error("no timezone"));
out_ = write_tm_str(out_, tm.tm_zone, loc_);
}
template ::value)>
diff --git a/include/fmt/color.h b/include/fmt/color.h
index 153784f5..bf0c32d6 100644
--- a/include/fmt/color.h
+++ b/include/fmt/color.h
@@ -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 inline void reset_color(buffer& buffer) {
template 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
@@ -528,6 +528,42 @@ void print(text_style ts, format_string fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward(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
+void println(FILE* f, text_style ts, format_string fmt, T&&... args) {
+ vprintln(f, ts, fmt.str, vargs{{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
+void println(text_style ts, format_string fmt, T&&... args) {
+ return println(stdout, ts, fmt, std::forward(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 fmt,
template
struct formatter, Char> : formatter {
template
- auto format(const detail::styled_arg& arg, FormatContext& ctx) const
- -> decltype(ctx.out()) {
+ FMT_CONSTEXPR auto format(const detail::styled_arg& arg,
+ FormatContext& ctx) const -> decltype(ctx.out()) {
const auto& ts = arg.style;
auto out = ctx.out();
diff --git a/include/fmt/compile.h b/include/fmt/compile.h
index 5c6820f7..2a0be049 100644
--- a/include/fmt/compile.h
+++ b/include/fmt/compile.h
@@ -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 struct field {
if constexpr (std::is_convertible>::value) {
auto s = basic_string_view(arg);
return copy(s.begin(), s.end(), out);
+ } else if constexpr (use_format_as::value) {
+ return write(out, format_as(arg));
+ } else if constexpr (use_format_as_member::value) {
+ return write(out, formatter::format_as(arg));
} else {
return write(out, arg);
}
diff --git a/include/fmt/core.h b/include/fmt/core.h
index f1490a9f..eff2ae89 100644
--- a/include/fmt/core.h
+++ b/include/fmt/core.h
@@ -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
diff --git a/include/fmt/fmt-c.h b/include/fmt/fmt-c.h
index 1ce41d37..3f8714d4 100644
--- a/include/fmt/fmt-c.h
+++ b/include/fmt/fmt-c.h
@@ -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 // bool
#include // size_t
+#include // 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
diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h
index cc110198..ecd3ffec 100644
--- a/include/fmt/format-inl.h
+++ b/include/fmt/format-inl.h
@@ -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 // ptrdiff_t
# include
# include // errno
-# include
-# include
-# include
-# include // std::bad_alloc
+# include // std::bad_alloc
#endif
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
@@ -1477,10 +1479,10 @@ template struct span {
};
template auto flockfile(F* f) -> decltype(_lock_file(f)) {
- _lock_file(f);
+ return _lock_file(f);
}
template auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
- _unlock_file(f);
+ return _unlock_file(f);
}
#ifndef getc_unlocked
@@ -1524,7 +1526,10 @@ template 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 class glibc_file : public file_base {
return memchr(end, '\n', static_cast(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::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::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_);
}
diff --git a/include/fmt/format.h b/include/fmt/format.h
index c9b95dbd..dcf51949 100644
--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -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
-struct has_back_insert_iterator_container_append : std::false_type {};
-
-template
-struct has_back_insert_iterator_container_append<
- OutputIt, InputIt,
- void_t())
- .append(std::declval(),
- std::declval()))>> : std::true_type {};
-
-template
-struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
-
-template
-struct has_back_insert_iterator_container_insert_at_end<
- OutputIt, InputIt,
- void_t())
- .insert(get_container(std::declval()).end(),
- std::declval(),
- std::declval()))>> : std::true_type {};
-
-// An optimized version of std::copy with the output value type (T).
-template ::value&&
- 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 ::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 ::value &&
- (has_back_insert_iterator_container_append<
- OutputIt, InputIt>::value ||
- has_back_insert_iterator_container_insert_at_end<
- OutputIt, InputIt>::value)))>
-FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
- while (begin != end) *out++ = static_cast(*begin++);
- return out;
-}
-
template
FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt {
return copy(s.begin(), s.end(), out);
@@ -1115,12 +1064,6 @@ template 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(n));
+ n /= pow10_19;
+ if ((n >> 64) == 0) return count_digits(static_cast(n)) + 19;
+ n /= pow10_19;
+ return count_digits(static_cast(n)) + 38;
+}
+#endif
+
// Counts the number of digits in n. BITS = log2(radix).
template
FMT_CONSTEXPR auto count_digits(UInt n) -> int {
@@ -1282,10 +1239,30 @@ template
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() == 128) {
+ const uint64_t pow10_19 = 10000000000000000000ULL;
+ auto v = static_cast(value);
+ unsigned pos = to_unsigned(size);
+ while ((v >> 64) != 0) {
+ auto chunk = static_cast(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(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() == 32) {
auto p = value * static_cast((1ull << 39) / 100 + 1);
write2digits_i(out + n, p >> (39 - 7) & ((1 << 7) - 1));
value = static_cast(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
+FMT_CONSTEXPR auto write_int_chr(OutputIt out, UInt abs_value, bool negative,
+ const format_specs& specs) -> OutputIt {
+ if (negative || abs_value > max_value>())
+ report_error("character value out of range");
+ return write_char(out, static_cast(abs_value), specs);
+}
+
// Writes a decimal integer with digit grouping.
template
auto write_int(OutputIt out, UInt value, unsigned prefix,
@@ -2048,7 +2034,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
format_base2e(1, appender(buffer), value, num_digits);
break;
case presentation_type::chr:
- return write_char(out, static_cast(value), specs);
+ return write_int_chr(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 arg,
prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
break;
case presentation_type::chr:
- return write_char(out, static_cast(abs_value), specs);
+ return write_int_chr(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(out, arg, specs);
}
-template ::value &&
- !std::is_same::value &&
- !std::is_same::value)>
-FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value,
- const format_specs& specs, locale_ref loc)
- -> basic_appender {
- if (specs.localized() && write_loc(out, value, specs, loc)) return out;
- return write_int_noinline(out, make_write_int_arg(value, specs.sign()),
- specs);
-}
-
-// An inlined version of write used in format string compilation.
template ::value &&
!std::is_same::value &&
- !std::is_same::value &&
- !std::is_same>::value)>
+ !std::is_same::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(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>::value)
+ return write_int_noinline(out, arg, specs);
+ return write_int(out, arg, specs);
}
template
@@ -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
@@ -3941,7 +3918,7 @@ template 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()) out_ = it;
}
diff --git a/include/fmt/os.h b/include/fmt/os.h
index 3fbd6905..a3ee58f3 100644
--- a/include/fmt/os.h
+++ b/include/fmt/os.h
@@ -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.
diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h
index a139e825..da9c65e0 100644
--- a/include/fmt/ostream.h
+++ b/include/fmt/ostream.h
@@ -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.
diff --git a/include/fmt/printf.h b/include/fmt/printf.h
index 7f6f6f3a..8d792fbd 100644
--- a/include/fmt/printf.h
+++ b/include/fmt/printf.h
@@ -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
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& buf, basic_string_view format,
}
write(out, basic_string_view(start, to_unsigned(it - 1 - start)));
+ if (it == end) report_error("invalid format string");
+
auto specs = format_specs();
specs.set_align(align::right);
diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h
index d7dbc168..99bef841 100644
--- a/include/fmt/ranges.h
+++ b/include/fmt/ranges.h
@@ -69,11 +69,12 @@ struct has_member_fn_begin_end_t().begin()),
// Member function overloads.
template
-auto range_begin(T&& rng) -> decltype(static_cast(rng).begin()) {
+FMT_CONSTEXPR auto range_begin(T&& rng)
+ -> decltype(static_cast(rng).begin()) {
return static_cast(rng).begin();
}
template
-auto range_end(T&& rng) -> decltype(static_cast(rng).end()) {
+FMT_CONSTEXPR auto range_end(T&& rng) -> decltype(static_cast(rng).end()) {
return static_cast(rng).end();
}
@@ -129,6 +130,13 @@ template class is_tuple_like_ {
!std::is_void(nullptr))>::value;
};
+template
+struct is_optional_like_ : std::false_type {};
+template
+struct is_optional_like_().has_value()),
+ decltype(std::declval().value())>>
+ : std::true_type {};
+
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template
@@ -343,8 +351,9 @@ struct formatter struct is_range {
- static constexpr bool value =
- detail::is_range_::value && !detail::has_to_string_view::value;
+ static constexpr bool value = detail::is_range_::value &&
+ !detail::is_optional_like_::value &&
+ !detail::has_to_string_view::value;
};
namespace detail {
@@ -460,7 +469,8 @@ struct range_formatter<
}
template
- 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
- 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 sep;
- join_view(It b, Sentinel e, basic_string_view s)
+ FMT_CONSTEXPR join_view(It b, Sentinel e, basic_string_view s)
: begin(std::move(b)), end(e), sep(s) {}
};
@@ -652,7 +662,8 @@ struct formatter, Char> {
}
template
- 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::value, It, It&>;
iter it = value.begin;
@@ -675,7 +686,7 @@ template struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view sep;
- tuple_join_view(const Tuple& t, basic_string_view s)
+ FMT_CONSTEXPR tuple_join_view(const Tuple& t, basic_string_view s)
: tuple(t), sep{s} {}
};
@@ -694,8 +705,9 @@ struct formatter, Char,
}
template
- auto format(const tuple_join_view& value,
- FormatContext& ctx) const -> typename FormatContext::iterator {
+ FMT_CONSTEXPR auto format(const tuple_join_view& value,
+ FormatContext& ctx) const ->
+ typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size());
}
@@ -726,15 +738,17 @@ struct formatter, Char,
}
template
- auto do_format(const tuple_join_view&, FormatContext& ctx,
- std::integral_constant) const ->
+ FMT_CONSTEXPR auto do_format(const tuple_join_view&,
+ FormatContext& ctx,
+ std::integral_constant) const ->
typename FormatContext::iterator {
return ctx.out();
}
template
- auto do_format(const tuple_join_view& value, FormatContext& ctx,
- std::integral_constant) const ->
+ FMT_CONSTEXPR auto do_format(const tuple_join_view& value,
+ FormatContext& ctx,
+ std::integral_constant) const ->
typename FormatContext::iterator {
using std::get;
auto out =
@@ -813,7 +827,7 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view {
* // Output: 01, 02, 03
*/
template ::value)>
-auto join(Range&& r, string_view sep)
+FMT_CONSTEXPR auto join(Range&& r, string_view sep)
-> join_view {
return {detail::range_begin(r), detail::range_end(r), sep};
diff --git a/include/fmt/std.h b/include/fmt/std.h
index b6b98bb7..379b186e 100644
--- a/include/fmt/std.h
+++ b/include/fmt/std.h
@@ -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& quoted,
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template
-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::value)
return write_escaped_string(out, detail::to_string_view(v));
if constexpr (std::is_same_v) return write_escaped_char(out, v);
@@ -508,7 +508,7 @@ template struct formatter {
}
template
- auto format(const std::monostate&, FormatContext& ctx) const
+ FMT_CONSTEXPR auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write(ctx.out(), "monostate");
}
@@ -524,7 +524,7 @@ struct formatter
- 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
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
- auto out = ctx.out();
+ return write(ctx.out(), ex);
+ }
+
+ private:
+ template
+ 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(out, string_view(ex.what()));
+#endif // FMT_USE_RTTI
+ out = detail::write_bytes(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(&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 : formatter {
+ template
+ 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