Repository: fmtlib/fmt Branch: master Commit: 4968433a6abe Files: 134 Total size: 2.9 MB Directory structure: gitextract_t95f2iu3/ ├── .clang-format ├── .clang-tidy ├── .cmake-format ├── .github/ │ ├── FUNDING.yml │ ├── dependabot.yml │ ├── issue_template.md │ ├── pull_request_template.md │ └── workflows/ │ ├── cifuzz.yml │ ├── doc.yml │ ├── lint.yml │ ├── linux.yml │ ├── macos.yml │ ├── scorecard.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.md ├── ChangeLog.md ├── LICENSE ├── README.md ├── doc/ │ ├── ChangeLog-old.md │ ├── LICENSE-exception │ ├── api.md │ ├── fmt.css │ ├── fmt.js │ ├── get-started.md │ ├── index.md │ ├── python-license.txt │ └── syntax.md ├── include/ │ └── fmt/ │ ├── args.h │ ├── base.h │ ├── chrono.h │ ├── color.h │ ├── compile.h │ ├── core.h │ ├── fmt-c.h │ ├── format-inl.h │ ├── format.h │ ├── os.h │ ├── ostream.h │ ├── printf.h │ ├── ranges.h │ ├── std.h │ └── xchar.h ├── src/ │ ├── fmt-c.cc │ ├── fmt.cc │ ├── format.cc │ └── os.cc ├── support/ │ ├── Android.mk │ ├── AndroidManifest.xml │ ├── C++.sublime-syntax │ ├── README │ ├── Vagrantfile │ ├── build.gradle │ ├── check-commits │ ├── cmake/ │ │ ├── FindSetEnv.cmake │ │ ├── JoinPaths.cmake │ │ ├── fmt-config.cmake.in │ │ └── fmt.pc.in │ ├── docopt.py │ ├── gradle/ │ │ └── wrapper/ │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── mkdocs │ ├── mkdocs.yml │ ├── printable.py │ ├── python/ │ │ └── mkdocstrings_handlers/ │ │ └── cxx/ │ │ ├── __init__.py │ │ └── templates/ │ │ └── README │ └── release.py └── test/ ├── CMakeLists.txt ├── add-subdirectory-test/ │ ├── CMakeLists.txt │ └── main.cc ├── args-test.cc ├── assert-test.cc ├── base-test.cc ├── c-test.c ├── chrono-test.cc ├── color-test.cc ├── compile-error-test/ │ └── CMakeLists.txt ├── compile-test.cc ├── cuda-test/ │ ├── CMakeLists.txt │ ├── cpp14.cc │ └── cuda-cpp14.cu ├── detect-stdfs.cc ├── enforce-checks-test.cc ├── find-package-test/ │ ├── CMakeLists.txt │ └── main.cc ├── format-impl-test.cc ├── format-test.cc ├── fuzzing/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── README.md │ ├── build.sh │ ├── chrono-duration.cc │ ├── chrono-timepoint.cc │ ├── float.cc │ ├── fuzzer-common.h │ ├── main.cc │ ├── named-arg.cc │ ├── one-arg.cc │ └── two-args.cc ├── gtest/ │ ├── .clang-format │ ├── CMakeLists.txt │ ├── gmock/ │ │ └── gmock.h │ ├── gmock-gtest-all.cc │ └── gtest/ │ ├── gtest-spi.h │ └── gtest.h ├── gtest-extra-test.cc ├── gtest-extra.cc ├── gtest-extra.h ├── header-only-test.cc ├── mock-allocator.h ├── module-test.cc ├── no-builtin-types-test.cc ├── noexception-test.cc ├── os-test.cc ├── ostream-test.cc ├── perf-sanity.cc ├── posix-mock-test.cc ├── posix-mock.h ├── printf-test.cc ├── ranges-odr-test.cc ├── ranges-test.cc ├── scan-test.cc ├── scan.h ├── static-export-test/ │ ├── CMakeLists.txt │ ├── library.cc │ └── main.cc ├── std-test.cc ├── test-assert.h ├── test-main.cc ├── unicode-test.cc ├── util.cc ├── util.h └── xchar-test.cc ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ # Run manually to reformat a file: # clang-format -i --style=file Language: Cpp BasedOnStyle: Google IndentPPDirectives: AfterHash IndentCaseLabels: false AlwaysBreakTemplateDeclarations: false DerivePointerAlignment: false AllowShortCaseLabelsOnASingleLine: true QualifierAlignment: Left AlignConsecutiveShortCaseStatements: Enabled: true AcrossEmptyLines: true AcrossComments: true AlignCaseColons: false ================================================ FILE: .clang-tidy ================================================ Checks: modernize-use-trailing-return-type CheckOptions: - key: modernize-use-trailing-return-type.TransformLambdas value: none ================================================ FILE: .cmake-format ================================================ format: separate_ctrl_name_with_space: true markup: enable_markup: false ================================================ FILE: .github/FUNDING.yml ================================================ github: vitaut ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" # Necessary to update action hashes. directory: "/" schedule: interval: "monthly" # Allow up to 3 opened pull requests for github-actions versions. open-pull-requests-limit: 3 ================================================ FILE: .github/issue_template.md ================================================ ================================================ FILE: .github/pull_request_template.md ================================================ ================================================ FILE: .github/workflows/cifuzz.yml ================================================ name: CIFuzz on: [pull_request] permissions: contents: read jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@92182553173581f871130c71c71b17f003d47b0a with: oss-fuzz-project-name: 'fmt' dry-run: false language: c++ - name: Run fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@92182553173581f871130c71c71b17f003d47b0a with: oss-fuzz-project-name: 'fmt' fuzz-seconds: 300 dry-run: false language: c++ - name: Upload crash uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts ================================================ FILE: .github/workflows/doc.yml ================================================ name: doc on: [push, pull_request] permissions: contents: read jobs: build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Add Ubuntu mirrors run: | # Github Actions caching proxy is at times unreliable # see https://github.com/actions/runner-images/issues/7048 printf 'http://azure.archive.ubuntu.com/ubuntu\tpriority:1\n' | sudo tee /etc/apt/mirrors.txt curl http://mirrors.ubuntu.com/mirrors.txt | sudo tee --append /etc/apt/mirrors.txt sudo sed -i 's~http://azure.archive.ubuntu.com/ubuntu/~mirror+file:/etc/apt/mirrors.txt~' /etc/apt/sources.list - name: Create build environment 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 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)" git config --global user.email "$(git --no-pager log --format=format:'%ae' -n 1)" - name: Build working-directory: ${{runner.workspace}}/build run: $GITHUB_WORKSPACE/support/mkdocs deploy dev - name: Deploy env: KEY: "${{secrets.KEY}}" if: env.KEY != '' && github.ref == 'refs/heads/master' working-directory: ${{runner.workspace}}/fmt/build/fmt.dev run: git push https://$KEY@github.com/fmtlib/fmt.dev.git ================================================ FILE: .github/workflows/lint.yml ================================================ name: lint on: pull_request: paths: - '**.h' - '**.cc' - '**.cmake' - '**/CMakeLists.txt' permissions: contents: read jobs: clang-format: runs-on: ubuntu-latest steps: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.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 - name: Run clang-format run: | find include src -name '*.h' -o -name '*.cc' | \ xargs clang-format-21 -i -style=file -fallback-style=none git diff --exit-code cmake-format: runs-on: ubuntu-latest steps: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install cmake-format run: pip install cmakelang - name: Run cmake-format run: | find . -name CMakeLists.txt -o -name '*.cmake' | \ xargs cmake-format -i git diff --exit-code ================================================ FILE: .github/workflows/linux.yml ================================================ name: linux on: [push, pull_request] permissions: contents: read jobs: build: runs-on: ubuntu-22.04 strategy: matrix: cxx: [g++-4.9, g++-11, clang++-3.6, clang++-11] build_type: [Debug, Release] std: [11] shared: [""] include: - cxx: g++-4.9 - cxx: clang++-3.6 - cxx: g++-11 build_type: Debug std: 14 install: sudo apt install g++-11 - cxx: g++-11 build_type: Debug std: 17 - cxx: g++-11 build_type: Debug std: 20 install: sudo apt install g++-11 - cxx: g++-13 build_type: Release std: 23 install: sudo apt install g++-13 shared: -DBUILD_SHARED_LIBS=ON - cxx: clang++-11 build_type: Debug std: 17 cxxflags: -stdlib=libc++ install: sudo apt install clang-11 libc++-11-dev libc++abi-11-dev - cxx: clang++-11 install: sudo apt install clang-11 - cxx: clang++-11 build_type: Debug fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON std: 17 install: sudo apt install clang-11 - cxx: clang++-14 build_type: Debug std: 20 - cxx: clang++-14 build_type: Debug std: 20 cxxflags: -stdlib=libc++ install: sudo apt install libc++-14-dev libc++abi-14-dev steps: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Set timezone run: sudo timedatectl set-timezone 'Europe/Kyiv' - name: Install GCC 4.9 run: | sudo apt update sudo apt install libatomic1 libc6-dev libgomp1 libitm1 libmpc3 # https://launchpad.net/ubuntu/xenial/amd64/g++-4.9/4.9.3-13ubuntu2 wget --no-verbose \ http://launchpadlibrarian.net/230069137/libmpfr4_3.1.3-2_amd64.deb \ http://launchpadlibrarian.net/253728424/libasan1_4.9.3-13ubuntu2_amd64.deb \ http://launchpadlibrarian.net/445346135/libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346112/libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/253728426/libgcc-4.9-dev_4.9.3-13ubuntu2_amd64.deb \ http://launchpadlibrarian.net/253728432/libstdc++-4.9-dev_4.9.3-13ubuntu2_amd64.deb \ http://launchpadlibrarian.net/253728314/gcc-4.9-base_4.9.3-13ubuntu2_amd64.deb \ http://launchpadlibrarian.net/445345919/gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/253728399/cpp-4.9_4.9.3-13ubuntu2_amd64.deb \ http://launchpadlibrarian.net/253728404/gcc-4.9_4.9.3-13ubuntu2_amd64.deb \ http://launchpadlibrarian.net/253728401/g++-4.9_4.9.3-13ubuntu2_amd64.deb sudo dpkg -i \ libmpfr4_3.1.3-2_amd64.deb \ libasan1_4.9.3-13ubuntu2_amd64.deb \ libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libgcc-4.9-dev_4.9.3-13ubuntu2_amd64.deb \ libstdc++-4.9-dev_4.9.3-13ubuntu2_amd64.deb \ gcc-4.9-base_4.9.3-13ubuntu2_amd64.deb \ gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \ cpp-4.9_4.9.3-13ubuntu2_amd64.deb \ gcc-4.9_4.9.3-13ubuntu2_amd64.deb \ g++-4.9_4.9.3-13ubuntu2_amd64.deb if: ${{ matrix.cxx == 'g++-4.9' }} - name: Install Clang 3.6 run: | sudo apt update sudo apt install libtinfo5 # https://code.launchpad.net/ubuntu/xenial/amd64/clang-3.6/1:3.6.2-3ubuntu2 wget --no-verbose \ http://launchpadlibrarian.net/230019046/libffi6_3.2.1-4_amd64.deb \ http://launchpadlibrarian.net/445346109/libasan2_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346135/libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346112/libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346128/libmpx0_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346113/libgcc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346131/libstdc++-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/445346022/libobjc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/254405108/libllvm3.6v5_3.6.2-3ubuntu2_amd64.deb \ http://launchpadlibrarian.net/254405097/libclang-common-3.6-dev_3.6.2-3ubuntu2_amd64.deb \ http://launchpadlibrarian.net/254405101/libclang1-3.6_3.6.2-3ubuntu2_amd64.deb \ http://launchpadlibrarian.net/445345919/gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \ http://launchpadlibrarian.net/254405091/clang-3.6_3.6.2-3ubuntu2_amd64.deb sudo dpkg -i \ libffi6_3.2.1-4_amd64.deb \ libasan2_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libcilkrts5_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libmpx0_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libgcc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libstdc++-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libobjc-5-dev_5.4.0-6ubuntu1~16.04.12_amd64.deb \ libllvm3.6v5_3.6.2-3ubuntu2_amd64.deb \ libclang-common-3.6-dev_3.6.2-3ubuntu2_amd64.deb \ libclang1-3.6_3.6.2-3ubuntu2_amd64.deb \ gcc-5-base_5.4.0-6ubuntu1~16.04.12_amd64.deb \ clang-3.6_3.6.2-3ubuntu2_amd64.deb if: ${{ matrix.cxx == 'clang++-3.6' }} - name: Add repositories for newer GCC run: | sudo apt-add-repository ppa:ubuntu-toolchain-r/test if: ${{ matrix.cxx == 'g++-13' }} - name: Add Ubuntu mirrors run: | # GitHub Actions caching proxy is at times unreliable # see https://github.com/actions/runner-images/issues/7048. mirrors=/etc/apt/mirrors.txt printf 'http://azure.archive.ubuntu.com/ubuntu\tpriority:1\n' | \ sudo tee $mirrors curl http://mirrors.ubuntu.com/mirrors.txt | sudo tee --append $mirrors sudo sed -i \ "s~http://azure.archive.ubuntu.com/ubuntu/~mirror+file:$mirrors~" \ /etc/apt/sources.list - name: Create build environment run: | sudo apt update ${{matrix.install}} sudo apt install locales-all cmake -E make_directory ${{runner.workspace}}/build - name: Configure working-directory: ${{runner.workspace}}/build env: CXX: ${{matrix.cxx}} CXXFLAGS: ${{matrix.cxxflags}} run: | cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_CXX_VISIBILITY_PRESET=hidden \ -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \ -DFMT_DOC=OFF -DFMT_PEDANTIC=ON -DFMT_WERROR=ON \ ${{matrix.fuzz}} ${{matrix.shared}} $GITHUB_WORKSPACE - name: Build working-directory: ${{runner.workspace}}/build run: | threads=`nproc` cmake --build . --config ${{matrix.build_type}} --parallel $threads - name: Test working-directory: ${{runner.workspace}}/build run: ctest -C ${{matrix.build_type}} env: CTEST_OUTPUT_ON_FAILURE: True ================================================ FILE: .github/workflows/macos.yml ================================================ name: macos on: [push, pull_request] permissions: contents: read jobs: build: strategy: matrix: os: [macos-14] build_type: [Debug, Release] std: [11, 17, 20, 23] shared: [""] include: - os: macos-14 std: 23 build_type: Release shared: -DBUILD_SHARED_LIBS=ON runs-on: '${{ matrix.os }}' steps: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Set timezone run: sudo systemsetup -settimezone 'Europe/Minsk' - name: Select Xcode 14.3 (macOS 13) run: sudo xcode-select -s "/Applications/Xcode_14.3.app" if: ${{ matrix.os == 'macos-13' }} - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure working-directory: ${{runner.workspace}}/build run: | cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.shared}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \ -DFMT_DOC=OFF -DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE - name: Build working-directory: ${{runner.workspace}}/build run: | threads=`sysctl -n hw.logicalcpu` cmake --build . --config ${{matrix.build_type}} --parallel $threads - name: Test working-directory: ${{runner.workspace}}/build run: ctest -C ${{matrix.build_type}} env: CTEST_OUTPUT_ON_FAILURE: True ================================================ FILE: .github/workflows/scorecard.yml ================================================ # 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 on: # 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: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: - cron: '26 14 * * 5' push: branches: [ "master" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write steps: - name: "Checkout code" uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - 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 # - Allows the repository to include the Scorecard badge. # - 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 with: sarif_file: results.sarif ================================================ FILE: .github/workflows/windows.yml ================================================ name: windows on: [push, pull_request] permissions: contents: read jobs: build: runs-on: ${{matrix.os}} strategy: matrix: # windows-2022 has MSVC 2022 installed: # https://github.com/actions/virtual-environments. os: [windows-2022] platform: [Win32, x64] toolset: [v142] standard: [14, 17, 20] shared: ["", -DBUILD_SHARED_LIBS=ON] build_type: [Debug, Release] exclude: - { toolset: v142, standard: 14 } - { platform: Win32, standard: 14 } - { platform: Win32, standard: 20 } - { platform: x64, standard: 14, shared: -DBUILD_SHARED_LIBS=ON } - { platform: x64, standard: 20, shared: -DBUILD_SHARED_LIBS=ON } include: - os: windows-2022 platform: x64 toolset: v143 build_type: Debug standard: 20 steps: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Set timezone run: tzutil /s "FLE Standard Time" - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure # Use a bash shell for $GITHUB_WORKSPACE. shell: bash working-directory: ${{runner.workspace}}/build run: | cmake -A ${{matrix.platform}} -T ${{matrix.toolset}} \ -DCMAKE_CXX_STANDARD=${{matrix.standard}} \ ${{matrix.shared}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ $GITHUB_WORKSPACE - name: Build working-directory: ${{runner.workspace}}/build run: | $threads = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors cmake --build . --config ${{matrix.build_type}} --parallel $threads - name: Test working-directory: ${{runner.workspace}}/build run: ctest -C ${{matrix.build_type}} -V env: CTEST_OUTPUT_ON_FAILURE: True mingw: runs-on: windows-latest defaults: run: shell: msys2 {0} strategy: matrix: sys: [ mingw64, ucrt64 ] steps: - name: Set timezone run: tzutil /s "FLE Standard Time" shell: cmd - uses: msys2/setup-msys2@40677d36a502eb2cf0fb808cc9dec31bf6152638 # v2.28.0 with: release: false msystem: ${{matrix.sys}} pacboy: cc:p cmake:p ninja:p lld:p - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Configure run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug env: { LDFLAGS: -fuse-ld=lld } - name: Build run: cmake --build ../build - name: Test run: ctest -j `nproc` --test-dir ../build env: CTEST_OUTPUT_ON_FAILURE: True ================================================ FILE: .gitignore ================================================ *.a *.so* *.xcodeproj *~ .vscode/ .cache/ .vs/ /CMakeScripts /Testing /_CPack_Packages /install_manifest.txt CMakeCache.txt CMakeUserPresets.json CMakeFiles CPack*.cmake CTestTestfile.cmake FMT.build Makefile bin/ build/ cmake_install.cmake fmt-*.cmake fmt.pc ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.8...3.28) include_guard(GLOBAL) # Fallback for using newer policies on CMake <3.12. if (${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) endif () # Determine if fmt is built as a subproject (using add_subdirectory) or if it is # the master project. if (NOT DEFINED FMT_MASTER_PROJECT) set(FMT_MASTER_PROJECT OFF) # Checking project name is more reliable than checking source directories. if (NOT DEFINED PROJECT_NAME) set(FMT_MASTER_PROJECT ON) message(STATUS "CMake version: ${CMAKE_VERSION}") endif () endif () # Joins arguments and places the results in ${result_var}. function (join result_var) set(result "") foreach (arg ${ARGN}) set(result "${result}${arg}") endforeach () set(${result_var} "${result}" PARENT_SCOPE) endfunction () # Sets a cache variable with a docstring joined from multiple arguments: # set_verbose( CACHE ...) # This allows splitting a long docstring for readability. function (set_verbose variable value _cache type) join(doc ${ARGN}) set(${variable} ${value} CACHE ${type} ${doc}) endfunction () # Set the default CMAKE_BUILD_TYPE to Release. # This should be done before the project command since the latter can set # CMAKE_BUILD_TYPE itself (it does so for nmake). if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) set_verbose( CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") endif () project(FMT CXX) # Determine support for the C++ module scanning and CXX_MODULE File_Sets # Requires C++20, CMake >= 3.28 and # Generators(Ninja >= 1.11 OR Visual Studio >= 17.4). # Compilers GCC>=14, Clang>=16 or MSVC >= 17.4 # Source: https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html set(FMT_USE_CMAKE_MODULES FALSE) if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND CMAKE_CXX_STANDARD GREATER_EQUAL 20) if (CMAKE_GENERATOR STREQUAL "Ninja") execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" "--version" 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) 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 GREATER_EQUAL 1934)) set(FMT_USE_CMAKE_MODULES TRUE) endif () endif () elseif (CMAKE_GENERATOR MATCHES "^Visual Studio" AND MSVC_VERSION GREATER_EQUAL 1934) set(FMT_USE_CMAKE_MODULES TRUE) endif () endif () option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT}) option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT}) option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) option(FMT_FUZZ "Generate the fuzz target." OFF) option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) option(FMT_OS "Include OS-specific APIs." ON) option(FMT_MODULE "Build a module library." ${FMT_USE_CMAKE_MODULES}) option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF) option(FMT_UNICODE "Enable Unicode support." ON) option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF) set(FMT_SYSTEM_HEADERS_ATTRIBUTE "") if (FMT_SYSTEM_HEADERS) set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) endif () include(GNUInstallDirs) # CMAKE_INSTALL_INCLUDEDIR set_verbose( FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING "Installation directory for include files, a relative path that " "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.") set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") # Get version from base.h. file(READ include/fmt/base.h base_h) if (NOT base_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])") message(FATAL_ERROR "Cannot get FMT_VERSION from base.h.") endif () # Use math to skip leading zeros if any. math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}. ${CPACK_PACKAGE_VERSION_PATCH}) message(STATUS "{fmt} version: ${FMT_VERSION}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) endif () list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") include(CheckCXXCompilerFlag) include(JoinPaths) if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING "Preset for the export of private symbols.") set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS hidden default) endif () if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN) set_verbose( CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL "Whether to add a compile flag to hide symbols of inline " "functions.") endif () if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic -Wold-style-cast -Wundef -Wredundant-decls -Wwrite-strings -Wpointer-arith -Wcast-qual -Wformat=2 -Wmissing-include-dirs -Wcast-align -Wctor-dtor-privacy -Wdisabled-optimization -Winvalid-pch -Woverloaded-virtual -Wconversion -Wundef -Wno-ctor-dtor-privacy -Wno-format-nonliteral) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-dangling-else -Wno-unused-local-typedefs) endif () if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast -Wvector-operation-performance -Wsized-deallocation -Wshadow) endif () if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 -Wduplicated-cond) # Workaround for GCC 12-15 regression: # a false positive null-dereference in vector.resize # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860. if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnull-dereference) endif () endif () set(WERROR_FLAG -Werror) endif () if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef -Wdeprecated -Wweak-vtables -Wshadow -Wno-gnu-zero-variadic-macro-arguments) check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING) if (HAS_NULLPTR_WARNING) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wzero-as-null-pointer-constant) endif () set(WERROR_FLAG -Werror) endif () if (MSVC) set(PEDANTIC_COMPILE_FLAGS /W3) set(WERROR_FLAG /WX) endif () if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") # If Microsoft SDK is installed create script run-msbuild.bat that # calls SetEnv.cmd to set up build environment and runs msbuild. # It is useful when building Visual Studio projects with the SDK # toolchain rather than Visual Studio. include(FindSetEnv) if (WINSDK_SETENV) set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"") endif () # Set FrameworkPathOverride to get rid of MSB3644 warnings. join(netfxpath "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\" ".NETFramework\\v4.0") file(WRITE run-msbuild.bat "${MSBUILD_SETUP} ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") endif () # Sets up a top-level fmt target. Targets that depend on other top-level targets # should call this because they'll automatically get the required properties. function (setup_target target kind) add_library(fmt::${target} ALIAS ${target}) target_include_directories( ${target} ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE ${kind} $ $) if (NOT MSVC) # Unicode is always supported on compilers other than MSVC. elseif (FMT_UNICODE) # Unicode support requires compiling with /utf-8. target_compile_options( ${target} ${kind} $<$,$>:/utf-8>) else () 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}") endfunction () set(FMT_HEADERS) foreach ( header args.h base.h chrono.h color.h compile.h core.h format.h format-inl.h os.h ostream.h printf.h ranges.h std.h xchar.h) set(FMT_HEADERS ${FMT_HEADERS} include/fmt/${header}) endforeach () # Add the main fmt library. add_library(fmt src/format.cc ${FMT_HEADERS} README.md ChangeLog.md) setup_target(fmt PUBLIC) set_target_properties(fmt PROPERTIES PUBLIC_HEADER "${FMT_HEADERS}") if (FMT_OS) target_sources(fmt PRIVATE src/os.cc) else () target_compile_definitions(fmt PRIVATE FMT_OS=0) endif () if (FMT_WERROR) target_compile_options(fmt PRIVATE ${WERROR_FLAG}) endif () if (FMT_PEDANTIC) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () if (cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) target_compile_features(fmt PUBLIC cxx_std_11) else () message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler") endif () # Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target # property because it's not set by default. set(FMT_LIB_NAME fmt) if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX}) endif () if (BUILD_SHARED_LIBS) target_compile_definitions( fmt PRIVATE FMT_LIB_EXPORT INTERFACE FMT_SHARED) endif () if (FMT_SAFE_DURATION_CAST) target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) endif () # Adds a library compiled with C++20 module support. # # Usage: # add_module_library( [sources...] [USE_CMAKE_MODULES TRUE]) function (add_module_library name) cmake_parse_arguments(AML "" "USE_CMAKE_MODULES" "" ${ARGN}) set(sources ${AML_UNPARSED_ARGUMENTS}) add_library(${name}) set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) # Modules require C++20. target_compile_features(${name} PUBLIC cxx_std_20) if (MSVC) if (NOT CMAKE_GENERATOR STREQUAL "Ninja") set(BMI_DIR "${CMAKE_CURRENT_BINARY_DIR}") file(TO_NATIVE_PATH "${BMI_DIR}/${name}.ifc" BMI) target_compile_options( ${name} PRIVATE /interface /ifcOutput ${BMI} INTERFACE /reference fmt=${BMI}) set_target_properties(${name} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI}) set_source_files_properties(${BMI} PROPERTIES GENERATED ON) endif () endif () if (${AML_USE_CMAKE_MODULES}) target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES FILES ${sources}) return() endif () if (CMAKE_COMPILER_IS_GNUCXX) target_compile_options(${name} PUBLIC -fmodules-ts) endif () # `std` is affected by CMake options and may be higher than C++20. get_target_property(std ${name} CXX_STANDARD) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(pcms) foreach (src ${sources}) get_filename_component(pcm ${src} NAME_WE) set(pcm ${pcm}.pcm) # Propagate -fmodule-file=*.pcm to targets that link with this library. target_compile_options( ${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm}) # Use an absolute path to prevent target_link_libraries prepending -l # to it. set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm}) add_custom_command( OUTPUT ${pcm} COMMAND ${CMAKE_CXX_COMPILER} -std=c++${std} -x c++-module --precompile -c -o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src} "-I$,;-I>" # Required by the -I generator expression above. COMMAND_EXPAND_LISTS DEPENDS ${src}) endforeach () # Add .pcm files as sources to make sure they are built before the library. set(sources) foreach (pcm ${pcms}) get_filename_component(pcm_we ${pcm} NAME_WE) set(obj ${pcm_we}.o) # Use an absolute path to prevent target_link_libraries prepending -l. set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj}) add_custom_command( OUTPUT ${obj} COMMAND ${CMAKE_CXX_COMPILER} $ -c -o ${obj} ${pcm} DEPENDS ${pcm}) endforeach () endif () target_sources(${name} PRIVATE ${sources}) endfunction () if (FMT_MODULE) add_module_library(fmt-module src/fmt.cc USE_CMAKE_MODULES ${FMT_USE_CMAKE_MODULES}) setup_target(fmt-module PUBLIC) endif () add_library(fmt-header-only INTERFACE) 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) target_compile_features(fmt-c INTERFACE c_std_11) if (MSVC) target_compile_options(fmt-c PUBLIC /Zc:preprocessor) endif () target_link_libraries(fmt-c PUBLIC fmt::fmt) add_library(fmt::fmt-c ALIAS fmt-c) set_target_properties(fmt-c PROPERTIES PUBLIC_HEADER include/fmt/fmt-c.h) # Install targets. if (FMT_INSTALL) include(CMakePackageConfigHelpers) set_verbose( FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING "Installation directory for cmake files, a relative path that " "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute " "path.") 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 "Installation directory for libraries, a relative path that " "will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.") set_verbose( FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE STRING "Installation directory for pkgconfig (.pc) files, a relative " "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an " "absolute path.") # Generate the version, config and target files into the build directory. write_basic_package_version_file( ${version_config} VERSION ${FMT_VERSION} COMPATIBILITY AnyNewerVersion) join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}") join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}") configure_file("${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" "${pkgconfig}" @ONLY) configure_package_config_file( ${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} COMPONENT fmt_core EXPORT ${targets_export_name} LIBRARY DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR} 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} DESTINATION ${FMT_CMAKE_DIR} COMPONENT fmt_core) install( EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} NAMESPACE fmt:: COMPONENT fmt_core) install( FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}" COMPONENT fmt_core) endif () function (add_doc_target) find_program(DOXYGEN doxygen PATHS "$ENV{ProgramFiles}/doxygen/bin" "$ENV{ProgramFiles\(x86\)}/doxygen/bin") if (NOT DOXYGEN) message(STATUS "Target 'doc' disabled because doxygen not found") return() endif () find_program(MKDOCS mkdocs) if (NOT MKDOCS) message(STATUS "Target 'doc' disabled because mkdocs not found") return() endif () set(sources) foreach (source api.md index.md syntax.md get-started.md fmt.css fmt.js) set(sources ${sources} doc/${source}) endforeach () add_custom_target( doc COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/support/python ${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR}/support/mkdocs.yml # MkDocs requires the site dir to be outside of the doc dir. --site-dir ${CMAKE_CURRENT_BINARY_DIR}/doc-html --no-directory-urls SOURCES ${sources}) install( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt COMPONENT fmt_doc OPTIONAL) endfunction () if (FMT_DOC) add_doc_target() endif () if (FMT_TEST) enable_testing() add_subdirectory(test) endif () # Control fuzzing independent of the unit tests. if (FMT_FUZZ) add_subdirectory(test/fuzzing) # The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing # mode and make fuzzing practically possible. It is similar to # FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to # avoid interfering with fuzzing of projects that use {fmt}. # See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode. target_compile_definitions(fmt PUBLIC FMT_FUZZ) endif () set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) if (FMT_MASTER_PROJECT AND EXISTS ${gitignore}) # Get the list of ignored files from .gitignore. file(STRINGS ${gitignore} lines) list(REMOVE_ITEM lines /doc/html) foreach (line ${lines}) string(REPLACE "." "[.]" line "${line}") string(REPLACE "*" ".*" line "${line}") set(ignored_files ${ignored_files} "${line}$" "${line}/") endforeach () set(ignored_files ${ignored_files} /.git /build/doxyxml .vagrant) set(CPACK_SOURCE_GENERATOR ZIP) set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION}) set(CPACK_PACKAGE_NAME fmt) set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md) include(CPack) endif () ================================================ FILE: CONTRIBUTING.md ================================================ Contributing to {fmt} ===================== By submitting a pull request or a patch, you represent that you have the right to license your contribution to the {fmt} project owners and the community, agree that your contributions are licensed under the {fmt} license, and agree to future changes to the licensing. All C++ code must adhere to [Google C++ Style Guide]( https://google.github.io/styleguide/cppguide.html) with the following exceptions: * Exceptions are permitted * snake_case should be used instead of UpperCamelCase for function and type names All documentation must adhere to the [Google Developer Documentation Style Guide](https://developers.google.com/style). Thanks for contributing! ================================================ FILE: ChangeLog.md ================================================ # 12.1.0 - 2025-10-29 - Optimized `buffer::append`, resulting in up to ~16% improvement on spdlog benchmarks (https://github.com/fmtlib/fmt/pull/4541). Thanks @fyrsta7. - Worked around an ABI incompatibility in `std::locale_ref` between clang and gcc (https://github.com/fmtlib/fmt/issues/4573). - Made `std::variant` and `std::expected` formatters work with `format_as` (https://github.com/fmtlib/fmt/issues/4574, https://github.com/fmtlib/fmt/pull/4575). Thanks @phprus. - Made `fmt::join` work with C++ modules (https://github.com/fmtlib/fmt/issues/4379, https://github.com/fmtlib/fmt/pull/4577). Thanks @Arghnews. - Exported `fmt::is_compiled_string` and `operator""_cf` from the module (https://github.com/fmtlib/fmt/pull/4544). Thanks @CrackedMatter. - Fixed a compatibility issue with C++ modules in clang (https://github.com/fmtlib/fmt/pull/4548). Thanks @tsarn. - Added support for cv-qualified types to the `std::optional` formatter (https://github.com/fmtlib/fmt/issues/4561, https://github.com/fmtlib/fmt/pull/4562). Thanks @OleksandrKvl. - Added demangling support (used in exception and `std::type_info` formatters) for libc++ and clang-cl (https://github.com/fmtlib/fmt/issues/4542, https://github.com/fmtlib/fmt/pull/4560, https://github.com/fmtlib/fmt/issues/4568, https://github.com/fmtlib/fmt/pull/4571). Thanks @FatihBAKIR and @rohitsutreja. - Switched to global `malloc`/`free` to enable allocator customization (https://github.com/fmtlib/fmt/issues/4569, https://github.com/fmtlib/fmt/pull/4570). Thanks @rohitsutreja. - Made the `FMT_USE_CONSTEVAL` macro configurable by users (https://github.com/fmtlib/fmt/pull/4546). Thanks @SnapperTT. - Fixed compilation with locales disabled in the header-only mode (https://github.com/fmtlib/fmt/issues/4550). - Fixed compilation with clang 21 and `-std=c++20` (https://github.com/fmtlib/fmt/issues/4552). - Fixed a dynamic linking issue with clang-cl (https://github.com/fmtlib/fmt/issues/4576, https://github.com/fmtlib/fmt/pull/4584). Thanks @FatihBAKIR. - Fixed a warning suppression leakage on gcc (https://github.com/fmtlib/fmt/pull/4588). Thanks @ZedThree. - Made more internal color APIs `constexpr` (https://github.com/fmtlib/fmt/pull/4581). Thanks @ishani. - Fixed compatibility with clang as a host compiler for NVCC (https://github.com/fmtlib/fmt/pull/4564). Thanks @valgur. - Fixed various warnings and lint issues (https://github.com/fmtlib/fmt/issues/4565, https://github.com/fmtlib/fmt/pull/4572, https://github.com/fmtlib/fmt/pull/4557). Thanks @LiangHuDream and @teruyamato0731. - Improved documentation (https://github.com/fmtlib/fmt/issues/4549, https://github.com/fmtlib/fmt/pull/4551, https://github.com/fmtlib/fmt/issues/4566, https://github.com/fmtlib/fmt/pull/4567, https://github.com/fmtlib/fmt/pull/4578,). Thanks @teruyamato0731, @petersteneteg and @zimmerman-dev. # 12.0.0 - 2025-09-17 - Optimized the default floating point formatting (https://github.com/fmtlib/fmt/issues/3675, https://github.com/fmtlib/fmt/issues/4516). In particular, formatting a `double` with format string compilation into a stack allocated buffer is more than 60% faster in version 12.0 compared to 11.2 according to [dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark): ``` Function Time (ns) Speedup fmt11 34.471 1.00x fmt12 21.000 1.64x ``` - Added `constexpr` support to `fmt::format`. For example: ```c++ #include using namespace fmt::literals; std::string s = fmt::format(""_cf, 42); ``` now works at compile time provided that `std::string` supports `constexpr` (https://github.com/fmtlib/fmt/issues/3403, https://github.com/fmtlib/fmt/pull/4456). Thanks @msvetkin. - Added `FMT_STATIC_FORMAT` that allows formatting into a string of the exact required size at compile time. For example: ```c++ #include constexpr auto s = FMT_STATIC_FORMAT("{}", 42); ``` compiles to just ```s __ZL1s: .asciiz "42" ``` It can be accessed as a C string with `s.c_str()` or as a string view with `s.str()`. - Improved C++20 module support (https://github.com/fmtlib/fmt/pull/4451, https://github.com/fmtlib/fmt/pull/4459, https://github.com/fmtlib/fmt/pull/4476, https://github.com/fmtlib/fmt/pull/4488, https://github.com/fmtlib/fmt/issues/4491, https://github.com/fmtlib/fmt/pull/4495). Thanks @arBmind, @tkhyn, @Mishura4, @anonymouspc and @autoantwort. - Switched to using estimated display width in precision. For example: ```c++ fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱"); ``` prints ![](https://github.com/user-attachments/assets/6c4446b3-13eb-43b9-b74a-b4543540ad6a) because `🐱` has an estimated width of 2 (https://github.com/fmtlib/fmt/issues/4272, https://github.com/fmtlib/fmt/pull/4443, https://github.com/fmtlib/fmt/pull/4475). Thanks @nikhilreddydev and @localspook. - Fix interaction between debug presentation, precision, and width for strings (https://github.com/fmtlib/fmt/pull/4478). Thanks @localspook. - Implemented allocator propagation on `basic_memory_buffer` move (https://github.com/fmtlib/fmt/issues/4487, https://github.com/fmtlib/fmt/pull/4490). Thanks @toprakmurat. - Fixed an ambiguity between `std::reference_wrapper` and `format_as` formatters (https://github.com/fmtlib/fmt/issues/4424, https://github.com/fmtlib/fmt/pull/4434). Thanks @jeremy-rifkin. - Removed the following deprecated APIs: - `has_formatter`: use `is_formattable` instead, - `basic_format_args::parse_context_type`, `basic_format_args::formatter_type` and similar aliases in context types, - wide stream overload of `fmt::printf`, - wide stream overloads of `fmt::print` that take text styles, - `is_*char` traits, - `fmt::localtime`. - Deprecated wide overloads of `fmt::fprintf` and `fmt::sprintf`. - Improved diagnostics for the incorrect usage of `fmt::ptr` (https://github.com/fmtlib/fmt/pull/4453). Thanks @TobiSchluter. - Made handling of ANSI escape sequences more efficient (https://github.com/fmtlib/fmt/pull/4511, https://github.com/fmtlib/fmt/pull/4528). Thanks @localspook and @Anas-Hamdane. - Fixed a buffer overflow on all emphasis flags set (https://github.com/fmtlib/fmt/pull/4498). Thanks @dominicpoeschko. - Fixed an integer overflow for precision close to the max `int` value. - Fixed compatibility with WASI (https://github.com/fmtlib/fmt/issues/4496, https://github.com/fmtlib/fmt/pull/4497). Thanks @whitequark. - Fixed `back_insert_iterator` detection, preventing a fallback on slower path that handles arbitrary iterators (https://github.com/fmtlib/fmt/issues/4454). - Fixed handling of invalid glibc `FILE` buffers (https://github.com/fmtlib/fmt/issues/4469). - Added `wchar_t` support to the `std::byte` formatter (https://github.com/fmtlib/fmt/issues/4479, https://github.com/fmtlib/fmt/pull/4480). Thanks @phprus. - Changed component prefix from `fmt-` to `fmt_` for compatibility with NSIS/CPack on Windows, e.g. `fmt-doc` changed to `fmt_doc` (https://github.com/fmtlib/fmt/issues/4441, https://github.com/fmtlib/fmt/pull/4442). Thanks @n-stein. - Added the `FMT_CUSTOM_ASSERT_FAIL` macro to simplify providing a custom `fmt::assert_fail` implementation (https://github.com/fmtlib/fmt/pull/4505). Thanks @HazardyKnusperkeks. - Switched to `FMT_THROW` on reporting format errors so that it can be overriden by users when exceptions are disabled (https://github.com/fmtlib/fmt/pull/4521). Thanks @HazardyKnusperkeks. - Improved master project detection and disabled install targets when using {fmt} as a subproject by default (https://github.com/fmtlib/fmt/pull/4536). Thanks @crueter. - Made various code improvements (https://github.com/fmtlib/fmt/pull/4445, https://github.com/fmtlib/fmt/pull/4448, https://github.com/fmtlib/fmt/pull/4473, https://github.com/fmtlib/fmt/pull/4522). Thanks @localspook, @tchaikov and @way4sahil. - Added Conan instructions to the docs (https://github.com/fmtlib/fmt/pull/4537). Thanks @uilianries. - Removed Bazel files to avoid issues with downstream packaging (https://github.com/fmtlib/fmt/pull/4530). Thanks @mering. - Added more entries for generated files to `.gitignore` (https://github.com/fmtlib/fmt/pull/4355, https://github.com/fmtlib/fmt/pull/4512). Thanks @dinomight and @localspook. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/4447, https://github.com/fmtlib/fmt/issues/4470, https://github.com/fmtlib/fmt/pull/4474, https://github.com/fmtlib/fmt/pull/4477, https://github.com/fmtlib/fmt/pull/4471, https://github.com/fmtlib/fmt/pull/4483, https://github.com/fmtlib/fmt/pull/4515, https://github.com/fmtlib/fmt/issues/4533, https://github.com/fmtlib/fmt/pull/4534). Thanks @dodomorandi, @localspook, @remyjette, @Tomek-Stolarczyk, @Mishura4, @mattiasljungstrom and @FatihBAKIR. # 11.2.0 - 2025-05-03 - Added the `s` specifier for `std::error_code`. It allows formatting an error message as a string. For example: ```c++ #include int main() { auto ec = std::make_error_code(std::errc::no_such_file_or_directory); fmt::print("{:s}\n", ec); } ``` prints ``` No such file or directory ``` (The actual message is platform-specific.) - Fixed formatting of `std::chrono::local_time` and `tm` (https://github.com/fmtlib/fmt/issues/3815, https://github.com/fmtlib/fmt/issues/4350). For example ([godbolt](https://www.godbolt.org/z/8o4b1PPn5)): ```c++ #include int main() { std::chrono::zoned_time zt( std::chrono::current_zone(), std::chrono::system_clock::now()); fmt::print("{}", zt.get_local_time()); } ``` is now formatted consistenly across platforms. - Added diagnostics for cases when timezone information is not available. For example: ```c++ fmt::print("{:Z}", std::chrono::local_seconds()); ``` now gives a compile-time error. - Deprecated `fmt::localtime` in favor of `std::localtime`. - Fixed compilation with GCC 15 and C++20 modules enabled (https://github.com/fmtlib/fmt/pull/4347). Thanks @tkhyn. - Fixed handling of named arguments in format specs (https://github.com/fmtlib/fmt/issues/4360, https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight. - Added error reporting for duplicate named arguments (https://github.com/fmtlib/fmt/issues/4282, https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight. - Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0` (https://github.com/fmtlib/fmt/issues/4375, https://github.com/fmtlib/fmt/issues/4394). - Optimized `text_style` using bit packing (https://github.com/fmtlib/fmt/pull/4363). Thanks @localspook. - Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180, https://github.com/fmtlib/fmt/pull/4383). Thanks @localspook. - Fixed a flush issue in `fmt::print` when using libstdc++ (https://github.com/fmtlib/fmt/issues/4398). - Fixed `fmt::println` usage with `FMT_ENFORCE_COMPILE_STRING` and legacy compile-time checks (https://github.com/fmtlib/fmt/pull/4407). Thanks @madmaxoft. - Removed legacy header `fmt/core.h` from docs (https://github.com/fmtlib/fmt/pull/4421, https://github.com/fmtlib/fmt/pull/4422). Thanks @krzysztofkortas. - Worked around limitations of `__builtin_strlen` during constant evaluation (https://github.com/fmtlib/fmt/issues/4423, https://github.com/fmtlib/fmt/pull/4429). Thanks @BRevzin. - Worked around a bug in MSVC v141 (https://github.com/fmtlib/fmt/issues/4412, https://github.com/fmtlib/fmt/pull/4413). Thanks @hirohira9119. - Removed the `fmt_detail` namespace (https://github.com/fmtlib/fmt/issues/4324). - Removed specializations of `std::is_floating_point` in tests (https://github.com/fmtlib/fmt/issues/4417). - Fixed a CMake error when setting `CMAKE_MODULE_PATH` in the pedantic mode (https://github.com/fmtlib/fmt/pull/4426). Thanks @rlalik. - Updated the Bazel config (https://github.com/fmtlib/fmt/pull/4400). Thanks @Vertexwahn. # 11.1.4 - 2025-02-26 - Fixed ABI compatibility with earlier 11.x versions on Windows (https://github.com/fmtlib/fmt/issues/4359). - Improved the logic of switching between fixed and exponential format for `float` (https://github.com/fmtlib/fmt/issues/3649). - Moved `is_compiled_string` to the public API (https://github.com/fmtlib/fmt/issues/4335, https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb. - Simplified implementation of `operator""_cf` (https://github.com/fmtlib/fmt/pull/4349). Thanks @localspook. - Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329). Thanks @localspook. - Fixed handling of BMI paths with the Ninja generator (https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn. - Fixed gcc 8.3 compile errors (https://github.com/fmtlib/fmt/issues/4331, https://github.com/fmtlib/fmt/pull/4336). Thanks @sergiud. - Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/pull/4356). Thanks @dinomight. # 11.1.3 - 2025-01-25 - Fixed compilation on GCC 9.4 (https://github.com/fmtlib/fmt/issues/4313). - Worked around an internal compiler error when using C++20 modules with GCC 14.2 and earlier (https://github.com/fmtlib/fmt/issues/4295). - Worked around a bug in GCC 6 (https://github.com/fmtlib/fmt/issues/4318). - Fixed an issue caused by instantiating `formatter` (https://github.com/fmtlib/fmt/issues/4303, https://github.com/fmtlib/fmt/pull/4325). Thanks @timsong-cpp. - Fixed formatting into `std::ostreambuf_iterator` when using format string compilation (https://github.com/fmtlib/fmt/issues/4309, https://github.com/fmtlib/fmt/pull/4312). Thanks @phprus. - Restored a constraint on the map formatter so that it correctly reports as unformattable when the element is (https://github.com/fmtlib/fmt/pull/4326). Thanks @timsong-cpp. - Reduced the size of format specs (https://github.com/fmtlib/fmt/issues/4298). - Readded `args()` to `fmt::format_context` (https://github.com/fmtlib/fmt/issues/4307, https://github.com/fmtlib/fmt/pull/4310). Thanks @Erroneous1. - Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/issues/4314, https://github.com/fmtlib/fmt/pull/4322). Thanks @ZehMatt. - Fixed a pedantic mode error in the CMake config (https://github.com/fmtlib/fmt/pull/4327). Thanks @rlalik. # 11.1.2 - 2025-01-12 - Fixed ABI compatibility with earlier 11.x versions (https://github.com/fmtlib/fmt/issues/4292). - Added `wchar_t` support to the `std::bitset` formatter (https://github.com/fmtlib/fmt/issues/4285, https://github.com/fmtlib/fmt/pull/4286, https://github.com/fmtlib/fmt/issues/4289, https://github.com/fmtlib/fmt/pull/4290). Thanks @phprus. - Prefixed CMake components with `fmt-` to simplify usage of {fmt} via `add_subdirectory` (https://github.com/fmtlib/fmt/issues/4283). - Updated docs for meson (https://github.com/fmtlib/fmt/pull/4291). Thanks @trim21. - Fixed a compilation error in chrono on nvcc (https://github.com/fmtlib/fmt/issues/4297, https://github.com/fmtlib/fmt/pull/4301). Thanks @breyerml. - Fixed various warnings (https://github.com/fmtlib/fmt/pull/4288, https://github.com/fmtlib/fmt/pull/4299). Thanks @GamesTrap and @edo9300. # 11.1.1 - 2024-12-27 - Fixed ABI compatibility with earlier 11.x versions (https://github.com/fmtlib/fmt/issues/4278). - Defined CMake components (`core` and `doc`) to allow docs to be installed separately (https://github.com/fmtlib/fmt/pull/4276). Thanks @carlsmedstad. # 11.1.0 - 2024-12-25 - Improved C++20 module support (https://github.com/fmtlib/fmt/issues/4081, https://github.com/fmtlib/fmt/pull/4083, https://github.com/fmtlib/fmt/pull/4084, https://github.com/fmtlib/fmt/pull/4152, https://github.com/fmtlib/fmt/issues/4153, https://github.com/fmtlib/fmt/pull/4169, https://github.com/fmtlib/fmt/issues/4190, https://github.com/fmtlib/fmt/issues/4234, https://github.com/fmtlib/fmt/pull/4239). Thanks @kamrann and @Arghnews. - Reduced debug (unoptimized) binary code size and the number of template instantiations when passing formatting arguments. For example, unoptimized binary code size for `fmt::print("{}", 42)` was reduced by ~40% on GCC and ~60% on clang (x86-64). GCC: - Before: 161 instructions of which 105 are in reusable functions ([godbolt](https://www.godbolt.org/z/s9bGoo4ze)). - After: 116 instructions of which 60 are in reusable functions ([godbolt](https://www.godbolt.org/z/r7GGGxMs6)). Clang: - Before: 310 instructions of which 251 are in reusable functions ([godbolt](https://www.godbolt.org/z/Ts88b7M9o)). - After: 194 instructions of which 135 are in reusable functions ([godbolt](https://www.godbolt.org/z/vcrjP8ceW)). - Added an experimental `fmt::writer` API that can be used for writing to different destinations such as files or strings (https://github.com/fmtlib/fmt/issues/2354). For example ([godbolt](https://www.godbolt.org/z/rWoKfbP7e)): ```c++ #include void write_text(fmt::writer w) { w.print("The answer is {}.", 42); } int main() { // Write to FILE. write_text(stdout); // Write to fmt::ostream. auto f = fmt::output_file("myfile"); write_text(f); // Write to std::string. auto sb = fmt::string_buffer(); write_text(sb); std::string s = sb.str(); } ``` - Added width and alignment support to the formatter of `std::error_code`. - Made `std::expected` formattable (https://github.com/fmtlib/fmt/issues/4145, https://github.com/fmtlib/fmt/pull/4148). For example ([godbolt](https://www.godbolt.org/z/hrj5c6G86)): ```c++ fmt::print("{}", std::expected()); ``` prints ``` expected() ``` Thanks @phprus. - Made `fmt::is_formattable` SFINAE-friendly (https://github.com/fmtlib/fmt/issues/4147). - Added support for `_BitInt` formatting when using clang (https://github.com/fmtlib/fmt/issues/4007, https://github.com/fmtlib/fmt/pull/4072, https://github.com/fmtlib/fmt/issues/4140, https://github.com/fmtlib/fmt/issues/4173, https://github.com/fmtlib/fmt/pull/4176). For example ([godbolt](https://www.godbolt.org/z/KWjbWec5z)): ```c++ using int42 = _BitInt(42); fmt::print("{}", int42(100)); ``` Thanks @Arghnews. - Added the `n` specifier for tuples and pairs (https://github.com/fmtlib/fmt/pull/4107). Thanks @someonewithpc. - Added support for tuple-like types to `fmt::join` (https://github.com/fmtlib/fmt/issues/4226, https://github.com/fmtlib/fmt/pull/4230). Thanks @phprus. - Made more types formattable at compile time (https://github.com/fmtlib/fmt/pull/4127). Thanks @AnthonyVH. - Implemented a more efficient compile-time `fmt::formatted_size` (https://github.com/fmtlib/fmt/issues/4102, https://github.com/fmtlib/fmt/pull/4103). Thanks @phprus. - Fixed compile-time formatting of some string types (https://github.com/fmtlib/fmt/pull/4065). Thanks @torshepherd. - Made compiled version of `fmt::format_to` work with `std::back_insert_iterator>` (https://github.com/fmtlib/fmt/issues/4206, https://github.com/fmtlib/fmt/pull/4211). Thanks @phprus. - Added a formatter for `std::reference_wrapper` (https://github.com/fmtlib/fmt/pull/4163, https://github.com/fmtlib/fmt/pull/4164). Thanks @yfeldblum and @phprus. - Added experimental padding support (glibc `strftime` extension) to `%m`, `%j` and `%Y` (https://github.com/fmtlib/fmt/pull/4161). Thanks @KKhanhH. - Made microseconds formatted as `us` instead of `µs` if the Unicode support is disabled (https://github.com/fmtlib/fmt/issues/4088). - Fixed an unreleased regression in transcoding of surrogate pairs (https://github.com/fmtlib/fmt/issues/4094, https://github.com/fmtlib/fmt/pull/4095). Thanks @phprus. - Made `fmt::appender` satisfy `std::output_iterator` concept (https://github.com/fmtlib/fmt/issues/4092, https://github.com/fmtlib/fmt/pull/4093). Thanks @phprus. - Made `std::iterator_traits` standard-conforming (https://github.com/fmtlib/fmt/pull/4185). Thanks @CaseyCarter. - Made it easier to reuse `fmt::formatter` for types with an implicit conversion to `std::string_view` (https://github.com/fmtlib/fmt/issues/4036, https://github.com/fmtlib/fmt/pull/4055). Thanks @Arghnews. - Made it possible to disable `` use via `FMT_CPP_LIB_FILESYSTEM` for compatibility with some video game console SDKs, e.g. Nintendo Switch SDK (https://github.com/fmtlib/fmt/issues/4257, https://github.com/fmtlib/fmt/pull/4258, https://github.com/fmtlib/fmt/pull/4259). Thanks @W4RH4WK and @phprus. - Fixed compatibility with platforms that use 80-bit `long double` (https://github.com/fmtlib/fmt/issues/4245, https://github.com/fmtlib/fmt/pull/4246). Thanks @jsirpoma. - Added support for UTF-32 code units greater than `0xFFFF` in fill (https://github.com/fmtlib/fmt/issues/4201). - Fixed handling of legacy encodings on Windows with GCC (https://github.com/fmtlib/fmt/issues/4162). - Made `fmt::to_string` take `fmt::basic_memory_buffer` by const reference (https://github.com/fmtlib/fmt/issues/4261, https://github.com/fmtlib/fmt/pull/4262). Thanks @sascha-devel. - Added `fmt::dynamic_format_arg_store::size` (https://github.com/fmtlib/fmt/pull/4270). Thanks @hannes-harnisch. - Removed the ability to control locale usage via an undocumented `FMT_STATIC_THOUSANDS_SEPARATOR` in favor of `FMT_USE_LOCALE`. - Renamed `FMT_EXCEPTIONS` to `FMT_USE_EXCEPTIONS` for consistency with other similar macros. - Improved include directory ordering to reduce the chance of including incorrect headers when using multiple versions of {fmt} (https://github.com/fmtlib/fmt/pull/4116). Thanks @cdzhan. - Made it possible to compile a subset of {fmt} without the C++ runtime. - Improved documentation and README (https://github.com/fmtlib/fmt/pull/4066, https://github.com/fmtlib/fmt/issues/4117, https://github.com/fmtlib/fmt/issues/4203, https://github.com/fmtlib/fmt/pull/4235). Thanks @zyctree and @nikola-sh. - Improved the documentation generator (https://github.com/fmtlib/fmt/pull/4110, https://github.com/fmtlib/fmt/pull/4115). Thanks @rturrado. - Improved CI (https://github.com/fmtlib/fmt/pull/4155, https://github.com/fmtlib/fmt/pull/4151). Thanks @phprus. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/2708, https://github.com/fmtlib/fmt/issues/4091, https://github.com/fmtlib/fmt/issues/4109, https://github.com/fmtlib/fmt/issues/4113, https://github.com/fmtlib/fmt/issues/4125, https://github.com/fmtlib/fmt/issues/4129, https://github.com/fmtlib/fmt/pull/4130, https://github.com/fmtlib/fmt/pull/4131, https://github.com/fmtlib/fmt/pull/4132, https://github.com/fmtlib/fmt/issues/4133, https://github.com/fmtlib/fmt/issues/4144, https://github.com/fmtlib/fmt/issues/4150, https://github.com/fmtlib/fmt/issues/4158, https://github.com/fmtlib/fmt/pull/4159, https://github.com/fmtlib/fmt/issues/4160, https://github.com/fmtlib/fmt/pull/4170, https://github.com/fmtlib/fmt/issues/4177, https://github.com/fmtlib/fmt/pull/4187, https://github.com/fmtlib/fmt/pull/4188, https://github.com/fmtlib/fmt/pull/4194, https://github.com/fmtlib/fmt/pull/4200, https://github.com/fmtlib/fmt/issues/4205, https://github.com/fmtlib/fmt/issues/4207, https://github.com/fmtlib/fmt/pull/4208, https://github.com/fmtlib/fmt/pull/4210, https://github.com/fmtlib/fmt/issues/4220, https://github.com/fmtlib/fmt/issues/4231, https://github.com/fmtlib/fmt/issues/4232, https://github.com/fmtlib/fmt/pull/4233, https://github.com/fmtlib/fmt/pull/4236, https://github.com/fmtlib/fmt/pull/4267, https://github.com/fmtlib/fmt/pull/4271). Thanks @torsten48, @Arghnews, @tinfoilboy, @aminya, @Ottani, @zeroomega, @c4v4, @kongy, @vinayyadav3016, @sergio-nsk, @phprus and @YexuanXiao. # 11.0.2 - 2024-07-20 - Fixed compatibility with non-POSIX systems (https://github.com/fmtlib/fmt/issues/4054, https://github.com/fmtlib/fmt/issues/4060). - Fixed performance regressions when using `std::back_insert_iterator` with `fmt::format_to` (https://github.com/fmtlib/fmt/issues/4070). - Fixed handling of `std::generator` and move-only iterators (https://github.com/fmtlib/fmt/issues/4053, https://github.com/fmtlib/fmt/pull/4057). Thanks @Arghnews. - Made `formatter::parse` work with types convertible to `std::string_view` (https://github.com/fmtlib/fmt/issues/4036, https://github.com/fmtlib/fmt/pull/4055). Thanks @Arghnews. - Made `volatile void*` formattable (https://github.com/fmtlib/fmt/issues/4049, https://github.com/fmtlib/fmt/pull/4056). Thanks @Arghnews. - Made `Glib::ustring` not be confused with `std::string` (https://github.com/fmtlib/fmt/issues/4052). - Made `fmt::context` iterator compatible with STL algorithms that rely on iterator category (https://github.com/fmtlib/fmt/issues/4079). # 11.0.1 - 2024-07-05 - Fixed version number in the inline namespace (https://github.com/fmtlib/fmt/issues/4047). - Fixed disabling Unicode support via CMake (https://github.com/fmtlib/fmt/issues/4051). - Fixed deprecated `visit_format_arg` (https://github.com/fmtlib/fmt/pull/4043). Thanks @nebkat. - Fixed handling of a sign and improved the `std::complex` formater (https://github.com/fmtlib/fmt/pull/4034, https://github.com/fmtlib/fmt/pull/4050). Thanks @tesch1 and @phprus. - Fixed ADL issues in `fmt::printf` when using C++20 (https://github.com/fmtlib/fmt/pull/4042). Thanks @toge. - Removed a redundant check in the formatter for `std::expected` (https://github.com/fmtlib/fmt/pull/4040). Thanks @phprus. # 11.0.0 - 2024-07-01 - Added `fmt/base.h` which provides a subset of the API with minimal include dependencies and enough functionality to replace all uses of the `printf` family of functions. This brings the compile time of code using {fmt} much closer to the equivalent `printf` code as shown on the following benchmark that compiles 100 source files: | Method | Compile Time (s) | |--------------|------------------| | printf | 1.6 | | IOStreams | 25.9 | | fmt 10.x | 19.0 | | fmt 11.0 | 4.8 | | tinyformat | 29.1 | | Boost Format | 55.0 | This gives almost 4x improvement in build speed compared to version 10. Note that the benchmark is purely formatting code and includes. In real projects the difference from `printf` will be smaller partly because common standard headers will be included in almost any translation unit (TU) anyway. In particular, in every case except `printf` above ~1s is spent in total on including `` in all TUs. - Optimized includes in other headers such as `fmt/format.h` which is now roughly equivalent to the old `fmt/core.h` in terms of build speed. - Migrated the documentation at https://fmt.dev/ from Sphinx to MkDocs. - Improved C++20 module support (https://github.com/fmtlib/fmt/issues/3990, https://github.com/fmtlib/fmt/pull/3991, https://github.com/fmtlib/fmt/issues/3993, https://github.com/fmtlib/fmt/pull/3994, https://github.com/fmtlib/fmt/pull/3997, https://github.com/fmtlib/fmt/pull/3998, https://github.com/fmtlib/fmt/pull/4004, https://github.com/fmtlib/fmt/pull/4005, https://github.com/fmtlib/fmt/pull/4006, https://github.com/fmtlib/fmt/pull/4013, https://github.com/fmtlib/fmt/pull/4027, https://github.com/fmtlib/fmt/pull/4029). In particular, native CMake support for modules is now used if available. Thanks @yujincheng08 and @matt77hias. - Added an option to replace standard includes with `import std` enabled via the `FMT_IMPORT_STD` macro (https://github.com/fmtlib/fmt/issues/3921, https://github.com/fmtlib/fmt/pull/3928). Thanks @matt77hias. - Exported `fmt::range_format`, `fmt::range_format_kind` and `fmt::compiled_string` from the `fmt` module (https://github.com/fmtlib/fmt/pull/3970, https://github.com/fmtlib/fmt/pull/3999). Thanks @matt77hias and @yujincheng08. - Improved integration with stdio in `fmt::print`, enabling direct writes into a C stream buffer in common cases. This may give significant performance improvements ranging from tens of percent to [2x]( https://stackoverflow.com/a/78457454/471164) and eliminates dynamic memory allocations on the buffer level. It is currently enabled for built-in and string types with wider availability coming up in future releases. For example, it gives ~24% improvement on a [simple benchmark]( https://isocpp.org/files/papers/P3107R5.html#perf) compiled with Apple clang version 15.0.0 (clang-1500.1.0.2.5) and run on macOS 14.2.1: ``` ------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------- printf 81.8 ns 81.5 ns 8496899 fmt::print (10.x) 63.8 ns 61.9 ns 11524151 fmt::print (11.0) 51.3 ns 51.0 ns 13846580 ``` - Improved safety of `fmt::format_to` when writing to an array (https://github.com/fmtlib/fmt/pull/3805). For example ([godbolt](https://www.godbolt.org/z/cYrn8dWY8)): ```c++ auto volkswagen = char[4]; auto result = fmt::format_to(volkswagen, "elephant"); ``` no longer results in a buffer overflow. Instead the output will be truncated and you can get the end iterator and whether truncation occurred from the `result` object. Thanks @ThePhD. - Enabled Unicode support by default in MSVC, bringing it on par with other compilers and making it unnecessary for users to enable it explicitly. Most of {fmt} is encoding-agnostic but this prevents mojibake in places where encoding matters such as path formatting and terminal output. You can control the Unicode support via the CMake `FMT_UNICODE` option. Note that some {fmt} packages such as the one in vcpkg have already been compiled with Unicode enabled. - Added a formatter for `std::expected` (https://github.com/fmtlib/fmt/pull/3834). Thanks @dominicpoeschko. - Added a formatter for `std::complex` (https://github.com/fmtlib/fmt/issues/1467, https://github.com/fmtlib/fmt/issues/3886, https://github.com/fmtlib/fmt/pull/3892, https://github.com/fmtlib/fmt/pull/3900). Thanks @phprus. - Added a formatter for `std::type_info` (https://github.com/fmtlib/fmt/pull/3978). Thanks @matt77hias. - Specialized `formatter` for `std::basic_string` types with custom traits and allocators (https://github.com/fmtlib/fmt/issues/3938, https://github.com/fmtlib/fmt/pull/3943). Thanks @dieram3. - Added formatters for `std::chrono::day`, `std::chrono::month`, `std::chrono::year` and `std::chrono::year_month_day` (https://github.com/fmtlib/fmt/issues/3758, https://github.com/fmtlib/fmt/issues/3772, https://github.com/fmtlib/fmt/pull/3906, https://github.com/fmtlib/fmt/pull/3913). For example: ```c++ #include #include int main() { fmt::print(fg(fmt::color::green), "{}\n", std::chrono::day(7)); } ``` prints a green day: image Thanks @zivshek. - Fixed handling of precision in `%S` (https://github.com/fmtlib/fmt/issues/3794, https://github.com/fmtlib/fmt/pull/3814). Thanks @js324. - Added support for the `-` specifier (glibc `strftime` extension) to day of the month (`%d`) and week of the year (`%W`, `%U`, `%V`) specifiers (https://github.com/fmtlib/fmt/pull/3976). Thanks @ZaheenJ. - Fixed the scope of the `-` extension in chrono formatting so that it doesn't apply to subsequent specifiers (https://github.com/fmtlib/fmt/issues/3811, https://github.com/fmtlib/fmt/pull/3812). Thanks @phprus. - Improved handling of `time_point::min()` (https://github.com/fmtlib/fmt/issues/3282). - Added support for character range formatting (https://github.com/fmtlib/fmt/issues/3857, https://github.com/fmtlib/fmt/pull/3863). Thanks @js324. - Added `string` and `debug_string` range formatters (https://github.com/fmtlib/fmt/pull/3973, https://github.com/fmtlib/fmt/pull/4024). Thanks @matt77hias. - Enabled ADL for `begin` and `end` in `fmt::join` (https://github.com/fmtlib/fmt/issues/3813, https://github.com/fmtlib/fmt/pull/3824). Thanks @bbolli. - Made contiguous iterator optimizations apply to `std::basic_string` iterators (https://github.com/fmtlib/fmt/pull/3798). Thanks @phprus. - Added support for ranges with mutable `begin` and `end` (https://github.com/fmtlib/fmt/issues/3752, https://github.com/fmtlib/fmt/pull/3800, https://github.com/fmtlib/fmt/pull/3955). Thanks @tcbrindle and @Arghnews. - Added support for move-only iterators to `fmt::join` (https://github.com/fmtlib/fmt/issues/3802, https://github.com/fmtlib/fmt/pull/3946). Thanks @Arghnews. - Moved range and iterator overloads of `fmt::join` to `fmt/ranges.h`, next to other overloads. - Fixed handling of types with `begin` returning `void` such as Eigen matrices (https://github.com/fmtlib/fmt/issues/3839, https://github.com/fmtlib/fmt/pull/3964). Thanks @Arghnews. - Added an `fmt::formattable` concept (https://github.com/fmtlib/fmt/pull/3974). Thanks @matt77hias. - Added support for `__float128` (https://github.com/fmtlib/fmt/issues/3494). - Fixed rounding issues when formatting `long double` with fixed precision (https://github.com/fmtlib/fmt/issues/3539). - Made `fmt::isnan` not trigger floating-point exception for NaN values (https://github.com/fmtlib/fmt/issues/3948, https://github.com/fmtlib/fmt/pull/3951). Thanks @alexdewar. - Removed dependency on `` for `std::allocator_traits` when possible (https://github.com/fmtlib/fmt/pull/3804). Thanks @phprus. - Enabled compile-time checks in formatting functions that take text colors and styles. - Deprecated wide stream overloads of `fmt::print` that take text styles. - Made format string compilation work with clang 12 and later despite only partial non-type template parameter support (https://github.com/fmtlib/fmt/issues/4000, https://github.com/fmtlib/fmt/pull/4001). Thanks @yujincheng08. - Made `fmt::iterator_buffer`'s move constructor `noexcept` (https://github.com/fmtlib/fmt/pull/3808). Thanks @waywardmonkeys. - Started enforcing that `formatter::format` is const for compatibility with `std::format` (https://github.com/fmtlib/fmt/issues/3447). - Added `fmt::basic_format_arg::visit` and deprecated `fmt::visit_format_arg`. - Made `fmt::basic_string_view` not constructible from `nullptr` for consistency with `std::string_view` in C++23 (https://github.com/fmtlib/fmt/pull/3846). Thanks @dalle. - Fixed `fmt::group_digits` for negative integers (https://github.com/fmtlib/fmt/issues/3891, https://github.com/fmtlib/fmt/pull/3901). Thanks @phprus. - Fixed handling of negative ids in `fmt::basic_format_args::get` (https://github.com/fmtlib/fmt/pull/3945). Thanks @marlenecota. - Fixed handling of a buffer boundary on flush (https://github.com/fmtlib/fmt/issues/4229). - Improved named argument validation (https://github.com/fmtlib/fmt/issues/3817). - Disabled copy construction/assignment for `fmt::format_arg_store` and fixed moved construction (https://github.com/fmtlib/fmt/pull/3833). Thanks @ivafanas. - Worked around a locale issue in RHEL/devtoolset (https://github.com/fmtlib/fmt/issues/3858, https://github.com/fmtlib/fmt/pull/3859). Thanks @g199209. - Added RTTI detection for MSVC (https://github.com/fmtlib/fmt/pull/3821, https://github.com/fmtlib/fmt/pull/3963). Thanks @edo9300. - Migrated the documentation from Sphinx to MkDocs. - Improved documentation and README (https://github.com/fmtlib/fmt/issues/3775, https://github.com/fmtlib/fmt/pull/3784, https://github.com/fmtlib/fmt/issues/3788, https://github.com/fmtlib/fmt/pull/3789, https://github.com/fmtlib/fmt/pull/3793, https://github.com/fmtlib/fmt/issues/3818, https://github.com/fmtlib/fmt/pull/3820, https://github.com/fmtlib/fmt/pull/3822, https://github.com/fmtlib/fmt/pull/3843, https://github.com/fmtlib/fmt/pull/3890, https://github.com/fmtlib/fmt/issues/3894, https://github.com/fmtlib/fmt/pull/3895, https://github.com/fmtlib/fmt/pull/3905, https://github.com/fmtlib/fmt/issues/3942, https://github.com/fmtlib/fmt/pull/4008). Thanks @zencatalyst, WolleTD, @tupaschoal, @Dobiasd, @frank-weinberg, @bbolli, @phprus, @waywardmonkeys, @js324 and @tchaikov. - Improved CI and tests (https://github.com/fmtlib/fmt/issues/3878, https://github.com/fmtlib/fmt/pull/3883, https://github.com/fmtlib/fmt/issues/3897, https://github.com/fmtlib/fmt/pull/3979, https://github.com/fmtlib/fmt/pull/3980, https://github.com/fmtlib/fmt/pull/3988, https://github.com/fmtlib/fmt/pull/4010, https://github.com/fmtlib/fmt/pull/4012, https://github.com/fmtlib/fmt/pull/4038). Thanks @vgorrX, @waywardmonkeys, @tchaikov and @phprus. - Fixed buffer overflow when using format string compilation with debug format and `std::back_insert_iterator` (https://github.com/fmtlib/fmt/issues/3795, https://github.com/fmtlib/fmt/pull/3797). Thanks @phprus. - Improved Bazel support (https://github.com/fmtlib/fmt/pull/3792, https://github.com/fmtlib/fmt/pull/3801, https://github.com/fmtlib/fmt/pull/3962, https://github.com/fmtlib/fmt/pull/3965). Thanks @Vertexwahn. - Improved/fixed the CMake config (https://github.com/fmtlib/fmt/issues/3777, https://github.com/fmtlib/fmt/pull/3783, https://github.com/fmtlib/fmt/issues/3847, https://github.com/fmtlib/fmt/pull/3907). Thanks @phprus and @xTachyon. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/3685, https://github.com/fmtlib/fmt/issues/3769, https://github.com/fmtlib/fmt/issues/3796, https://github.com/fmtlib/fmt/issues/3803, https://github.com/fmtlib/fmt/pull/3806, https://github.com/fmtlib/fmt/pull/3807, https://github.com/fmtlib/fmt/issues/3809, https://github.com/fmtlib/fmt/pull/3810, https://github.com/fmtlib/fmt/issues/3830, https://github.com/fmtlib/fmt/pull/3832, https://github.com/fmtlib/fmt/issues/3835, https://github.com/fmtlib/fmt/pull/3844, https://github.com/fmtlib/fmt/issues/3854, https://github.com/fmtlib/fmt/pull/3856, https://github.com/fmtlib/fmt/pull/3865, https://github.com/fmtlib/fmt/pull/3866, https://github.com/fmtlib/fmt/pull/3880, https://github.com/fmtlib/fmt/issues/3881, https://github.com/fmtlib/fmt/issues/3884, https://github.com/fmtlib/fmt/issues/3898, https://github.com/fmtlib/fmt/pull/3899, https://github.com/fmtlib/fmt/pull/3909, https://github.com/fmtlib/fmt/pull/3917, https://github.com/fmtlib/fmt/pull/3923, https://github.com/fmtlib/fmt/pull/3924, https://github.com/fmtlib/fmt/issues/3925, https://github.com/fmtlib/fmt/pull/3930, https://github.com/fmtlib/fmt/pull/3931, https://github.com/fmtlib/fmt/pull/3933, https://github.com/fmtlib/fmt/issues/3935, https://github.com/fmtlib/fmt/pull/3937, https://github.com/fmtlib/fmt/pull/3967, https://github.com/fmtlib/fmt/pull/3968, https://github.com/fmtlib/fmt/pull/3972, https://github.com/fmtlib/fmt/pull/3983, https://github.com/fmtlib/fmt/issues/3992, https://github.com/fmtlib/fmt/pull/3995, https://github.com/fmtlib/fmt/pull/4009, https://github.com/fmtlib/fmt/pull/4023). Thanks @hmbj, @phprus, @res2k, @Baardi, @matt77hias, @waywardmonkeys, @hmbj, @yakra, @prlw1, @Arghnews, @mtillmann0, @ShifftC, @eepp, @jimmy-park and @ChristianGebhardt. # 10.2.1 - 2024-01-04 - Fixed ABI compatibility with earlier 10.x versions (https://github.com/fmtlib/fmt/issues/3785, https://github.com/fmtlib/fmt/pull/3786). Thanks @saraedum. # 10.2.0 - 2024-01-01 - Added support for the `%j` specifier (the number of days) for `std::chrono::duration` (https://github.com/fmtlib/fmt/issues/3643, https://github.com/fmtlib/fmt/pull/3732). Thanks @intelfx. - Added support for the chrono suffix for days and changed the suffix for minutes from "m" to the correct "min" (https://github.com/fmtlib/fmt/issues/3662, https://github.com/fmtlib/fmt/pull/3664). For example ([godbolt](https://godbolt.org/z/9KhMnq9ba)): ```c++ #include int main() { fmt::print("{}\n", std::chrono::days(42)); // prints "42d" } ``` Thanks @Richardk2n. - Fixed an overflow in `std::chrono::time_point` formatting with large dates (https://github.com/fmtlib/fmt/issues/3725, https://github.com/fmtlib/fmt/pull/3727). Thanks @cschreib. - Added a formatter for `std::source_location` (https://github.com/fmtlib/fmt/pull/3730). For example ([godbolt](https://godbolt.org/z/YajfKjhhr)): ```c++ #include #include int main() { fmt::print("{}\n", std::source_location::current()); } ``` prints ``` /app/example.cpp:5:51: int main() ``` Thanks @felix642. - Added a formatter for `std::bitset` (https://github.com/fmtlib/fmt/pull/3660). For example ([godbolt](https://godbolt.org/z/bdEaGeYxe)): ```c++ #include #include int main() { fmt::print("{}\n", std::bitset<6>(42)); // prints "101010" } ``` Thanks @muggenhor. - Added an experimental `nested_formatter` that provides an easy way of applying a formatter to one or more subobjects while automatically handling width, fill and alignment. For example: ```c++ #include struct point { double x, y; }; template <> struct fmt::formatter : nested_formatter { auto format(point p, format_context& ctx) const { return write_padded(ctx, [=](auto out) { return format_to(out, "({}, {})", nested(p.x), nested(p.y)); }); } }; int main() { fmt::print("[{:>20.2f}]", point{1, 2}); } ``` prints ``` [ (1.00, 2.00)] ``` - Added the generic representation (`g`) to `std::filesystem::path` (https://github.com/fmtlib/fmt/issues/3715, https://github.com/fmtlib/fmt/pull/3729). For example: ```c++ #include #include int main() { fmt::print("{:g}\n", std::filesystem::path("C:\\foo")); } ``` prints `"C:/foo"` on Windows. Thanks @js324. - Made `format_as` work with references (https://github.com/fmtlib/fmt/pull/3739). Thanks @tchaikov. - Fixed formatting of invalid UTF-8 with precision (https://github.com/fmtlib/fmt/issues/3284). - Fixed an inconsistency between `fmt::to_string` and `fmt::format` (https://github.com/fmtlib/fmt/issues/3684). - Disallowed unsafe uses of `fmt::styled` (https://github.com/fmtlib/fmt/issues/3625): ```c++ auto s = fmt::styled(std::string("dangle"), fmt::emphasis::bold); fmt::print("{}\n", s); // compile error ``` Pass `fmt::styled(...)` as a parameter instead. - Added a null check when formatting a C string with the `s` specifier (https://github.com/fmtlib/fmt/issues/3706). - Disallowed the `c` specifier for `bool` (https://github.com/fmtlib/fmt/issues/3726, https://github.com/fmtlib/fmt/pull/3734). Thanks @js324. - Made the default formatting unlocalized in `fmt::ostream_formatter` for consistency with the rest of the library (https://github.com/fmtlib/fmt/issues/3460). - Fixed localized formatting in bases other than decimal (https://github.com/fmtlib/fmt/issues/3693, https://github.com/fmtlib/fmt/pull/3750). Thanks @js324. - Fixed a performance regression in experimental `fmt::ostream::print` (https://github.com/fmtlib/fmt/issues/3674). - Added synchronization with the underlying output stream when writing to the Windows console (https://github.com/fmtlib/fmt/pull/3668, https://github.com/fmtlib/fmt/issues/3688, https://github.com/fmtlib/fmt/pull/3689). Thanks @Roman-Koshelev and @dimztimz. - Changed to only export `format_error` when {fmt} is built as a shared library (https://github.com/fmtlib/fmt/issues/3626, https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus. - Made `fmt::streamed` `constexpr`. (https://github.com/fmtlib/fmt/pull/3650). Thanks @muggenhor. - Made `fmt::format_int` `constexpr` (https://github.com/fmtlib/fmt/issues/4031, https://github.com/fmtlib/fmt/pull/4032). Thanks @dixlorenz. - Enabled `consteval` on older versions of MSVC (https://github.com/fmtlib/fmt/pull/3757). Thanks @phprus. - Added an option to build without `wchar_t` support on Windows (https://github.com/fmtlib/fmt/issues/3631, https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. - Improved build and CI configuration (https://github.com/fmtlib/fmt/pull/3679, https://github.com/fmtlib/fmt/issues/3701, https://github.com/fmtlib/fmt/pull/3702, https://github.com/fmtlib/fmt/pull/3749). Thanks @jcar87, @pklima and @tchaikov. - Fixed various warnings, compilation and test issues (https://github.com/fmtlib/fmt/issues/3607, https://github.com/fmtlib/fmt/pull/3610, https://github.com/fmtlib/fmt/pull/3624, https://github.com/fmtlib/fmt/pull/3630, https://github.com/fmtlib/fmt/pull/3634, https://github.com/fmtlib/fmt/pull/3638, https://github.com/fmtlib/fmt/issues/3645, https://github.com/fmtlib/fmt/issues/3646, https://github.com/fmtlib/fmt/pull/3647, https://github.com/fmtlib/fmt/pull/3652, https://github.com/fmtlib/fmt/issues/3654, https://github.com/fmtlib/fmt/pull/3663, https://github.com/fmtlib/fmt/issues/3670, https://github.com/fmtlib/fmt/pull/3680, https://github.com/fmtlib/fmt/issues/3694, https://github.com/fmtlib/fmt/pull/3695, https://github.com/fmtlib/fmt/pull/3699, https://github.com/fmtlib/fmt/issues/3705, https://github.com/fmtlib/fmt/issues/3710, https://github.com/fmtlib/fmt/issues/3712, https://github.com/fmtlib/fmt/pull/3713, https://github.com/fmtlib/fmt/issues/3714, https://github.com/fmtlib/fmt/pull/3716, https://github.com/fmtlib/fmt/pull/3723, https://github.com/fmtlib/fmt/issues/3738, https://github.com/fmtlib/fmt/issues/3740, https://github.com/fmtlib/fmt/pull/3741, https://github.com/fmtlib/fmt/pull/3743, https://github.com/fmtlib/fmt/issues/3745, https://github.com/fmtlib/fmt/pull/3747, https://github.com/fmtlib/fmt/pull/3748, https://github.com/fmtlib/fmt/pull/3751, https://github.com/fmtlib/fmt/pull/3754, https://github.com/fmtlib/fmt/pull/3755, https://github.com/fmtlib/fmt/issues/3760, https://github.com/fmtlib/fmt/pull/3762, https://github.com/fmtlib/fmt/issues/3763, https://github.com/fmtlib/fmt/pull/3764, https://github.com/fmtlib/fmt/issues/3774, https://github.com/fmtlib/fmt/pull/3779). Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning, @cptFracassa, @kuguma, @PeterJohnson, @H1X4Dev, @asantoni, @eltociear, @msimberg, @tchaikov, @waywardmonkeys. - Improved documentation and README (https://github.com/fmtlib/fmt/issues/2086, https://github.com/fmtlib/fmt/issues/3637, https://github.com/fmtlib/fmt/pull/3642, https://github.com/fmtlib/fmt/pull/3653, https://github.com/fmtlib/fmt/pull/3655, https://github.com/fmtlib/fmt/pull/3661, https://github.com/fmtlib/fmt/issues/3673, https://github.com/fmtlib/fmt/pull/3677, https://github.com/fmtlib/fmt/pull/3737, https://github.com/fmtlib/fmt/issues/3742, https://github.com/fmtlib/fmt/pull/3744). Thanks @idzm, @perlun, @joycebrum, @fennewald, @reinhardt1053, @GeorgeLS. - Updated CI dependencies (https://github.com/fmtlib/fmt/pull/3615, https://github.com/fmtlib/fmt/pull/3622, https://github.com/fmtlib/fmt/pull/3623, https://github.com/fmtlib/fmt/pull/3666, https://github.com/fmtlib/fmt/pull/3696, https://github.com/fmtlib/fmt/pull/3697, https://github.com/fmtlib/fmt/pull/3759, https://github.com/fmtlib/fmt/pull/3782). # 10.1.1 - 2023-08-28 - Added formatters for `std::atomic` and `atomic_flag` (https://github.com/fmtlib/fmt/pull/3574, https://github.com/fmtlib/fmt/pull/3594). Thanks @wangzw and @AlexGuteniev. - Fixed an error about partial specialization of `formatter` after instantiation when compiled with gcc and C++20 (https://github.com/fmtlib/fmt/issues/3584). - Fixed compilation as a C++20 module with gcc and clang (https://github.com/fmtlib/fmt/issues/3587, https://github.com/fmtlib/fmt/pull/3597, https://github.com/fmtlib/fmt/pull/3605). Thanks @MathewBensonCode. - Made `fmt::to_string` work with types that have `format_as` overloads (https://github.com/fmtlib/fmt/pull/3575). Thanks @phprus. - Made `formatted_size` work with integral format specifiers at compile time (https://github.com/fmtlib/fmt/pull/3591). Thanks @elbeno. - Fixed a warning about the `no_unique_address` attribute on clang-cl (https://github.com/fmtlib/fmt/pull/3599). Thanks @lukester1975. - Improved compatibility with the legacy GBK encoding (https://github.com/fmtlib/fmt/issues/3598, https://github.com/fmtlib/fmt/pull/3599). Thanks @YuHuanTin. - Added OpenSSF Scorecard analysis (https://github.com/fmtlib/fmt/issues/3530, https://github.com/fmtlib/fmt/pull/3571). Thanks @joycebrum. - Updated CI dependencies (https://github.com/fmtlib/fmt/pull/3591, https://github.com/fmtlib/fmt/pull/3592, https://github.com/fmtlib/fmt/pull/3593, https://github.com/fmtlib/fmt/pull/3602). # 10.1.0 - 2023-08-12 - Optimized format string compilation resulting in up to 40% speed up in compiled `format_to` and \~4x speed up in compiled `format_to_n` on a concatenation benchmark (https://github.com/fmtlib/fmt/issues/3133, https://github.com/fmtlib/fmt/issues/3484). {fmt} 10.0: --------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------- BM_format_to 78.9 ns 78.9 ns 8881746 BM_format_to_n 568 ns 568 ns 1232089 {fmt} 10.1: --------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------- BM_format_to 54.9 ns 54.9 ns 12727944 BM_format_to_n 133 ns 133 ns 5257795 - Optimized storage of an empty allocator in `basic_memory_buffer` (https://github.com/fmtlib/fmt/pull/3485). Thanks @Minty-Meeo. - Added formatters for proxy references to elements of `std::vector` and `std::bitset` (https://github.com/fmtlib/fmt/issues/3567, https://github.com/fmtlib/fmt/pull/3570). For example ([godbolt](https://godbolt.org/z/zYb79Pvn8)): ```c++ #include #include int main() { auto v = std::vector{true}; fmt::print("{}", v[0]); } ``` Thanks @phprus and @felix642. - Fixed an ambiguous formatter specialization for containers that look like container adaptors such as `boost::flat_set` (https://github.com/fmtlib/fmt/issues/3556, https://github.com/fmtlib/fmt/pull/3561). Thanks @5chmidti. - Fixed compilation when formatting durations not convertible from `std::chrono::seconds` (https://github.com/fmtlib/fmt/pull/3430). Thanks @patlkli. - Made the `formatter` specialization for `char*` const-correct (https://github.com/fmtlib/fmt/pull/3432). Thanks @timsong-cpp. - Made `{}` and `{:}` handled consistently during compile-time checks (https://github.com/fmtlib/fmt/issues/3526). - Disallowed passing temporaries to `make_format_args` to improve API safety by preventing dangling references. - Improved the compile-time error for unformattable types (https://github.com/fmtlib/fmt/pull/3478). Thanks @BRevzin. - Improved the floating-point formatter (https://github.com/fmtlib/fmt/pull/3448, https://github.com/fmtlib/fmt/pull/3450). Thanks @florimond-collette. - Fixed handling of precision for `long double` larger than 64 bits. (https://github.com/fmtlib/fmt/issues/3539, https://github.com/fmtlib/fmt/issues/3564). - Made floating-point and chrono tests less platform-dependent (https://github.com/fmtlib/fmt/issues/3337, https://github.com/fmtlib/fmt/issues/3433, https://github.com/fmtlib/fmt/pull/3434). Thanks @phprus. - Removed the remnants of the Grisu floating-point formatter that has been replaced by Dragonbox in earlier versions. - Added `throw_format_error` to the public API (https://github.com/fmtlib/fmt/pull/3551). Thanks @mjerabek. - Made `FMT_THROW` assert even if assertions are disabled when compiling with exceptions disabled (https://github.com/fmtlib/fmt/issues/3418, https://github.com/fmtlib/fmt/pull/3439). Thanks @BRevzin. - Made `format_as` and `std::filesystem::path` formatter work with exotic code unit types. (https://github.com/fmtlib/fmt/pull/3457, https://github.com/fmtlib/fmt/pull/3476). Thanks @gix and @hmbj. - Added support for the `?` format specifier to `std::filesystem::path` and made the default unescaped for consistency with strings. - Deprecated the wide stream overload of `printf`. - Removed unused `basic_printf_parse_context`. - Improved RTTI detection used when formatting exceptions (https://github.com/fmtlib/fmt/pull/3468). Thanks @danakj. - Improved compatibility with VxWorks7 (https://github.com/fmtlib/fmt/pull/3467). Thanks @wenshan1. - Improved documentation (https://github.com/fmtlib/fmt/issues/3174, https://github.com/fmtlib/fmt/issues/3423, https://github.com/fmtlib/fmt/pull/3454, https://github.com/fmtlib/fmt/issues/3458, https://github.com/fmtlib/fmt/pull/3461, https://github.com/fmtlib/fmt/issues/3487, https://github.com/fmtlib/fmt/pull/3515). Thanks @zencatalyst, @rlalik and @mikecrowe. - Improved build and CI configurations (https://github.com/fmtlib/fmt/issues/3449, https://github.com/fmtlib/fmt/pull/3451, https://github.com/fmtlib/fmt/pull/3452, https://github.com/fmtlib/fmt/pull/3453, https://github.com/fmtlib/fmt/pull/3459, https://github.com/fmtlib/fmt/issues/3481, https://github.com/fmtlib/fmt/pull/3486, https://github.com/fmtlib/fmt/issues/3489, https://github.com/fmtlib/fmt/pull/3496, https://github.com/fmtlib/fmt/issues/3517, https://github.com/fmtlib/fmt/pull/3523, https://github.com/fmtlib/fmt/pull/3563). Thanks @joycebrum, @glebm, @phprus, @petrmanek, @setoye and @abouvier. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/3408, https://github.com/fmtlib/fmt/issues/3424, https://github.com/fmtlib/fmt/issues/3444, https://github.com/fmtlib/fmt/pull/3446, https://github.com/fmtlib/fmt/pull/3475, https://github.com/fmtlib/fmt/pull/3482, https://github.com/fmtlib/fmt/issues/3492, https://github.com/fmtlib/fmt/pull/3493, https://github.com/fmtlib/fmt/pull/3508, https://github.com/fmtlib/fmt/issues/3509, https://github.com/fmtlib/fmt/issues/3533, https://github.com/fmtlib/fmt/pull/3542, https://github.com/fmtlib/fmt/issues/3543, https://github.com/fmtlib/fmt/issues/3540, https://github.com/fmtlib/fmt/pull/3544, https://github.com/fmtlib/fmt/issues/3548, https://github.com/fmtlib/fmt/pull/3549, https://github.com/fmtlib/fmt/pull/3550, https://github.com/fmtlib/fmt/pull/3552). Thanks @adesitter, @hmbj, @Minty-Meeo, @phprus, @TobiSchluter, @kieranclancy, @alexeedm, @jurihock, @Ozomahtli and @razaqq. # 10.0.0 - 2023-05-09 - Replaced Grisu with a new floating-point formatting algorithm for given precision (https://github.com/fmtlib/fmt/issues/3262, https://github.com/fmtlib/fmt/issues/2750, https://github.com/fmtlib/fmt/pull/3269, https://github.com/fmtlib/fmt/pull/3276). The new algorithm is based on Dragonbox already used for the shortest representation and gives substantial performance improvement: ![](https://user-images.githubusercontent.com/33922675/211956670-84891a09-6867-47d9-82fc-3230da7abe0f.png) - Red: new algorithm - Green: new algorithm with `FMT_USE_FULL_CACHE_DRAGONBOX` defined to 1 - Blue: old algorithm Thanks @jk-jeon. - Replaced `snprintf`-based hex float formatter with an internal implementation (https://github.com/fmtlib/fmt/pull/3179, https://github.com/fmtlib/fmt/pull/3203). This removes the last usage of `s(n)printf` in {fmt}. Thanks @phprus. - Fixed alignment of floating-point numbers with localization (https://github.com/fmtlib/fmt/issues/3263, https://github.com/fmtlib/fmt/pull/3272). Thanks @ShawnZhong. - Made handling of `#` consistent with `std::format`. - Improved C++20 module support (https://github.com/fmtlib/fmt/pull/3134, https://github.com/fmtlib/fmt/pull/3254, https://github.com/fmtlib/fmt/pull/3386, https://github.com/fmtlib/fmt/pull/3387, https://github.com/fmtlib/fmt/pull/3388, https://github.com/fmtlib/fmt/pull/3392, https://github.com/fmtlib/fmt/pull/3397, https://github.com/fmtlib/fmt/pull/3399, https://github.com/fmtlib/fmt/pull/3400). Thanks @laitingsheng, @Orvid and @DanielaE. - Switched to the [modules CMake library](https://github.com/vitaut/modules) which allows building {fmt} as a C++20 module with clang: CXX=clang++ cmake -DFMT_MODULE=ON . make - Made `format_as` work with any user-defined type and not just enums. For example ([godbolt](https://godbolt.org/z/b7rqhq5Kh)): ```c++ #include struct floaty_mc_floatface { double value; }; auto format_as(floaty_mc_floatface f) { return f.value; } int main() { fmt::print("{:8}\n", floaty_mc_floatface{0.42}); // prints " 0.42" } ``` - Removed deprecated implicit conversions for enums and conversions to primitive types for compatibility with `std::format` and to prevent potential ODR violations. Use `format_as` instead. - Added support for fill, align and width to the time point formatter (https://github.com/fmtlib/fmt/issues/3237, https://github.com/fmtlib/fmt/pull/3260, https://github.com/fmtlib/fmt/pull/3275). For example ([godbolt](https://godbolt.org/z/rKP6MGz6c)): ```c++ #include int main() { // prints " 2023" fmt::print("{:>8%Y}\n", std::chrono::system_clock::now()); } ``` Thanks @ShawnZhong. - Implemented formatting of subseconds (https://github.com/fmtlib/fmt/issues/2207, https://github.com/fmtlib/fmt/issues/3117, https://github.com/fmtlib/fmt/pull/3115, https://github.com/fmtlib/fmt/pull/3143, https://github.com/fmtlib/fmt/pull/3144, https://github.com/fmtlib/fmt/pull/3349). For example ([godbolt](https://godbolt.org/z/45738oGEo)): ```c++ #include int main() { // prints 01.234567 fmt::print("{:%S}\n", std::chrono::microseconds(1234567)); } ``` Thanks @patrickroocks @phprus and @BRevzin. - Added precision support to `%S` (https://github.com/fmtlib/fmt/pull/3148). Thanks @SappyJoy - Added support for `std::utc_time` (https://github.com/fmtlib/fmt/issues/3098, https://github.com/fmtlib/fmt/pull/3110). Thanks @patrickroocks. - Switched formatting of `std::chrono::system_clock` from local time to UTC for compatibility with the standard (https://github.com/fmtlib/fmt/issues/3199, https://github.com/fmtlib/fmt/pull/3230). Thanks @ned14. - Added support for `%Ez` and `%Oz` to chrono formatters. (https://github.com/fmtlib/fmt/issues/3220, https://github.com/fmtlib/fmt/pull/3222). Thanks @phprus. - Improved validation of format specifiers for `std::chrono::duration` (https://github.com/fmtlib/fmt/issues/3219, https://github.com/fmtlib/fmt/pull/3232). Thanks @ShawnZhong. - Fixed formatting of time points before the epoch (https://github.com/fmtlib/fmt/issues/3117, https://github.com/fmtlib/fmt/pull/3261). For example ([godbolt](https://godbolt.org/z/f7bcznb3W)): ```c++ #include int main() { auto t = std::chrono::system_clock::from_time_t(0) - std::chrono::milliseconds(250); fmt::print("{:%S}\n", t); // prints 59.750000000 } ``` Thanks @ShawnZhong. - Experimental: implemented glibc extension for padding seconds, minutes and hours (https://github.com/fmtlib/fmt/issues/2959, https://github.com/fmtlib/fmt/pull/3271). Thanks @ShawnZhong. - Added a formatter for `std::exception` (https://github.com/fmtlib/fmt/issues/2977, https://github.com/fmtlib/fmt/issues/3012, https://github.com/fmtlib/fmt/pull/3062, https://github.com/fmtlib/fmt/pull/3076, https://github.com/fmtlib/fmt/pull/3119). For example ([godbolt](https://godbolt.org/z/8xoWGs9e4)): ```c++ #include #include int main() { try { std::vector().at(0); } catch(const std::exception& e) { fmt::print("{}", e); } } ``` prints: vector::_M_range_check: __n (which is 0) >= this->size() (which is 0) on libstdc++. Thanks @zach2good and @phprus. - Moved `std::error_code` formatter from `fmt/os.h` to `fmt/std.h`. (https://github.com/fmtlib/fmt/pull/3125). Thanks @phprus. - Added formatters for standard container adapters: `std::priority_queue`, `std::queue` and `std::stack` (https://github.com/fmtlib/fmt/issues/3215, https://github.com/fmtlib/fmt/pull/3279). For example ([godbolt](https://godbolt.org/z/74h1xY9qK)): ```c++ #include #include #include int main() { auto s = std::stack>(); for (auto b: {true, false, true}) s.push(b); fmt::print("{}\n", s); // prints [true, false, true] } ``` Thanks @ShawnZhong. - Added a formatter for `std::optional` to `fmt/std.h` (https://github.com/fmtlib/fmt/issues/1367, https://github.com/fmtlib/fmt/pull/3303). Thanks @tom-huntington. - Fixed formatting of valueless by exception variants (https://github.com/fmtlib/fmt/pull/3347). Thanks @TheOmegaCarrot. - Made `fmt::ptr` accept `unique_ptr` with a custom deleter (https://github.com/fmtlib/fmt/pull/3177). Thanks @hmbj. - Fixed formatting of noncopyable ranges and nested ranges of chars (https://github.com/fmtlib/fmt/pull/3158 https://github.com/fmtlib/fmt/issues/3286, https://github.com/fmtlib/fmt/pull/3290). Thanks @BRevzin. - Fixed issues with formatting of paths and ranges of paths (https://github.com/fmtlib/fmt/issues/3319, https://github.com/fmtlib/fmt/pull/3321 https://github.com/fmtlib/fmt/issues/3322). Thanks @phprus. - Improved handling of invalid Unicode in paths. - Enabled compile-time checks on Apple clang 14 and later (https://github.com/fmtlib/fmt/pull/3331). Thanks @cloyce. - Improved compile-time checks of named arguments (https://github.com/fmtlib/fmt/issues/3105, https://github.com/fmtlib/fmt/pull/3214). Thanks @rbrich. - Fixed formatting when both alignment and `0` are given (https://github.com/fmtlib/fmt/issues/3236, https://github.com/fmtlib/fmt/pull/3248). Thanks @ShawnZhong. - Improved Unicode support in the experimental file API on Windows (https://github.com/fmtlib/fmt/issues/3234, https://github.com/fmtlib/fmt/pull/3293). Thanks @Fros1er. - Unified UTF transcoding (https://github.com/fmtlib/fmt/pull/3416). Thanks @phprus. - Added support for UTF-8 digit separators via an experimental locale facet (https://github.com/fmtlib/fmt/issues/1861). For example ([godbolt](https://godbolt.org/z/f7bcznb3W)): ```c++ auto loc = std::locale( std::locale(), new fmt::format_facet("’")); auto s = fmt::format(loc, "{:L}", 1000); ``` where `’` is U+2019 used as a digit separator in the de_CH locale. - Added an overload of `formatted_size` that takes a locale (https://github.com/fmtlib/fmt/issues/3084, https://github.com/fmtlib/fmt/pull/3087). Thanks @gerboengels. - Removed the deprecated `FMT_DEPRECATED_OSTREAM`. - Fixed a UB when using a null `std::string_view` with `fmt::to_string` or format string compilation (https://github.com/fmtlib/fmt/issues/3241, https://github.com/fmtlib/fmt/pull/3244). Thanks @phprus. - Added `starts_with` to the fallback `string_view` implementation (https://github.com/fmtlib/fmt/pull/3080). Thanks @phprus. - Added `fmt::basic_format_string::get()` for compatibility with `basic_format_string` (https://github.com/fmtlib/fmt/pull/3111). Thanks @huangqinjin. - Added `println` for compatibility with C++23 (https://github.com/fmtlib/fmt/pull/3267). Thanks @ShawnZhong. - Renamed the `FMT_EXPORT` macro for shared library usage to `FMT_LIB_EXPORT`. - Improved documentation (https://github.com/fmtlib/fmt/issues/3108, https://github.com/fmtlib/fmt/issues/3169, https://github.com/fmtlib/fmt/pull/3243). https://github.com/fmtlib/fmt/pull/3404, https://github.com/fmtlib/fmt/pull/4002). Thanks @Cleroth, @Vertexwahn and @yujincheng08. - Improved build configuration and tests (https://github.com/fmtlib/fmt/pull/3118, https://github.com/fmtlib/fmt/pull/3120, https://github.com/fmtlib/fmt/pull/3188, https://github.com/fmtlib/fmt/issues/3189, https://github.com/fmtlib/fmt/pull/3198, https://github.com/fmtlib/fmt/pull/3205, https://github.com/fmtlib/fmt/pull/3207, https://github.com/fmtlib/fmt/pull/3210, https://github.com/fmtlib/fmt/pull/3240, https://github.com/fmtlib/fmt/pull/3256, https://github.com/fmtlib/fmt/pull/3264, https://github.com/fmtlib/fmt/issues/3299, https://github.com/fmtlib/fmt/pull/3302, https://github.com/fmtlib/fmt/pull/3312, https://github.com/fmtlib/fmt/issues/3317, https://github.com/fmtlib/fmt/pull/3328, https://github.com/fmtlib/fmt/pull/3333, https://github.com/fmtlib/fmt/pull/3369, https://github.com/fmtlib/fmt/issues/3373, https://github.com/fmtlib/fmt/pull/3395, https://github.com/fmtlib/fmt/pull/3406, https://github.com/fmtlib/fmt/pull/3411). Thanks @dimztimz, @phprus, @DavidKorczynski, @ChrisThrasher, @FrancoisCarouge, @kennyweiss, @luzpaz, @codeinred, @Mixaill, @joycebrum, @kevinhwang and @Vertexwahn. - Fixed a regression in handling empty format specifiers after a colon (`{:}`) (https://github.com/fmtlib/fmt/pull/3086). Thanks @oxidase. - Worked around a broken implementation of `std::is_constant_evaluated` in some versions of libstdc++ on clang (https://github.com/fmtlib/fmt/issues/3247, https://github.com/fmtlib/fmt/pull/3281). Thanks @phprus. - Fixed formatting of volatile variables (https://github.com/fmtlib/fmt/pull/3068). - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/3057, https://github.com/fmtlib/fmt/pull/3066, https://github.com/fmtlib/fmt/pull/3072, https://github.com/fmtlib/fmt/pull/3082, https://github.com/fmtlib/fmt/pull/3091, https://github.com/fmtlib/fmt/issues/3092, https://github.com/fmtlib/fmt/pull/3093, https://github.com/fmtlib/fmt/pull/3095, https://github.com/fmtlib/fmt/issues/3096, https://github.com/fmtlib/fmt/pull/3097, https://github.com/fmtlib/fmt/issues/3128, https://github.com/fmtlib/fmt/pull/3129, https://github.com/fmtlib/fmt/pull/3137, https://github.com/fmtlib/fmt/pull/3139, https://github.com/fmtlib/fmt/issues/3140, https://github.com/fmtlib/fmt/pull/3142, https://github.com/fmtlib/fmt/issues/3149, https://github.com/fmtlib/fmt/pull/3150, https://github.com/fmtlib/fmt/issues/3154, https://github.com/fmtlib/fmt/issues/3163, https://github.com/fmtlib/fmt/issues/3178, https://github.com/fmtlib/fmt/pull/3184, https://github.com/fmtlib/fmt/pull/3196, https://github.com/fmtlib/fmt/issues/3204, https://github.com/fmtlib/fmt/pull/3206, https://github.com/fmtlib/fmt/pull/3208, https://github.com/fmtlib/fmt/issues/3213, https://github.com/fmtlib/fmt/pull/3216, https://github.com/fmtlib/fmt/issues/3224, https://github.com/fmtlib/fmt/issues/3226, https://github.com/fmtlib/fmt/issues/3228, https://github.com/fmtlib/fmt/pull/3229, https://github.com/fmtlib/fmt/pull/3259, https://github.com/fmtlib/fmt/issues/3274, https://github.com/fmtlib/fmt/issues/3287, https://github.com/fmtlib/fmt/pull/3288, https://github.com/fmtlib/fmt/issues/3292, https://github.com/fmtlib/fmt/pull/3295, https://github.com/fmtlib/fmt/pull/3296, https://github.com/fmtlib/fmt/issues/3298, https://github.com/fmtlib/fmt/issues/3325, https://github.com/fmtlib/fmt/pull/3326, https://github.com/fmtlib/fmt/issues/3334, https://github.com/fmtlib/fmt/issues/3342, https://github.com/fmtlib/fmt/pull/3343, https://github.com/fmtlib/fmt/issues/3351, https://github.com/fmtlib/fmt/pull/3352, https://github.com/fmtlib/fmt/pull/3362, https://github.com/fmtlib/fmt/issues/3365, https://github.com/fmtlib/fmt/pull/3366, https://github.com/fmtlib/fmt/pull/3374, https://github.com/fmtlib/fmt/issues/3377, https://github.com/fmtlib/fmt/pull/3378, https://github.com/fmtlib/fmt/issues/3381, https://github.com/fmtlib/fmt/pull/3398, https://github.com/fmtlib/fmt/pull/3413, https://github.com/fmtlib/fmt/issues/3415). Thanks @phprus, @gsjaardema, @NewbieOrange, @EngineLessCC, @asmaloney, @HazardyKnusperkeks, @sergiud, @Youw, @thesmurph, @czudziakm, @Roman-Koshelev, @chronoxor, @ShawnZhong, @russelltg, @glebm, @tmartin-gh, @Zhaojun-Liu, @louiswins and @mogemimi. # 9.1.0 - 2022-08-27 - `fmt::formatted_size` now works at compile time (https://github.com/fmtlib/fmt/pull/3026). For example ([godbolt](https://godbolt.org/z/1MW5rMdf8)): ```c++ #include int main() { using namespace fmt::literals; constexpr size_t n = fmt::formatted_size("{}"_cf, 42); fmt::print("{}\n", n); // prints 2 } ``` Thanks @marksantaniello. - Fixed handling of invalid UTF-8 (https://github.com/fmtlib/fmt/pull/3038, https://github.com/fmtlib/fmt/pull/3044, https://github.com/fmtlib/fmt/pull/3056). Thanks @phprus and @skeeto. - Improved Unicode support in `ostream` overloads of `print` (https://github.com/fmtlib/fmt/pull/2994, https://github.com/fmtlib/fmt/pull/3001, https://github.com/fmtlib/fmt/pull/3025). Thanks @dimztimz. - Fixed handling of the sign specifier in localized formatting on systems with 32-bit `wchar_t` (https://github.com/fmtlib/fmt/issues/3041). - Added support for wide streams to `fmt::streamed` (https://github.com/fmtlib/fmt/pull/2994). Thanks @phprus. - Added the `n` specifier that disables the output of delimiters when formatting ranges (https://github.com/fmtlib/fmt/pull/2981, https://github.com/fmtlib/fmt/pull/2983). For example ([godbolt](https://godbolt.org/z/roKqGdj8c)): ```c++ #include #include int main() { auto v = std::vector{1, 2, 3}; fmt::print("{:n}\n", v); // prints 1, 2, 3 } ``` Thanks @BRevzin. - Worked around problematic `std::string_view` constructors introduced in C++23 (https://github.com/fmtlib/fmt/issues/3030, https://github.com/fmtlib/fmt/issues/3050). Thanks @strega-nil-ms. - Improve handling (exclusion) of recursive ranges (https://github.com/fmtlib/fmt/issues/2968, https://github.com/fmtlib/fmt/pull/2974). Thanks @Dani-Hub. - Improved error reporting in format string compilation (https://github.com/fmtlib/fmt/issues/3055). - Improved the implementation of [Dragonbox](https://github.com/jk-jeon/dragonbox), the algorithm used for the default floating-point formatting (https://github.com/fmtlib/fmt/pull/2984). Thanks @jk-jeon. - Fixed issues with floating-point formatting on exotic platforms. - Improved the implementation of chrono formatting (https://github.com/fmtlib/fmt/pull/3010). Thanks @phprus. - Improved documentation (https://github.com/fmtlib/fmt/pull/2966, https://github.com/fmtlib/fmt/pull/3009, https://github.com/fmtlib/fmt/issues/3020, https://github.com/fmtlib/fmt/pull/3037). Thanks @mwinterb, @jcelerier and @remiburtin. - Improved build configuration (https://github.com/fmtlib/fmt/pull/2991, https://github.com/fmtlib/fmt/pull/2995, https://github.com/fmtlib/fmt/issues/3004, https://github.com/fmtlib/fmt/pull/3007, https://github.com/fmtlib/fmt/pull/3040). Thanks @dimztimz and @hwhsu1231. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/2969, https://github.com/fmtlib/fmt/pull/2971, https://github.com/fmtlib/fmt/issues/2975, https://github.com/fmtlib/fmt/pull/2982, https://github.com/fmtlib/fmt/pull/2985, https://github.com/fmtlib/fmt/issues/2988, https://github.com/fmtlib/fmt/issues/2989, https://github.com/fmtlib/fmt/issues/3000, https://github.com/fmtlib/fmt/issues/3006, https://github.com/fmtlib/fmt/issues/3014, https://github.com/fmtlib/fmt/issues/3015, https://github.com/fmtlib/fmt/pull/3021, https://github.com/fmtlib/fmt/issues/3023, https://github.com/fmtlib/fmt/pull/3024, https://github.com/fmtlib/fmt/pull/3029, https://github.com/fmtlib/fmt/pull/3043, https://github.com/fmtlib/fmt/issues/3052, https://github.com/fmtlib/fmt/pull/3053, https://github.com/fmtlib/fmt/pull/3054). Thanks @h-friederich, @dimztimz, @olupton, @bernhardmgruber and @phprus. # 9.0.0 - 2022-07-04 - Switched to the internal floating point formatter for all decimal presentation formats. In particular this results in consistent rounding on all platforms and removing the `s[n]printf` fallback for decimal FP formatting. - Compile-time floating point formatting no longer requires the header-only mode. For example ([godbolt](https://godbolt.org/z/G37PTeG3b)): ```c++ #include #include consteval auto compile_time_dtoa(double value) -> std::array { auto result = std::array(); fmt::format_to(result.data(), FMT_COMPILE("{}"), value); return result; } constexpr auto answer = compile_time_dtoa(0.42); ``` works with the default settings. - Improved the implementation of [Dragonbox](https://github.com/jk-jeon/dragonbox), the algorithm used for the default floating-point formatting (https://github.com/fmtlib/fmt/pull/2713, https://github.com/fmtlib/fmt/pull/2750). Thanks @jk-jeon. - Made `fmt::to_string` work with `__float128`. This uses the internal FP formatter and works even on system without `__float128` support in `[s]printf`. - Disabled automatic `std::ostream` insertion operator (`operator<<`) discovery when `fmt/ostream.h` is included to prevent ODR violations. You can get the old behavior by defining `FMT_DEPRECATED_OSTREAM` but this will be removed in the next major release. Use `fmt::streamed` or `fmt::ostream_formatter` to enable formatting via `std::ostream` instead. - Added `fmt::ostream_formatter` that can be used to write `formatter` specializations that perform formatting via `std::ostream`. For example ([godbolt](https://godbolt.org/z/5sEc5qMsf)): ```c++ #include struct date { int year, month, day; friend std::ostream& operator<<(std::ostream& os, const date& d) { return os << d.year << '-' << d.month << '-' << d.day; } }; template <> struct fmt::formatter : ostream_formatter {}; std::string s = fmt::format("The date is {}", date{2012, 12, 9}); // s == "The date is 2012-12-9" ``` - Added the `fmt::streamed` function that takes an object and formats it via `std::ostream`. For example ([godbolt](https://godbolt.org/z/5G3346G1f)): ```c++ #include #include int main() { fmt::print("Current thread id: {}\n", fmt::streamed(std::this_thread::get_id())); } ``` Note that `fmt/std.h` provides a `formatter` specialization for `std::thread::id` so you don\'t need to format it via `std::ostream`. - Deprecated implicit conversions of unscoped enums to integers for consistency with scoped enums. - Added an argument-dependent lookup based `format_as` extension API to simplify formatting of enums. - Added experimental `std::variant` formatting support (https://github.com/fmtlib/fmt/pull/2941). For example ([godbolt](https://godbolt.org/z/KG9z6cq68)): ```c++ #include #include int main() { auto v = std::variant(42); fmt::print("{}\n", v); } ``` prints: variant(42) Thanks @jehelset. - Added experimental `std::filesystem::path` formatting support (https://github.com/fmtlib/fmt/issues/2865, https://github.com/fmtlib/fmt/pull/2902, https://github.com/fmtlib/fmt/issues/2917, https://github.com/fmtlib/fmt/pull/2918). For example ([godbolt](https://godbolt.org/z/o44dMexEb)): ```c++ #include #include int main() { fmt::print("There is no place like {}.", std::filesystem::path("/home")); } ``` prints: There is no place like "/home". Thanks @phprus. - Added a `std::thread::id` formatter to `fmt/std.h`. For example ([godbolt](https://godbolt.org/z/j1azbYf3E)): ```c++ #include #include int main() { fmt::print("Current thread id: {}\n", std::this_thread::get_id()); } ``` - Added `fmt::styled` that applies a text style to an individual argument (https://github.com/fmtlib/fmt/pull/2793). For example ([godbolt](https://godbolt.org/z/vWGW7v5M6)): ```c++ #include #include int main() { auto now = std::chrono::system_clock::now(); fmt::print( "[{}] {}: {}\n", fmt::styled(now, fmt::emphasis::bold), fmt::styled("error", fg(fmt::color::red)), "something went wrong"); } ``` prints ![](https://user-images.githubusercontent.com/576385/175071215-12809244-dab0-4005-96d8-7cd911c964d5.png) Thanks @rbrugo. - Made `fmt::print` overload for text styles correctly handle UTF-8 (https://github.com/fmtlib/fmt/issues/2681, https://github.com/fmtlib/fmt/pull/2701). Thanks @AlexGuteniev. - Fixed Unicode handling when writing to an ostream. - Added support for nested specifiers to range formatting (https://github.com/fmtlib/fmt/pull/2673). For example ([godbolt](https://godbolt.org/z/xd3Gj38cf)): ```c++ #include #include int main() { fmt::print("{::#x}\n", std::vector{10, 20, 30}); } ``` prints `[0xa, 0x14, 0x1e]`. Thanks @BRevzin. - Implemented escaping of wide strings in ranges (https://github.com/fmtlib/fmt/pull/2904). Thanks @phprus. - Added support for ranges with `begin` / `end` found via the argument-dependent lookup (https://github.com/fmtlib/fmt/pull/2807). Thanks @rbrugo. - Fixed formatting of certain kinds of ranges of ranges (https://github.com/fmtlib/fmt/pull/2787). Thanks @BRevzin. - Fixed handling of maps with element types other than `std::pair` (https://github.com/fmtlib/fmt/pull/2944). Thanks @BrukerJWD. - Made tuple formatter enabled only if elements are formattable (https://github.com/fmtlib/fmt/issues/2939, https://github.com/fmtlib/fmt/pull/2940). Thanks @jehelset. - Made `fmt::join` compatible with format string compilation (https://github.com/fmtlib/fmt/issues/2719, https://github.com/fmtlib/fmt/pull/2720). Thanks @phprus. - Made compile-time checks work with named arguments of custom types and `std::ostream` `print` overloads (https://github.com/fmtlib/fmt/issues/2816, https://github.com/fmtlib/fmt/issues/2817, https://github.com/fmtlib/fmt/pull/2819). Thanks @timsong-cpp. - Removed `make_args_checked` because it is no longer needed for compile-time checks (https://github.com/fmtlib/fmt/pull/2760). Thanks @phprus. - Removed the following deprecated APIs: `_format`, `arg_join`, the `format_to` overload that takes a memory buffer, `[v]fprintf` that takes an `ostream`. - Removed the deprecated implicit conversion of `[const] signed char*` and `[const] unsigned char*` to C strings. - Removed the deprecated `fmt/locale.h`. - Replaced the deprecated `fileno()` with `descriptor()` in `buffered_file`. - Moved `to_string_view` to the `detail` namespace since it\'s an implementation detail. - Made access mode of a created file consistent with `fopen` by setting `S_IWGRP` and `S_IWOTH` (https://github.com/fmtlib/fmt/pull/2733). Thanks @arogge. - Removed a redundant buffer resize when formatting to `std::ostream` (https://github.com/fmtlib/fmt/issues/2842, https://github.com/fmtlib/fmt/pull/2843). Thanks @jcelerier. - Made precision computation for strings consistent with width (https://github.com/fmtlib/fmt/issues/2888). - Fixed handling of locale separators in floating point formatting (https://github.com/fmtlib/fmt/issues/2830). - Made sign specifiers work with `__int128_t` (https://github.com/fmtlib/fmt/issues/2773). - Improved support for systems such as CHERI with extra data stored in pointers (https://github.com/fmtlib/fmt/pull/2932). Thanks @davidchisnall. - Improved documentation (https://github.com/fmtlib/fmt/pull/2706, https://github.com/fmtlib/fmt/pull/2712, https://github.com/fmtlib/fmt/pull/2789, https://github.com/fmtlib/fmt/pull/2803, https://github.com/fmtlib/fmt/pull/2805, https://github.com/fmtlib/fmt/pull/2815, https://github.com/fmtlib/fmt/pull/2924). Thanks @BRevzin, @Pokechu22, @setoye, @rtobar, @rbrugo, @anoonD and @leha-bot. - Improved build configuration (https://github.com/fmtlib/fmt/pull/2766, https://github.com/fmtlib/fmt/pull/2772, https://github.com/fmtlib/fmt/pull/2836, https://github.com/fmtlib/fmt/pull/2852, https://github.com/fmtlib/fmt/pull/2907, https://github.com/fmtlib/fmt/pull/2913, https://github.com/fmtlib/fmt/pull/2914). Thanks @kambala-decapitator, @mattiasljungstrom, @kieselnb, @nathannaveen and @Vertexwahn. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/2408, https://github.com/fmtlib/fmt/issues/2507, https://github.com/fmtlib/fmt/issues/2697, https://github.com/fmtlib/fmt/issues/2715, https://github.com/fmtlib/fmt/issues/2717, https://github.com/fmtlib/fmt/pull/2722, https://github.com/fmtlib/fmt/pull/2724, https://github.com/fmtlib/fmt/pull/2725, https://github.com/fmtlib/fmt/issues/2726, https://github.com/fmtlib/fmt/pull/2728, https://github.com/fmtlib/fmt/pull/2732, https://github.com/fmtlib/fmt/issues/2738, https://github.com/fmtlib/fmt/pull/2742, https://github.com/fmtlib/fmt/issues/2744, https://github.com/fmtlib/fmt/issues/2745, https://github.com/fmtlib/fmt/issues/2746, https://github.com/fmtlib/fmt/issues/2754, https://github.com/fmtlib/fmt/pull/2755, https://github.com/fmtlib/fmt/issues/2757, https://github.com/fmtlib/fmt/pull/2758, https://github.com/fmtlib/fmt/issues/2761, https://github.com/fmtlib/fmt/pull/2762, https://github.com/fmtlib/fmt/issues/2763, https://github.com/fmtlib/fmt/pull/2765, https://github.com/fmtlib/fmt/issues/2769, https://github.com/fmtlib/fmt/pull/2770, https://github.com/fmtlib/fmt/issues/2771, https://github.com/fmtlib/fmt/issues/2777, https://github.com/fmtlib/fmt/pull/2779, https://github.com/fmtlib/fmt/pull/2782, https://github.com/fmtlib/fmt/pull/2783, https://github.com/fmtlib/fmt/issues/2794, https://github.com/fmtlib/fmt/issues/2796, https://github.com/fmtlib/fmt/pull/2797, https://github.com/fmtlib/fmt/pull/2801, https://github.com/fmtlib/fmt/pull/2802, https://github.com/fmtlib/fmt/issues/2808, https://github.com/fmtlib/fmt/issues/2818, https://github.com/fmtlib/fmt/pull/2819, https://github.com/fmtlib/fmt/issues/2829, https://github.com/fmtlib/fmt/issues/2835, https://github.com/fmtlib/fmt/issues/2848, https://github.com/fmtlib/fmt/issues/2860, https://github.com/fmtlib/fmt/pull/2861, https://github.com/fmtlib/fmt/pull/2882, https://github.com/fmtlib/fmt/issues/2886, https://github.com/fmtlib/fmt/issues/2891, https://github.com/fmtlib/fmt/pull/2892, https://github.com/fmtlib/fmt/issues/2895, https://github.com/fmtlib/fmt/issues/2896, https://github.com/fmtlib/fmt/pull/2903, https://github.com/fmtlib/fmt/issues/2906, https://github.com/fmtlib/fmt/issues/2908, https://github.com/fmtlib/fmt/pull/2909, https://github.com/fmtlib/fmt/issues/2920, https://github.com/fmtlib/fmt/pull/2922, https://github.com/fmtlib/fmt/pull/2927, https://github.com/fmtlib/fmt/pull/2929, https://github.com/fmtlib/fmt/issues/2936, https://github.com/fmtlib/fmt/pull/2937, https://github.com/fmtlib/fmt/pull/2938, https://github.com/fmtlib/fmt/pull/2951, https://github.com/fmtlib/fmt/issues/2954, https://github.com/fmtlib/fmt/pull/2957, https://github.com/fmtlib/fmt/issues/2958, https://github.com/fmtlib/fmt/pull/2960). Thanks @matrackif @Tobi823, @ivan-volnov, @VasiliPupkin256, @federico-busato, @barcharcraz, @jk-jeon, @HazardyKnusperkeks, @dalboris, @seanm, @gsjaardema, @timsong-cpp, @seanm, @frithrah, @chronoxor, @Agga, @madmaxoft, @JurajX, @phprus and @Dani-Hub. # 8.1.1 - 2022-01-06 - Restored ABI compatibility with version 8.0.x (https://github.com/fmtlib/fmt/issues/2695, https://github.com/fmtlib/fmt/pull/2696). Thanks @saraedum. - Fixed chrono formatting on big endian systems (https://github.com/fmtlib/fmt/issues/2698, https://github.com/fmtlib/fmt/pull/2699). Thanks @phprus and @xvitaly. - Fixed a linkage error with mingw (https://github.com/fmtlib/fmt/issues/2691, https://github.com/fmtlib/fmt/pull/2692). Thanks @rbberger. # 8.1.0 - 2022-01-02 - Optimized chrono formatting (https://github.com/fmtlib/fmt/pull/2500, https://github.com/fmtlib/fmt/pull/2537, https://github.com/fmtlib/fmt/issues/2541, https://github.com/fmtlib/fmt/pull/2544, https://github.com/fmtlib/fmt/pull/2550, https://github.com/fmtlib/fmt/pull/2551, https://github.com/fmtlib/fmt/pull/2576, https://github.com/fmtlib/fmt/issues/2577, https://github.com/fmtlib/fmt/pull/2586, https://github.com/fmtlib/fmt/pull/2591, https://github.com/fmtlib/fmt/pull/2594, https://github.com/fmtlib/fmt/pull/2602, https://github.com/fmtlib/fmt/pull/2617, https://github.com/fmtlib/fmt/issues/2628, https://github.com/fmtlib/fmt/pull/2633, https://github.com/fmtlib/fmt/issues/2670, https://github.com/fmtlib/fmt/pull/2671). Processing of some specifiers such as `%z` and `%Y` is now up to 10-20 times faster, for example on GCC 11 with libstdc++: ---------------------------------------------------------------------------- Benchmark Before After ---------------------------------------------------------------------------- FMTFormatter_z 261 ns 26.3 ns FMTFormatterCompile_z 246 ns 11.6 ns FMTFormatter_Y 263 ns 26.1 ns FMTFormatterCompile_Y 244 ns 10.5 ns ---------------------------------------------------------------------------- Thanks @phprus and @toughengineer. - Implemented subsecond formatting for chrono durations (https://github.com/fmtlib/fmt/pull/2623). For example ([godbolt](https://godbolt.org/z/es7vWTETe)): ```c++ #include int main() { fmt::print("{:%S}", std::chrono::milliseconds(1234)); } ``` prints \"01.234\". Thanks @matrackif. - Fixed handling of precision 0 when formatting chrono durations (https://github.com/fmtlib/fmt/issues/2587, https://github.com/fmtlib/fmt/pull/2588). Thanks @lukester1975. - Fixed an overflow on invalid inputs in the `tm` formatter (https://github.com/fmtlib/fmt/pull/2564). Thanks @phprus. - Added `fmt::group_digits` that formats integers with a non-localized digit separator (comma) for groups of three digits. For example ([godbolt](https://godbolt.org/z/TxGxG9Poq)): ```c++ #include int main() { fmt::print("{} dollars", fmt::group_digits(1000000)); } ``` prints \"1,000,000 dollars\". - Added support for faint, conceal, reverse and blink text styles (https://github.com/fmtlib/fmt/pull/2394): Thanks @benit8 and @data-man. - Added experimental support for compile-time floating point formatting (https://github.com/fmtlib/fmt/pull/2426, https://github.com/fmtlib/fmt/pull/2470). It is currently limited to the header-only mode. Thanks @alexezeder. - Added UDL-based named argument support to compile-time format string checks (https://github.com/fmtlib/fmt/issues/2640, https://github.com/fmtlib/fmt/pull/2649). For example ([godbolt](https://godbolt.org/z/ohGbbvonv)): ```c++ #include int main() { using namespace fmt::literals; fmt::print("{answer:s}", "answer"_a=42); } ``` gives a compile-time error on compilers with C++20 `consteval` and non-type template parameter support (gcc 10+) because `s` is not a valid format specifier for an integer. Thanks @alexezeder. - Implemented escaping of string range elements. For example ([godbolt](https://godbolt.org/z/rKvM1vKf3)): ```c++ #include #include int main() { fmt::print("{}", std::vector{"\naan"}); } ``` is now printed as: ["\naan"] instead of: [" aan"] - Added an experimental `?` specifier for escaping strings. (https://github.com/fmtlib/fmt/pull/2674). Thanks @BRevzin. - Switched to JSON-like representation of maps and sets for consistency with Python\'s `str.format`. For example ([godbolt](https://godbolt.org/z/seKjoY9W5)): ```c++ #include #include int main() { fmt::print("{}", std::map{{"answer", 42}}); } ``` is now printed as: {"answer": 42} - Extended `fmt::join` to support C++20-only ranges (https://github.com/fmtlib/fmt/pull/2549). Thanks @BRevzin. - Optimized handling of non-const-iterable ranges and implemented initial support for non-const-formattable types. - Disabled implicit conversions of scoped enums to integers that was accidentally introduced in earlier versions (https://github.com/fmtlib/fmt/pull/1841). - Deprecated implicit conversion of `[const] signed char*` and `[const] unsigned char*` to C strings. - Deprecated `_format`, a legacy UDL-based format API (https://github.com/fmtlib/fmt/pull/2646). Thanks @alexezeder. - Marked `format`, `formatted_size` and `to_string` as `[[nodiscard]]` (https://github.com/fmtlib/fmt/pull/2612). @0x8000-0000. - Added missing diagnostic when trying to format function and member pointers as well as objects convertible to pointers which is explicitly disallowed (https://github.com/fmtlib/fmt/issues/2598, https://github.com/fmtlib/fmt/pull/2609, https://github.com/fmtlib/fmt/pull/2610). Thanks @AlexGuteniev. - Optimized writing to a contiguous buffer with `format_to_n` (https://github.com/fmtlib/fmt/pull/2489). Thanks @Roman-Koshelev. - Optimized writing to non-`char` buffers (https://github.com/fmtlib/fmt/pull/2477). Thanks @Roman-Koshelev. - Decimal point is now localized when using the `L` specifier. - Improved floating point formatter implementation (https://github.com/fmtlib/fmt/pull/2498, https://github.com/fmtlib/fmt/pull/2499). Thanks @Roman-Koshelev. - Fixed handling of very large precision in fixed format (https://github.com/fmtlib/fmt/pull/2616). - Made a table of cached powers used in FP formatting static (https://github.com/fmtlib/fmt/pull/2509). Thanks @jk-jeon. - Resolved a lookup ambiguity with C++20 format-related functions due to ADL (https://github.com/fmtlib/fmt/issues/2639, https://github.com/fmtlib/fmt/pull/2641). Thanks @mkurdej. - Removed unnecessary inline namespace qualification (https://github.com/fmtlib/fmt/issues/2642, https://github.com/fmtlib/fmt/pull/2643). Thanks @mkurdej. - Implemented argument forwarding in `format_to_n` (https://github.com/fmtlib/fmt/issues/2462, https://github.com/fmtlib/fmt/pull/2463). Thanks @owent. - Fixed handling of implicit conversions in `fmt::to_string` and format string compilation (https://github.com/fmtlib/fmt/issues/2565). - Changed the default access mode of files created by `fmt::output_file` to `-rw-r--r--` for consistency with `fopen` (https://github.com/fmtlib/fmt/issues/2530). - Make `fmt::ostream::flush` public (https://github.com/fmtlib/fmt/issues/2435). - Improved C++14/17 attribute detection (https://github.com/fmtlib/fmt/pull/2615). Thanks @AlexGuteniev. - Improved `consteval` detection for MSVC (https://github.com/fmtlib/fmt/pull/2559). Thanks @DanielaE. - Improved documentation (https://github.com/fmtlib/fmt/issues/2406, https://github.com/fmtlib/fmt/pull/2446, https://github.com/fmtlib/fmt/issues/2493, https://github.com/fmtlib/fmt/issues/2513, https://github.com/fmtlib/fmt/pull/2515, https://github.com/fmtlib/fmt/issues/2522, https://github.com/fmtlib/fmt/pull/2562, https://github.com/fmtlib/fmt/pull/2575, https://github.com/fmtlib/fmt/pull/2606, https://github.com/fmtlib/fmt/pull/2620, https://github.com/fmtlib/fmt/issues/2676). Thanks @sobolevn, @UnePierre, @zhsj, @phprus, @ericcurtin and @Lounarok. - Improved fuzzers and added a fuzzer for chrono timepoint formatting (https://github.com/fmtlib/fmt/pull/2461, https://github.com/fmtlib/fmt/pull/2469). @pauldreik, - Added the `FMT_SYSTEM_HEADERS` CMake option setting which marks {fmt}\'s headers as system. It can be used to suppress warnings (https://github.com/fmtlib/fmt/issues/2644, https://github.com/fmtlib/fmt/pull/2651). Thanks @alexezeder. - Added the Bazel build system support (https://github.com/fmtlib/fmt/pull/2505, https://github.com/fmtlib/fmt/pull/2516). Thanks @Vertexwahn. - Improved build configuration and tests (https://github.com/fmtlib/fmt/issues/2437, https://github.com/fmtlib/fmt/pull/2558, https://github.com/fmtlib/fmt/pull/2648, https://github.com/fmtlib/fmt/pull/2650, https://github.com/fmtlib/fmt/pull/2663, https://github.com/fmtlib/fmt/pull/2677). Thanks @DanielaE, @alexezeder and @phprus. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/2353, https://github.com/fmtlib/fmt/pull/2356, https://github.com/fmtlib/fmt/pull/2399, https://github.com/fmtlib/fmt/issues/2408, https://github.com/fmtlib/fmt/pull/2414, https://github.com/fmtlib/fmt/pull/2427, https://github.com/fmtlib/fmt/pull/2432, https://github.com/fmtlib/fmt/pull/2442, https://github.com/fmtlib/fmt/pull/2434, https://github.com/fmtlib/fmt/issues/2439, https://github.com/fmtlib/fmt/pull/2447, https://github.com/fmtlib/fmt/pull/2450, https://github.com/fmtlib/fmt/issues/2455, https://github.com/fmtlib/fmt/issues/2465, https://github.com/fmtlib/fmt/issues/2472, https://github.com/fmtlib/fmt/issues/2474, https://github.com/fmtlib/fmt/pull/2476, https://github.com/fmtlib/fmt/issues/2478, https://github.com/fmtlib/fmt/issues/2479, https://github.com/fmtlib/fmt/issues/2481, https://github.com/fmtlib/fmt/pull/2482, https://github.com/fmtlib/fmt/pull/2483, https://github.com/fmtlib/fmt/issues/2490, https://github.com/fmtlib/fmt/pull/2491, https://github.com/fmtlib/fmt/pull/2510, https://github.com/fmtlib/fmt/pull/2518, https://github.com/fmtlib/fmt/issues/2528, https://github.com/fmtlib/fmt/pull/2529, https://github.com/fmtlib/fmt/pull/2539, https://github.com/fmtlib/fmt/issues/2540, https://github.com/fmtlib/fmt/pull/2545, https://github.com/fmtlib/fmt/pull/2555, https://github.com/fmtlib/fmt/issues/2557, https://github.com/fmtlib/fmt/issues/2570, https://github.com/fmtlib/fmt/pull/2573, https://github.com/fmtlib/fmt/pull/2582, https://github.com/fmtlib/fmt/issues/2605, https://github.com/fmtlib/fmt/pull/2611, https://github.com/fmtlib/fmt/pull/2647, https://github.com/fmtlib/fmt/issues/2627, https://github.com/fmtlib/fmt/pull/2630, https://github.com/fmtlib/fmt/issues/2635, https://github.com/fmtlib/fmt/issues/2638, https://github.com/fmtlib/fmt/issues/2653, https://github.com/fmtlib/fmt/issues/2654, https://github.com/fmtlib/fmt/issues/2661, https://github.com/fmtlib/fmt/pull/2664, https://github.com/fmtlib/fmt/pull/2684). Thanks @DanielaE, @mwinterb, @cdacamar, @TrebledJ, @bodomartin, @cquammen, @white238, @mmarkeloff, @palacaze, @jcelerier, @mborn-adi, @BrukerJWD, @spyridon97, @phprus, @oliverlee, @joshessman-llnl, @akohlmey, @timkalu, @olupton, @Acretock, @alexezeder, @andrewcorrigan, @lucpelletier and @HazardyKnusperkeks. # 8.0.1 - 2021-07-02 - Fixed the version number in the inline namespace (https://github.com/fmtlib/fmt/issues/2374). - Added a missing presentation type check for `std::string` (https://github.com/fmtlib/fmt/issues/2402). - Fixed a linkage error when mixing code built with clang and gcc (https://github.com/fmtlib/fmt/issues/2377). - Fixed documentation issues (https://github.com/fmtlib/fmt/pull/2396, https://github.com/fmtlib/fmt/issues/2403, https://github.com/fmtlib/fmt/issues/2406). Thanks @mkurdej. - Removed dead code in FP formatter ( https://github.com/fmtlib/fmt/pull/2398). Thanks @javierhonduco. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/2351, https://github.com/fmtlib/fmt/issues/2359, https://github.com/fmtlib/fmt/pull/2365, https://github.com/fmtlib/fmt/issues/2368, https://github.com/fmtlib/fmt/pull/2370, https://github.com/fmtlib/fmt/pull/2376, https://github.com/fmtlib/fmt/pull/2381, https://github.com/fmtlib/fmt/pull/2382, https://github.com/fmtlib/fmt/issues/2386, https://github.com/fmtlib/fmt/pull/2389, https://github.com/fmtlib/fmt/pull/2395, https://github.com/fmtlib/fmt/pull/2397, https://github.com/fmtlib/fmt/issues/2400, https://github.com/fmtlib/fmt/issues/2401, https://github.com/fmtlib/fmt/pull/2407). Thanks @zx2c4, @AidanSun05, @mattiasljungstrom, @joemmett, @erengy, @patlkli, @gsjaardema and @phprus. # 8.0.0 - 2021-06-21 - Enabled compile-time format string checks by default. For example ([godbolt](https://godbolt.org/z/sMxcohGjz)): ```c++ #include int main() { fmt::print("{:d}", "I am not a number"); } ``` gives a compile-time error on compilers with C++20 `consteval` support (gcc 10+, clang 11+) because `d` is not a valid format specifier for a string. To pass a runtime string wrap it in `fmt::runtime`: ```c++ fmt::print(fmt::runtime("{:d}"), "I am not a number"); ``` - Added compile-time formatting (https://github.com/fmtlib/fmt/pull/2019, https://github.com/fmtlib/fmt/pull/2044, https://github.com/fmtlib/fmt/pull/2056, https://github.com/fmtlib/fmt/pull/2072, https://github.com/fmtlib/fmt/pull/2075, https://github.com/fmtlib/fmt/issues/2078, https://github.com/fmtlib/fmt/pull/2129, https://github.com/fmtlib/fmt/pull/2326). For example ([godbolt](https://godbolt.org/z/Mxx9d89jM)): ```c++ #include consteval auto compile_time_itoa(int value) -> std::array { auto result = std::array(); fmt::format_to(result.data(), FMT_COMPILE("{}"), value); return result; } constexpr auto answer = compile_time_itoa(42); ``` Most of the formatting functionality is available at compile time with a notable exception of floating-point numbers and pointers. Thanks @alexezeder. - Optimized handling of format specifiers during format string compilation. For example, hexadecimal formatting (`"{:x}"`) is now 3-7x faster than before when using `format_to` with format string compilation and a stack-allocated buffer (https://github.com/fmtlib/fmt/issues/1944). Before (7.1.3): ---------------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------------- FMTCompileOld/0 15.5 ns 15.5 ns 43302898 FMTCompileOld/42 16.6 ns 16.6 ns 43278267 FMTCompileOld/273123 18.7 ns 18.6 ns 37035861 FMTCompileOld/9223372036854775807 19.4 ns 19.4 ns 35243000 ---------------------------------------------------------------------------- After (8.x): ---------------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------------- FMTCompileNew/0 1.99 ns 1.99 ns 360523686 FMTCompileNew/42 2.33 ns 2.33 ns 279865664 FMTCompileNew/273123 3.72 ns 3.71 ns 190230315 FMTCompileNew/9223372036854775807 5.28 ns 5.26 ns 130711631 ---------------------------------------------------------------------------- It is even faster than `std::to_chars` from libc++ compiled with clang on macOS: ---------------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------------- ToChars/0 4.42 ns 4.41 ns 160196630 ToChars/42 5.00 ns 4.98 ns 140735201 ToChars/273123 7.26 ns 7.24 ns 95784130 ToChars/9223372036854775807 8.77 ns 8.75 ns 75872534 ---------------------------------------------------------------------------- In other cases, especially involving `std::string` construction, the speed up is usually lower because handling format specifiers takes a smaller fraction of the total time. - Added the `_cf` user-defined literal to represent a compiled format string. It can be used instead of the `FMT_COMPILE` macro (https://github.com/fmtlib/fmt/pull/2043, https://github.com/fmtlib/fmt/pull/2242): ```c++ #include using namespace fmt::literals; auto s = fmt::format(FMT_COMPILE("{}"), 42); // 🙁 not modern auto s = fmt::format("{}"_cf, 42); // 🙂 modern as hell ``` It requires compiler support for class types in non-type template parameters (a C++20 feature) which is available in GCC 9.3+. Thanks @alexezeder. - Format string compilation now requires `format` functions of `formatter` specializations for user-defined types to be `const`: ```c++ template <> struct fmt::formatter: formatter { template auto format(my_type obj, FormatContext& ctx) const { // Note const here. // ... } }; ``` - Added UDL-based named argument support to format string compilation (https://github.com/fmtlib/fmt/pull/2243, https://github.com/fmtlib/fmt/pull/2281). For example: ```c++ #include using namespace fmt::literals; auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42); ``` Here the argument named \"answer\" is resolved at compile time with no runtime overhead. Thanks @alexezeder. - Added format string compilation support to `fmt::print` (https://github.com/fmtlib/fmt/issues/2280, https://github.com/fmtlib/fmt/pull/2304). Thanks @alexezeder. - Added initial support for compiling {fmt} as a C++20 module (https://github.com/fmtlib/fmt/pull/2235, https://github.com/fmtlib/fmt/pull/2240, https://github.com/fmtlib/fmt/pull/2260, https://github.com/fmtlib/fmt/pull/2282, https://github.com/fmtlib/fmt/pull/2283, https://github.com/fmtlib/fmt/pull/2288, https://github.com/fmtlib/fmt/pull/2298, https://github.com/fmtlib/fmt/pull/2306, https://github.com/fmtlib/fmt/pull/2307, https://github.com/fmtlib/fmt/pull/2309, https://github.com/fmtlib/fmt/pull/2318, https://github.com/fmtlib/fmt/pull/2324, https://github.com/fmtlib/fmt/pull/2332, https://github.com/fmtlib/fmt/pull/2340). Thanks @DanielaE. - Made symbols private by default reducing shared library size (https://github.com/fmtlib/fmt/pull/2301). For example there was a \~15% reported reduction on one platform. Thanks @sergiud. - Optimized includes making the result of preprocessing `fmt/format.h` \~20% smaller with libstdc++/C++20 and slightly improving build times (https://github.com/fmtlib/fmt/issues/1998). - Added support of ranges with non-const `begin` / `end` (https://github.com/fmtlib/fmt/pull/1953). Thanks @kitegi. - Added support of `std::byte` and other formattable types to `fmt::join` (https://github.com/fmtlib/fmt/issues/1981, https://github.com/fmtlib/fmt/issues/2040, https://github.com/fmtlib/fmt/pull/2050, https://github.com/fmtlib/fmt/issues/2262). For example: ```c++ #include #include #include int main() { auto bytes = std::vector{std::byte(4), std::byte(2)}; fmt::print("{}", fmt::join(bytes, "")); } ``` prints \"42\". Thanks @kamibo. - Implemented the default format for `std::chrono::system_clock` (https://github.com/fmtlib/fmt/issues/2319, https://github.com/fmtlib/fmt/pull/2345). For example: ```c++ #include int main() { fmt::print("{}", std::chrono::system_clock::now()); } ``` prints \"2021-06-18 15:22:00\" (the output depends on the current date and time). Thanks @sunmy2019. - Made more chrono specifiers locale independent by default. Use the `'L'` specifier to get localized formatting. For example: ```c++ #include int main() { std::locale::global(std::locale("ru_RU.UTF-8")); auto monday = std::chrono::weekday(1); fmt::print("{}\n", monday); // prints "Mon" fmt::print("{:L}\n", monday); // prints "пн" } ``` - Improved locale handling in chrono formatting (https://github.com/fmtlib/fmt/issues/2337, https://github.com/fmtlib/fmt/pull/2349, https://github.com/fmtlib/fmt/pull/2350). Thanks @phprus. - Deprecated `fmt/locale.h` moving the formatting functions that take a locale to `fmt/format.h` (`char`) and `fmt/xchar` (other overloads). This doesn\'t introduce a dependency on `` so there is virtually no compile time effect. - Deprecated an undocumented `format_to` overload that takes `basic_memory_buffer`. - Made parameter order in `vformat_to` consistent with `format_to` (https://github.com/fmtlib/fmt/issues/2327). - Added support for time points with arbitrary durations (https://github.com/fmtlib/fmt/issues/2208). For example: ```c++ #include int main() { using tp = std::chrono::time_point< std::chrono::system_clock, std::chrono::seconds>; fmt::print("{:%S}", tp(std::chrono::seconds(42))); } ``` prints \"42\". - Formatting floating-point numbers no longer produces trailing zeros by default for consistency with `std::format`. For example: ```c++ #include int main() { fmt::print("{0:.3}", 1.1); } ``` prints \"1.1\". Use the `'#'` specifier to keep trailing zeros. - Dropped a limit on the number of elements in a range and replaced `{}` with `[]` as range delimiters for consistency with Python\'s `str.format`. - The `'L'` specifier for locale-specific numeric formatting can now be combined with presentation specifiers as in `std::format`. For example: ```c++ #include #include int main() { std::locale::global(std::locale("fr_FR.UTF-8")); fmt::print("{0:.2Lf}", 0.42); } ``` prints \"0,42\". The deprecated `'n'` specifier has been removed. - Made the `0` specifier ignored for infinity and NaN (https://github.com/fmtlib/fmt/issues/2305, https://github.com/fmtlib/fmt/pull/2310). Thanks @Liedtke. - Made the hexfloat formatting use the right alignment by default (https://github.com/fmtlib/fmt/issues/2308, https://github.com/fmtlib/fmt/pull/2317). Thanks @Liedtke. - Removed the deprecated numeric alignment (`'='`). Use the `'0'` specifier instead. - Removed the deprecated `fmt/posix.h` header that has been replaced with `fmt/os.h`. - Removed the deprecated `format_to_n_context`, `format_to_n_args` and `make_format_to_n_args`. They have been replaced with `format_context`, `` format_args` and ``make_format_args\`\` respectively. - Moved `wchar_t`-specific functions and types to `fmt/xchar.h`. You can define `FMT_DEPRECATED_INCLUDE_XCHAR` to automatically include `fmt/xchar.h` from `fmt/format.h` but this will be disabled in the next major release. - Fixed handling of the `'+'` specifier in localized formatting (https://github.com/fmtlib/fmt/issues/2133). - Added support for the `'s'` format specifier that gives textual representation of `bool` (https://github.com/fmtlib/fmt/issues/2094, https://github.com/fmtlib/fmt/pull/2109). For example: ```c++ #include int main() { fmt::print("{:s}", true); } ``` prints \"true\". Thanks @powercoderlol. - Made `fmt::ptr` work with function pointers (https://github.com/fmtlib/fmt/pull/2131). For example: ```c++ #include int main() { fmt::print("My main: {}\n", fmt::ptr(main)); } ``` Thanks @mikecrowe. - The undocumented support for specializing `formatter` for pointer types has been removed. - Fixed `fmt::formatted_size` with format string compilation (https://github.com/fmtlib/fmt/pull/2141, https://github.com/fmtlib/fmt/pull/2161). Thanks @alexezeder. - Fixed handling of empty format strings during format string compilation (https://github.com/fmtlib/fmt/issues/2042): ```c++ auto s = fmt::format(FMT_COMPILE("")); ``` Thanks @alexezeder. - Fixed handling of enums in `fmt::to_string` (https://github.com/fmtlib/fmt/issues/2036). - Improved width computation (https://github.com/fmtlib/fmt/issues/2033, https://github.com/fmtlib/fmt/issues/2091). For example: ```c++ #include int main() { fmt::print("{:-<10}{}\n", "你好", "世界"); fmt::print("{:-<10}{}\n", "hello", "world"); } ``` prints ![](https://user-images.githubusercontent.com/576385/119840373-cea3ca80-beb9-11eb-91e0-54266c48e181.png) on a modern terminal. - The experimental fast output stream (`fmt::ostream`) is now truncated by default for consistency with `fopen` (https://github.com/fmtlib/fmt/issues/2018). For example: ```c++ #include int main() { fmt::ostream out1 = fmt::output_file("guide"); out1.print("Zaphod"); out1.close(); fmt::ostream out2 = fmt::output_file("guide"); out2.print("Ford"); } ``` writes \"Ford\" to the file \"guide\". To preserve the old file content if any pass `fmt::file::WRONLY | fmt::file::CREATE` flags to `fmt::output_file`. - Fixed moving of `fmt::ostream` that holds buffered data (https://github.com/fmtlib/fmt/issues/2197, https://github.com/fmtlib/fmt/pull/2198). Thanks @vtta. - Replaced the `fmt::system_error` exception with a function of the same name that constructs `std::system_error` (https://github.com/fmtlib/fmt/issues/2266). - Replaced the `fmt::windows_error` exception with a function of the same name that constructs `std::system_error` with the category returned by `fmt::system_category()` (https://github.com/fmtlib/fmt/issues/2274, https://github.com/fmtlib/fmt/pull/2275). The latter is similar to `std::system_category` but correctly handles UTF-8. Thanks @phprus. - Replaced `fmt::error_code` with `std::error_code` and made it formattable (https://github.com/fmtlib/fmt/issues/2269, https://github.com/fmtlib/fmt/pull/2270, https://github.com/fmtlib/fmt/pull/2273). Thanks @phprus. - Added speech synthesis support (https://github.com/fmtlib/fmt/pull/2206). - Made `format_to` work with a memory buffer that has a custom allocator (https://github.com/fmtlib/fmt/pull/2300). Thanks @voxmea. - Added `Allocator::max_size` support to `basic_memory_buffer`. (https://github.com/fmtlib/fmt/pull/1960). Thanks @phprus. - Added wide string support to `fmt::join` (https://github.com/fmtlib/fmt/pull/2236). Thanks @crbrz. - Made iterators passed to `formatter` specializations via a format context satisfy C++20 `std::output_iterator` requirements (https://github.com/fmtlib/fmt/issues/2156, https://github.com/fmtlib/fmt/pull/2158, https://github.com/fmtlib/fmt/issues/2195, https://github.com/fmtlib/fmt/pull/2204). Thanks @randomnetcat. - Optimized the `printf` implementation (https://github.com/fmtlib/fmt/pull/1982, https://github.com/fmtlib/fmt/pull/1984, https://github.com/fmtlib/fmt/pull/2016, https://github.com/fmtlib/fmt/pull/2164). Thanks @rimathia and @moiwi. - Improved detection of `constexpr` `char_traits` (https://github.com/fmtlib/fmt/pull/2246, https://github.com/fmtlib/fmt/pull/2257). Thanks @phprus. - Fixed writing to `stdout` when it is redirected to `NUL` on Windows (https://github.com/fmtlib/fmt/issues/2080). - Fixed exception propagation from iterators (https://github.com/fmtlib/fmt/issues/2097). - Improved `strftime` error handling (https://github.com/fmtlib/fmt/issues/2238, https://github.com/fmtlib/fmt/pull/2244). Thanks @yumeyao. - Stopped using deprecated GCC UDL template extension. - Added `fmt/args.h` to the install target (https://github.com/fmtlib/fmt/issues/2096). - Error messages are now passed to assert when exceptions are disabled (https://github.com/fmtlib/fmt/pull/2145). Thanks @NobodyXu. - Added the `FMT_MASTER_PROJECT` CMake option to control build and install targets when {fmt} is included via `add_subdirectory` (https://github.com/fmtlib/fmt/issues/2098, https://github.com/fmtlib/fmt/pull/2100). Thanks @randomizedthinking. - Improved build configuration (https://github.com/fmtlib/fmt/pull/2026, https://github.com/fmtlib/fmt/pull/2122). Thanks @luncliff and @ibaned. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/1947, https://github.com/fmtlib/fmt/pull/1959, https://github.com/fmtlib/fmt/pull/1963, https://github.com/fmtlib/fmt/pull/1965, https://github.com/fmtlib/fmt/issues/1966, https://github.com/fmtlib/fmt/pull/1974, https://github.com/fmtlib/fmt/pull/1975, https://github.com/fmtlib/fmt/pull/1990, https://github.com/fmtlib/fmt/issues/2000, https://github.com/fmtlib/fmt/pull/2001, https://github.com/fmtlib/fmt/issues/2002, https://github.com/fmtlib/fmt/issues/2004, https://github.com/fmtlib/fmt/pull/2006, https://github.com/fmtlib/fmt/pull/2009, https://github.com/fmtlib/fmt/pull/2010, https://github.com/fmtlib/fmt/issues/2038, https://github.com/fmtlib/fmt/issues/2039, https://github.com/fmtlib/fmt/issues/2047, https://github.com/fmtlib/fmt/pull/2053, https://github.com/fmtlib/fmt/issues/2059, https://github.com/fmtlib/fmt/pull/2065, https://github.com/fmtlib/fmt/pull/2067, https://github.com/fmtlib/fmt/pull/2068, https://github.com/fmtlib/fmt/pull/2073, https://github.com/fmtlib/fmt/issues/2103, https://github.com/fmtlib/fmt/issues/2105, https://github.com/fmtlib/fmt/pull/2106, https://github.com/fmtlib/fmt/pull/2107, https://github.com/fmtlib/fmt/issues/2116, https://github.com/fmtlib/fmt/pull/2117, https://github.com/fmtlib/fmt/issues/2118, https://github.com/fmtlib/fmt/pull/2119, https://github.com/fmtlib/fmt/issues/2127, https://github.com/fmtlib/fmt/pull/2128, https://github.com/fmtlib/fmt/issues/2140, https://github.com/fmtlib/fmt/issues/2142, https://github.com/fmtlib/fmt/pull/2143, https://github.com/fmtlib/fmt/pull/2144, https://github.com/fmtlib/fmt/issues/2147, https://github.com/fmtlib/fmt/issues/2148, https://github.com/fmtlib/fmt/issues/2149, https://github.com/fmtlib/fmt/pull/2152, https://github.com/fmtlib/fmt/pull/2160, https://github.com/fmtlib/fmt/issues/2170, https://github.com/fmtlib/fmt/issues/2175, https://github.com/fmtlib/fmt/issues/2176, https://github.com/fmtlib/fmt/pull/2177, https://github.com/fmtlib/fmt/issues/2178, https://github.com/fmtlib/fmt/pull/2179, https://github.com/fmtlib/fmt/issues/2180, https://github.com/fmtlib/fmt/issues/2181, https://github.com/fmtlib/fmt/pull/2183, https://github.com/fmtlib/fmt/issues/2184, https://github.com/fmtlib/fmt/issues/2185, https://github.com/fmtlib/fmt/pull/2186, https://github.com/fmtlib/fmt/pull/2187, https://github.com/fmtlib/fmt/pull/2190, https://github.com/fmtlib/fmt/pull/2192, https://github.com/fmtlib/fmt/pull/2194, https://github.com/fmtlib/fmt/pull/2205, https://github.com/fmtlib/fmt/issues/2210, https://github.com/fmtlib/fmt/pull/2211, https://github.com/fmtlib/fmt/pull/2215, https://github.com/fmtlib/fmt/pull/2216, https://github.com/fmtlib/fmt/pull/2218, https://github.com/fmtlib/fmt/pull/2220, https://github.com/fmtlib/fmt/issues/2228, https://github.com/fmtlib/fmt/pull/2229, https://github.com/fmtlib/fmt/pull/2230, https://github.com/fmtlib/fmt/issues/2233, https://github.com/fmtlib/fmt/pull/2239, https://github.com/fmtlib/fmt/issues/2248, https://github.com/fmtlib/fmt/issues/2252, https://github.com/fmtlib/fmt/pull/2253, https://github.com/fmtlib/fmt/pull/2255, https://github.com/fmtlib/fmt/issues/2261, https://github.com/fmtlib/fmt/issues/2278, https://github.com/fmtlib/fmt/issues/2284, https://github.com/fmtlib/fmt/pull/2287, https://github.com/fmtlib/fmt/pull/2289, https://github.com/fmtlib/fmt/pull/2290, https://github.com/fmtlib/fmt/pull/2293, https://github.com/fmtlib/fmt/issues/2295, https://github.com/fmtlib/fmt/pull/2296, https://github.com/fmtlib/fmt/pull/2297, https://github.com/fmtlib/fmt/issues/2311, https://github.com/fmtlib/fmt/pull/2313, https://github.com/fmtlib/fmt/pull/2315, https://github.com/fmtlib/fmt/issues/2320, https://github.com/fmtlib/fmt/pull/2321, https://github.com/fmtlib/fmt/pull/2323, https://github.com/fmtlib/fmt/issues/2328, https://github.com/fmtlib/fmt/pull/2329, https://github.com/fmtlib/fmt/pull/2333, https://github.com/fmtlib/fmt/pull/2338, https://github.com/fmtlib/fmt/pull/2341). Thanks @darklukee, @fagg, @killerbot242, @jgopel, @yeswalrus, @Finkman, @HazardyKnusperkeks, @dkavolis, @concatime, @chronoxor, @summivox, @yNeo, @Apache-HB, @alexezeder, @toojays, @Brainy0207, @vadz, @imsherlock, @phprus, @white238, @yafshar, @BillyDonahue, @jstaahl, @denchat, @DanielaE, @ilyakurdyukov, @ilmai, @JessyDL, @sergiud, @mwinterb, @sven-herrmann, @jmelas, @twoixter, @crbrz and @upsj. - Improved documentation (https://github.com/fmtlib/fmt/issues/1986, https://github.com/fmtlib/fmt/pull/2051, https://github.com/fmtlib/fmt/issues/2057, https://github.com/fmtlib/fmt/pull/2081, https://github.com/fmtlib/fmt/issues/2084, https://github.com/fmtlib/fmt/pull/2312). Thanks @imba-tjd, @0x416c69 and @mordante. - Continuous integration and test improvements (https://github.com/fmtlib/fmt/issues/1969, https://github.com/fmtlib/fmt/pull/1991, https://github.com/fmtlib/fmt/pull/2020, https://github.com/fmtlib/fmt/pull/2110, https://github.com/fmtlib/fmt/pull/2114, https://github.com/fmtlib/fmt/issues/2196, https://github.com/fmtlib/fmt/pull/2217, https://github.com/fmtlib/fmt/pull/2247, https://github.com/fmtlib/fmt/pull/2256, https://github.com/fmtlib/fmt/pull/2336, https://github.com/fmtlib/fmt/pull/2346). Thanks @jgopel, @alexezeder and @DanielaE. The change log for versions 0.8.0 - 7.1.3 is available [here]( doc/ChangeLog-old.md). ================================================ FILE: LICENSE ================================================ 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 "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ {fmt} [![image](https://github.com/fmtlib/fmt/actions/workflows/linux.yml/badge.svg?branch=master)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux) [![image](https://github.com/fmtlib/fmt/actions/workflows/macos.yml/badge.svg?branch=master)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos) [![image](https://github.com/fmtlib/fmt/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows) [![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880) [![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt) [![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt) **{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) Q&A: ask questions on [StackOverflow with the tag fmt](https://stackoverflow.com/questions/tagged/fmt). Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v). # Features - Simple [format API](https://fmt.dev/latest/api/) with positional arguments for localization - Implementation of [C++20 std::format](https://en.cppreference.com/w/cpp/utility/format) and [C++23 std::print](https://en.cppreference.com/w/cpp/io/print) - [Format string syntax](https://fmt.dev/latest/syntax/) similar to Python\'s [format](https://docs.python.org/3/library/stdtypes.html#str.format) - Fast IEEE 754 floating-point formatter with correct rounding, shortness and round-trip guarantees using the [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm - Portable Unicode support - Safe [printf implementation](https://fmt.dev/latest/api/#printf-formatting) including the POSIX extension for positional arguments - Extensibility: [support for user-defined types](https://fmt.dev/latest/api/#formatting-user-defined-types) - High performance: faster than common standard library 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) - 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 bloat](#compile-time-and-code-bloat) - Reliability: the library has an extensive set of [tests](https://github.com/fmtlib/fmt/tree/master/test) and is [continuously fuzzed](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1) - Safety: the library is fully type-safe, errors in format strings can be reported at compile time, automatic memory management prevents buffer overflow errors - Ease of use: small self-contained code base, no external dependencies, permissive MIT [license](https://github.com/fmtlib/fmt/blob/master/LICENSE) - [Portability](https://fmt.dev/latest/#portability) with consistent output across platforms and support for older compilers - Clean warning-free codebase even on high warning levels such as `-Wall -Wextra -pedantic` - Locale independence by default - Optional header-only configuration enabled with the `FMT_HEADER_ONLY` macro See the [documentation](https://fmt.dev) for more details. # Examples **Print to stdout** ([run](https://godbolt.org/z/Tevcjh)) ``` c++ #include int main() { fmt::print("Hello, world!\n"); } ``` **Format a string** ([run](https://godbolt.org/z/oK8h33)) ``` c++ std::string s = fmt::format("The answer is {}.", 42); // s == "The answer is 42." ``` **Format a string using positional arguments** ([run](https://godbolt.org/z/Yn7Txe)) ``` c++ std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); // s == "I'd rather be happy than right." ``` **Print dates and times** ([run](https://godbolt.org/z/c31ExdY3W)) ``` c++ #include int main() { auto now = std::chrono::system_clock::now(); fmt::print("Date and time: {}\n", now); fmt::print("Time: {:%H:%M}\n", now); } ``` Output: Date and time: 2023-12-26 19:10:31.557195597 Time: 19:10 **Print a container** ([run](https://godbolt.org/z/MxM1YqjE7)) ``` c++ #include #include int main() { std::vector v = {1, 2, 3}; fmt::print("{}\n", v); } ``` Output: [1, 2, 3] **Check a format string at compile time** ``` c++ std::string s = fmt::format("{:d}", "I am not a number"); ``` This gives a compile-time error in C++20 because `d` is an invalid format specifier for a string. **Write a file from a single thread** ``` c++ #include int main() { auto out = fmt::output_file("guide.txt"); out.print("Don't {}", "Panic"); } ``` This can be [up to 9 times faster than `fprintf`]( http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html). **Print with colors and text styles** ``` c++ #include int main() { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "Hello, {}!\n", "world"); fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, "你好{}!\n", "世界"); } ``` Output on a modern terminal with Unicode support: ![image](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7) # Benchmarks ## Speed tests | Library | Method | Run Time, s | |-------------------|---------------|-------------| | libc | printf | 0.66 | | libc++ | std::ostream | 1.63 | | {fmt} 12.1 | fmt::print | 0.44 | | Boost Format 1.88 | boost::format | 3.89 | | Folly Format | folly::format | 1.28 | {fmt} is the fastest of the benchmarked methods, \~50% faster than `printf`. The above results were generated by building `tinyformat_test.cpp` on macOS 15.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and taking the best of three runs. In the test, the format string `"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000 times with output sent to `/dev/null`; for further details refer to the [source](https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc). {fmt} is up to 20-30x faster than `std::ostringstream` and `sprintf` on IEEE754 `float` and `double` formatting ([dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark)) and faster than [double-conversion](https://github.com/google/double-conversion) and [ryu](https://github.com/ulfjack/ryu): [![image](https://user-images.githubusercontent.com/576385/95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png)](https://fmt.dev/unknown_mac64_clang12.0.html) ## Compile time and code bloat The script [bloat-test.py][test] from [format-benchmark][bench] tests compile time and code bloat for nontrivial projects. It generates 100 translation units and uses `printf()` or its alternative five times in each to simulate a medium-sized project. The resulting executable size and compile time (Apple clang version 15.0.0 (clang-1500.1.0.2.5), macOS Sonoma, best of three) is shown in the following tables. [test]: https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py [bench]: https://github.com/fmtlib/format-benchmark **Optimized build (-O3)** | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | |-----------------|-----------------|----------------------|--------------------| | printf | 1.6 | 54 | 50 | | IOStreams | 28.4 | 98 | 84 | | {fmt} `1122268` | 5.0 | 54 | 50 | | tinyformat | 32.6 | 164 | 136 | | Boost Format | 55.0 | 530 | 317 | {fmt} is fast to compile and is comparable to `printf` in terms of per-call binary size (within a rounding error on this system). **Non-optimized build** | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | |-----------------|-----------------|----------------------|--------------------| | printf | 1.4 | 54 | 50 | | IOStreams | 27.0 | 88 | 68 | | {fmt} `1122268` | 4.7 | 87 | 84 | | tinyformat | 28.1 | 185 | 145 | | Boost Format | 38.9 | 678 | 381 | `libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries to compare formatting function overhead only. Boost Format is a header-only library so it doesn\'t provide any linkage options. ## Running the tests Please refer to [Building the library](https://fmt.dev/latest/get-started/#building-from-source) for instructions on how to build the library and run the unit tests. Benchmarks reside in a separate repository, [format-benchmarks](https://github.com/fmtlib/format-benchmark), so to run the benchmarks you first need to clone this repository and generate Makefiles with CMake: $ git clone --recursive https://github.com/fmtlib/format-benchmark.git $ cd format-benchmark $ cmake . Then you can run the speed test: $ make speed-test or the bloat test: $ make bloat-test # Migrating code [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v18 provides the [modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html) check that is capable of converting occurrences of `printf` and `fprintf` to `fmt::print` if configured to do so. (By default it converts to `std::print`.) # Notable projects using this library - [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform real-time strategy game - [AMPL/MP](https://github.com/ampl/mp): an open-source library for mathematical programming - [Apple's FoundationDB](https://github.com/apple/foundationdb): an open-source, distributed, transactional key-value store - [Aseprite](https://github.com/aseprite/aseprite): animated sprite editor & pixel art tool - [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft operations suite - [Blizzard Battle.net](https://battle.net/): an online gaming platform - [Celestia](https://celestia.space/): real-time 3D visualization of space - [Ceph](https://ceph.com/): a scalable distributed storage system - [ccache](https://ccache.dev/): a compiler cache - [ClickHouse](https://github.com/ClickHouse/ClickHouse): an analytical database management system - [ContextVision](https://www.contextvision.com/): medical imaging software - [Contour](https://github.com/contour-terminal/contour/): a modern terminal emulator - [CUAUV](https://cuauv.org/): Cornell University\'s autonomous underwater vehicle - [Drake](https://drake.mit.edu/): a planning, control, and analysis toolbox for nonlinear dynamical systems (MIT) - [Envoy](https://github.com/envoyproxy/envoy): C++ L7 proxy and communication bus (Lyft) - [FiveM](https://fivem.net/): a modification framework for GTA V - [fmtlog](https://github.com/MengRao/fmtlog): a performant fmtlib-style logging library with latency in nanoseconds - [Folly](https://github.com/facebook/folly): Facebook open-source library - [GemRB](https://gemrb.org/): a portable open-source implementation of Bioware's Infinity Engine - [Grand Mountain Adventure](https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/): a beautiful open-world ski & snowboarding game - [HarpyWar/pvpgn](https://github.com/pvpgn/pvpgn-server): Player vs Player Gaming Network with tweaks - [KBEngine](https://github.com/kbengine/kbengine): an open-source MMOG server engine - [Keypirinha](https://keypirinha.com/): a semantic launcher for Windows - [Kodi](https://kodi.tv/) (formerly xbmc): home theater software - [Knuth](https://kth.cash/): high-performance Bitcoin full-node - [libunicode](https://github.com/contour-terminal/libunicode/): a modern C++17 Unicode library - [MariaDB](https://mariadb.org/): relational database management system - [Microsoft Verona](https://github.com/microsoft/verona): research programming language for concurrent ownership - [MongoDB](https://mongodb.com/): distributed document database - [MongoDB Smasher](https://github.com/duckie/mongo_smasher): a small tool to generate randomized datasets - [OpenSpace](https://openspaceproject.com/): an open-source astrovisualization framework - [PenUltima Online (POL)](https://www.polserver.com/): an MMO server, compatible with most Ultima Online clients - [PyTorch](https://github.com/pytorch/pytorch): an open-source machine learning library - [quasardb](https://www.quasardb.net/): a distributed, high-performance, associative database - [Quill](https://github.com/odygrd/quill): asynchronous low-latency logging library - [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to simplify navigation, and execute complex multi-line terminal command sequences - [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis 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 client library - [Salesforce Analytics Cloud](https://www.salesforce.com/analytics-cloud/overview/): business intelligence software - [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 C++ framework for high-performance server applications on modern hardware - [spdlog](https://github.com/gabime/spdlog): super fast C++ logging library - [Stellar](https://www.stellar.org/): financial platform - [Touch Surgery](https://www.touchsurgery.com/): surgery simulator - [TrinityCore](https://github.com/TrinityCore/TrinityCore): open-source MMORPG framework - [🐙 userver framework](https://userver.tech/): open-source asynchronous framework with a rich set of abstractions and database drivers - [Windows Terminal](https://github.com/microsoft/terminal): the new Windows terminal [More\...](https://github.com/search?q=fmtlib&type=Code) 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 ([vitaut](https://github.com/vitaut)) with contributions from many other people. See [Contributors](https://github.com/fmtlib/fmt/graphs/contributors) and [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. ================================================ FILE: doc/ChangeLog-old.md ================================================ # 7.1.3 - 2020-11-24 - Fixed handling of buffer boundaries in `format_to_n` (https://github.com/fmtlib/fmt/issues/1996, https://github.com/fmtlib/fmt/issues/2029). - Fixed linkage errors when linking with a shared library (https://github.com/fmtlib/fmt/issues/2011). - Reintroduced ostream support to range formatters (https://github.com/fmtlib/fmt/issues/2014). - Worked around an issue with mixing std versions in gcc (https://github.com/fmtlib/fmt/issues/2017). # 7.1.2 - 2020-11-04 - Fixed floating point formatting with large precision (https://github.com/fmtlib/fmt/issues/1976). # 7.1.1 - 2020-11-01 - Fixed ABI compatibility with 7.0.x (https://github.com/fmtlib/fmt/issues/1961). - Added the `FMT_ARM_ABI_COMPATIBILITY` macro to work around ABI incompatibility between GCC and Clang on ARM (https://github.com/fmtlib/fmt/issues/1919). - Worked around a SFINAE bug in GCC 8 (https://github.com/fmtlib/fmt/issues/1957). - Fixed linkage errors when building with GCC\'s LTO (https://github.com/fmtlib/fmt/issues/1955). - Fixed a compilation error when building without `__builtin_clz` or equivalent (https://github.com/fmtlib/fmt/pull/1968). Thanks @tohammer. - Fixed a sign conversion warning (https://github.com/fmtlib/fmt/pull/1964). Thanks @OptoCloud. # 7.1.0 - 2020-10-25 - Switched from [Grisu3](https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf) to [Dragonbox](https://github.com/jk-jeon/dragonbox) for the default floating-point formatting which gives the shortest decimal representation with round-trip guarantee and correct rounding (https://github.com/fmtlib/fmt/pull/1882, https://github.com/fmtlib/fmt/pull/1887, https://github.com/fmtlib/fmt/pull/1894). This makes {fmt} up to 20-30x faster than common implementations of `std::ostringstream` and `sprintf` on [dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark) and faster than double-conversion and Ryū: ![](https://user-images.githubusercontent.com/576385/95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png) It is possible to get even better performance at the cost of larger binary size by compiling with the `FMT_USE_FULL_CACHE_DRAGONBOX` macro set to 1. Thanks @jk-jeon. - Added an experimental unsynchronized file output API which, together with [format string compilation](https://fmt.dev/latest/api.html#compile-api), can give [5-9 times speed up compared to fprintf](https://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html) on common platforms ([godbolt](https://godbolt.org/z/nsTcG8)): ```c++ #include int main() { auto f = fmt::output_file("guide"); f.print("The answer is {}.", 42); } ``` - Added a formatter for `std::chrono::time_point` (https://github.com/fmtlib/fmt/issues/1819, https://github.com/fmtlib/fmt/pull/1837). For example ([godbolt](https://godbolt.org/z/c4M6fh)): ```c++ #include int main() { auto now = std::chrono::system_clock::now(); fmt::print("The time is {:%H:%M:%S}.\n", now); } ``` Thanks @adamburgess. - Added support for ranges with non-const `begin`/`end` to `fmt::join` (https://github.com/fmtlib/fmt/issues/1784, https://github.com/fmtlib/fmt/pull/1786). For example ([godbolt](https://godbolt.org/z/jP63Tv)): ```c++ #include #include int main() { using std::literals::string_literals::operator""s; auto strs = std::array{"a"s, "bb"s, "ccc"s}; auto range = strs | ranges::views::filter( [] (const std::string &x) { return x.size() != 2; } ); fmt::print("{}\n", fmt::join(range, "")); } ``` prints \"accc\". Thanks @tonyelewis. - Added a `memory_buffer::append` overload that takes a range (https://github.com/fmtlib/fmt/pull/1806). Thanks @BRevzin. - Improved handling of single code units in `FMT_COMPILE`. For example: ```c++ #include char* f(char* buf) { return fmt::format_to(buf, FMT_COMPILE("x{}"), 42); } ``` compiles to just ([godbolt](https://godbolt.org/z/5vncz3)): ```asm _Z1fPc: movb $120, (%rdi) xorl %edx, %edx cmpl $42, _ZN3fmt2v76detail10basic_dataIvE23zero_or_powers_of_10_32E+8(%rip) movl $3, %eax seta %dl subl %edx, %eax movzwl _ZN3fmt2v76detail10basic_dataIvE6digitsE+84(%rip), %edx cltq addq %rdi, %rax movw %dx, -2(%rax) ret ``` Here a single `mov` instruction writes `'x'` (`$120`) to the output buffer. - Added dynamic width support to format string compilation (https://github.com/fmtlib/fmt/issues/1809). - Improved error reporting for unformattable types: now you\'ll get the type name directly in the error message instead of the note: ```c++ #include struct how_about_no {}; int main() { fmt::print("{}", how_about_no()); } ``` Error ([godbolt](https://godbolt.org/z/GoxM4e)): `fmt/core.h:1438:3: error: static_assert failed due to requirement 'fmt::v7::formattable()' "Cannot format an argument. To make type T formattable provide a formatter specialization: https://fmt.dev/latest/api.html#udt" ...` - Added the [make_args_checked](https://fmt.dev/7.1.0/api.html#argument-lists) function template that allows you to write formatting functions with compile-time format string checks and avoid binary code bloat ([godbolt](https://godbolt.org/z/PEf9qr)): ```c++ void vlog(const char* file, int line, fmt::string_view format, fmt::format_args args) { fmt::print("{}: {}: ", file, line); fmt::vprint(format, args); } template void log(const char* file, int line, const S& format, Args&&... args) { vlog(file, line, format, fmt::make_args_checked(format, args...)); } #define MY_LOG(format, ...) \ log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) MY_LOG("invalid squishiness: {}", 42); ``` - Replaced `snprintf` fallback with a faster internal IEEE 754 `float` and `double` formatter for arbitrary precision. For example ([godbolt](https://godbolt.org/z/dPhWvj)): ```c++ #include int main() { fmt::print("{:.500}\n", 4.9406564584124654E-324); } ``` prints `4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208324319360923829e-324`. - Made `format_to_n` and `formatted_size` part of the [core API](https://fmt.dev/latest/api.html#core-api) ([godbolt](https://godbolt.org/z/sPjY1K)): ```c++ #include int main() { char buffer[10]; auto result = fmt::format_to_n(buffer, sizeof(buffer), "{}", 42); } ``` - Added `fmt::format_to_n` overload with format string compilation (https://github.com/fmtlib/fmt/issues/1764, https://github.com/fmtlib/fmt/pull/1767, https://github.com/fmtlib/fmt/pull/1869). For example ([godbolt](https://godbolt.org/z/93h86q)): ```c++ #include int main() { char buffer[8]; fmt::format_to_n(buffer, sizeof(buffer), FMT_COMPILE("{}"), 42); } ``` Thanks @Kurkin and @alexezeder. - Added `fmt::format_to` overload that take `text_style` (https://github.com/fmtlib/fmt/issues/1593, https://github.com/fmtlib/fmt/issues/1842, https://github.com/fmtlib/fmt/pull/1843). For example ([godbolt](https://godbolt.org/z/91153r)): ```c++ #include int main() { std::string out; fmt::format_to(std::back_inserter(out), fmt::emphasis::bold | fg(fmt::color::red), "The answer is {}.", 42); } ``` Thanks @Naios. - Made the `'#'` specifier emit trailing zeros in addition to the decimal point (https://github.com/fmtlib/fmt/issues/1797). For example ([godbolt](https://godbolt.org/z/bhdcW9)): ```c++ #include int main() { fmt::print("{:#.2g}", 0.5); } ``` prints `0.50`. - Changed the default floating point format to not include `.0` for consistency with `std::format` and `std::to_chars` (https://github.com/fmtlib/fmt/issues/1893, https://github.com/fmtlib/fmt/issues/1943). It is possible to get the decimal point and trailing zero with the `#` specifier. - Fixed an issue with floating-point formatting that could result in addition of a non-significant trailing zero in rare cases e.g. `1.00e-34` instead of `1.0e-34` (https://github.com/fmtlib/fmt/issues/1873, https://github.com/fmtlib/fmt/issues/1917). - Made `fmt::to_string` fallback on `ostream` insertion operator if the `formatter` specialization is not provided (https://github.com/fmtlib/fmt/issues/1815, https://github.com/fmtlib/fmt/pull/1829). Thanks @alexezeder. - Added support for the append mode to the experimental file API and improved `fcntl.h` detection. (https://github.com/fmtlib/fmt/pull/1847, https://github.com/fmtlib/fmt/pull/1848). Thanks @t-wiser. - Fixed handling of types that have both an implicit conversion operator and an overloaded `ostream` insertion operator (https://github.com/fmtlib/fmt/issues/1766). - Fixed a slicing issue in an internal iterator type (https://github.com/fmtlib/fmt/pull/1822). Thanks @BRevzin. - Fixed an issue in locale-specific integer formatting (https://github.com/fmtlib/fmt/issues/1927). - Fixed handling of exotic code unit types (https://github.com/fmtlib/fmt/issues/1870, https://github.com/fmtlib/fmt/issues/1932). - Improved `FMT_ALWAYS_INLINE` (https://github.com/fmtlib/fmt/pull/1878). Thanks @jk-jeon. - Removed dependency on `windows.h` (https://github.com/fmtlib/fmt/pull/1900). Thanks @bernd5. - Optimized counting of decimal digits on MSVC (https://github.com/fmtlib/fmt/pull/1890). Thanks @mwinterb. - Improved documentation (https://github.com/fmtlib/fmt/issues/1772, https://github.com/fmtlib/fmt/pull/1775, https://github.com/fmtlib/fmt/pull/1792, https://github.com/fmtlib/fmt/pull/1838, https://github.com/fmtlib/fmt/pull/1888, https://github.com/fmtlib/fmt/pull/1918, https://github.com/fmtlib/fmt/pull/1939). Thanks @leolchat, @pepsiman, @Klaim, @ravijanjam, @francesco-st and @udnaan. - Added the `FMT_REDUCE_INT_INSTANTIATIONS` CMake option that reduces the binary code size at the cost of some integer formatting performance. This can be useful for extremely memory-constrained embedded systems (https://github.com/fmtlib/fmt/issues/1778, https://github.com/fmtlib/fmt/pull/1781). Thanks @kammce. - Added the `FMT_USE_INLINE_NAMESPACES` macro to control usage of inline namespaces (https://github.com/fmtlib/fmt/pull/1945). Thanks @darklukee. - Improved build configuration (https://github.com/fmtlib/fmt/pull/1760, https://github.com/fmtlib/fmt/pull/1770, https://github.com/fmtlib/fmt/issues/1779, https://github.com/fmtlib/fmt/pull/1783, https://github.com/fmtlib/fmt/pull/1823). Thanks @dvetutnev, @xvitaly, @tambry, @medithe and @martinwuehrer. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/1790, https://github.com/fmtlib/fmt/pull/1802, https://github.com/fmtlib/fmt/pull/1808, https://github.com/fmtlib/fmt/issues/1810, https://github.com/fmtlib/fmt/issues/1811, https://github.com/fmtlib/fmt/pull/1812, https://github.com/fmtlib/fmt/pull/1814, https://github.com/fmtlib/fmt/pull/1816, https://github.com/fmtlib/fmt/pull/1817, https://github.com/fmtlib/fmt/pull/1818, https://github.com/fmtlib/fmt/issues/1825, https://github.com/fmtlib/fmt/pull/1836, https://github.com/fmtlib/fmt/pull/1855, https://github.com/fmtlib/fmt/pull/1856, https://github.com/fmtlib/fmt/pull/1860, https://github.com/fmtlib/fmt/pull/1877, https://github.com/fmtlib/fmt/pull/1879, https://github.com/fmtlib/fmt/pull/1880, https://github.com/fmtlib/fmt/issues/1896, https://github.com/fmtlib/fmt/pull/1897, https://github.com/fmtlib/fmt/pull/1898, https://github.com/fmtlib/fmt/issues/1904, https://github.com/fmtlib/fmt/pull/1908, https://github.com/fmtlib/fmt/issues/1911, https://github.com/fmtlib/fmt/issues/1912, https://github.com/fmtlib/fmt/issues/1928, https://github.com/fmtlib/fmt/pull/1929, https://github.com/fmtlib/fmt/issues/1935, https://github.com/fmtlib/fmt/pull/1937, https://github.com/fmtlib/fmt/pull/1942, https://github.com/fmtlib/fmt/issues/1949). Thanks @TheQwertiest, @medithe, @martinwuehrer, @n16h7hunt3r, @Othereum, @gsjaardema, @AlexanderLanin, @gcerretani, @chronoxor, @noizefloor, @akohlmey, @jk-jeon, @rimathia, @rglarix, @moiwi, @heckad, @MarcDirven. @BartSiwek and @darklukee. # 7.0.3 - 2020-08-06 - Worked around broken `numeric_limits` for 128-bit integers (https://github.com/fmtlib/fmt/issues/1787). - Added error reporting on missing named arguments (https://github.com/fmtlib/fmt/issues/1796). - Stopped using 128-bit integers with clang-cl (https://github.com/fmtlib/fmt/pull/1800). Thanks @Kingcom. - Fixed issues in locale-specific integer formatting (https://github.com/fmtlib/fmt/issues/1782, https://github.com/fmtlib/fmt/issues/1801). # 7.0.2 - 2020-07-29 - Worked around broken `numeric_limits` for 128-bit integers (https://github.com/fmtlib/fmt/issues/1725). - Fixed compatibility with CMake 3.4 (https://github.com/fmtlib/fmt/issues/1779). - Fixed handling of digit separators in locale-specific formatting (https://github.com/fmtlib/fmt/issues/1782). # 7.0.1 - 2020-07-07 - Updated the inline version namespace name. - Worked around a gcc bug in mangling of alias templates (https://github.com/fmtlib/fmt/issues/1753). - Fixed a linkage error on Windows (https://github.com/fmtlib/fmt/issues/1757). Thanks @Kurkin. - Fixed minor issues with the documentation. # 7.0.0 - 2020-07-05 - Reduced the library size. For example, on macOS a stripped test binary statically linked with {fmt} [shrank from \~368k to less than 100k](http://www.zverovich.net/2020/05/21/reducing-library-size.html). - Added a simpler and more efficient [format string compilation API](https://fmt.dev/7.0.0/api.html#compile-api): ```c++ #include // Converts 42 into std::string using the most efficient method and no // runtime format string processing. std::string s = fmt::format(FMT_COMPILE("{}"), 42); ``` The old `fmt::compile` API is now deprecated. - Optimized integer formatting: `format_to` with format string compilation and a stack-allocated buffer is now [faster than to_chars on both libc++ and libstdc++](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html). - Optimized handling of small format strings. For example, ```c++ fmt::format("Result: {}: ({},{},{},{})", str1, str2, str3, str4, str5) ``` is now \~40% faster (https://github.com/fmtlib/fmt/issues/1685). - Applied extern templates to improve compile times when using the core API and `fmt/format.h` (https://github.com/fmtlib/fmt/issues/1452). For example, on macOS with clang the compile time of a test translation unit dropped from 2.3s to 0.3s with `-O2` and from 0.6s to 0.3s with the default settings (`-O0`). Before (`-O2`): % time c++ -c test.cc -I include -std=c++17 -O2 c++ -c test.cc -I include -std=c++17 -O2 2.22s user 0.08s system 99% cpu 2.311 total After (`-O2`): % time c++ -c test.cc -I include -std=c++17 -O2 c++ -c test.cc -I include -std=c++17 -O2 0.26s user 0.04s system 98% cpu 0.303 total Before (default): % time c++ -c test.cc -I include -std=c++17 c++ -c test.cc -I include -std=c++17 0.53s user 0.06s system 98% cpu 0.601 total After (default): % time c++ -c test.cc -I include -std=c++17 c++ -c test.cc -I include -std=c++17 0.24s user 0.06s system 98% cpu 0.301 total It is still recommended to use `fmt/core.h` instead of `fmt/format.h` but the compile time difference is now smaller. Thanks @alex3d for the suggestion. - Named arguments are now stored on stack (no dynamic memory allocations) and the compiled code is more compact and efficient. For example ```c++ #include int main() { fmt::print("The answer is {answer}\n", fmt::arg("answer", 42)); } ``` compiles to just ([godbolt](https://godbolt.org/z/NcfEp_)) ```asm .LC0: .string "answer" .LC1: .string "The answer is {answer}\n" main: sub rsp, 56 mov edi, OFFSET FLAT:.LC1 mov esi, 23 movabs rdx, 4611686018427387905 lea rax, [rsp+32] lea rcx, [rsp+16] mov QWORD PTR [rsp+8], 1 mov QWORD PTR [rsp], rax mov DWORD PTR [rsp+16], 42 mov QWORD PTR [rsp+32], OFFSET FLAT:.LC0 mov DWORD PTR [rsp+40], 0 call fmt::v6::vprint(fmt::v6::basic_string_view, fmt::v6::format_args) xor eax, eax add rsp, 56 ret .L.str.1: .asciz "answer" ``` - Implemented compile-time checks for dynamic width and precision (https://github.com/fmtlib/fmt/issues/1614): ```c++ #include int main() { fmt::print(FMT_STRING("{0:{1}}"), 42); } ``` now gives a compilation error because argument 1 doesn\'t exist: In file included from test.cc:1: include/fmt/format.h:2726:27: error: constexpr variable 'invalid_format' must be initialized by a constant expression FMT_CONSTEXPR_DECL bool invalid_format = ^ ... include/fmt/core.h:569:26: note: in call to '&checker(s, {}).context_->on_error(&"argument not found"[0])' if (id >= num_args_) on_error("argument not found"); ^ - Added sentinel support to `fmt::join` (https://github.com/fmtlib/fmt/pull/1689) ```c++ struct zstring_sentinel {}; bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } struct zstring { const char* p; const char* begin() const { return p; } zstring_sentinel end() const { return {}; } }; auto s = fmt::format("{}", fmt::join(zstring{"hello"}, "_")); // s == "h_e_l_l_o" ``` Thanks @BRevzin. - Added support for named arguments, `clear` and `reserve` to `dynamic_format_arg_store` (https://github.com/fmtlib/fmt/issues/1655, https://github.com/fmtlib/fmt/pull/1663, https://github.com/fmtlib/fmt/pull/1674, https://github.com/fmtlib/fmt/pull/1677). Thanks @vsolontsov-ll. - Added support for the `'c'` format specifier to integral types for compatibility with `std::format` (https://github.com/fmtlib/fmt/issues/1652). - Replaced the `'n'` format specifier with `'L'` for compatibility with `std::format` (https://github.com/fmtlib/fmt/issues/1624). The `'n'` specifier can be enabled via the `FMT_DEPRECATED_N_SPECIFIER` macro. - The `'='` format specifier is now disabled by default for compatibility with `std::format`. It can be enabled via the `FMT_DEPRECATED_NUMERIC_ALIGN` macro. - Removed the following deprecated APIs: - `FMT_STRING_ALIAS` and `fmt` macros - replaced by `FMT_STRING` - `fmt::basic_string_view::char_type` - replaced by `fmt::basic_string_view::value_type` - `convert_to_int` - `format_arg_store::types` - `*parse_context` - replaced by `*format_parse_context` - `FMT_DEPRECATED_INCLUDE_OS` - `FMT_DEPRECATED_PERCENT` - incompatible with `std::format` - `*writer` - replaced by compiled format API - Renamed the `internal` namespace to `detail` (https://github.com/fmtlib/fmt/issues/1538). The former is still provided as an alias if the `FMT_USE_INTERNAL` macro is defined. - Improved compatibility between `fmt::printf` with the standard specs (https://github.com/fmtlib/fmt/issues/1595, https://github.com/fmtlib/fmt/pull/1682, https://github.com/fmtlib/fmt/pull/1683, https://github.com/fmtlib/fmt/pull/1687, https://github.com/fmtlib/fmt/pull/1699). Thanks @rimathia. - Fixed handling of `operator<<` overloads that use `copyfmt` (https://github.com/fmtlib/fmt/issues/1666). - Added the `FMT_OS` CMake option to control inclusion of OS-specific APIs in the fmt target. This can be useful for embedded platforms (https://github.com/fmtlib/fmt/issues/1654, https://github.com/fmtlib/fmt/pull/1656). Thanks @kwesolowski. - Replaced `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` with the `FMT_FUZZ` macro to prevent interfering with fuzzing of projects using {fmt} (https://github.com/fmtlib/fmt/pull/1650). Thanks @asraa. - Fixed compatibility with emscripten (https://github.com/fmtlib/fmt/issues/1636, https://github.com/fmtlib/fmt/pull/1637). Thanks @ArthurSonzogni. - Improved documentation (https://github.com/fmtlib/fmt/issues/704, https://github.com/fmtlib/fmt/pull/1643, https://github.com/fmtlib/fmt/pull/1660, https://github.com/fmtlib/fmt/pull/1681, https://github.com/fmtlib/fmt/pull/1691, https://github.com/fmtlib/fmt/pull/1706, https://github.com/fmtlib/fmt/pull/1714, https://github.com/fmtlib/fmt/pull/1721, https://github.com/fmtlib/fmt/pull/1739, https://github.com/fmtlib/fmt/pull/1740, https://github.com/fmtlib/fmt/pull/1741, https://github.com/fmtlib/fmt/pull/1751). Thanks @senior7515, @lsr0, @puetzk, @fpelliccioni, Alexey Kuzmenko, @jelly, @claremacrae, @jiapengwen, @gsjaardema and @alexey-milovidov. - Implemented various build configuration fixes and improvements (https://github.com/fmtlib/fmt/pull/1603, https://github.com/fmtlib/fmt/pull/1657, https://github.com/fmtlib/fmt/pull/1702, https://github.com/fmtlib/fmt/pull/1728). Thanks @scramsby, @jtojnar, @orivej and @flagarde. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/1616, https://github.com/fmtlib/fmt/issues/1620, https://github.com/fmtlib/fmt/issues/1622, https://github.com/fmtlib/fmt/issues/1625, https://github.com/fmtlib/fmt/pull/1627, https://github.com/fmtlib/fmt/issues/1628, https://github.com/fmtlib/fmt/pull/1629, https://github.com/fmtlib/fmt/issues/1631, https://github.com/fmtlib/fmt/pull/1633, https://github.com/fmtlib/fmt/pull/1649, https://github.com/fmtlib/fmt/issues/1658, https://github.com/fmtlib/fmt/pull/1661, https://github.com/fmtlib/fmt/pull/1667, https://github.com/fmtlib/fmt/issues/1668, https://github.com/fmtlib/fmt/pull/1669, https://github.com/fmtlib/fmt/issues/1692, https://github.com/fmtlib/fmt/pull/1696, https://github.com/fmtlib/fmt/pull/1697, https://github.com/fmtlib/fmt/issues/1707, https://github.com/fmtlib/fmt/pull/1712, https://github.com/fmtlib/fmt/pull/1716, https://github.com/fmtlib/fmt/pull/1722, https://github.com/fmtlib/fmt/issues/1724, https://github.com/fmtlib/fmt/pull/1729, https://github.com/fmtlib/fmt/pull/1738, https://github.com/fmtlib/fmt/issues/1742, https://github.com/fmtlib/fmt/issues/1743, https://github.com/fmtlib/fmt/pull/1744, https://github.com/fmtlib/fmt/issues/1747, https://github.com/fmtlib/fmt/pull/1750). Thanks @gsjaardema, @gabime, @johnor, @Kurkin, @invexed, @peterbell10, @daixtrose, @petrutlucian94, @Neargye, @ambitslix, @gabime, @tohammer and @0x8000-0000. # 6.2.1 - 2020-05-09 - Fixed ostream support in `sprintf` (https://github.com/fmtlib/fmt/issues/1631). - Fixed type detection when using implicit conversion to `string_view` and ostream `operator<<` inconsistently (https://github.com/fmtlib/fmt/issues/1662). # 6.2.0 - 2020-04-05 - Improved error reporting when trying to format an object of a non-formattable type: ```c++ fmt::format("{}", S()); ``` now gives: include/fmt/core.h:1015:5: error: static_assert failed due to requirement 'formattable' "Cannot format argument. To make type T formattable provide a formatter specialization: https://fmt.dev/latest/api.html#formatting-user-defined-types" static_assert( ^ ... note: in instantiation of function template specialization 'fmt::v6::format' requested here fmt::format("{}", S()); ^ if `S` is not formattable. - Reduced the library size by \~10%. - Always print decimal point if `#` is specified (https://github.com/fmtlib/fmt/issues/1476, https://github.com/fmtlib/fmt/issues/1498): ```c++ fmt::print("{:#.0f}", 42.0); ``` now prints `42.` - Implemented the `'L'` specifier for locale-specific numeric formatting to improve compatibility with `std::format`. The `'n'` specifier is now deprecated and will be removed in the next major release. - Moved OS-specific APIs such as `windows_error` from `fmt/format.h` to `fmt/os.h`. You can define `FMT_DEPRECATED_INCLUDE_OS` to automatically include `fmt/os.h` from `fmt/format.h` for compatibility but this will be disabled in the next major release. - Added precision overflow detection in floating-point formatting. - Implemented detection of invalid use of `fmt::arg`. - Used `type_identity` to block unnecessary template argument deduction. Thanks Tim Song. - Improved UTF-8 handling (https://github.com/fmtlib/fmt/issues/1109): ```c++ fmt::print("┌{0:─^{2}}┐\n" "│{1: ^{2}}│\n" "└{0:─^{2}}┘\n", "", "Прывітанне, свет!", 21); ``` now prints: ┌─────────────────────┐ │ Прывітанне, свет! │ └─────────────────────┘ on systems that support Unicode. - Added experimental dynamic argument storage (https://github.com/fmtlib/fmt/issues/1170, https://github.com/fmtlib/fmt/pull/1584): ```c++ fmt::dynamic_format_arg_store store; store.push_back("answer"); store.push_back(42); fmt::vprint("The {} is {}.\n", store); ``` prints: The answer is 42. Thanks @vsolontsov-ll. - Made `fmt::join` accept `initializer_list` (https://github.com/fmtlib/fmt/pull/1591). Thanks @Rapotkinnik. - Fixed handling of empty tuples (https://github.com/fmtlib/fmt/issues/1588). - Fixed handling of output iterators in `format_to_n` (https://github.com/fmtlib/fmt/issues/1506). - Fixed formatting of `std::chrono::duration` types to wide output (https://github.com/fmtlib/fmt/pull/1533). Thanks @zeffy. - Added const `begin` and `end` overload to buffers (https://github.com/fmtlib/fmt/pull/1553). Thanks @dominicpoeschko. - Added the ability to disable floating-point formatting via `FMT_USE_FLOAT`, `FMT_USE_DOUBLE` and `FMT_USE_LONG_DOUBLE` macros for extremely memory-constrained embedded system (https://github.com/fmtlib/fmt/pull/1590). Thanks @albaguirre. - Made `FMT_STRING` work with `constexpr` `string_view` (https://github.com/fmtlib/fmt/pull/1589). Thanks @scramsby. - Implemented a minor optimization in the format string parser (https://github.com/fmtlib/fmt/pull/1560). Thanks @IkarusDeveloper. - Improved attribute detection (https://github.com/fmtlib/fmt/pull/1469, https://github.com/fmtlib/fmt/pull/1475, https://github.com/fmtlib/fmt/pull/1576). Thanks @federico-busato, @chronoxor and @refnum. - Improved documentation (https://github.com/fmtlib/fmt/pull/1481, https://github.com/fmtlib/fmt/pull/1523). Thanks @JackBoosY and @imba-tjd. - Fixed symbol visibility on Linux when compiling with `-fvisibility=hidden` (https://github.com/fmtlib/fmt/pull/1535). Thanks @milianw. - Implemented various build configuration fixes and improvements (https://github.com/fmtlib/fmt/issues/1264, https://github.com/fmtlib/fmt/issues/1460, https://github.com/fmtlib/fmt/pull/1534, https://github.com/fmtlib/fmt/issues/1536, https://github.com/fmtlib/fmt/issues/1545, https://github.com/fmtlib/fmt/pull/1546, https://github.com/fmtlib/fmt/issues/1566, https://github.com/fmtlib/fmt/pull/1582, https://github.com/fmtlib/fmt/issues/1597, https://github.com/fmtlib/fmt/pull/1598). Thanks @ambitslix, @jwillikers and @stac47. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/1433, https://github.com/fmtlib/fmt/issues/1461, https://github.com/fmtlib/fmt/pull/1470, https://github.com/fmtlib/fmt/pull/1480, https://github.com/fmtlib/fmt/pull/1485, https://github.com/fmtlib/fmt/pull/1492, https://github.com/fmtlib/fmt/issues/1493, https://github.com/fmtlib/fmt/issues/1504, https://github.com/fmtlib/fmt/pull/1505, https://github.com/fmtlib/fmt/pull/1512, https://github.com/fmtlib/fmt/issues/1515, https://github.com/fmtlib/fmt/pull/1516, https://github.com/fmtlib/fmt/pull/1518, https://github.com/fmtlib/fmt/pull/1519, https://github.com/fmtlib/fmt/pull/1520, https://github.com/fmtlib/fmt/pull/1521, https://github.com/fmtlib/fmt/pull/1522, https://github.com/fmtlib/fmt/issues/1524, https://github.com/fmtlib/fmt/pull/1530, https://github.com/fmtlib/fmt/issues/1531, https://github.com/fmtlib/fmt/pull/1532, https://github.com/fmtlib/fmt/issues/1539, https://github.com/fmtlib/fmt/issues/1547, https://github.com/fmtlib/fmt/issues/1548, https://github.com/fmtlib/fmt/pull/1554, https://github.com/fmtlib/fmt/issues/1567, https://github.com/fmtlib/fmt/pull/1568, https://github.com/fmtlib/fmt/pull/1569, https://github.com/fmtlib/fmt/pull/1571, https://github.com/fmtlib/fmt/pull/1573, https://github.com/fmtlib/fmt/pull/1575, https://github.com/fmtlib/fmt/pull/1581, https://github.com/fmtlib/fmt/issues/1583, https://github.com/fmtlib/fmt/issues/1586, https://github.com/fmtlib/fmt/issues/1587, https://github.com/fmtlib/fmt/issues/1594, https://github.com/fmtlib/fmt/pull/1596, https://github.com/fmtlib/fmt/issues/1604, https://github.com/fmtlib/fmt/pull/1606, https://github.com/fmtlib/fmt/issues/1607, https://github.com/fmtlib/fmt/issues/1609). Thanks @marti4d, @iPherian, @parkertomatoes, @gsjaardema, @chronoxor, @DanielaE, @torsten48, @tohammer, @lefticus, @ryusakki, @adnsv, @fghzxm, @refnum, @pramodk, @Spirrwell and @scramsby. # 6.1.2 - 2019-12-11 - Fixed ABI compatibility with `libfmt.so.6.0.0` (https://github.com/fmtlib/fmt/issues/1471). - Fixed handling types convertible to `std::string_view` (https://github.com/fmtlib/fmt/pull/1451). Thanks @denizevrenci. - Made CUDA test an opt-in enabled via the `FMT_CUDA_TEST` CMake option. - Fixed sign conversion warnings (https://github.com/fmtlib/fmt/pull/1440). Thanks @0x8000-0000. # 6.1.1 - 2019-12-04 - Fixed shared library build on Windows (https://github.com/fmtlib/fmt/pull/1443, https://github.com/fmtlib/fmt/issues/1445, https://github.com/fmtlib/fmt/pull/1446, https://github.com/fmtlib/fmt/issues/1450). Thanks @egorpugin and @bbolli. - Added a missing decimal point in exponent notation with trailing zeros. - Removed deprecated `format_arg_store::TYPES`. # 6.1.0 - 2019-12-01 - {fmt} now formats IEEE 754 `float` and `double` using the shortest decimal representation with correct rounding by default: ```c++ #include #include int main() { fmt::print("{}", M_PI); } ``` prints `3.141592653589793`. - Made the fast binary to decimal floating-point formatter the default, simplified it and improved performance. {fmt} is now 15 times faster than libc++\'s `std::ostringstream`, 11 times faster than `printf` and 10% faster than double-conversion on [dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark): | Function | Time (ns) | Speedup | | ------------- | --------: | ------: | | ostringstream | 1,346.30 | 1.00x | | ostrstream | 1,195.74 | 1.13x | | sprintf | 995.08 | 1.35x | | doubleconv | 99.10 | 13.59x | | fmt | 88.34 | 15.24x | ![](https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png) - {fmt} no longer converts `float` arguments to `double`. In particular this improves the default (shortest) representation of floats and makes `fmt::format` consistent with `std::format` specs (https://github.com/fmtlib/fmt/issues/1336, https://github.com/fmtlib/fmt/issues/1353, https://github.com/fmtlib/fmt/pull/1360, https://github.com/fmtlib/fmt/pull/1361): ```c++ fmt::print("{}", 0.1f); ``` prints `0.1` instead of `0.10000000149011612`. Thanks @orivej. - Made floating-point formatting output consistent with `printf`/iostreams (https://github.com/fmtlib/fmt/issues/1376, https://github.com/fmtlib/fmt/issues/1417). - Added support for 128-bit integers (https://github.com/fmtlib/fmt/pull/1287): ```c++ fmt::print("{}", std::numeric_limits<__int128_t>::max()); ``` prints `170141183460469231731687303715884105727`. Thanks @denizevrenci. - The overload of `print` that takes `text_style` is now atomic, i.e. the output from different threads doesn\'t interleave (https://github.com/fmtlib/fmt/pull/1351). Thanks @tankiJong. - Made compile time in the header-only mode \~20% faster by reducing the number of template instantiations. `wchar_t` overload of `vprint` was moved from `fmt/core.h` to `fmt/format.h`. - Added an overload of `fmt::join` that works with tuples (https://github.com/fmtlib/fmt/issues/1322, https://github.com/fmtlib/fmt/pull/1330): ```c++ #include #include int main() { std::tuple t{'a', 1, 2.0f}; fmt::print("{}", t); } ``` prints `('a', 1, 2.0)`. Thanks @jeremyong. - Changed formatting of octal zero with prefix from \"00\" to \"0\": ```c++ fmt::print("{:#o}", 0); ``` prints `0`. - The locale is now passed to ostream insertion (`<<`) operators (https://github.com/fmtlib/fmt/pull/1406): ```c++ #include #include struct S { double value; }; std::ostream& operator<<(std::ostream& os, S s) { return os << s.value; } int main() { auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42}); // s == "0,42" } ``` Thanks @dlaugt. - Locale-specific number formatting now uses grouping (https://github.com/fmtlib/fmt/issues/1393, https://github.com/fmtlib/fmt/pull/1394). Thanks @skrdaniel. - Fixed handling of types with deleted implicit rvalue conversion to `const char**` (https://github.com/fmtlib/fmt/issues/1421): ```c++ struct mystring { operator const char*() const&; operator const char*() &; operator const char*() const&& = delete; operator const char*() && = delete; }; mystring str; fmt::print("{}", str); // now compiles ``` - Enums are now mapped to correct underlying types instead of `int` (https://github.com/fmtlib/fmt/pull/1286). Thanks @agmt. - Enum classes are no longer implicitly converted to `int` (https://github.com/fmtlib/fmt/issues/1424). - Added `basic_format_parse_context` for consistency with C++20 `std::format` and deprecated `basic_parse_context`. - Fixed handling of UTF-8 in precision (https://github.com/fmtlib/fmt/issues/1389, https://github.com/fmtlib/fmt/pull/1390). Thanks @tajtiattila. - {fmt} can now be installed on Linux, macOS and Windows with [Conda](https://docs.conda.io/en/latest/) using its [conda-forge](https://conda-forge.org) [package](https://github.com/conda-forge/fmt-feedstock) (https://github.com/fmtlib/fmt/pull/1410): conda install -c conda-forge fmt Thanks @tdegeus. - Added a CUDA test (https://github.com/fmtlib/fmt/pull/1285, https://github.com/fmtlib/fmt/pull/1317). Thanks @luncliff and @risa2000. - Improved documentation (https://github.com/fmtlib/fmt/pull/1276, https://github.com/fmtlib/fmt/issues/1291, https://github.com/fmtlib/fmt/issues/1296, https://github.com/fmtlib/fmt/pull/1315, https://github.com/fmtlib/fmt/pull/1332, https://github.com/fmtlib/fmt/pull/1337, https://github.com/fmtlib/fmt/issues/1395 https://github.com/fmtlib/fmt/pull/1418). Thanks @waywardmonkeys, @pauldreik and @jackoalan. - Various code improvements (https://github.com/fmtlib/fmt/pull/1358, https://github.com/fmtlib/fmt/pull/1407). Thanks @orivej and @dpacbach. - Fixed compile-time format string checks for user-defined types (https://github.com/fmtlib/fmt/issues/1292). - Worked around a false positive in `unsigned-integer-overflow` sanitizer (https://github.com/fmtlib/fmt/issues/1377). - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/issues/1273, https://github.com/fmtlib/fmt/pull/1278, https://github.com/fmtlib/fmt/pull/1280, https://github.com/fmtlib/fmt/issues/1281, https://github.com/fmtlib/fmt/issues/1288, https://github.com/fmtlib/fmt/pull/1290, https://github.com/fmtlib/fmt/pull/1301, https://github.com/fmtlib/fmt/issues/1305, https://github.com/fmtlib/fmt/issues/1306, https://github.com/fmtlib/fmt/issues/1309, https://github.com/fmtlib/fmt/pull/1312, https://github.com/fmtlib/fmt/issues/1313, https://github.com/fmtlib/fmt/issues/1316, https://github.com/fmtlib/fmt/issues/1319, https://github.com/fmtlib/fmt/pull/1320, https://github.com/fmtlib/fmt/pull/1326, https://github.com/fmtlib/fmt/pull/1328, https://github.com/fmtlib/fmt/issues/1344, https://github.com/fmtlib/fmt/pull/1345, https://github.com/fmtlib/fmt/pull/1347, https://github.com/fmtlib/fmt/pull/1349, https://github.com/fmtlib/fmt/issues/1354, https://github.com/fmtlib/fmt/issues/1362, https://github.com/fmtlib/fmt/issues/1366, https://github.com/fmtlib/fmt/pull/1364, https://github.com/fmtlib/fmt/pull/1370, https://github.com/fmtlib/fmt/pull/1371, https://github.com/fmtlib/fmt/issues/1385, https://github.com/fmtlib/fmt/issues/1388, https://github.com/fmtlib/fmt/pull/1397, https://github.com/fmtlib/fmt/pull/1414, https://github.com/fmtlib/fmt/pull/1416, https://github.com/fmtlib/fmt/issues/1422 https://github.com/fmtlib/fmt/pull/1427, https://github.com/fmtlib/fmt/issues/1431, https://github.com/fmtlib/fmt/pull/1433). Thanks @hhb, @gsjaardema, @gabime, @neheb, @vedranmiletic, @dkavolis, @mwinterb, @orivej, @denizevrenci, @leonklingele, @chronoxor, @kent-tri, @0x8000-0000 and @marti4d. # 6.0.0 - 2019-08-26 - Switched to the [MIT license]( https://github.com/fmtlib/fmt/blob/5a4b24613ba16cc689977c3b5bd8274a3ba1dd1f/LICENSE.rst) with an optional exception that allows distributing binary code without attribution. - Floating-point formatting is now locale-independent by default: ```c++ #include #include int main() { std::locale::global(std::locale("ru_RU.UTF-8")); fmt::print("value = {}", 4.2); } ``` prints \"value = 4.2\" regardless of the locale. For locale-specific formatting use the `n` specifier: ```c++ std::locale::global(std::locale("ru_RU.UTF-8")); fmt::print("value = {:n}", 4.2); ``` prints \"value = 4,2\". - Added an experimental Grisu floating-point formatting algorithm implementation (disabled by default). To enable it compile with the `FMT_USE_GRISU` macro defined to 1: ```c++ #define FMT_USE_GRISU 1 #include auto s = fmt::format("{}", 4.2); // formats 4.2 using Grisu ``` With Grisu enabled, {fmt} is 13x faster than `std::ostringstream` (libc++) and 10x faster than `sprintf` on [dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark) ([full results](https://fmt.dev/unknown_mac64_clang10.0.html)): ![](https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg) - Separated formatting and parsing contexts for consistency with [C++20 std::format](http://eel.is/c++draft/format), removing the undocumented `basic_format_context::parse_context()` function. - Added [oss-fuzz](https://github.com/google/oss-fuzz) support (https://github.com/fmtlib/fmt/pull/1199). Thanks @pauldreik. - `formatter` specializations now always take precedence over `operator<<` (https://github.com/fmtlib/fmt/issues/952): ```c++ #include #include struct S {}; std::ostream& operator<<(std::ostream& os, S) { return os << 1; } template <> struct fmt::formatter : fmt::formatter { auto format(S, format_context& ctx) { return formatter::format(2, ctx); } }; int main() { std::cout << S() << "\n"; // prints 1 using operator<< fmt::print("{}\n", S()); // prints 2 using formatter } ``` - Introduced the experimental `fmt::compile` function that does format string compilation (https://github.com/fmtlib/fmt/issues/618, https://github.com/fmtlib/fmt/issues/1169, https://github.com/fmtlib/fmt/pull/1171): ```c++ #include auto f = fmt::compile("{}"); std::string s = fmt::format(f, 42); // can be called multiple times to // format different values // s == "42" ``` It moves the cost of parsing a format string outside of the format function which can be beneficial when identically formatting many objects of the same types. Thanks @stryku. - Added experimental `%` format specifier that formats floating-point values as percentages (https://github.com/fmtlib/fmt/pull/1060, https://github.com/fmtlib/fmt/pull/1069, https://github.com/fmtlib/fmt/pull/1071): ```c++ auto s = fmt::format("{:.1%}", 0.42); // s == "42.0%" ``` Thanks @gawain-bolton. - Implemented precision for floating-point durations (https://github.com/fmtlib/fmt/issues/1004, https://github.com/fmtlib/fmt/pull/1012): ```c++ auto s = fmt::format("{:.1}", std::chrono::duration(1.234)); // s == 1.2s ``` Thanks @DanielaE. - Implemented `chrono` format specifiers `%Q` and `%q` that give the value and the unit respectively (https://github.com/fmtlib/fmt/pull/1019): ```c++ auto value = fmt::format("{:%Q}", 42s); // value == "42" auto unit = fmt::format("{:%q}", 42s); // unit == "s" ``` Thanks @DanielaE. - Fixed handling of dynamic width in chrono formatter: ```c++ auto s = fmt::format("{0:{1}%H:%M:%S}", std::chrono::seconds(12345), 12); // ^ width argument index ^ width // s == "03:25:45 " ``` Thanks Howard Hinnant. - Removed deprecated `fmt/time.h`. Use `fmt/chrono.h` instead. - Added `fmt::format` and `fmt::vformat` overloads that take `text_style` (https://github.com/fmtlib/fmt/issues/993, https://github.com/fmtlib/fmt/pull/994): ```c++ #include std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), "The answer is {}.", 42); ``` Thanks @Naios. - Removed the deprecated color API (`print_colored`). Use the new API, namely `print` overloads that take `text_style` instead. - Made `std::unique_ptr` and `std::shared_ptr` formattable as pointers via `fmt::ptr` (https://github.com/fmtlib/fmt/pull/1121): ```c++ std::unique_ptr p = ...; fmt::print("{}", fmt::ptr(p)); // prints p as a pointer ``` Thanks @sighingnow. - Made `print` and `vprint` report I/O errors (https://github.com/fmtlib/fmt/issues/1098, https://github.com/fmtlib/fmt/pull/1099). Thanks @BillyDonahue. - Marked deprecated APIs with the `[[deprecated]]` attribute and removed internal uses of deprecated APIs (https://github.com/fmtlib/fmt/pull/1022). Thanks @eliaskosunen. - Modernized the codebase using more C++11 features and removing workarounds. Most importantly, `buffer_context` is now an alias template, so use `buffer_context` instead of `buffer_context::type`. These features require GCC 4.8 or later. - `formatter` specializations now always take precedence over implicit conversions to `int` and the undocumented `convert_to_int` trait is now deprecated. - Moved the undocumented `basic_writer`, `writer`, and `wwriter` types to the `internal` namespace. - Removed deprecated `basic_format_context::begin()`. Use `out()` instead. - Disallowed passing the result of `join` as an lvalue to prevent misuse. - Refactored the undocumented structs that represent parsed format specifiers to simplify the API and allow multibyte fill. - Moved SFINAE to template parameters to reduce symbol sizes. - Switched to `fputws` for writing wide strings so that it\'s no longer required to call `_setmode` on Windows (https://github.com/fmtlib/fmt/issues/1229, https://github.com/fmtlib/fmt/pull/1243). Thanks @jackoalan. - Improved literal-based API (https://github.com/fmtlib/fmt/pull/1254). Thanks @sylveon. - Added support for exotic platforms without `uintptr_t` such as IBM i (AS/400) which has 128-bit pointers and only 64-bit integers (https://github.com/fmtlib/fmt/issues/1059). - Added [Sublime Text syntax highlighting config]( https://github.com/fmtlib/fmt/blob/master/support/C%2B%2B.sublime-syntax) (https://github.com/fmtlib/fmt/issues/1037). Thanks @Kronuz. - Added the `FMT_ENFORCE_COMPILE_STRING` macro to enforce the use of compile-time format strings (https://github.com/fmtlib/fmt/pull/1231). Thanks @jackoalan. - Stopped setting `CMAKE_BUILD_TYPE` if {fmt} is a subproject (https://github.com/fmtlib/fmt/issues/1081). - Various build improvements (https://github.com/fmtlib/fmt/pull/1039, https://github.com/fmtlib/fmt/pull/1078, https://github.com/fmtlib/fmt/pull/1091, https://github.com/fmtlib/fmt/pull/1103, https://github.com/fmtlib/fmt/pull/1177). Thanks @luncliff, @jasonszang, @olafhering, @Lecetem and @pauldreik. - Improved documentation (https://github.com/fmtlib/fmt/issues/1049, https://github.com/fmtlib/fmt/pull/1051, https://github.com/fmtlib/fmt/pull/1083, https://github.com/fmtlib/fmt/pull/1113, https://github.com/fmtlib/fmt/pull/1114, https://github.com/fmtlib/fmt/issues/1146, https://github.com/fmtlib/fmt/issues/1180, https://github.com/fmtlib/fmt/pull/1250, https://github.com/fmtlib/fmt/pull/1252, https://github.com/fmtlib/fmt/pull/1265). Thanks @mikelui, @foonathan, @BillyDonahue, @jwakely, @kaisbe and @sdebionne. - Fixed ambiguous formatter specialization in `fmt/ranges.h` (https://github.com/fmtlib/fmt/issues/1123). - Fixed formatting of a non-empty `std::filesystem::path` which is an infinitely deep range of its components (https://github.com/fmtlib/fmt/issues/1268). - Fixed handling of general output iterators when formatting characters (https://github.com/fmtlib/fmt/issues/1056, https://github.com/fmtlib/fmt/pull/1058). Thanks @abolz. - Fixed handling of output iterators in `formatter` specialization for ranges (https://github.com/fmtlib/fmt/issues/1064). - Fixed handling of exotic character types (https://github.com/fmtlib/fmt/issues/1188). - Made chrono formatting work with exceptions disabled (https://github.com/fmtlib/fmt/issues/1062). - Fixed DLL visibility issues (https://github.com/fmtlib/fmt/pull/1134, https://github.com/fmtlib/fmt/pull/1147). Thanks @denchat. - Disabled the use of UDL template extension on GCC 9 (https://github.com/fmtlib/fmt/issues/1148). - Removed misplaced `format` compile-time checks from `printf` (https://github.com/fmtlib/fmt/issues/1173). - Fixed issues in the experimental floating-point formatter (https://github.com/fmtlib/fmt/issues/1072, https://github.com/fmtlib/fmt/issues/1129, https://github.com/fmtlib/fmt/issues/1153, https://github.com/fmtlib/fmt/pull/1155, https://github.com/fmtlib/fmt/issues/1210, https://github.com/fmtlib/fmt/issues/1222). Thanks @alabuzhev. - Fixed bugs discovered by fuzzing or during fuzzing integration (https://github.com/fmtlib/fmt/issues/1124, https://github.com/fmtlib/fmt/issues/1127, https://github.com/fmtlib/fmt/issues/1132, https://github.com/fmtlib/fmt/pull/1135, https://github.com/fmtlib/fmt/issues/1136, https://github.com/fmtlib/fmt/issues/1141, https://github.com/fmtlib/fmt/issues/1142, https://github.com/fmtlib/fmt/issues/1178, https://github.com/fmtlib/fmt/issues/1179, https://github.com/fmtlib/fmt/issues/1194). Thanks @pauldreik. - Fixed building tests on FreeBSD and Hurd (https://github.com/fmtlib/fmt/issues/1043). Thanks @jackyf. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/998, https://github.com/fmtlib/fmt/pull/1006, https://github.com/fmtlib/fmt/issues/1008, https://github.com/fmtlib/fmt/issues/1011, https://github.com/fmtlib/fmt/issues/1025, https://github.com/fmtlib/fmt/pull/1027, https://github.com/fmtlib/fmt/pull/1028, https://github.com/fmtlib/fmt/pull/1029, https://github.com/fmtlib/fmt/pull/1030, https://github.com/fmtlib/fmt/pull/1031, https://github.com/fmtlib/fmt/pull/1054, https://github.com/fmtlib/fmt/issues/1063, https://github.com/fmtlib/fmt/pull/1068, https://github.com/fmtlib/fmt/pull/1074, https://github.com/fmtlib/fmt/pull/1075, https://github.com/fmtlib/fmt/pull/1079, https://github.com/fmtlib/fmt/pull/1086, https://github.com/fmtlib/fmt/issues/1088, https://github.com/fmtlib/fmt/pull/1089, https://github.com/fmtlib/fmt/pull/1094, https://github.com/fmtlib/fmt/issues/1101, https://github.com/fmtlib/fmt/pull/1102, https://github.com/fmtlib/fmt/issues/1105, https://github.com/fmtlib/fmt/pull/1107, https://github.com/fmtlib/fmt/issues/1115, https://github.com/fmtlib/fmt/issues/1117, https://github.com/fmtlib/fmt/issues/1118, https://github.com/fmtlib/fmt/issues/1120, https://github.com/fmtlib/fmt/issues/1123, https://github.com/fmtlib/fmt/pull/1139, https://github.com/fmtlib/fmt/issues/1140, https://github.com/fmtlib/fmt/issues/1143, https://github.com/fmtlib/fmt/pull/1144, https://github.com/fmtlib/fmt/pull/1150, https://github.com/fmtlib/fmt/pull/1151, https://github.com/fmtlib/fmt/issues/1152, https://github.com/fmtlib/fmt/issues/1154, https://github.com/fmtlib/fmt/issues/1156, https://github.com/fmtlib/fmt/pull/1159, https://github.com/fmtlib/fmt/issues/1175, https://github.com/fmtlib/fmt/issues/1181, https://github.com/fmtlib/fmt/issues/1186, https://github.com/fmtlib/fmt/pull/1187, https://github.com/fmtlib/fmt/pull/1191, https://github.com/fmtlib/fmt/issues/1197, https://github.com/fmtlib/fmt/issues/1200, https://github.com/fmtlib/fmt/issues/1203, https://github.com/fmtlib/fmt/issues/1205, https://github.com/fmtlib/fmt/pull/1206, https://github.com/fmtlib/fmt/issues/1213, https://github.com/fmtlib/fmt/issues/1214, https://github.com/fmtlib/fmt/pull/1217, https://github.com/fmtlib/fmt/issues/1228, https://github.com/fmtlib/fmt/pull/1230, https://github.com/fmtlib/fmt/issues/1232, https://github.com/fmtlib/fmt/pull/1235, https://github.com/fmtlib/fmt/pull/1236, https://github.com/fmtlib/fmt/issues/1240). Thanks @DanielaE, @mwinterb, @eliaskosunen, @morinmorin, @ricco19, @waywardmonkeys, @chronoxor, @remyabel, @pauldreik, @gsjaardema, @rcane, @mocabe, @denchat, @cjdb, @HazardyKnusperkeks, @vedranmiletic, @jackoalan, @DaanDeMeyer and @starkmapper. # 5.3.0 - 2018-12-28 - Introduced experimental chrono formatting support: ```c++ #include int main() { using namespace std::literals::chrono_literals; fmt::print("Default format: {} {}\n", 42s, 100ms); fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); } ``` prints: Default format: 42s 100ms strftime-like format: 03:15:30 - Added experimental support for emphasis (bold, italic, underline, strikethrough), colored output to a file stream, and improved colored formatting API (https://github.com/fmtlib/fmt/pull/961, https://github.com/fmtlib/fmt/pull/967, https://github.com/fmtlib/fmt/pull/973): ```c++ #include int main() { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "Hello, {}!\n", "world"); fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, "你好{}!\n", "世界"); } ``` prints the following on modern terminals with RGB color support: ![](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7) Thanks @Rakete1111. - Added support for 4-bit terminal colors (https://github.com/fmtlib/fmt/issues/968, https://github.com/fmtlib/fmt/pull/974) ```c++ #include int main() { print(fg(fmt::terminal_color::red), "stop\n"); } ``` Note that these colors vary by terminal: ![](https://user-images.githubusercontent.com/576385/50405925-dbfc7e00-0770-11e9-9b85-333fab0af9ac.png) Thanks @Rakete1111. - Parameterized formatting functions on the type of the format string (https://github.com/fmtlib/fmt/issues/880, https://github.com/fmtlib/fmt/pull/881, https://github.com/fmtlib/fmt/pull/883, https://github.com/fmtlib/fmt/pull/885, https://github.com/fmtlib/fmt/pull/897, https://github.com/fmtlib/fmt/issues/920). Any object of type `S` that has an overloaded `to_string_view(const S&)` returning `fmt::string_view` can be used as a format string: ```c++ namespace my_ns { inline string_view to_string_view(const my_string& s) { return {s.data(), s.length()}; } } std::string message = fmt::format(my_string("The answer is {}."), 42); ``` Thanks @DanielaE. - Made `std::string_view` work as a format string (https://github.com/fmtlib/fmt/pull/898): ```c++ auto message = fmt::format(std::string_view("The answer is {}."), 42); ``` Thanks @DanielaE. - Added wide string support to compile-time format string checks (https://github.com/fmtlib/fmt/pull/924): ```c++ print(fmt(L"{:f}"), 42); // compile-time error: invalid type specifier ``` Thanks @XZiar. - Made colored print functions work with wide strings (https://github.com/fmtlib/fmt/pull/867): ```c++ #include int main() { print(fg(fmt::color::red), L"{}\n", 42); } ``` Thanks @DanielaE. - Introduced experimental Unicode support (https://github.com/fmtlib/fmt/issues/628, https://github.com/fmtlib/fmt/pull/891): ```c++ using namespace fmt::literals; auto s = fmt::format("{:*^5}"_u, "🤡"_u); // s == "**🤡**"_u ``` - Improved locale support: ```c++ #include struct numpunct : std::numpunct { protected: char do_thousands_sep() const override { return '~'; } }; std::locale loc; auto s = fmt::format(std::locale(loc, new numpunct()), "{:n}", 1234567); // s == "1~234~567" ``` - Constrained formatting functions on proper iterator types (https://github.com/fmtlib/fmt/pull/921). Thanks @DanielaE. - Added `make_printf_args` and `make_wprintf_args` functions (https://github.com/fmtlib/fmt/pull/934). Thanks @tnovotny. - Deprecated `fmt::visit`, `parse_context`, and `wparse_context`. Use `fmt::visit_format_arg`, `format_parse_context`, and `wformat_parse_context` instead. - Removed undocumented `basic_fixed_buffer` which has been superseded by the iterator-based API (https://github.com/fmtlib/fmt/issues/873, https://github.com/fmtlib/fmt/pull/902). Thanks @superfunc. - Disallowed repeated leading zeros in an argument ID: ```c++ fmt::print("{000}", 42); // error ``` - Reintroduced support for gcc 4.4. - Fixed compilation on platforms with exotic `double` (https://github.com/fmtlib/fmt/issues/878). - Improved documentation (https://github.com/fmtlib/fmt/issues/164, https://github.com/fmtlib/fmt/issues/877, https://github.com/fmtlib/fmt/pull/901, https://github.com/fmtlib/fmt/pull/906, https://github.com/fmtlib/fmt/pull/979). Thanks @kookjr, @DarkDimius and @HecticSerenity. - Added pkgconfig support which makes it easier to consume the library from meson and other build systems (https://github.com/fmtlib/fmt/pull/916). Thanks @colemickens. - Various build improvements (https://github.com/fmtlib/fmt/pull/909, https://github.com/fmtlib/fmt/pull/926, https://github.com/fmtlib/fmt/pull/937, https://github.com/fmtlib/fmt/pull/953, https://github.com/fmtlib/fmt/pull/959). Thanks @tchaikov, @luncliff, @AndreasSchoenle, @hotwatermorning and @Zefz. - Improved `string_view` construction performance (https://github.com/fmtlib/fmt/pull/914). Thanks @gabime. - Fixed non-matching char types (https://github.com/fmtlib/fmt/pull/895). Thanks @DanielaE. - Fixed `format_to_n` with `std::back_insert_iterator` (https://github.com/fmtlib/fmt/pull/913). Thanks @DanielaE. - Fixed locale-dependent formatting (https://github.com/fmtlib/fmt/issues/905). - Fixed various compiler warnings and errors (https://github.com/fmtlib/fmt/pull/882, https://github.com/fmtlib/fmt/pull/886, https://github.com/fmtlib/fmt/pull/933, https://github.com/fmtlib/fmt/pull/941, https://github.com/fmtlib/fmt/issues/931, https://github.com/fmtlib/fmt/pull/943, https://github.com/fmtlib/fmt/pull/954, https://github.com/fmtlib/fmt/pull/956, https://github.com/fmtlib/fmt/pull/962, https://github.com/fmtlib/fmt/issues/965, https://github.com/fmtlib/fmt/issues/977, https://github.com/fmtlib/fmt/pull/983, https://github.com/fmtlib/fmt/pull/989). Thanks @Luthaf, @stevenhoving, @christinaa, @lgritz, @DanielaE, @0x8000-0000 and @liuping1997. # 5.2.1 - 2018-09-21 - Fixed `visit` lookup issues on gcc 7 & 8 (https://github.com/fmtlib/fmt/pull/870). Thanks @medithe. - Fixed linkage errors on older gcc. - Prevented `fmt/range.h` from specializing `fmt::basic_string_view` (https://github.com/fmtlib/fmt/issues/865, https://github.com/fmtlib/fmt/pull/868). Thanks @hhggit. - Improved error message when formatting unknown types (https://github.com/fmtlib/fmt/pull/872). Thanks @foonathan. - Disabled templated user-defined literals when compiled under nvcc (https://github.com/fmtlib/fmt/pull/875). Thanks @CandyGumdrop. - Fixed `format_to` formatting to `wmemory_buffer` (https://github.com/fmtlib/fmt/issues/874). # 5.2.0 - 2018-09-13 - Optimized format string parsing and argument processing which resulted in up to 5x speed up on long format strings and significant performance boost on various benchmarks. For example, version 5.2 is 2.22x faster than 5.1 on decimal integer formatting with `format_to` (macOS, clang-902.0.39.2): | Method | Time, s | Speedup | | -------------------------- | --------------: | ------: | | fmt::format 5.1 | 0.58 | | | fmt::format 5.2 | 0.35 | 1.66x | | fmt::format_to 5.1 | 0.51 | | | fmt::format_to 5.2 | 0.23 | 2.22x | | sprintf | 0.71 | | | std::to_string | 1.01 | | | std::stringstream | 1.73 | | - Changed the `fmt` macro from opt-out to opt-in to prevent name collisions. To enable it define the `FMT_STRING_ALIAS` macro to 1 before including `fmt/format.h`: ```c++ #define FMT_STRING_ALIAS 1 #include std::string answer = format(fmt("{}"), 42); ``` - Added compile-time format string checks to `format_to` overload that takes `fmt::memory_buffer` (https://github.com/fmtlib/fmt/issues/783): ```c++ fmt::memory_buffer buf; // Compile-time error: invalid type specifier. fmt::format_to(buf, fmt("{:d}"), "foo"); ``` - Moved experimental color support to `fmt/color.h` and enabled the new API by default. The old API can be enabled by defining the `FMT_DEPRECATED_COLORS` macro. - Added formatting support for types explicitly convertible to `fmt::string_view`: ```c++ struct foo { explicit operator fmt::string_view() const { return "foo"; } }; auto s = format("{}", foo()); ``` In particular, this makes formatting function work with `folly::StringPiece`. - Implemented preliminary support for `char*_t` by replacing the `format` function overloads with a single function template parameterized on the string type. - Added support for dynamic argument lists (https://github.com/fmtlib/fmt/issues/814, https://github.com/fmtlib/fmt/pull/819). Thanks @MikePopoloski. - Reduced executable size overhead for embedded targets using newlib nano by making locale dependency optional (https://github.com/fmtlib/fmt/pull/839). Thanks @teajay-fr. - Keep `noexcept` specifier when exceptions are disabled (https://github.com/fmtlib/fmt/issues/801, https://github.com/fmtlib/fmt/pull/810). Thanks @qis. - Fixed formatting of user-defined types providing `operator<<` with `format_to_n` (https://github.com/fmtlib/fmt/pull/806). Thanks @mkurdej. - Fixed dynamic linkage of new symbols (https://github.com/fmtlib/fmt/issues/808). - Fixed global initialization issue (https://github.com/fmtlib/fmt/issues/807): ```c++ // This works on compilers with constexpr support. static const std::string answer = fmt::format("{}", 42); ``` - Fixed various compiler warnings and errors (https://github.com/fmtlib/fmt/pull/804, https://github.com/fmtlib/fmt/issues/809, https://github.com/fmtlib/fmt/pull/811, https://github.com/fmtlib/fmt/issues/822, https://github.com/fmtlib/fmt/pull/827, https://github.com/fmtlib/fmt/issues/830, https://github.com/fmtlib/fmt/pull/838, https://github.com/fmtlib/fmt/issues/843, https://github.com/fmtlib/fmt/pull/844, https://github.com/fmtlib/fmt/issues/851, https://github.com/fmtlib/fmt/pull/852, https://github.com/fmtlib/fmt/pull/854). Thanks @henryiii, @medithe, and @eliasdaler. # 5.1.0 - 2018-07-05 - Added experimental support for RGB color output enabled with the `FMT_EXTENDED_COLORS` macro: ```c++ #define FMT_EXTENDED_COLORS #define FMT_HEADER_ONLY // or compile fmt with FMT_EXTENDED_COLORS defined #include fmt::print(fmt::color::steel_blue, "Some beautiful text"); ``` The old API (the `print_colored` and `vprint_colored` functions and the `color` enum) is now deprecated. (https://github.com/fmtlib/fmt/issues/762 https://github.com/fmtlib/fmt/pull/767). thanks @Remotion. - Added quotes to strings in ranges and tuples (https://github.com/fmtlib/fmt/pull/766). Thanks @Remotion. - Made `format_to` work with `basic_memory_buffer` (https://github.com/fmtlib/fmt/issues/776). - Added `vformat_to_n` and `wchar_t` overload of `format_to_n` (https://github.com/fmtlib/fmt/issues/764, https://github.com/fmtlib/fmt/issues/769). - Made `is_range` and `is_tuple_like` part of public (experimental) API to allow specialization for user-defined types (https://github.com/fmtlib/fmt/issues/751, https://github.com/fmtlib/fmt/pull/759). Thanks @drrlvn. - Added more compilers to continuous integration and increased `FMT_PEDANTIC` warning levels (https://github.com/fmtlib/fmt/pull/736). Thanks @eliaskosunen. - Fixed compilation with MSVC 2013. - Fixed handling of user-defined types in `format_to` (https://github.com/fmtlib/fmt/issues/793). - Forced linking of inline `vformat` functions into the library (https://github.com/fmtlib/fmt/issues/795). - Fixed incorrect call to on_align in `'{:}='` (https://github.com/fmtlib/fmt/issues/750). - Fixed floating-point formatting to a non-back_insert_iterator with sign & numeric alignment specified (https://github.com/fmtlib/fmt/issues/756). - Fixed formatting to an array with `format_to_n` (https://github.com/fmtlib/fmt/issues/778). - Fixed formatting of more than 15 named arguments (https://github.com/fmtlib/fmt/issues/754). - Fixed handling of compile-time strings when including `fmt/ostream.h`. (https://github.com/fmtlib/fmt/issues/768). - Fixed various compiler warnings and errors (https://github.com/fmtlib/fmt/issues/742, https://github.com/fmtlib/fmt/issues/748, https://github.com/fmtlib/fmt/issues/752, https://github.com/fmtlib/fmt/issues/770, https://github.com/fmtlib/fmt/pull/775, https://github.com/fmtlib/fmt/issues/779, https://github.com/fmtlib/fmt/pull/780, https://github.com/fmtlib/fmt/pull/790, https://github.com/fmtlib/fmt/pull/792, https://github.com/fmtlib/fmt/pull/800). Thanks @Remotion, @gabime, @foonathan, @Dark-Passenger and @0x8000-0000. # 5.0.0 - 2018-05-21 - Added a requirement for partial C++11 support, most importantly variadic templates and type traits, and dropped `FMT_VARIADIC_*` emulation macros. Variadic templates are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older compilers use {fmt} [version 4.x](https://github.com/fmtlib/fmt/releases/tag/4.1.0) which continues to be maintained and works with C++98 compilers. - Renamed symbols to follow standard C++ naming conventions and proposed a subset of the library for standardization in [P0645R2 Text Formatting](https://wg21.link/P0645). - Implemented `constexpr` parsing of format strings and [compile-time format string checks](https://fmt.dev/latest/api.html#compile-time-format-string-checks). For example ```c++ #include std::string s = format(fmt("{:d}"), "foo"); ``` gives a compile-time error because `d` is an invalid specifier for strings ([godbolt](https://godbolt.org/g/rnCy9Q)): ... :4:19: note: in instantiation of function template specialization 'fmt::v5::format' requested here std::string s = format(fmt("{:d}"), "foo"); ^ format.h:1337:13: note: non-constexpr function 'on_error' cannot be used in a constant expression handler.on_error("invalid type specifier"); Compile-time checks require relaxed `constexpr` (C++14 feature) support. If the latter is not available, checks will be performed at runtime. - Separated format string parsing and formatting in the extension API to enable compile-time format string processing. For example ```c++ struct Answer {}; namespace fmt { template <> struct formatter { constexpr auto parse(parse_context& ctx) { auto it = ctx.begin(); spec = *it; if (spec != 'd' && spec != 's') throw format_error("invalid specifier"); return ++it; } template auto format(Answer, FormatContext& ctx) { return spec == 's' ? format_to(ctx.begin(), "{}", "fourty-two") : format_to(ctx.begin(), "{}", 42); } char spec = 0; }; } std::string s = format(fmt("{:x}"), Answer()); ``` gives a compile-time error due to invalid format specifier ([godbolt](https://godbolt.org/g/2jQ1Dv)): ... :12:45: error: expression '' is not a constant expression throw format_error("invalid specifier"); - Added [iterator support](https://fmt.dev/latest/api.html#output-iterator-support): ```c++ #include #include std::vector out; fmt::format_to(std::back_inserter(out), "{}", 42); ``` - Added the [format_to_n](https://fmt.dev/latest/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args) function that restricts the output to the specified number of characters (https://github.com/fmtlib/fmt/issues/298): ```c++ char out[4]; fmt::format_to_n(out, sizeof(out), "{}", 12345); // out == "1234" (without terminating '\0') ``` - Added the [formatted_size]( https://fmt.dev/latest/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args) function for computing the output size: ```c++ #include auto size = fmt::formatted_size("{}", 12345); // size == 5 ``` - Improved compile times by reducing dependencies on standard headers and providing a lightweight [core API](https://fmt.dev/latest/api.html#core-api): ```c++ #include fmt::print("The answer is {}.", 42); ``` See [Compile time and code bloat](https://github.com/fmtlib/fmt#compile-time-and-code-bloat). - Added the [make_format_args]( https://fmt.dev/latest/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args) function for capturing formatting arguments: ```c++ // Prints formatted error message. void vreport_error(const char *format, fmt::format_args args) { fmt::print("Error: "); fmt::vprint(format, args); } template void report_error(const char *format, const Args & ... args) { vreport_error(format, fmt::make_format_args(args...)); } ``` - Added the `make_printf_args` function for capturing `printf` arguments (https://github.com/fmtlib/fmt/issues/687, https://github.com/fmtlib/fmt/pull/694). Thanks @Kronuz. - Added prefix `v` to non-variadic functions taking `format_args` to distinguish them from variadic ones: ```c++ std::string vformat(string_view format_str, format_args args); template std::string format(string_view format_str, const Args & ... args); ``` - Added experimental support for formatting ranges, containers and tuple-like types in `fmt/ranges.h` (https://github.com/fmtlib/fmt/pull/735): ```c++ #include std::vector v = {1, 2, 3}; fmt::print("{}", v); // prints {1, 2, 3} ``` Thanks @Remotion. - Implemented `wchar_t` date and time formatting (https://github.com/fmtlib/fmt/pull/712): ```c++ #include std::time_t t = std::time(nullptr); auto s = fmt::format(L"The date is {:%Y-%m-%d}.", *std::localtime(&t)); ``` Thanks @DanielaE. - Provided more wide string overloads (https://github.com/fmtlib/fmt/pull/724). Thanks @DanielaE. - Switched from a custom null-terminated string view class to `string_view` in the format API and provided `fmt::string_view` which implements a subset of `std::string_view` API for pre-C++17 systems. - Added support for `std::experimental::string_view` (https://github.com/fmtlib/fmt/pull/607): ```c++ #include #include fmt::print("{}", std::experimental::string_view("foo")); ``` Thanks @virgiliofornazin. - Allowed mixing named and automatic arguments: ```c++ fmt::format("{} {two}", 1, fmt::arg("two", 2)); ``` - Removed the write API in favor of the [format API](https://fmt.dev/latest/api.html#format-api) with compile-time handling of format strings. - Disallowed formatting of multibyte strings into a wide character target (https://github.com/fmtlib/fmt/pull/606). - Improved documentation (https://github.com/fmtlib/fmt/pull/515, https://github.com/fmtlib/fmt/issues/614, https://github.com/fmtlib/fmt/pull/617, https://github.com/fmtlib/fmt/pull/661, https://github.com/fmtlib/fmt/pull/680). Thanks @ibell, @mihaitodor and @johnthagen. - Implemented more efficient handling of large number of format arguments. - Introduced an inline namespace for symbol versioning. - Added debug postfix `d` to the `fmt` library name (https://github.com/fmtlib/fmt/issues/636). - Removed unnecessary `fmt/` prefix in includes (https://github.com/fmtlib/fmt/pull/397). Thanks @chronoxor. - Moved `fmt/*.h` to `include/fmt/*.h` to prevent irrelevant files and directories appearing on the include search paths when fmt is used as a subproject and moved source files to the `src` directory. - Added qmake project file `support/fmt.pro` (https://github.com/fmtlib/fmt/pull/641). Thanks @cowo78. - Added Gradle build file `support/build.gradle` (https://github.com/fmtlib/fmt/pull/649). Thanks @luncliff. - Removed `FMT_CPPFORMAT` CMake option. - Fixed a name conflict with the macro `CHAR_WIDTH` in glibc (https://github.com/fmtlib/fmt/pull/616). Thanks @aroig. - Fixed handling of nested braces in `fmt::join` (https://github.com/fmtlib/fmt/issues/638). - Added `SOURCELINK_SUFFIX` for compatibility with Sphinx 1.5 (https://github.com/fmtlib/fmt/pull/497). Thanks @ginggs. - Added a missing `inline` in the header-only mode (https://github.com/fmtlib/fmt/pull/626). Thanks @aroig. - Fixed various compiler warnings (https://github.com/fmtlib/fmt/pull/640, https://github.com/fmtlib/fmt/pull/656, https://github.com/fmtlib/fmt/pull/679, https://github.com/fmtlib/fmt/pull/681, https://github.com/fmtlib/fmt/pull/705, https://github.com/fmtlib/fmt/issues/715, https://github.com/fmtlib/fmt/pull/717, https://github.com/fmtlib/fmt/pull/720, https://github.com/fmtlib/fmt/pull/723, https://github.com/fmtlib/fmt/pull/726, https://github.com/fmtlib/fmt/pull/730, https://github.com/fmtlib/fmt/pull/739). Thanks @peterbell10, @LarsGullik, @foonathan, @eliaskosunen, @christianparpart, @DanielaE and @mwinterb. - Worked around an MSVC bug and fixed several warnings (https://github.com/fmtlib/fmt/pull/653). Thanks @alabuzhev. - Worked around GCC bug 67371 (https://github.com/fmtlib/fmt/issues/682). - Fixed compilation with `-fno-exceptions` (https://github.com/fmtlib/fmt/pull/655). Thanks @chenxiaolong. - Made `constexpr remove_prefix` gcc version check tighter (https://github.com/fmtlib/fmt/issues/648). - Renamed internal type enum constants to prevent collision with poorly written C libraries (https://github.com/fmtlib/fmt/issues/644). - Added detection of `wostream operator<<` (https://github.com/fmtlib/fmt/issues/650). - Fixed compilation on OpenBSD (https://github.com/fmtlib/fmt/pull/660). Thanks @hubslave. - Fixed compilation on FreeBSD 12 (https://github.com/fmtlib/fmt/pull/732). Thanks @dankm. - Fixed compilation when there is a mismatch between `-std` options between the library and user code (https://github.com/fmtlib/fmt/issues/664). - Fixed compilation with GCC 7 and `-std=c++11` (https://github.com/fmtlib/fmt/issues/734). - Improved generated binary code on GCC 7 and older (https://github.com/fmtlib/fmt/issues/668). - Fixed handling of numeric alignment with no width (https://github.com/fmtlib/fmt/issues/675). - Fixed handling of empty strings in UTF8/16 converters (https://github.com/fmtlib/fmt/pull/676). Thanks @vgalka-sl. - Fixed formatting of an empty `string_view` (https://github.com/fmtlib/fmt/issues/689). - Fixed detection of `string_view` on libc++ (https://github.com/fmtlib/fmt/issues/686). - Fixed DLL issues (https://github.com/fmtlib/fmt/pull/696). Thanks @sebkoenig. - Fixed compile checks for mixing narrow and wide strings (https://github.com/fmtlib/fmt/issues/690). - Disabled unsafe implicit conversion to `std::string` (https://github.com/fmtlib/fmt/issues/729). - Fixed handling of reused format specs (as in `fmt::join`) for pointers (https://github.com/fmtlib/fmt/pull/725). Thanks @mwinterb. - Fixed installation of `fmt/ranges.h` (https://github.com/fmtlib/fmt/pull/738). Thanks @sv1990. # 4.1.0 - 2017-12-20 - Added `fmt::to_wstring()` in addition to `fmt::to_string()` (https://github.com/fmtlib/fmt/pull/559). Thanks @alabuzhev. - Added support for C++17 `std::string_view` (https://github.com/fmtlib/fmt/pull/571 and https://github.com/fmtlib/fmt/pull/578). Thanks @thelostt and @mwinterb. - Enabled stream exceptions to catch errors (https://github.com/fmtlib/fmt/issues/581). Thanks @crusader-mike. - Allowed formatting of class hierarchies with `fmt::format_arg()` (https://github.com/fmtlib/fmt/pull/547). Thanks @rollbear. - Removed limitations on character types (https://github.com/fmtlib/fmt/pull/563). Thanks @Yelnats321. - Conditionally enabled use of `std::allocator_traits` (https://github.com/fmtlib/fmt/pull/583). Thanks @mwinterb. - Added support for `const` variadic member function emulation with `FMT_VARIADIC_CONST` (https://github.com/fmtlib/fmt/pull/591). Thanks @ludekvodicka. - Various bugfixes: bad overflow check, unsupported implicit type conversion when determining formatting function, test segfaults (https://github.com/fmtlib/fmt/issues/551), ill-formed macros (https://github.com/fmtlib/fmt/pull/542) and ambiguous overloads (https://github.com/fmtlib/fmt/issues/580). Thanks @xylosper. - Prevented warnings on MSVC (https://github.com/fmtlib/fmt/pull/605, https://github.com/fmtlib/fmt/pull/602, and https://github.com/fmtlib/fmt/pull/545), clang (https://github.com/fmtlib/fmt/pull/582), GCC (https://github.com/fmtlib/fmt/issues/573), various conversion warnings (https://github.com/fmtlib/fmt/pull/609, https://github.com/fmtlib/fmt/pull/567, https://github.com/fmtlib/fmt/pull/553 and https://github.com/fmtlib/fmt/pull/553), and added `override` and `[[noreturn]]` (https://github.com/fmtlib/fmt/pull/549 and https://github.com/fmtlib/fmt/issues/555). Thanks @alabuzhev, @virgiliofornazin, @alexanderbock, @yumetodo, @VaderY, @jpcima, @thelostt and @Manu343726. - Improved CMake: Used `GNUInstallDirs` to set installation location (https://github.com/fmtlib/fmt/pull/610) and fixed warnings (https://github.com/fmtlib/fmt/pull/536 and https://github.com/fmtlib/fmt/pull/556). Thanks @mikecrowe, @evgen231 and @henryiii. # 4.0.0 - 2017-06-27 - Removed old compatibility headers `cppformat/*.h` and CMake options (https://github.com/fmtlib/fmt/pull/527). Thanks @maddinat0r. - Added `string.h` containing `fmt::to_string()` as alternative to `std::to_string()` as well as other string writer functionality (https://github.com/fmtlib/fmt/issues/326 and https://github.com/fmtlib/fmt/pull/441): ```c++ #include "fmt/string.h" std::string answer = fmt::to_string(42); ``` Thanks @glebov-andrey. - Moved `fmt::printf()` to new `printf.h` header and allowed `%s` as generic specifier (https://github.com/fmtlib/fmt/pull/453), made `%.f` more conformant to regular `printf()` (https://github.com/fmtlib/fmt/pull/490), added custom writer support (https://github.com/fmtlib/fmt/issues/476) and implemented missing custom argument formatting (https://github.com/fmtlib/fmt/pull/339 and https://github.com/fmtlib/fmt/pull/340): ```c++ #include "fmt/printf.h" // %s format specifier can be used with any argument type. fmt::printf("%s", 42); ``` Thanks @mojoBrendan, @manylegged and @spacemoose. See also https://github.com/fmtlib/fmt/issues/360, https://github.com/fmtlib/fmt/issues/335 and https://github.com/fmtlib/fmt/issues/331. - Added `container.h` containing a `BasicContainerWriter` to write to containers like `std::vector` (https://github.com/fmtlib/fmt/pull/450). Thanks @polyvertex. - Added `fmt::join()` function that takes a range and formats its elements separated by a given string (https://github.com/fmtlib/fmt/pull/466): ```c++ #include "fmt/format.h" std::vector v = {1.2, 3.4, 5.6}; // Prints "(+01.20, +03.40, +05.60)". fmt::print("({:+06.2f})", fmt::join(v.begin(), v.end(), ", ")); ``` Thanks @olivier80. - Added support for custom formatting specifications to simplify customization of built-in formatting (https://github.com/fmtlib/fmt/pull/444). Thanks @polyvertex. See also https://github.com/fmtlib/fmt/issues/439. - Added `fmt::format_system_error()` for error code formatting (https://github.com/fmtlib/fmt/issues/323 and https://github.com/fmtlib/fmt/pull/526). Thanks @maddinat0r. - Added thread-safe `fmt::localtime()` and `fmt::gmtime()` as replacement for the standard version to `time.h` (https://github.com/fmtlib/fmt/pull/396). Thanks @codicodi. - Internal improvements to `NamedArg` and `ArgLists` (https://github.com/fmtlib/fmt/pull/389 and https://github.com/fmtlib/fmt/pull/390). Thanks @chronoxor. - Fixed crash due to bug in `FormatBuf` (https://github.com/fmtlib/fmt/pull/493). Thanks @effzeh. See also https://github.com/fmtlib/fmt/issues/480 and https://github.com/fmtlib/fmt/issues/491. - Fixed handling of wide strings in `fmt::StringWriter`. - Improved compiler error messages (https://github.com/fmtlib/fmt/issues/357). - Fixed various warnings and issues with various compilers (https://github.com/fmtlib/fmt/pull/494, https://github.com/fmtlib/fmt/pull/499, https://github.com/fmtlib/fmt/pull/483, https://github.com/fmtlib/fmt/pull/485, https://github.com/fmtlib/fmt/pull/482, https://github.com/fmtlib/fmt/pull/475, https://github.com/fmtlib/fmt/pull/473 and https://github.com/fmtlib/fmt/pull/414). Thanks @chronoxor, @zhaohuaxishi, @pkestene, @dschmidt and @0x414c. - Improved CMake: targets are now namespaced (https://github.com/fmtlib/fmt/pull/511 and https://github.com/fmtlib/fmt/pull/513), supported header-only `printf.h` (https://github.com/fmtlib/fmt/pull/354), fixed issue with minimal supported library subset (https://github.com/fmtlib/fmt/issues/418, https://github.com/fmtlib/fmt/pull/419 and https://github.com/fmtlib/fmt/pull/420). Thanks @bjoernthiel, @niosHD, @LogicalKnight and @alabuzhev. - Improved documentation (https://github.com/fmtlib/fmt/pull/393). Thanks @pwm1234. # 3.0.2 - 2017-06-14 - Added `FMT_VERSION` macro (https://github.com/fmtlib/fmt/issues/411). - Used `FMT_NULL` instead of literal `0` (https://github.com/fmtlib/fmt/pull/409). Thanks @alabuzhev. - Added extern templates for `format_float` (https://github.com/fmtlib/fmt/issues/413). - Fixed implicit conversion issue (https://github.com/fmtlib/fmt/issues/507). - Fixed signbit detection (https://github.com/fmtlib/fmt/issues/423). - Fixed naming collision (https://github.com/fmtlib/fmt/issues/425). - Fixed missing intrinsic for C++/CLI (https://github.com/fmtlib/fmt/pull/457). Thanks @calumr. - Fixed Android detection (https://github.com/fmtlib/fmt/pull/458). Thanks @Gachapen. - Use lean `windows.h` if not in header-only mode (https://github.com/fmtlib/fmt/pull/503). Thanks @Quentin01. - Fixed issue with CMake exporting C++11 flag (https://github.com/fmtlib/fmt/pull/455). Thanks @EricWF. - Fixed issue with nvcc and MSVC compiler bug and MinGW (https://github.com/fmtlib/fmt/issues/505). - Fixed DLL issues (https://github.com/fmtlib/fmt/pull/469 and https://github.com/fmtlib/fmt/pull/502). Thanks @richardeakin and @AndreasSchoenle. - Fixed test compilation under FreeBSD (https://github.com/fmtlib/fmt/issues/433). - Fixed various warnings (https://github.com/fmtlib/fmt/pull/403, https://github.com/fmtlib/fmt/pull/410 and https://github.com/fmtlib/fmt/pull/510). Thanks @Lecetem, @chenhayat and @trozen. - Worked around a broken `__builtin_clz` in clang with MS codegen (https://github.com/fmtlib/fmt/issues/519). - Removed redundant include (https://github.com/fmtlib/fmt/issues/479). - Fixed documentation issues. # 3.0.1 - 2016-11-01 - Fixed handling of thousands separator (https://github.com/fmtlib/fmt/issues/353). - Fixed handling of `unsigned char` strings (https://github.com/fmtlib/fmt/issues/373). - Corrected buffer growth when formatting time (https://github.com/fmtlib/fmt/issues/367). - Removed warnings under MSVC and clang (https://github.com/fmtlib/fmt/issues/318, https://github.com/fmtlib/fmt/issues/250, also merged https://github.com/fmtlib/fmt/pull/385 and https://github.com/fmtlib/fmt/pull/361). Thanks @jcelerier and @nmoehrle. - Fixed compilation issues under Android (https://github.com/fmtlib/fmt/pull/327, https://github.com/fmtlib/fmt/issues/345 and https://github.com/fmtlib/fmt/pull/381), FreeBSD (https://github.com/fmtlib/fmt/pull/358), Cygwin (https://github.com/fmtlib/fmt/issues/388), MinGW (https://github.com/fmtlib/fmt/issues/355) as well as other issues (https://github.com/fmtlib/fmt/issues/350, https://github.com/fmtlib/fmt/issues/355, https://github.com/fmtlib/fmt/pull/348, https://github.com/fmtlib/fmt/pull/402, https://github.com/fmtlib/fmt/pull/405). Thanks @dpantele, @hghwng, @arvedarved, @LogicalKnight and @JanHellwig. - Fixed some documentation issues and extended specification (https://github.com/fmtlib/fmt/issues/320, https://github.com/fmtlib/fmt/pull/333, https://github.com/fmtlib/fmt/issues/347, https://github.com/fmtlib/fmt/pull/362). Thanks @smellman. # 3.0.0 - 2016-05-07 - The project has been renamed from C++ Format (cppformat) to fmt for consistency with the used namespace and macro prefix (https://github.com/fmtlib/fmt/issues/307). Library headers are now located in the `fmt` directory: ```c++ #include "fmt/format.h" ``` Including `format.h` from the `cppformat` directory is deprecated but works via a proxy header which will be removed in the next major version. The documentation is now available at . - Added support for [strftime](http://en.cppreference.com/w/cpp/chrono/c/strftime)-like [date and time formatting](https://fmt.dev/3.0.0/api.html#date-and-time-formatting) (https://github.com/fmtlib/fmt/issues/283): ```c++ #include "fmt/time.h" std::time_t t = std::time(nullptr); // Prints "The date is 2016-04-29." (with the current date) fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t)); ``` - `std::ostream` support including formatting of user-defined types that provide overloaded `operator<<` has been moved to `fmt/ostream.h`: ```c++ #include "fmt/ostream.h" class Date { int year_, month_, day_; public: Date(int year, int month, int day) : year_(year), month_(month), day_(day) {} friend std::ostream &operator<<(std::ostream &os, const Date &d) { return os << d.year_ << '-' << d.month_ << '-' << d.day_; } }; std::string s = fmt::format("The date is {}", Date(2012, 12, 9)); // s == "The date is 2012-12-9" ``` - Added support for [custom argument formatters](https://fmt.dev/3.0.0/api.html#argument-formatters) (https://github.com/fmtlib/fmt/issues/235). - Added support for locale-specific integer formatting with the `n` specifier (https://github.com/fmtlib/fmt/issues/305): ```c++ std::setlocale(LC_ALL, "en_US.utf8"); fmt::print("cppformat: {:n}\n", 1234567); // prints 1,234,567 ``` - Sign is now preserved when formatting an integer with an incorrect `printf` format specifier (https://github.com/fmtlib/fmt/issues/265): ```c++ fmt::printf("%lld", -42); // prints -42 ``` Note that it would be an undefined behavior in `std::printf`. - Length modifiers such as `ll` are now optional in printf formatting functions and the correct type is determined automatically (https://github.com/fmtlib/fmt/issues/255): ```c++ fmt::printf("%d", std::numeric_limits::max()); ``` Note that it would be an undefined behavior in `std::printf`. - Added initial support for custom formatters (https://github.com/fmtlib/fmt/issues/231). - Fixed detection of user-defined literal support on Intel C++ compiler (https://github.com/fmtlib/fmt/issues/311, https://github.com/fmtlib/fmt/pull/312). Thanks @dean0x7d and @speth. - Reduced compile time (https://github.com/fmtlib/fmt/pull/243, https://github.com/fmtlib/fmt/pull/249, https://github.com/fmtlib/fmt/issues/317): ![](https://cloud.githubusercontent.com/assets/4831417/11614060/b9e826d2-9c36-11e5-8666-d4131bf503ef.png) ![](https://cloud.githubusercontent.com/assets/4831417/11614080/6ac903cc-9c37-11e5-8165-26df6efae364.png) Thanks @dean0x7d. - Compile test fixes (https://github.com/fmtlib/fmt/pull/313). Thanks @dean0x7d. - Documentation fixes (https://github.com/fmtlib/fmt/pull/239, https://github.com/fmtlib/fmt/issues/248, https://github.com/fmtlib/fmt/issues/252, https://github.com/fmtlib/fmt/pull/258, https://github.com/fmtlib/fmt/issues/260, https://github.com/fmtlib/fmt/issues/301, https://github.com/fmtlib/fmt/pull/309). Thanks @ReadmeCritic @Gachapen and @jwilk. - Fixed compiler and sanitizer warnings (https://github.com/fmtlib/fmt/issues/244, https://github.com/fmtlib/fmt/pull/256, https://github.com/fmtlib/fmt/pull/259, https://github.com/fmtlib/fmt/issues/263, https://github.com/fmtlib/fmt/issues/274, https://github.com/fmtlib/fmt/pull/277, https://github.com/fmtlib/fmt/pull/286, https://github.com/fmtlib/fmt/issues/291, https://github.com/fmtlib/fmt/issues/296, https://github.com/fmtlib/fmt/issues/308). Thanks @mwinterb, @pweiskircher and @Naios. - Improved compatibility with Windows Store apps (https://github.com/fmtlib/fmt/issues/280, https://github.com/fmtlib/fmt/pull/285) Thanks @mwinterb. - Added tests of compatibility with older C++ standards (https://github.com/fmtlib/fmt/pull/273). Thanks @niosHD. - Fixed Android build (https://github.com/fmtlib/fmt/pull/271). Thanks @newnon. - Changed `ArgMap` to be backed by a vector instead of a map. (https://github.com/fmtlib/fmt/issues/261, https://github.com/fmtlib/fmt/pull/262). Thanks @mwinterb. - Added `fprintf` overload that writes to a `std::ostream` (https://github.com/fmtlib/fmt/pull/251). Thanks @nickhutchinson. - Export symbols when building a Windows DLL (https://github.com/fmtlib/fmt/pull/245). Thanks @macdems. - Fixed compilation on Cygwin (https://github.com/fmtlib/fmt/issues/304). - Implemented a workaround for a bug in Apple LLVM version 4.2 of clang (https://github.com/fmtlib/fmt/issues/276). - Implemented a workaround for Google Test bug https://github.com/google/googletest/issues/705 on gcc 6 (https://github.com/fmtlib/fmt/issues/268). Thanks @octoploid. - Removed Biicode support because the latter has been discontinued. # 2.1.1 - 2016-04-11 - The install location for generated CMake files is now configurable via the `FMT_CMAKE_DIR` CMake variable (https://github.com/fmtlib/fmt/pull/299). Thanks @niosHD. - Documentation fixes (https://github.com/fmtlib/fmt/issues/252). # 2.1.0 - 2016-03-21 - Project layout and build system improvements (https://github.com/fmtlib/fmt/pull/267): - The code have been moved to the `cppformat` directory. Including `format.h` from the top-level directory is deprecated but works via a proxy header which will be removed in the next major version. - C++ Format CMake targets now have proper interface definitions. - Installed version of the library now supports the header-only configuration. - Targets `doc`, `install`, and `test` are now disabled if C++ Format is included as a CMake subproject. They can be enabled by setting `FMT_DOC`, `FMT_INSTALL`, and `FMT_TEST` in the parent project. Thanks @niosHD. # 2.0.1 - 2016-03-13 - Improved CMake find and package support (https://github.com/fmtlib/fmt/issues/264). Thanks @niosHD. - Fix compile error with Android NDK and mingw32 (https://github.com/fmtlib/fmt/issues/241). Thanks @Gachapen. - Documentation fixes (https://github.com/fmtlib/fmt/issues/248, https://github.com/fmtlib/fmt/issues/260). # 2.0.0 - 2015-12-01 ## General - \[Breaking\] Named arguments (https://github.com/fmtlib/fmt/pull/169, https://github.com/fmtlib/fmt/pull/173, https://github.com/fmtlib/fmt/pull/174): ```c++ fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); ``` Thanks @jamboree. - \[Experimental\] User-defined literals for format and named arguments (https://github.com/fmtlib/fmt/pull/204, https://github.com/fmtlib/fmt/pull/206, https://github.com/fmtlib/fmt/pull/207): ```c++ using namespace fmt::literals; fmt::print("The answer is {answer}.", "answer"_a=42); ``` Thanks @dean0x7d. - \[Breaking\] Formatting of more than 16 arguments is now supported when using variadic templates (https://github.com/fmtlib/fmt/issues/141). Thanks @Shauren. - Runtime width specification (https://github.com/fmtlib/fmt/pull/168): ```c++ fmt::format("{0:{1}}", 42, 5); // gives " 42" ``` Thanks @jamboree. - \[Breaking\] Enums are now formatted with an overloaded `std::ostream` insertion operator (`operator<<`) if available (https://github.com/fmtlib/fmt/issues/232). - \[Breaking\] Changed default `bool` format to textual, \"true\" or \"false\" (https://github.com/fmtlib/fmt/issues/170): ```c++ fmt::print("{}", true); // prints "true" ``` To print `bool` as a number use numeric format specifier such as `d`: ```c++ fmt::print("{:d}", true); // prints "1" ``` - `fmt::printf` and `fmt::sprintf` now support formatting of `bool` with the `%s` specifier giving textual output, \"true\" or \"false\" (https://github.com/fmtlib/fmt/pull/223): ```c++ fmt::printf("%s", true); // prints "true" ``` Thanks @LarsGullik. - \[Breaking\] `signed char` and `unsigned char` are now formatted as integers by default (https://github.com/fmtlib/fmt/pull/217). - \[Breaking\] Pointers to C strings can now be formatted with the `p` specifier (https://github.com/fmtlib/fmt/pull/223): ```c++ fmt::print("{:p}", "test"); // prints pointer value ``` Thanks @LarsGullik. - \[Breaking\] `fmt::printf` and `fmt::sprintf` now print null pointers as `(nil)` and null strings as `(null)` for consistency with glibc (https://github.com/fmtlib/fmt/pull/226). Thanks @LarsGullik. - \[Breaking\] `fmt::(s)printf` now supports formatting of objects of user-defined types that provide an overloaded `std::ostream` insertion operator (`operator<<`) (https://github.com/fmtlib/fmt/issues/201): ```c++ fmt::printf("The date is %s", Date(2012, 12, 9)); ``` - \[Breaking\] The `Buffer` template is now part of the public API and can be used to implement custom memory buffers (https://github.com/fmtlib/fmt/issues/140). Thanks @polyvertex. - \[Breaking\] Improved compatibility between `BasicStringRef` and [std::experimental::basic_string_view]( http://en.cppreference.com/w/cpp/experimental/basic_string_view) (https://github.com/fmtlib/fmt/issues/100, https://github.com/fmtlib/fmt/issues/159, https://github.com/fmtlib/fmt/issues/183): - Comparison operators now compare string content, not pointers - `BasicStringRef::c_str` replaced by `BasicStringRef::data` - `BasicStringRef` is no longer assumed to be null-terminated References to null-terminated strings are now represented by a new class, `BasicCStringRef`. - Dependency on pthreads introduced by Google Test is now optional (https://github.com/fmtlib/fmt/issues/185). - New CMake options `FMT_DOC`, `FMT_INSTALL` and `FMT_TEST` to control generation of `doc`, `install` and `test` targets respectively, on by default (https://github.com/fmtlib/fmt/issues/197, https://github.com/fmtlib/fmt/issues/198, https://github.com/fmtlib/fmt/issues/200). Thanks @maddinat0r. - `noexcept` is now used when compiling with MSVC2015 (https://github.com/fmtlib/fmt/pull/215). Thanks @dmkrepo. - Added an option to disable use of `windows.h` when `FMT_USE_WINDOWS_H` is defined as 0 before including `format.h` (https://github.com/fmtlib/fmt/issues/171). Thanks @alfps. - \[Breaking\] `windows.h` is now included with `NOMINMAX` unless `FMT_WIN_MINMAX` is defined. This is done to prevent breaking code using `std::min` and `std::max` and only affects the header-only configuration (https://github.com/fmtlib/fmt/issues/152, https://github.com/fmtlib/fmt/pull/153, https://github.com/fmtlib/fmt/pull/154). Thanks @DevO2012. - Improved support for custom character types (https://github.com/fmtlib/fmt/issues/171). Thanks @alfps. - Added an option to disable use of IOStreams when `FMT_USE_IOSTREAMS` is defined as 0 before including `format.h` (https://github.com/fmtlib/fmt/issues/205, https://github.com/fmtlib/fmt/pull/208). Thanks @JodiTheTigger. - Improved detection of `isnan`, `isinf` and `signbit`. ## Optimization - Made formatting of user-defined types more efficient with a custom stream buffer (https://github.com/fmtlib/fmt/issues/92, https://github.com/fmtlib/fmt/pull/230). Thanks @NotImplemented. - Further improved performance of `fmt::Writer` on integer formatting and fixed a minor regression. Now it is \~7% faster than `karma::generate` on Karma\'s benchmark (https://github.com/fmtlib/fmt/issues/186). - \[Breaking\] Reduced [compiled code size](https://github.com/fmtlib/fmt#compile-time-and-code-bloat) (https://github.com/fmtlib/fmt/issues/143, https://github.com/fmtlib/fmt/pull/149). ## Distribution - \[Breaking\] Headers are now installed in `${CMAKE_INSTALL_PREFIX}/include/cppformat` (https://github.com/fmtlib/fmt/issues/178). Thanks @jackyf. - \[Breaking\] Changed the library name from `format` to `cppformat` for consistency with the project name and to avoid potential conflicts (https://github.com/fmtlib/fmt/issues/178). Thanks @jackyf. - C++ Format is now available in [Debian](https://www.debian.org/) GNU/Linux ([stretch](https://packages.debian.org/source/stretch/cppformat), [sid](https://packages.debian.org/source/sid/cppformat)) and derived distributions such as [Ubuntu](https://launchpad.net/ubuntu/+source/cppformat) 15.10 and later (https://github.com/fmtlib/fmt/issues/155): $ sudo apt-get install libcppformat1-dev Thanks @jackyf. - [Packages for Fedora and RHEL](https://admin.fedoraproject.org/pkgdb/package/cppformat/) are now available. Thanks Dave Johansen. - C++ Format can now be installed via [Homebrew](http://brew.sh/) on OS X (https://github.com/fmtlib/fmt/issues/157): $ brew install cppformat Thanks @ortho and Anatoliy Bulukin. ## Documentation - Migrated from ReadTheDocs to GitHub Pages for better responsiveness and reliability (https://github.com/fmtlib/fmt/issues/128). New documentation address is . - Added [Building thedocumentation]( https://fmt.dev/2.0.0/usage.html#building-the-documentation) section to the documentation. - Documentation build script is now compatible with Python 3 and newer pip versions. (https://github.com/fmtlib/fmt/pull/189, https://github.com/fmtlib/fmt/issues/209). Thanks @JodiTheTigger and @xentec. - Documentation fixes and improvements (https://github.com/fmtlib/fmt/issues/36, https://github.com/fmtlib/fmt/issues/75, https://github.com/fmtlib/fmt/issues/125, https://github.com/fmtlib/fmt/pull/160, https://github.com/fmtlib/fmt/pull/161, https://github.com/fmtlib/fmt/issues/162, https://github.com/fmtlib/fmt/issues/165, https://github.com/fmtlib/fmt/issues/210). Thanks @syohex. - Fixed out-of-tree documentation build (https://github.com/fmtlib/fmt/issues/177). Thanks @jackyf. ## Fixes - Fixed `initializer_list` detection (https://github.com/fmtlib/fmt/issues/136). Thanks @Gachapen. - \[Breaking\] Fixed formatting of enums with numeric format specifiers in `fmt::(s)printf` (https://github.com/fmtlib/fmt/issues/131, https://github.com/fmtlib/fmt/issues/139): ```c++ enum { ANSWER = 42 }; fmt::printf("%d", ANSWER); ``` Thanks @Naios. - Improved compatibility with old versions of MinGW (https://github.com/fmtlib/fmt/issues/129, https://github.com/fmtlib/fmt/pull/130, https://github.com/fmtlib/fmt/issues/132). Thanks @cstamford. - Fixed a compile error on MSVC with disabled exceptions (https://github.com/fmtlib/fmt/issues/144). - Added a workaround for broken implementation of variadic templates in MSVC2012 (https://github.com/fmtlib/fmt/issues/148). - Placed the anonymous namespace within `fmt` namespace for the header-only configuration (https://github.com/fmtlib/fmt/issues/171). Thanks @alfps. - Fixed issues reported by Coverity Scan (https://github.com/fmtlib/fmt/issues/187, https://github.com/fmtlib/fmt/issues/192). - Implemented a workaround for a name lookup bug in MSVC2010 (https://github.com/fmtlib/fmt/issues/188). - Fixed compiler warnings (https://github.com/fmtlib/fmt/issues/95, https://github.com/fmtlib/fmt/issues/96, https://github.com/fmtlib/fmt/pull/114, https://github.com/fmtlib/fmt/issues/135, https://github.com/fmtlib/fmt/issues/142, https://github.com/fmtlib/fmt/issues/145, https://github.com/fmtlib/fmt/issues/146, https://github.com/fmtlib/fmt/issues/158, https://github.com/fmtlib/fmt/issues/163, https://github.com/fmtlib/fmt/issues/175, https://github.com/fmtlib/fmt/issues/190, https://github.com/fmtlib/fmt/pull/191, https://github.com/fmtlib/fmt/issues/194, https://github.com/fmtlib/fmt/pull/196, https://github.com/fmtlib/fmt/issues/216, https://github.com/fmtlib/fmt/pull/218, https://github.com/fmtlib/fmt/pull/220, https://github.com/fmtlib/fmt/pull/229, https://github.com/fmtlib/fmt/issues/233, https://github.com/fmtlib/fmt/issues/234, https://github.com/fmtlib/fmt/pull/236, https://github.com/fmtlib/fmt/issues/281, https://github.com/fmtlib/fmt/issues/289). Thanks @seanmiddleditch, @dixlorenz, @CarterLi, @Naios, @fmatthew5876, @LevskiWeng, @rpopescu, @gabime, @cubicool, @jkflying, @LogicalKnight, @inguin and @Jopie64. - Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le, s390x and SunOS 5.11 i386 (https://github.com/fmtlib/fmt/issues/138, https://github.com/fmtlib/fmt/issues/179, https://github.com/fmtlib/fmt/issues/180, https://github.com/fmtlib/fmt/issues/202, https://github.com/fmtlib/fmt/issues/225, [Red Hat Bugzilla Bug 1260297](https://bugzilla.redhat.com/show_bug.cgi?id=1260297)). Thanks @Naios, @jackyf and Dave Johansen. - Fixed a name conflict with macro `free` defined in `crtdbg.h` when `_CRTDBG_MAP_ALLOC` is set (https://github.com/fmtlib/fmt/issues/211). - Fixed shared library build on OS X (https://github.com/fmtlib/fmt/pull/212). Thanks @dean0x7d. - Fixed an overload conflict on MSVC when `/Zc:wchar_t-` option is specified (https://github.com/fmtlib/fmt/pull/214). Thanks @slavanap. - Improved compatibility with MSVC 2008 (https://github.com/fmtlib/fmt/pull/236). Thanks @Jopie64. - Improved compatibility with bcc32 (https://github.com/fmtlib/fmt/issues/227). - Fixed `static_assert` detection on Clang (https://github.com/fmtlib/fmt/pull/228). Thanks @dean0x7d. # 1.1.0 - 2015-03-06 - Added `BasicArrayWriter`, a class template that provides operations for formatting and writing data into a fixed-size array (https://github.com/fmtlib/fmt/issues/105 and https://github.com/fmtlib/fmt/issues/122): ```c++ char buffer[100]; fmt::ArrayWriter w(buffer); w.write("The answer is {}", 42); ``` - Added [0 A.D.](http://play0ad.com/) and [PenUltima Online (POL)](http://www.polserver.com/) to the list of notable projects using C++ Format. - C++ Format now uses MSVC intrinsics for better formatting performance (https://github.com/fmtlib/fmt/pull/115, https://github.com/fmtlib/fmt/pull/116, https://github.com/fmtlib/fmt/pull/118 and https://github.com/fmtlib/fmt/pull/121). Previously these optimizations where only used on GCC and Clang. Thanks @CarterLi and @objectx. - CMake install target (https://github.com/fmtlib/fmt/pull/119). Thanks @TrentHouliston. You can now install C++ Format with `make install` command. - Improved [Biicode](http://www.biicode.com/) support (https://github.com/fmtlib/fmt/pull/98 and https://github.com/fmtlib/fmt/pull/104). Thanks @MariadeAnton and @franramirez688. - Improved support for building with [Android NDK]( https://developer.android.com/tools/sdk/ndk/index.html) (https://github.com/fmtlib/fmt/pull/107). Thanks @newnon. The [android-ndk-example](https://github.com/fmtlib/android-ndk-example) repository provides and example of using C++ Format with Android NDK: ![](https://raw.githubusercontent.com/fmtlib/android-ndk-example/master/screenshot.png) - Improved documentation of `SystemError` and `WindowsError` (https://github.com/fmtlib/fmt/issues/54). - Various code improvements (https://github.com/fmtlib/fmt/pull/110, https://github.com/fmtlib/fmt/pull/111 https://github.com/fmtlib/fmt/pull/112). Thanks @CarterLi. - Improved compile-time errors when formatting wide into narrow strings (https://github.com/fmtlib/fmt/issues/117). - Fixed `BasicWriter::write` without formatting arguments when C++11 support is disabled (https://github.com/fmtlib/fmt/issues/109). - Fixed header-only build on OS X with GCC 4.9 (https://github.com/fmtlib/fmt/issues/124). - Fixed packaging issues (https://github.com/fmtlib/fmt/issues/94). - Added [changelog](https://github.com/fmtlib/fmt/blob/master/ChangeLog.md) (https://github.com/fmtlib/fmt/issues/103). # 1.0.0 - 2015-02-05 - Add support for a header-only configuration when `FMT_HEADER_ONLY` is defined before including `format.h`: ```c++ #define FMT_HEADER_ONLY #include "format.h" ``` - Compute string length in the constructor of `BasicStringRef` instead of the `size` method (https://github.com/fmtlib/fmt/issues/79). This eliminates size computation for string literals on reasonable optimizing compilers. - Fix formatting of types with overloaded `operator <<` for `std::wostream` (https://github.com/fmtlib/fmt/issues/86): ```c++ fmt::format(L"The date is {0}", Date(2012, 12, 9)); ``` - Fix linkage of tests on Arch Linux (https://github.com/fmtlib/fmt/issues/89). - Allow precision specifier for non-float arguments (https://github.com/fmtlib/fmt/issues/90): ```c++ fmt::print("{:.3}\n", "Carpet"); // prints "Car" ``` - Fix build on Android NDK (https://github.com/fmtlib/fmt/issues/93). - Improvements to documentation build procedure. - Remove `FMT_SHARED` CMake variable in favor of standard [BUILD_SHARED_LIBS]( http://www.cmake.org/cmake/help/v3.0/variable/BUILD_SHARED_LIBS.html). - Fix error handling in `fmt::fprintf`. - Fix a number of warnings. # 0.12.0 - 2014-10-25 - \[Breaking\] Improved separation between formatting and buffer management. `Writer` is now a base class that cannot be instantiated directly. The new `MemoryWriter` class implements the default buffer management with small allocations done on stack. So `fmt::Writer` should be replaced with `fmt::MemoryWriter` in variable declarations. Old code: ```c++ fmt::Writer w; ``` New code: ```c++ fmt::MemoryWriter w; ``` If you pass `fmt::Writer` by reference, you can continue to do so: ```c++ void f(fmt::Writer &w); ``` This doesn\'t affect the formatting API. - Support for custom memory allocators (https://github.com/fmtlib/fmt/issues/69) - Formatting functions now accept [signed char]{.title-ref} and [unsigned char]{.title-ref} strings as arguments (https://github.com/fmtlib/fmt/issues/73): ```c++ auto s = format("GLSL version: {}", glGetString(GL_VERSION)); ``` - Reduced code bloat. According to the new [benchmark results](https://github.com/fmtlib/fmt#compile-time-and-code-bloat), cppformat is close to `printf` and by the order of magnitude better than Boost Format in terms of compiled code size. - Improved appearance of the documentation on mobile by using the [Sphinx Bootstrap theme](http://ryan-roemer.github.io/sphinx-bootstrap-theme/): | Old | New | | --- | --- | | ![](https://cloud.githubusercontent.com/assets/576385/4792130/cd256436-5de3-11e4-9a62-c077d0c2b003.png) | ![](https://cloud.githubusercontent.com/assets/576385/4792131/cd29896c-5de3-11e4-8f59-cac952942bf0.png) | # 0.11.0 - 2014-08-21 - Safe printf implementation with a POSIX extension for positional arguments: ```c++ fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("%1$s, %3$d %2$s", weekday, month, day); ``` - Arguments of `char` type can now be formatted as integers (Issue https://github.com/fmtlib/fmt/issues/55): ```c++ fmt::format("0x{0:02X}", 'a'); ``` - Deprecated parts of the API removed. - The library is now built and tested on MinGW with Appveyor in addition to existing test platforms Linux/GCC, OS X/Clang, Windows/MSVC. # 0.10.0 - 2014-07-01 **Improved API** - All formatting methods are now implemented as variadic functions instead of using `operator<<` for feeding arbitrary arguments into a temporary formatter object. This works both with C++11 where variadic templates are used and with older standards where variadic functions are emulated by providing lightweight wrapper functions defined with the `FMT_VARIADIC` macro. You can use this macro for defining your own portable variadic functions: ```c++ void report_error(const char *format, const fmt::ArgList &args) { fmt::print("Error: {}"); fmt::print(format, args); } FMT_VARIADIC(void, report_error, const char *) report_error("file not found: {}", path); ``` Apart from a more natural syntax, this also improves performance as there is no need to construct temporary formatter objects and control arguments\' lifetimes. Because the wrapper functions are very lightweight, this doesn\'t cause code bloat even in pre-C++11 mode. - Simplified common case of formatting an `std::string`. Now it requires a single function call: ```c++ std::string s = format("The answer is {}.", 42); ``` Previously it required 2 function calls: ```c++ std::string s = str(Format("The answer is {}.") << 42); ``` Instead of unsafe `c_str` function, `fmt::Writer` should be used directly to bypass creation of `std::string`: ```c++ fmt::Writer w; w.write("The answer is {}.", 42); w.c_str(); // returns a C string ``` This doesn\'t do dynamic memory allocation for small strings and is less error prone as the lifetime of the string is the same as for `std::string::c_str` which is well understood (hopefully). - Improved consistency in naming functions that are a part of the public API. Now all public functions are lowercase following the standard library conventions. Previously it was a combination of lowercase and CapitalizedWords. Issue https://github.com/fmtlib/fmt/issues/50. - Old functions are marked as deprecated and will be removed in the next release. **Other Changes** - Experimental support for printf format specifications (work in progress): ```c++ fmt::printf("The answer is %d.", 42); std::string s = fmt::sprintf("Look, a %s!", "string"); ``` - Support for hexadecimal floating point format specifiers `a` and `A`: ```c++ print("{:a}", -42.0); // Prints -0x1.5p+5 print("{:A}", -42.0); // Prints -0X1.5P+5 ``` - CMake option `FMT_SHARED` that specifies whether to build format as a shared library (off by default). # 0.9.0 - 2014-05-13 - More efficient implementation of variadic formatting functions. - `Writer::Format` now has a variadic overload: ```c++ Writer out; out.Format("Look, I'm {}!", "variadic"); ``` - For efficiency and consistency with other overloads, variadic overload of the `Format` function now returns `Writer` instead of `std::string`. Use the `str` function to convert it to `std::string`: ```c++ std::string s = str(Format("Look, I'm {}!", "variadic")); ``` - Replaced formatter actions with output sinks: `NoAction` -\> `NullSink`, `Write` -\> `FileSink`, `ColorWriter` -\> `ANSITerminalSink`. This improves naming consistency and shouldn\'t affect client code unless these classes are used directly which should be rarely needed. - Added `ThrowSystemError` function that formats a message and throws `SystemError` containing the formatted message and system-specific error description. For example, the following code ```c++ FILE *f = fopen(filename, "r"); if (!f) ThrowSystemError(errno, "Failed to open file '{}'") << filename; ``` will throw `SystemError` exception with description \"Failed to open file \'\\': No such file or directory\" if file doesn\'t exist. - Support for AppVeyor continuous integration platform. - `Format` now throws `SystemError` in case of I/O errors. - Improve test infrastructure. Print functions are now tested by redirecting the output to a pipe. # 0.8.0 - 2014-04-14 - Initial release ================================================ FILE: doc/LICENSE-exception ================================================ 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 "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- Optional exception to the license --- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the above copyright and permission notices. ================================================ FILE: doc/api.md ================================================ # API Reference The {fmt} library API consists of the following components: - [`fmt/base.h`](#base-api): the base API providing main formatting functions for `char`/UTF-8 with C++20 compile-time checks and minimal dependencies - [`fmt/format.h`](#format-api): `fmt::format` and other formatting functions as well as locale support - [`fmt/ranges.h`](#ranges-api): formatting of ranges and tuples - [`fmt/chrono.h`](#chrono-api): date and time formatting - [`fmt/std.h`](#std-api): formatters for standard library types - [`fmt/compile.h`](#compile-api): format string compilation - [`fmt/color.h`](#color-api): terminal colors and text styles - [`fmt/os.h`](#os-api): system APIs - [`fmt/ostream.h`](#ostream-api): `std::ostream` support - [`fmt/args.h`](#args-api): dynamic argument lists - [`fmt/printf.h`](#printf-api): safe `printf` - [`fmt/xchar.h`](#xchar-api): optional `wchar_t` support All functions and types provided by the library reside in namespace `fmt` and macros have prefix `FMT_`. ## C++ Module API With the C++ module API, the headers listed above don't need to be included. You can use the `import fmt;` statement instead. All other functionality, listed below, remains the same. ## Base API `fmt/base.h` defines the base API which provides main formatting functions for `char`/UTF-8 with C++20 compile-time checks. It has minimal include dependencies for better compile times. This header is only beneficial when using {fmt} as a library (the default) and not in the header-only mode. It also provides `formatter` specializations for the following types: - `int`, `long long` - `unsigned`, `unsigned long long` - `float`, `double`, `long double` - `bool` - `char` - `const char*`, [`fmt::string_view`](#basic_string_view) - `const void*` The following functions use [format string syntax](syntax.md) similar to that of [str.format](https://docs.python.org/3/library/stdtypes.html#str.format) in Python. They take *fmt* and *args* as arguments. *fmt* is a format string that contains literal text and replacement fields surrounded by braces `{}`. The fields are replaced with formatted arguments in the resulting string. [`fmt::format_string`](#format_string) is a format string which can be implicitly constructed from a string literal or a `constexpr` string and is checked at compile time in C++20. To pass a runtime format string wrap it in [`fmt::runtime`](#runtime). *args* is an argument list representing objects to be formatted. I/O errors are reported as [`std::system_error`]( https://en.cppreference.com/w/cpp/error/system_error) exceptions unless specified otherwise. ::: print(format_string, T&&...) ::: print(FILE*, format_string, T&&...) ::: println(format_string, T&&...) ::: println(FILE*, format_string, T&&...) ::: format_to(OutputIt&&, format_string, T&&...) ::: format_to_n(OutputIt, size_t, format_string, T&&...) ::: format_to_n_result ::: formatted_size(format_string, T&&...) ### Formatting User-Defined Types The {fmt} library provides formatters for many standard C++ types. See [`fmt/ranges.h`](#ranges-api) for ranges and tuples including standard containers such as `std::vector`, [`fmt/chrono.h`](#chrono-api) for date and time formatting and [`fmt/std.h`](#std-api) for other standard library types. There are two ways to make a user-defined type formattable: providing a `format_as` function or specializing the `formatter` struct template. Formatting of non-void pointer types is intentionally disallowed and they cannot be made formattable via either extension API. Use `format_as` if you want to make your type formattable as some other type with the same format specifiers. The `format_as` function should take an object of your type and return an object of a formattable type. It should be defined in the same namespace as your type. Example ([run](https://godbolt.org/z/nvME4arz8)): #include namespace kevin_namespacy { enum class film { house_of_cards, american_beauty, se7en = 7 }; auto format_as(film f) { return fmt::underlying(f); } } int main() { fmt::print("{}\n", kevin_namespacy::film::se7en); // Output: 7 } Using a specialization is more complex, but gives you full control over parsing and formatting. To use this method, specialize the `formatter` struct template for your type and implement `parse` and `format` methods. The recommended way of defining a formatter is by reusing an existing one via inheritance or composition. This way you can support standard format specifiers without implementing them yourself. For example: ```c++ // color.h: #include enum class color {red, green, blue}; template <> struct fmt::formatter: formatter { // parse is inherited from formatter. auto format(color c, format_context& ctx) const -> format_context::iterator; }; ``` ```c++ // color.cc: #include "color.h" #include auto fmt::formatter::format(color c, format_context& ctx) const -> format_context::iterator { string_view name = "unknown"; switch (c) { case color::red: name = "red"; break; case color::green: name = "green"; break; case color::blue: name = "blue"; break; } return formatter::format(name, ctx); } ``` Note that `formatter::format` is defined in `fmt/format.h` so it has to be included in the source file. Since `parse` is inherited from `formatter` it will recognize all string format specifications, for example ```c++ fmt::format("{:>10}", color::blue) ``` will return `" blue"`. In general the formatter has the following form: template <> struct fmt::formatter { // Parses format specifiers and stores them in the formatter. // // [ctx.begin(), ctx.end()) is a, possibly empty, character range that // contains a part of the format string starting from the format // specifications to be parsed, e.g. in // // fmt::format("{:f} continued", ...); // // the range will contain "f} continued". The formatter should parse // specifiers until '}' or the end of the range. In this example the // formatter should parse the 'f' specifier and return an iterator // pointing to '}'. constexpr auto parse(format_parse_context& ctx) -> format_parse_context::iterator; // Formats value using the parsed format specification stored in this // formatter and writes the output to ctx.out(). auto format(const T& value, format_context& ctx) const -> format_context::iterator; }; It is recommended to at least support fill, align and width that apply to the whole object and have the same semantics as in standard formatters. You can also write a formatter for a hierarchy of classes: ```c++ // demo.h: #include #include struct A { virtual ~A() {} virtual std::string name() const { return "A"; } }; struct B : A { virtual std::string name() const { return "B"; } }; template struct fmt::formatter, char>> : fmt::formatter { auto format(const A& a, format_context& ctx) const { return formatter::format(a.name(), ctx); } }; ``` ```c++ // demo.cc: #include "demo.h" #include int main() { B b; A& a = b; fmt::print("{}", a); // Output: B } ``` Providing both a `formatter` specialization and a `format_as` overload is disallowed. ::: basic_format_parse_context ::: context ::: format_context ### Compile-Time Checks Compile-time format string checks are enabled by default on compilers that support C++20 `consteval`. On older compilers you can use the [FMT_STRING](#legacy-checks) macro defined in `fmt/format.h` instead. Unused arguments are allowed as in Python's `str.format` and ordinary functions. See [Type Erasure](#type-erasure) for an example of how to enable compile-time checks in your own functions with `fmt::format_string` while avoiding template bloat. ::: fstring ::: format_string ::: runtime(string_view) ### Type Erasure You can create your own formatting function with compile-time checks and small binary footprint, for example ([run](https://godbolt.org/z/b9Pbasvzc)): ```c++ #include void vlog(const char* file, int line, fmt::string_view fmt, fmt::format_args args) { fmt::print("{}: {}: {}", file, line, fmt::vformat(fmt, args)); } template void log(const char* file, int line, fmt::format_string fmt, T&&... args) { vlog(file, line, fmt, fmt::make_format_args(args...)); } #define MY_LOG(fmt, ...) log(__FILE__, __LINE__, fmt, __VA_ARGS__) MY_LOG("invalid squishiness: {}", 42); ``` Note that `vlog` is not parameterized on argument types which improves compile times and reduces binary code size compared to a fully parameterized version. ::: make_format_args(T&...) ::: basic_format_args ::: format_args ::: basic_format_arg ### Named Arguments ::: arg(const char*, const T&) ### Compatibility ::: basic_string_view ::: string_view ## Format API `fmt/format.h` defines the full format API providing additional formatting functions and locale support. ::: format(format_string, T&&...) ::: vformat(string_view, format_args) ::: operator""_a() ### Utilities ::: ptr(T) ::: underlying(Enum) ::: to_string(const T&) ::: group_digits(T) ::: detail::buffer ::: basic_memory_buffer ### System Errors {fmt} does not use `errno` to communicate errors to the user, but it may call system functions which set `errno`. Users should not make any assumptions about the value of `errno` being preserved by library functions. ::: system_error ::: format_system_error ### Custom Allocators The {fmt} library supports custom dynamic memory allocators. A custom allocator class can be specified as a template argument to [`fmt::basic_memory_buffer`](#basic_memory_buffer): using custom_memory_buffer = fmt::basic_memory_buffer; It is also possible to write a formatting function that uses a custom allocator: using custom_string = std::basic_string, custom_allocator>; auto vformat(custom_allocator alloc, fmt::string_view fmt, fmt::format_args args) -> custom_string { auto buf = custom_memory_buffer(alloc); fmt::vformat_to(std::back_inserter(buf), fmt, args); return custom_string(buf.data(), buf.size(), alloc); } template auto format(custom_allocator alloc, fmt::string_view fmt, const Args& ... args) -> custom_string { return vformat(alloc, fmt, fmt::make_format_args(args...)); } The allocator will be used for the output container only. Formatting functions normally don't do any allocations for built-in and string types except for non-default floating-point formatting that occasionally falls back on `sprintf`. ### Locale All formatting is locale-independent by default. Use the `'L'` format specifier to insert the appropriate number separator characters from the locale: #include #include std::locale::global(std::locale("en_US.UTF-8")); auto s = fmt::format("{:L}", 1000000); // s == "1,000,000" `fmt/format.h` provides the following overloads of formatting functions that take `std::locale` as a parameter. The locale type is a template parameter to avoid the expensive `` include. ::: format(locale_ref, format_string, T&&...) ::: format_to(OutputIt, locale_ref, format_string, T&&...) ::: formatted_size(locale_ref, format_string, T&&...) ### Legacy Compile-Time Checks `FMT_STRING` enables compile-time checks on older compilers. It requires C++14 or later and is a no-op in C++11. ::: FMT_STRING To force the use of legacy compile-time checks, define the preprocessor variable `FMT_ENFORCE_COMPILE_STRING`. When set, functions accepting `FMT_STRING` will fail to compile with regular strings. ## Range and Tuple Formatting `fmt/ranges.h` provides formatting support for ranges and tuples: #include fmt::print("{}", std::tuple{'a', 42}); // Output: ('a', 42) Using `fmt::join`, you can separate tuple elements with a custom separator: #include auto t = std::tuple{1, 'a'}; fmt::print("{}", fmt::join(t, ", ")); // Output: 1, a ::: join(Range&&, string_view) ::: join(It, Sentinel, string_view) ::: join(std::initializer_list, string_view) ## Date and Time Formatting `fmt/chrono.h` provides formatters for - [`std::chrono::duration`](https://en.cppreference.com/w/cpp/chrono/duration) - [`std::chrono::time_point`]( https://en.cppreference.com/w/cpp/chrono/time_point) - [`std::tm`](https://en.cppreference.com/w/cpp/chrono/c/tm) The format syntax is described in [Chrono Format Specifications](syntax.md# chrono-format-specifications). **Example**: #include int main() { auto now = std::chrono::system_clock::now(); fmt::print("The date is {:%Y-%m-%d}.\n", now); // Output: The date is 2020-11-07. // (with 2020-11-07 replaced by the current date) using namespace std::literals::chrono_literals; fmt::print("Default format: {} {}\n", 42s, 100ms); // Output: Default format: 42s 100ms fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); // Output: strftime-like format: 03:15:30 } ::: gmtime(std::time_t) ## Standard Library Types Formatting `fmt/std.h` provides formatters for: - [`std::atomic`](https://en.cppreference.com/w/cpp/atomic/atomic) - [`std::atomic_flag`](https://en.cppreference.com/w/cpp/atomic/atomic_flag) - [`std::bitset`](https://en.cppreference.com/w/cpp/utility/bitset) - [`std::error_code`](https://en.cppreference.com/w/cpp/error/error_code) - [`std::exception`](https://en.cppreference.com/w/cpp/error/exception) - [`std::filesystem::path`](https://en.cppreference.com/w/cpp/filesystem/path) - [`std::monostate`]( https://en.cppreference.com/w/cpp/utility/variant/monostate) - [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) - [`std::source_location`]( https://en.cppreference.com/w/cpp/utility/source_location) - [`std::thread::id`](https://en.cppreference.com/w/cpp/thread/thread/id) - [`std::variant`](https://en.cppreference.com/w/cpp/utility/variant/variant) ::: ptr(const std::unique_ptr&) ::: ptr(const std::shared_ptr&) ### Variants A `std::variant` can be formatted only if every alternative is formattable, and requires the `__cpp_lib_variant` [library feature](https://en.cppreference.com/w/cpp/feature_test). **Example**: #include fmt::print("{}", std::variant('x')); // Output: variant('x') fmt::print("{}", std::variant()); // Output: variant(monostate) ## Bit-Fields and Packed Structs To format a bit-field or a field of a struct with `__attribute__((packed))` applied to it, you need to convert it to the underlying or compatible type via a cast or a unary `+` ([godbolt](https://www.godbolt.org/z/3qKKs6T5Y)): ```c++ struct smol { int bit : 1; }; auto s = smol(); fmt::print("{}", +s.bit); ``` This is a known limitation of "perfect" forwarding in C++. ## Compile-Time Support `fmt/compile.h` provides format string compilation and compile-time (`constexpr`) formatting enabled via the `FMT_COMPILE` macro or the `_cf` user-defined literal defined in namespace `fmt::literals`. Format strings marked with `FMT_COMPILE` or `_cf` are parsed, checked and converted into efficient formatting code at compile-time. This supports arguments of built-in and string types as well as user-defined types with `format` methods taking the format context type as a template parameter in their `formatter` specializations. For example ([run](https://www.godbolt.org/z/3c13erEoq)): struct point { double x; double y; }; template <> struct fmt::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } template auto format(const point& p, FormatContext& ctx) const { return format_to(ctx.out(), "({}, {})"_cf, p.x, p.y); } }; using namespace fmt::literals; std::string s = fmt::format("{}"_cf, point(4, 2)); Format string compilation can generate more binary code compared to the default API and is only recommended in places where formatting is a performance bottleneck. The same APIs support formatting at compile time e.g. in `constexpr` and `consteval` functions. Additionally there is an experimental `FMT_STATIC_FORMAT` that allows formatting into a string of the exact required size at compile time. Compile-time formatting works with built-in and user-defined formatters that have `constexpr` `format` methods. Example: template <> struct fmt::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } template constexpr auto format(const point& p, FormatContext& ctx) const { return format_to(ctx.out(), "({}, {})"_cf, p.x, p.y); } }; constexpr auto s = FMT_STATIC_FORMAT("{}", point(4, 2)); const char* cstr = s.c_str(); // Points the static string "(4, 2)". ::: operator""_cf ::: FMT_COMPILE ::: FMT_STATIC_FORMAT ## Terminal Colors and Text Styles `fmt/color.h` provides support for terminal color and text style output. ::: print(text_style, format_string, T&&...) ::: fg(detail::color_type) ::: bg(detail::color_type) ::: styled(const T&, text_style) ## System APIs ::: ostream ::: output_file(cstring_view, T...) ::: windows_error ## `std::ostream` Support `fmt/ostream.h` provides `std::ostream` support including formatting of user-defined types that have an overloaded insertion operator (`operator<<`). In order to make a type formattable via `std::ostream` you should provide a `formatter` specialization inherited from `ostream_formatter`: #include struct date { int year, month, day; friend std::ostream& operator<<(std::ostream& os, const date& d) { return os << d.year << '-' << d.month << '-' << d.day; } }; template <> struct fmt::formatter : ostream_formatter {}; std::string s = fmt::format("The date is {}", date{2012, 12, 9}); // s == "The date is 2012-12-9" ::: streamed(const T&) ::: print(std::ostream&, format_string, T&&...) ## Dynamic Argument Lists The header `fmt/args.h` provides `dynamic_format_arg_store`, a builder-like API that can be used to construct format argument lists dynamically. ::: dynamic_format_arg_store ## Safe `printf` The header `fmt/printf.h` provides `printf`-like formatting functionality. The following functions use [printf format string syntax](https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html) with the POSIX extension for positional arguments. Unlike their standard counterparts, the `fmt` functions are type-safe and throw an exception if an argument type doesn't match its format specification. ::: printf(string_view, const T&...) ::: fprintf(std::FILE*, string_view, const T&...) ::: sprintf(string_view, const T&...) ## Wide Strings The optional header `fmt/xchar.h` provides support for `wchar_t` and exotic character types. ::: wstring_view ::: wformat_context ::: to_wstring(const T&) ## Compatibility with C++20 `std::format` {fmt} implements nearly all of the [C++20 formatting library](https://en.cppreference.com/w/cpp/utility/format) with the following differences: - Names are defined in the `fmt` namespace instead of `std` to avoid collisions with standard library implementations. - Width calculation doesn't use grapheme clusterization. The latter has been implemented in a separate branch but hasn't been integrated yet. - The default floating-point representation in {fmt} uses the smallest precision that provides round-trip guarantees similarly to other languages like Java and Python. `std::format` is currently specified in terms of `std::to_chars` which tries to generate the smallest number of characters (ignoring redundant digits and sign in exponent) and may produce more decimal digits than necessary. ## Configuration Options {fmt} provides configuration via CMake options and preprocessor macros to enable or disable features and to optimize for binary size. For example, you can disable OS-specific APIs defined in `fmt/os.h` with `-DFMT_OS=OFF` when configuring CMake. ### CMake Options - **`FMT_OS`**: When set to `OFF`, disables OS-specific APIs (`fmt/os.h`). - **`FMT_UNICODE`**: When set to `OFF`, disables Unicode support on Windows/MSVC. Unicode support is always enabled on other platforms. ### Macros - **`FMT_HEADER_ONLY`**: Enables the header-only mode when defined. It is an alternative to using the `fmt::fmt-header-only` CMake target. Default: not defined. - **`FMT_USE_EXCEPTIONS`**: Disables the use of exceptions when set to `0`. Default: `1` (`0` if compiled with `-fno-exceptions`). - **`FMT_USE_LOCALE`**: When set to `0`, disables locale support. Default: `1` (`0` when `FMT_OPTIMIZE_SIZE > 1`). - **`FMT_CUSTOM_ASSERT_FAIL`**: When set to `1`, allows users to provide a custom `fmt::assert_fail` function which is called on assertion failures and, if exceptions are disabled, on runtime errors. Default: `0`. - **`FMT_BUILTIN_TYPES`**: When set to `0`, disables built-in handling of arithmetic and string types other than `int`. This reduces library size at the cost of per-call overhead. Default: `1`. - **`FMT_OPTIMIZE_SIZE`**: Controls binary size optimizations: - `0` - off (default) - `1` - disables locale support and applies some optimizations - `2` - disables some Unicode features, named arguments and applies more aggressive optimizations ### Binary Size Optimization To minimize the binary footprint of {fmt} as much as possible at the cost of some features, you can use the following configuration: - CMake options: - `FMT_OS=OFF` - Macros: - `FMT_BUILTIN_TYPES=0` - `FMT_OPTIMIZE_SIZE=2` ================================================ FILE: doc/fmt.css ================================================ :root { --md-primary-fg-color: #0050D0; } .md-grid { max-width: 960px; } @media (min-width: 400px) { .md-tabs { display: block; } } .docblock { border-left: .05rem solid var(--md-primary-fg-color); } .docblock-desc { margin-left: 1em; } code, pre > code.decl { white-space: pre-wrap; } code.decl > div { text-indent: -2ch; /* Negative indent to counteract the indent on the first line */ padding-left: 2ch; /* Add padding to the left to create an indent */ } .features-container { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; /* Center the items horizontally */ } .feature { flex: 1 1 calc(50% - 20px); /* Two columns with space between */ max-width: 600px; /* Set the maximum width for the feature boxes */ box-sizing: border-box; padding: 10px; overflow: hidden; /* Hide overflow content */ text-overflow: ellipsis; /* Handle text overflow */ white-space: normal; /* Allow text wrapping */ } .feature h2 { margin-top: 0px; font-weight: bold; } @media (max-width: 768px) { .feature { flex: 1 1 100%; /* Stack columns on smaller screens */ max-width: 100%; /* Allow full width on smaller screens */ white-space: normal; /* Allow text wrapping on smaller screens */ } } ================================================ FILE: doc/fmt.js ================================================ document$.subscribe(() => { hljs.highlightAll(), { language: 'c++' } }) ================================================ FILE: doc/get-started.md ================================================ # Get Started Compile and run {fmt} examples online with [Compiler Explorer]( https://godbolt.org/z/P7h6cd6o3). {fmt} is compatible with any build system. The next section describes its usage with CMake, while the [Build Systems](#build-systems) section covers the rest. ## CMake {fmt} provides three CMake targets: `fmt::fmt` for the standard compiled library, `fmt::fmt-module` for the C++ module library and `fmt::fmt-header-only` for the header-only library. It is recommended to use the compiled library or the module library for improved build times. There are three primary ways to use {fmt} with CMake: * **FetchContent**: Starting from CMake 3.11, you can use [`FetchContent`]( https://cmake.org/cmake/help/v3.30/module/FetchContent.html) to automatically download {fmt} as a dependency at configure time: include(FetchContent) FetchContent_Declare( fmt GIT_REPOSITORY https://github.com/fmtlib/fmt GIT_TAG e69e5f977d458f2650bb346dadf2ad30c5320281) # 10.2.1 FetchContent_MakeAvailable(fmt) target_link_libraries( fmt::fmt) * **Installed**: You can find and use an [installed](#installation) version of {fmt} in your `CMakeLists.txt` file as follows: find_package(fmt) target_link_libraries( fmt::fmt) * **Embedded**: You can add the {fmt} source tree to your project and include it in your `CMakeLists.txt` file: add_subdirectory(fmt) target_link_libraries( fmt::fmt) ### Alternative Targets In order to use the header-only target or the module target, simply substitute the `fmt::fmt` in the above steps with `fmt::fmt-header-only` or `fmt::fmt-module` accordingly. ## Installation ### Debian/Ubuntu To install {fmt} on Debian, Ubuntu, or any other Debian-based Linux distribution, use the following command: apt install libfmt-dev ### Homebrew Install {fmt} on macOS using [Homebrew](https://brew.sh/): brew install fmt ### Conda Install {fmt} on Linux, macOS, and Windows with [Conda]( https://docs.conda.io/en/latest/), using its [conda-forge package]( https://github.com/conda-forge/fmt-feedstock): conda install -c conda-forge fmt ### vcpkg Download and install {fmt} using the vcpkg package manager: git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install ./vcpkg install fmt ### Conan You can download and install {fmt} using the [Conan](https://conan.io/) package manager: conan install -r conancenter --requires="fmt/[*]" --build=missing ## Building from Source CMake works by generating native makefiles or project files that can be used in the compiler environment of your choice. The typical workflow starts with: mkdir build # Create a directory to hold the build output. cd build cmake .. # Generate native build scripts. run in the `fmt` repository. If you are on a Unix-like system, you should now see a Makefile in the current directory. Now you can build the library by running `make`. Once the library has been built you can invoke `make test` to run the tests. You can control generation of the make `test` target with the `FMT_TEST` CMake option. This can be useful if you include fmt as a subdirectory in your project but don't want to add fmt's tests to your `test` target. To build a shared library set the `BUILD_SHARED_LIBS` CMake variable to `TRUE`: cmake -DBUILD_SHARED_LIBS=TRUE .. To build a static library with position-independent code (e.g. for linking it into another shared library such as a Python extension), set the `CMAKE_POSITION_INDEPENDENT_CODE` CMake variable to `TRUE`: cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE .. After building the library you can install it on a Unix-like system by running `sudo make install`. ### Building the Docs To build the documentation you need the following software installed on your system: - [Python](https://www.python.org/) - [Doxygen](http://www.stack.nl/~dimitri/doxygen/) - [MkDocs](https://www.mkdocs.org/) with `mkdocs-material`, `mkdocstrings`, `pymdown-extensions` and `mike` First generate makefiles or project files using CMake as described in the previous section. Then compile the `doc` target/project, for example: make doc This will generate the HTML documentation in `doc/html`. ## Build Systems ### build2 You can use [build2](https://build2.org), a dependency manager and a build system, to use {fmt}. Currently this package is available in these package repositories: - for released and published versions. - for unreleased or custom versions. **Usage:** - `build2` package name: `fmt` - Library target name: `lib{fmt}` To make your `build2` project depend on `fmt`: - Add one of the repositories to your configurations, or in your `repositories.manifest`, if not already there: : role: prerequisite location: https://pkg.cppget.org/1/stable - Add this package as a dependency to your `manifest` file (example for version 10): depends: fmt ~10.0.0 - Import the target and use it as a prerequisite to your own target using `fmt` in the appropriate `buildfile`: import fmt = fmt%lib{fmt} lib{mylib} : cxx{**} ... $fmt Then build your project as usual with `b` or `bdep update`. ### Meson [Meson WrapDB](https://mesonbuild.com/Wrapdb-projects.html) includes an `fmt` package. **Usage:** - Install the `fmt` subproject from the WrapDB by running: meson wrap install fmt from the root of your project. - In your project's `meson.build` file, add an entry for the new subproject: fmt = subproject('fmt') fmt_dep = fmt.get_variable('fmt_dep') - Include the new dependency object to link with fmt: my_build_target = executable( 'name', 'src/main.cc', dependencies: [fmt_dep]) **Options:** If desired, {fmt} can be built as a static library, or as a header-only library. For a static build, use the following subproject definition: fmt = subproject('fmt', default_options: 'default_library=static') fmt_dep = fmt.get_variable('fmt_dep') For the header-only version, use: fmt = subproject('fmt', default_options: ['header-only=true']) fmt_dep = fmt.get_variable('fmt_header_only_dep') ### Android NDK {fmt} provides [Android.mk file]( https://github.com/fmtlib/fmt/blob/master/support/Android.mk) that can be used to build the library with [Android NDK]( https://developer.android.com/tools/sdk/ndk/index.html). ### Other To use the {fmt} library with any other build system, add `include/fmt/base.h`, `include/fmt/format.h`, `include/fmt/format-inl.h`, `src/format.cc` and optionally other headers from a [release archive]( https://github.com/fmtlib/fmt/releases) or the [git repository]( https://github.com/fmtlib/fmt) to your project, add `include` to include directories and make sure `src/format.cc` is compiled and linked with your code. ================================================ FILE: doc/index.md ================================================ --- hide: - navigation - toc --- # A modern formatting library

Safety

Inspired by Python's formatting facility, {fmt} provides a safe replacement for the printf family of functions. Errors in format strings, which are a common source of vulnerabilities in C, are reported at compile time. For example:

fmt::format("{:d}", "I am not a number");
will give a compile-time error because d is not a valid format specifier for strings. APIs like fmt::format prevent buffer overflow errors via automatic memory management.

→ Learn more

Extensibility

Formatting of most standard types, including all containers, dates, and times is supported out-of-the-box. For example:

fmt::print("{}", std::vector{1, 2, 3});
prints the vector in a JSON-like format:
[1, 2, 3]
You can make your own types formattable and even make compile-time checks work for them.

→ Learn more

Performance

{fmt} can be anywhere from tens of percent to 20-30 times faster than iostreams and sprintf, especially for numeric formatting. The library minimizes dynamic memory allocations and can optionally compile format strings to optimal code.

Unicode support

{fmt} provides portable Unicode support on major operating systems with UTF-8 and char strings. For example:

fmt::print("Слава Україні!");
will be printed correctly on Linux, macOS, and even Windows console, irrespective of the codepages.

The default is locale-independent, but you can opt into localized formatting and {fmt} makes it work with Unicode, addressing issues in the standard library.

Fast compilation

The library makes extensive use of type erasure to achieve fast compilation. fmt/base.h provides a subset of the API with minimal include dependencies and enough functionality to replace all uses of *printf.

Code using {fmt} is usually several times faster to compile than the equivalent iostreams code, and while printf compiles faster still, the gap is narrowing.

→ Learn more

Small binary footprint

Type erasure is also used to prevent template bloat, resulting in compact per-call binary code. For example, a call to fmt::print with a single argument is just a few instructions, comparable to printf despite adding runtime safety, and much smaller than the equivalent iostreams code.

The library itself has small binary footprint and some components such as floating-point formatting can be disabled to make it even smaller for resource-constrained devices.

Portability

{fmt} has a small self-contained codebase with the core consisting of just three headers and no external dependencies.

The library is highly portable and requires only a minimal subset of C++11 features which are available in GCC 4.9, Clang 3.6, MSVC 19.10 (2017) and later. Newer compiler and standard library features are used if available, and enable additional functionality.

Where possible, the output of formatting functions is consistent across platforms.

Open source

{fmt} is in the top hundred open-source C++ libraries on GitHub and has hundreds of all-time contributors.

The library is distributed under a permissive MIT license and is relied upon by many open-source projects, including Blender, PyTorch, Apple's FoundationDB, Windows Terminal, MongoDB, and others.

================================================ FILE: doc/python-license.txt ================================================ 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. ================================================ FILE: doc/syntax.md ================================================ # 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. 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 `}}`. The grammar for a replacement field is as follows:
replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
arg_id            ::= integer | identifier
integer           ::= digit+
digit             ::= "0"..."9"
identifier        ::= id_start id_continue*
id_start          ::= "a"..."z" | "A"..."Z" | "_"
id_continue       ::= id_start | digit
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. See also the [Format Specification Mini-Language](#format-specification-mini-language) 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: ```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}" ``` 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*. Most built-in types support a common formatting mini-language, which is described in the next section. 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:
format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
fill        ::= <a character other than '{' or '}'>
align       ::= "<" | ">" | "^"
sign        ::= "+" | "-" | " "
width       ::= integer | "{" [arg_id] "}"
precision   ::= integer | "{" [arg_id] "}"
type        ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" |
                "g" | "G" | "o" | "p" | "s" | "x" | "X" | "?"
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. The meaning of the various alignment options is as follows:
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.
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. The *sign* option is only valid for floating point and signed integer types, and can be one of the following:
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.
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. *width* is a decimal integer defining the minimum field width. If not specified, then the field width will be determined by the content. 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 *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. The `'L'` option uses the current locale setting to insert the appropriate number separator characters. This option is only valid for numeric types. Finally, the *type* determines how the data should be presented. The available string presentation types are:
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 available character presentation types are:
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 available integer presentation types are:
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'.
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. The available presentation types for floating-point values are:
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.
The available presentation types for pointers are:
Type Meaning
'p' Pointer format. This is the default type for pointers and may be omitted.
none The same as 'p'.
## Chrono Format Specifications Format specifications for chrono duration and time point types as well as `std::tm` have the following syntax:
chrono_format_spec ::= [[fill]align][width]["." precision][chrono_specs]
chrono_specs       ::= conversion_spec |
                       chrono_specs (conversion_spec | literal_char)
conversion_spec    ::= "%" [padding_modifier] [locale_modifier] chrono_type
literal_char       ::= <a character other than '{', '}' or '%'>
padding_modifier   ::= "-" | "_"  | "0"
locale_modifier    ::= "E" | "O"
chrono_type        ::= "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" |
                       "F" | "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" |
                       "n" | "p" | "q" | "Q" | "r" | "R" | "S" | "t" | "T" |
                       "u" | "U" | "V" | "w" | "W" | "x" | "X" | "y" | "Y" |
                       "z" | "Z" | "%"
Literal chars are copied unchanged to the output. Precision is valid only for `std::chrono::duration` types with a floating-point representation type. The available presentation types (*chrono_type*) are:
Type Meaning
'a' The abbreviated weekday name, e.g. "Sat". If the value does not contain a valid weekday, an exception of type format_error is thrown.
'A' The full weekday name, e.g. "Saturday". If the value does not contain a valid weekday, an exception of type format_error is thrown.
'b' The abbreviated month name, e.g. "Nov". If the value does not contain a valid month, an exception of type format_error is thrown.
'B' The full month name, e.g. "November". If the value does not contain a valid month, an exception of type format_error is thrown.
'c' The date and time representation, e.g. "Sat Nov 12 22:04:00 1955". The modified command %Ec produces the locale's alternate date and time representation.
'C' The year divided by 100 using floored division, e.g. "19". If the result is a single decimal digit, it is prefixed with 0. The modified command %EC produces the locale's alternative representation of the century.
'd' The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with 0. The modified command %Od produces the locale's alternative representation.
'D' Equivalent to %m/%d/%y, e.g. "11/12/55".
'e' The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with a space. The modified command %Oe produces the locale's alternative representation.
'F' Equivalent to %Y-%m-%d, e.g. "1955-11-12".
'g' The last two decimal digits of the ISO week-based year. If the result is a single digit it is prefixed by 0.
'G' The ISO week-based year as a decimal number. If the result is less than four digits it is left-padded with 0 to four digits.
'h' Equivalent to %b, e.g. "Nov".
'H' The hour (24-hour clock) as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OH produces the locale's alternative representation.
'I' The hour (12-hour clock) as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OI produces the locale's alternative representation.
'j' If the type being formatted is a specialization of duration, the decimal number of days without padding. Otherwise, the day of the year as a decimal number. Jan 1 is 001. If the result is less than three digits, it is left-padded with 0 to three digits.
'm' The month as a decimal number. Jan is 01. If the result is a single digit, it is prefixed with 0. The modified command %Om produces the locale's alternative representation.
'M' The minute as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OM produces the locale's alternative representation.
'n' A new-line character.
'p' The AM/PM designations associated with a 12-hour clock.
'q' The duration's unit suffix.
'Q' The duration's numeric value (as if extracted via .count()).
'r' The 12-hour clock time, e.g. "10:04:00 PM".
'R' Equivalent to %H:%M, e.g. "22:04".
'S' Seconds as a decimal number. If the number of seconds is less than 10, the result is prefixed with 0. If the precision of the input cannot be exactly represented with seconds, then the format is a decimal floating-point number with a fixed format and a precision matching that of the precision of the input (or to a microseconds precision if the conversion to floating-point decimal seconds cannot be made within 18 fractional digits). The modified command %OS produces the locale's alternative representation.
't' A horizontal-tab character.
'T' Equivalent to %H:%M:%S.
'u' The ISO weekday as a decimal number (1-7), where Monday is 1. The modified command %Ou produces the locale's alternative representation.
'U' The week number of the year as a decimal number. The first Sunday of the year is the first day of week 01. Days of the same year prior to that are in week 00. If the result is a single digit, it is prefixed with 0. The modified command %OU produces the locale's alternative representation.
'V' The ISO week-based week number as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OV produces the locale's alternative representation.
'w' The weekday as a decimal number (0-6), where Sunday is 0. The modified command %Ow produces the locale's alternative representation.
'W' The week number of the year as a decimal number. The first Monday of the year is the first day of week 01. Days of the same year prior to that are in week 00. If the result is a single digit, it is prefixed with 0. The modified command %OW produces the locale's alternative representation.
'x' The date representation, e.g. "11/12/55". The modified command %Ex produces the locale's alternate date representation.
'X' The time representation, e.g. "10:04:00". The modified command %EX produces the locale's alternate time representation.
'y' The last two decimal digits of the year. If the result is a single digit it is prefixed by 0. The modified command %Oy produces the locale's alternative representation. The modified command %Ey produces the locale's alternative representation of offset from %EC (year only).
'Y' The year as a decimal number. If the result is less than four digits it is left-padded with 0 to four digits. The modified command %EY produces the locale's alternative full year representation.
'z' The offset from UTC in the ISO 8601:2004 format. For example -0430 refers to 4 hours 30 minutes behind UTC. If the offset is zero, +0000 is used. The modified commands %Ez and %Oz insert a : between the hours and minutes: -04:30. If the offset information is not available, an exception of type format_error is thrown.
'Z' The time zone abbreviation. If the time zone abbreviation is not available, an exception of type format_error is thrown.
'%' A % character.
Specifiers that have a calendaric component such as `'d'` (the day of month) are valid only for `std::tm` and time points but not durations. The available padding modifiers (*padding_modifier*) are: | Type | Meaning | |-------|-----------------------------------------| | `'_'` | Pad a numeric result with spaces. | | `'-'` | Do not pad a numeric result string. | | `'0'` | Pad a numeric result string with zeros. | These modifiers are only supported for the `'H'`, `'I'`, `'M'`, `'S'`, `'U'`, `'V'`, `'W'`, `'Y'`, `'d'`, `'j'` and `'m'` presentation types. ## Range Format Specifications Format specifications for range types have the following syntax:
range_format_spec ::= ["n"][range_type][":" range_underlying_spec]
The `'n'` option formats the range without the opening and closing brackets. The available presentation types for `range_type` are: | Type | Meaning | |--------|------------------------------------------------------------| | none | Default format. | | `'s'` | String format. The range is formatted as a string. | | `'?⁠s'` | Debug format. The range is formatted as an escaped string. | If `range_type` is `'s'` or `'?s'`, the range element type must be a character type. The `'n'` option and `range_underlying_spec` are mutually exclusive with `'s'` and `'?s'`. The `range_underlying_spec` is parsed based on the formatter of the range's element type. By default, a range of characters or strings is printed escaped and quoted. But if any `range_underlying_spec` is provided (even if it is empty), then the characters or strings are printed according to the provided specification. Examples: ```c++ fmt::print("{}", std::vector{10, 20, 30}); // Output: [10, 20, 30] fmt::print("{::#x}", std::vector{10, 20, 30}); // Output: [0xa, 0x14, 0x1e] fmt::print("{}", std::vector{'h', 'e', 'l', 'l', 'o'}); // Output: ['h', 'e', 'l', 'l', 'o'] fmt::print("{:n}", std::vector{'h', 'e', 'l', 'l', 'o'}); // Output: 'h', 'e', 'l', 'l', 'o' fmt::print("{:s}", std::vector{'h', 'e', 'l', 'l', 'o'}); // Output: "hello" fmt::print("{:?s}", std::vector{'h', 'e', 'l', 'l', 'o', '\n'}); // Output: "hello\n" fmt::print("{::}", std::vector{'h', 'e', 'l', 'l', 'o'}); // Output: [h, e, l, l, o] fmt::print("{::d}", std::vector{'h', 'e', 'l', 'l', 'o'}); // Output: [104, 101, 108, 108, 111] fmt::print("{:n:f}", std::array{std::numbers::pi, std::numbers::e}); // Output: 3.141593, 2.718282 ``` ## Format Examples 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: ```c++ fmt::print( "┌{0:─^{2}}┐\n" "│{1: ^{2}}│\n" "└{0:─^{2}}┘\n", "", "Hello, world!", 20); ``` prints: ``` ┌────────────────────┐ │ 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" ``` ================================================ FILE: include/fmt/args.h ================================================ // Formatting library for C++ - dynamic argument lists // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_ARGS_H_ #define FMT_ARGS_H_ #ifndef FMT_MODULE # include // std::reference_wrapper # include // std::unique_ptr # include #endif #include "format.h" // std_string_view FMT_BEGIN_NAMESPACE namespace detail { template struct is_reference_wrapper : std::false_type {}; template struct is_reference_wrapper> : std::true_type {}; template auto unwrap(const T& v) -> const T& { return v; } template auto unwrap(const std::reference_wrapper& v) -> const T& { return static_cast(v); } // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC // 2022 (v17.10.0). // // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for // templates it doesn't complain about inability to deduce single translation // unit for placing vtable. So node is made a fake template. template struct node { virtual ~node() = default; std::unique_ptr> next; }; class dynamic_arg_list { template struct typed_node : node<> { T value; template FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} template FMT_CONSTEXPR typed_node(const basic_string_view& arg) : value(arg.data(), arg.size()) {} }; std::unique_ptr> head_; public: template auto push(const Arg& arg) -> const T& { auto new_node = std::unique_ptr>(new typed_node(arg)); auto& value = new_node->value; new_node->next = std::move(head_); head_ = std::move(new_node); return value; } }; } // namespace detail /** * A dynamic list of formatting arguments with storage. * * It can be implicitly converted into `fmt::basic_format_args` for passing * into type-erased formatting functions such as `fmt::vformat`. */ FMT_EXPORT template class dynamic_format_arg_store { private: using char_type = typename Context::char_type; template struct need_copy { static constexpr detail::type mapped_type = detail::mapped_type_constant::value; enum { value = !(detail::is_reference_wrapper::value || std::is_same>::value || std::is_same>::value || (mapped_type != detail::type::cstring_type && mapped_type != detail::type::string_type && mapped_type != detail::type::custom_type)) }; }; template using stored_t = conditional_t< std::is_convertible>::value && !detail::is_reference_wrapper::value, std::basic_string, T>; // Storage of basic_format_arg must be contiguous. std::vector> data_; std::vector> named_info_; // Storage of arguments not fitting into basic_format_arg must grow // without relocation because items in data_ refer to it. detail::dynamic_arg_list dynamic_args_; friend class basic_format_args; auto data() const -> const basic_format_arg* { return named_info_.empty() ? data_.data() : data_.data() + 1; } template void emplace_arg(const T& arg) { data_.emplace_back(arg); } template void emplace_arg(const named_arg& arg) { if (named_info_.empty()) data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); data_.emplace_back(detail::unwrap(arg.value)); auto pop_one = [](std::vector>* data) { data->pop_back(); }; std::unique_ptr>, decltype(pop_one)> guard{&data_, pop_one}; named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); data_[0] = {named_info_.data(), named_info_.size()}; guard.release(); } public: constexpr dynamic_format_arg_store() = default; operator basic_format_args() const { return basic_format_args(data(), static_cast(data_.size()), !named_info_.empty()); } /** * Adds an argument into the dynamic store for later passing to a formatting * function. * * Note that custom types and string types (but not string views) are copied * into the store dynamically allocating memory if necessary. * * **Example**: * * fmt::dynamic_format_arg_store store; * store.push_back(42); * store.push_back("abc"); * store.push_back(1.5f); * std::string result = fmt::vformat("{} and {} and {}", store); */ template void push_back(const T& arg) { if FMT_CONSTEXPR20 (need_copy::value) emplace_arg(dynamic_args_.push>(arg)); else emplace_arg(detail::unwrap(arg)); } /** * Adds a reference to the argument into the dynamic store for later passing * to a formatting function. * * **Example**: * * fmt::dynamic_format_arg_store store; * char band[] = "Rolling Stones"; * store.push_back(std::cref(band)); * band[9] = 'c'; // Changing str affects the output. * std::string result = fmt::vformat("{}", store); * // result == "Rolling Scones" */ template void push_back(std::reference_wrapper arg) { static_assert( need_copy::value, "objects of built-in types and string views are always copied"); emplace_arg(arg.get()); } /** * Adds named argument into the dynamic store for later passing to a * formatting function. `std::reference_wrapper` is supported to avoid * copying of the argument. The name is always copied into the store. */ template void push_back(const named_arg& arg) { const char_type* arg_name = dynamic_args_.push>(arg.name).c_str(); if FMT_CONSTEXPR20 (need_copy::value) { emplace_arg( fmt::arg(arg_name, dynamic_args_.push>(arg.value))); } else { emplace_arg(fmt::arg(arg_name, arg.value)); } } /// Erase all elements from the store. void clear() { data_.clear(); named_info_.clear(); dynamic_args_ = {}; } /// Reserves space to store at least `new_cap` arguments including /// `new_cap_named` named arguments. void reserve(size_t new_cap, size_t new_cap_named) { FMT_ASSERT(new_cap >= new_cap_named, "set of arguments includes set of named arguments"); data_.reserve(new_cap); named_info_.reserve(new_cap_named); } /// Returns the number of elements in the store. auto size() const noexcept -> size_t { return data_.size(); } }; FMT_END_NAMESPACE #endif // FMT_ARGS_H_ ================================================ FILE: include/fmt/base.h ================================================ // Formatting library for C++ - the base API for char/UTF-8 // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_BASE_H_ #define FMT_BASE_H_ #if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) # define FMT_MODULE #endif #ifndef FMT_MODULE # include // CHAR_BIT # include // FILE # include // memcmp # include // std::enable_if #endif // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 120101 // Detect compiler versions. #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else # define FMT_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #else # define FMT_GCC_VERSION 0 #endif #if defined(__ICL) # define FMT_ICC_VERSION __ICL #elif defined(__INTEL_COMPILER) # define FMT_ICC_VERSION __INTEL_COMPILER #else # define FMT_ICC_VERSION 0 #endif #if defined(_MSC_VER) # define FMT_MSC_VERSION _MSC_VER #else # define FMT_MSC_VERSION 0 #endif // Detect standard library versions. #ifdef _GLIBCXX_RELEASE # define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE #else # define FMT_GLIBCXX_RELEASE 0 #endif #ifdef _LIBCPP_VERSION # define FMT_LIBCPP_VERSION _LIBCPP_VERSION #else # define FMT_LIBCPP_VERSION 0 #endif #ifdef _MSVC_LANG # define FMT_CPLUSPLUS _MSVC_LANG #else # define FMT_CPLUSPLUS __cplusplus #endif // Detect __has_*. #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) #else # define FMT_HAS_FEATURE(x) 0 #endif #ifdef __has_include # define FMT_HAS_INCLUDE(x) __has_include(x) #else # define FMT_HAS_INCLUDE(x) 0 #endif #ifdef __has_builtin # define FMT_HAS_BUILTIN(x) __has_builtin(x) #else # define FMT_HAS_BUILTIN(x) 0 #endif #ifdef __has_cpp_attribute # define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) // Detect C++14 relaxed constexpr. #ifdef FMT_USE_CONSTEXPR // Use the provided definition. #elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L // GCC only allows constexpr member functions in non-literal types since 7.2: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297. # define FMT_USE_CONSTEXPR 1 #elif FMT_ICC_VERSION # define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628 #elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 # define FMT_USE_CONSTEXPR 1 #else # define FMT_USE_CONSTEXPR 0 #endif #if FMT_USE_CONSTEXPR # define FMT_CONSTEXPR constexpr #else # define FMT_CONSTEXPR #endif // Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. #ifdef FMT_USE_CONSTEVAL // Use the provided definition. #elif !defined(__cpp_lib_is_constant_evaluated) # define FMT_USE_CONSTEVAL 0 #elif FMT_CPLUSPLUS < 201709L # define FMT_USE_CONSTEVAL 0 #elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10 # define FMT_USE_CONSTEVAL 0 #elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000 # define FMT_USE_CONSTEVAL 0 #elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L # define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14. #elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1940 # define FMT_USE_CONSTEVAL 0 // consteval is broken in some MSVC2022 versions. #elif defined(__cpp_consteval) # define FMT_USE_CONSTEVAL 1 #elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101 # define FMT_USE_CONSTEVAL 1 #else # define FMT_USE_CONSTEVAL 0 #endif #if FMT_USE_CONSTEVAL # define FMT_CONSTEVAL consteval # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEVAL # define FMT_CONSTEXPR20 #endif // Check if exceptions are disabled. #ifdef FMT_USE_EXCEPTIONS // Use the provided definition. #elif defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_USE_EXCEPTIONS 0 #elif defined(__clang__) && !defined(__cpp_exceptions) # define FMT_USE_EXCEPTIONS 0 #elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS # define FMT_USE_EXCEPTIONS 0 #else # define FMT_USE_EXCEPTIONS 1 #endif #if FMT_USE_EXCEPTIONS # define FMT_TRY try # define FMT_CATCH(x) catch (x) #else # define FMT_TRY if (true) # define FMT_CATCH(x) if (false) #endif #ifdef FMT_NO_UNIQUE_ADDRESS // Use the provided definition. #elif FMT_CPLUSPLUS < 202002L // Not supported. #elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) # define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] // VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). #elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION # define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #endif #ifndef FMT_NO_UNIQUE_ADDRESS # define FMT_NO_UNIQUE_ADDRESS #endif #if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) # define FMT_FALLTHROUGH [[fallthrough]] #elif defined(__clang__) # define FMT_FALLTHROUGH [[clang::fallthrough]] #elif FMT_GCC_VERSION >= 700 && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) # define FMT_FALLTHROUGH [[gnu::fallthrough]] #else # define FMT_FALLTHROUGH #endif // Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. #if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__) # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #endif #ifdef FMT_NODISCARD // Use the provided definition. #elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard) # define FMT_NODISCARD [[nodiscard]] #else # define FMT_NODISCARD #endif #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_VISIBILITY(value) __attribute__((visibility(value))) #else # define FMT_VISIBILITY(value) #endif // Detect pragmas. #define FMT_PRAGMA_IMPL(x) _Pragma(#x) #if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER) // Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884 // and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582. # define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x) #else # define FMT_PRAGMA_GCC(x) #endif #if FMT_CLANG_VERSION # define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x) #else # define FMT_PRAGMA_CLANG(x) #endif // Enable minimal optimizations for more compact code in debug mode. FMT_PRAGMA_GCC(push_options) #if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) FMT_PRAGMA_GCC(optimize("Og")) #endif #ifdef FMT_DEPRECATED // Use the provided definition. #elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) # define FMT_DEPRECATED [[deprecated]] #else # define FMT_DEPRECATED /* deprecated */ #endif #ifdef FMT_ALWAYS_INLINE // Use the provided definition. #elif FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) #else # define FMT_ALWAYS_INLINE inline #endif // A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. #ifdef NDEBUG # define FMT_INLINE FMT_ALWAYS_INLINE #else # define FMT_INLINE inline #endif #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ inline namespace v12 { # define FMT_END_NAMESPACE \ } \ } #endif #ifndef FMT_EXPORT # define FMT_EXPORT # define FMT_BEGIN_EXPORT # define FMT_END_EXPORT #endif #ifdef _WIN32 # define FMT_WIN32 1 #else # define FMT_WIN32 0 #endif #if !defined(FMT_HEADER_ONLY) && FMT_WIN32 # if defined(FMT_LIB_EXPORT) # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) # endif #elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) # define FMT_API FMT_VISIBILITY("default") #endif #ifndef FMT_API # define FMT_API #endif #ifndef FMT_OPTIMIZE_SIZE # define FMT_OPTIMIZE_SIZE 0 #endif // FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher // per-call binary size by passing built-in types through the extension API. #ifndef FMT_BUILTIN_TYPES # define FMT_BUILTIN_TYPES 1 #endif #define FMT_APPLY_VARIADIC(expr) \ using unused = int[]; \ (void)unused { 0, (expr, 0)... } FMT_BEGIN_NAMESPACE // Implementations of enable_if_t and other metafunctions for older systems. template using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; template using bool_constant = std::integral_constant; template using remove_reference_t = typename std::remove_reference::type; template using remove_const_t = typename std::remove_const::type; template using remove_cvref_t = typename std::remove_cv>::type; template using make_unsigned_t = typename std::make_unsigned::type; template using underlying_t = typename std::underlying_type::type; template using decay_t = typename std::decay::type; using nullptr_t = decltype(nullptr); using ullong = unsigned long long; #if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION // A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context. template struct void_t_impl { using type = void; }; template using void_t = typename void_t_impl::type; #else template using void_t = void; #endif struct monostate { constexpr monostate() {} }; // An enable_if helper to be used in template parameters which results in much // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed // to workaround a bug in MSVC 2019 (see #1140 and #1186). #ifdef FMT_DOC # define FMT_ENABLE_IF(...) #else # define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 #endif template constexpr auto min_of(T a, T b) -> T { return a < b ? a : b; } template constexpr auto max_of(T a, T b) -> T { return a > b ? a : b; } FMT_NORETURN FMT_API void assert_fail(const char* file, int line, const char* message); namespace detail { // Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. // (void)var does not work on many Intel compilers. template FMT_CONSTEXPR void ignore_unused(const T&...) {} constexpr auto is_constant_evaluated(bool default_value = false) noexcept -> bool { // Workaround for incompatibility between clang 14 and libstdc++ consteval-based // std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247. #if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) ignore_unused(default_value); return __builtin_is_constant_evaluated(); #elif defined(__cpp_lib_is_constant_evaluated) ignore_unused(default_value); return std::is_constant_evaluated(); #else return default_value; #endif } #ifdef FMT_ASSERT // Use the provided definition. #elif defined(NDEBUG) // FMT_ASSERT is not empty to avoid -Wempty-body. # define FMT_ASSERT(condition, message) \ fmt::detail::ignore_unused((condition), (message)) #else # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ? (void)0 \ : ::fmt::assert_fail(__FILE__, __LINE__, (message))) #endif #ifdef FMT_USE_INT128 // Use the provided definition. #elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ !(FMT_CLANG_VERSION && FMT_MSC_VERSION) # define FMT_USE_INT128 1 using native_int128 = __int128_t; using native_uint128 = __uint128_t; inline auto map(native_int128 x) -> native_int128 { return x; } inline auto map(native_uint128 x) -> native_uint128 { return x; } #else # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 // Fallbacks to reduce conditional compilation and SFINAE. enum class native_int128 {}; enum class native_uint128 {}; inline auto map(native_int128) -> monostate { return {}; } inline auto map(native_uint128) -> monostate { return {}; } #endif // Casts a nonnegative integer to unsigned. template FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); return static_cast>(value); } template using unsigned_char = conditional_t; // A heuristic to detect std::string and std::[experimental::]string_view. // It is mainly used to avoid dependency on <[experimental/]string_view>. template struct is_std_string_like : std::false_type {}; template struct is_std_string_like().find_first_of( typename T::value_type(), 0))>> : std::is_convertible().data()), const typename T::value_type*> {}; // Check if the literal encoding is UTF-8. enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' }; enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; #ifndef FMT_UNICODE # define FMT_UNICODE 1 #endif static_assert(!FMT_UNICODE || use_utf8, "Unicode support requires compiling with /utf-8"); template constexpr auto narrow(T*) -> char* { return nullptr; } constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* { return s; } template FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int { if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); for (; n != 0; ++s1, ++s2, --n) { if (*s1 < *s2) return -1; if (*s1 > *s2) return 1; } return 0; } namespace adl { using namespace std; template auto invoke_back_inserter() -> decltype(back_inserter(std::declval())); } // namespace adl template struct is_back_insert_iterator : std::false_type {}; template struct is_back_insert_iterator< It, bool_constant()), It>::value>> : std::true_type {}; // Extracts a reference to the container from *insert_iterator. template inline FMT_CONSTEXPR auto get_container(OutputIt it) -> typename OutputIt::container_type& { struct accessor : OutputIt { constexpr accessor(OutputIt base) : OutputIt(base) {} using OutputIt::container; }; return *accessor(it).container; } } // namespace detail // Parsing-related public API and forward declarations. FMT_BEGIN_EXPORT /** * An implementation of `std::basic_string_view` for pre-C++17 providing a * subset of the API. `fmt::basic_string_view` is used in the public API even * if `std::basic_string_view` is available to prevent issues when a library is * compiled with a different `-std` option than the client code (which is not * recommended). */ template class basic_string_view { private: const Char* data_; size_t size_; public: using value_type = Char; using iterator = const Char*; constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} constexpr basic_string_view(const Char* s, size_t count) noexcept : data_(s), size_(count) {} #if FMT_GCC_VERSION FMT_ALWAYS_INLINE #endif FMT_CONSTEXPR basic_string_view(const Char* s) : data_(s) { #if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION if (std::is_same::value && !detail::is_constant_evaluated()) { size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr. return; } #endif size_t len = 0; while (*s++) ++len; size_ = len; } template < typename S, FMT_ENABLE_IF(detail::is_std_string_like::value&& // std::is_same::value)> constexpr basic_string_view(const S& s) noexcept : data_(s.data()), size_(s.size()) {} constexpr auto data() const noexcept -> const Char* { return data_; } constexpr auto size() const noexcept -> size_t { return size_; } constexpr auto begin() const noexcept -> iterator { return data_; } constexpr auto end() const noexcept -> iterator { return data_ + size_; } constexpr auto operator[](size_t pos) const noexcept -> const Char& { return data_[pos]; } FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { data_ += n; size_ -= n; } FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept -> bool { return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; } FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { return size_ >= 1 && *data_ == c; } FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { return starts_with(basic_string_view(s)); } FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { int cmp = detail::compare(data_, other.data_, min_of(size_, other.size_)); if (cmp != 0) return cmp; return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); } FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) == 0; } friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) != 0; } friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) < 0; } friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) <= 0; } friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) > 0; } friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) >= 0; } }; 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; // Longer aliases for C++20 compatibility. template using basic_format_parse_context = parse_context; using format_parse_context = parse_context; template using basic_format_context = conditional_t::value, context, generic_context>; using format_context = context; template using buffered_context = conditional_t::value, context, generic_context, Char>>; template class basic_format_arg; template class basic_format_args; // A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). using format_args = basic_format_args; // A formatter for objects of type T. template struct formatter { // A deleted default constructor indicates a disabled formatter. formatter() = delete; }; template struct locking : std::false_type {}; /// Reports a format error at compile time or, via a `format_error` exception, /// at runtime. // This function is intentionally not constexpr to give a compile-time error. FMT_NORETURN FMT_API void report_error(const char* message); enum class presentation_type : unsigned char { // Common specifiers: none = 0, debug = 1, // '?' string = 2, // 's' (string, bool) // Integral, bool and character specifiers: dec = 3, // 'd' hex, // 'x' or 'X' oct, // 'o' bin, // 'b' or 'B' chr, // 'c' // String and pointer specifiers: pointer = 3, // 'p' // Floating-point specifiers: exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation) fixed, // 'f' or 'F' general, // 'g' or 'G' hexfloat // 'a' or 'A' }; enum class align { none, left, right, center, numeric }; enum class sign { none, minus, plus, space }; enum class arg_id_kind { none, index, name }; // Basic format specifiers for built-in and string types. class basic_specs { private: // Data is arranged as follows: // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |type |align| w | p | s |u|#|L| f | unused | // +-----+-----+---+---+---+-+-+-+-----+---------------------------+ // // w - dynamic width info // p - dynamic precision info // s - sign // u - uppercase (e.g. 'X' for 'x') // # - alternate form ('#') // L - localized // f - fill size // // Bitfields are not used because of compiler bugs such as gcc bug 61414. enum : unsigned { type_mask = 0x00007, align_mask = 0x00038, width_mask = 0x000C0, precision_mask = 0x00300, sign_mask = 0x00C00, uppercase_mask = 0x01000, alternate_mask = 0x02000, localized_mask = 0x04000, fill_size_mask = 0x38000, align_shift = 3, width_shift = 6, precision_shift = 8, sign_shift = 10, fill_size_shift = 15, max_fill_size = 4 }; unsigned data_ = 1 << fill_size_shift; static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, ""); // Character (code unit) type is erased to prevent template bloat. char fill_data_[max_fill_size] = {' '}; FMT_CONSTEXPR void set_fill_size(size_t size) { data_ = (data_ & ~fill_size_mask) | (unsigned(size) << fill_size_shift); } public: constexpr auto type() const -> presentation_type { return static_cast(data_ & type_mask); } FMT_CONSTEXPR void set_type(presentation_type t) { data_ = (data_ & ~type_mask) | unsigned(t); } constexpr auto align() const -> align { return static_cast((data_ & align_mask) >> align_shift); } FMT_CONSTEXPR void set_align(fmt::align a) { data_ = (data_ & ~align_mask) | (unsigned(a) << align_shift); } constexpr auto dynamic_width() const -> arg_id_kind { return static_cast((data_ & width_mask) >> width_shift); } FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) { data_ = (data_ & ~width_mask) | (unsigned(w) << width_shift); } FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind { return static_cast((data_ & precision_mask) >> precision_shift); } FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) { data_ = (data_ & ~precision_mask) | (unsigned(p) << precision_shift); } constexpr auto dynamic() const -> bool { return (data_ & (width_mask | precision_mask)) != 0; } constexpr auto sign() const -> sign { return static_cast((data_ & sign_mask) >> sign_shift); } FMT_CONSTEXPR void set_sign(fmt::sign s) { data_ = (data_ & ~sign_mask) | (unsigned(s) << sign_shift); } constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; } FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; } constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; } FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; } FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; } constexpr auto localized() const -> bool { return (data_ & localized_mask) != 0; } FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; } constexpr auto fill_size() const -> size_t { return (data_ & fill_size_mask) >> fill_size_shift; } template ::value)> constexpr auto fill() const -> const Char* { return fill_data_; } template ::value)> constexpr auto fill() const -> const Char* { return nullptr; } template constexpr auto fill_unit() const -> Char { using uchar = unsigned char; return Char(uchar(fill_data_[0]) | uchar(fill_data_[1]) << 8 | uchar(fill_data_[2]) << 16); } FMT_CONSTEXPR void set_fill(char c) { fill_data_[0] = c; set_fill_size(1); } template FMT_CONSTEXPR void set_fill(basic_string_view s) { auto size = s.size(); set_fill_size(size); if (size == 1) { unsigned uchar = static_cast>(s[0]); fill_data_[0] = char(uchar); fill_data_[1] = char(uchar >> 8); fill_data_[2] = char(uchar >> 16); return; } FMT_ASSERT(size <= max_fill_size, "invalid fill"); for (size_t i = 0; i < size; ++i) fill_data_[i & 3] = char(s[i]); } FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) { set_fill_size(specs.fill_size()); for (size_t i = 0; i < max_fill_size; ++i) fill_data_[i] = specs.fill_data_[i]; } }; // Format specifiers for built-in and string types. struct format_specs : basic_specs { int width; int precision; constexpr format_specs() : width(0), precision(-1) {} }; /** * Parsing context consisting of a format string range being parsed and an * argument counter for automatic indexing. */ template class parse_context { private: basic_string_view fmt_; int next_arg_id_; enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 }; FMT_CONSTEXPR void do_check_arg_id(int arg_id); public: using char_type = Char; using iterator = const Char*; constexpr explicit parse_context(basic_string_view fmt, int next_arg_id = 0) : fmt_(fmt), next_arg_id_(next_arg_id) {} /// Returns an iterator to the beginning of the format string range being /// parsed. constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); } /// Returns an iterator past the end of the format string range being parsed. constexpr auto end() const noexcept -> iterator { return fmt_.end(); } /// Advances the begin iterator to `it`. FMT_CONSTEXPR void advance_to(iterator it) { fmt_.remove_prefix(detail::to_unsigned(it - begin())); } /// Reports an error if using the manual argument indexing; otherwise returns /// the next argument index and switches to the automatic indexing. FMT_CONSTEXPR auto next_arg_id() -> int { if (next_arg_id_ < 0) { report_error("cannot switch from manual to automatic argument indexing"); return 0; } int id = next_arg_id_++; do_check_arg_id(id); return id; } /// Reports an error if using the automatic argument indexing; otherwise /// switches to the manual indexing. FMT_CONSTEXPR void check_arg_id(int id) { if (next_arg_id_ > 0) { report_error("cannot switch from automatic to manual argument indexing"); return; } next_arg_id_ = -1; do_check_arg_id(id); } FMT_CONSTEXPR void check_arg_id(basic_string_view) { next_arg_id_ = -1; } FMT_CONSTEXPR void check_dynamic_spec(int arg_id); }; #ifndef FMT_USE_LOCALE # define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) #endif // A type-erased reference to std::locale to avoid the heavy include. class locale_ref { #if FMT_USE_LOCALE private: const void* locale_; // A type-erased pointer to std::locale. public: constexpr locale_ref() : locale_(nullptr) {} template locale_ref(const Locale& loc) : locale_(&loc) { // Check if std::isalpha is found via ADL to reduce the chance of misuse. detail::ignore_unused(sizeof(isalpha('x', loc))); } inline explicit operator bool() const noexcept { return locale_ != nullptr; } #else public: inline explicit operator bool() const noexcept { return false; } #endif // FMT_USE_LOCALE public: template auto get() const -> Locale; }; FMT_END_EXPORT namespace detail { // Specifies if `T` is a code unit type. template struct is_code_unit : std::false_type {}; template <> struct is_code_unit : std::true_type {}; template <> struct is_code_unit : std::true_type {}; template <> struct is_code_unit : std::true_type {}; template <> struct is_code_unit : std::true_type {}; #ifdef __cpp_char8_t template <> struct is_code_unit : bool_constant {}; #endif // Constructs fmt::basic_string_view from types implicitly convertible // to it, deducing Char. Explicitly convertible types such as the ones returned // from FMT_STRING are intentionally excluded. template ::value)> constexpr auto to_string_view(const Char* s) -> basic_string_view { return s; } template ::value)> constexpr auto to_string_view(const T& s) -> basic_string_view { return s; } template constexpr auto to_string_view(basic_string_view s) -> basic_string_view { return s; } template struct has_to_string_view : std::false_type {}; // detail:: is intentional since to_string_view is not an extension point. template struct has_to_string_view< T, void_t()))>> : std::true_type {}; /// String's character (code unit) type. detail:: is intentional to prevent ADL. template ()))> using char_t = typename V::value_type; enum class type { none_type, // Integer types should go first, int_type, uint_type, long_long_type, ulong_long_type, int128_type, uint128_type, bool_type, char_type, last_integer_type = char_type, // followed by floating-point types. float_type, double_type, long_double_type, last_numeric_type = long_double_type, cstring_type, string_type, pointer_type, custom_type }; // Maps core type T to the corresponding type enum constant. template struct type_constant : std::integral_constant {}; #define FMT_TYPE_CONSTANT(Type, constant) \ template \ struct type_constant \ : std::integral_constant {} FMT_TYPE_CONSTANT(int, int_type); FMT_TYPE_CONSTANT(unsigned, uint_type); FMT_TYPE_CONSTANT(long long, long_long_type); FMT_TYPE_CONSTANT(ullong, ulong_long_type); FMT_TYPE_CONSTANT(native_int128, int128_type); FMT_TYPE_CONSTANT(native_uint128, uint128_type); FMT_TYPE_CONSTANT(bool, bool_type); FMT_TYPE_CONSTANT(Char, char_type); FMT_TYPE_CONSTANT(float, float_type); FMT_TYPE_CONSTANT(double, double_type); FMT_TYPE_CONSTANT(long double, long_double_type); FMT_TYPE_CONSTANT(const Char*, cstring_type); FMT_TYPE_CONSTANT(basic_string_view, string_type); FMT_TYPE_CONSTANT(const void*, pointer_type); constexpr auto is_integral_type(type t) -> bool { return t > type::none_type && t <= type::last_integer_type; } constexpr auto is_arithmetic_type(type t) -> bool { return t > type::none_type && t <= type::last_numeric_type; } constexpr auto set(type rhs) -> int { return 1 << int(rhs); } constexpr auto in(type t, int set) -> bool { return ((set >> int(t)) & 1) != 0; } // Bitsets of types. enum { sint_set = set(type::int_type) | set(type::long_long_type) | set(type::int128_type), uint_set = set(type::uint_type) | set(type::ulong_long_type) | set(type::uint128_type), bool_set = set(type::bool_type), char_set = set(type::char_type), float_set = set(type::float_type) | set(type::double_type) | set(type::long_double_type), string_set = set(type::string_type), cstring_set = set(type::cstring_type), pointer_set = set(type::pointer_type) }; struct view {}; template struct is_view : std::false_type {}; template struct is_view> : std::is_base_of {}; // DEPRECATED! named_arg will be moved to the fmt namespace. template struct named_arg; template struct is_named_arg : std::false_type {}; template struct is_static_named_arg : std::false_type {}; template struct is_named_arg> : std::true_type {}; template struct named_arg : view { const Char* name; const T& value; named_arg(const Char* n, const T& v) : name(n), value(v) {} static_assert(!is_named_arg::value, "nested named arguments"); }; template constexpr auto count() -> int { return B ? 1 : 0; } template constexpr auto count() -> int { return (B1 ? 1 : 0) + count(); } template constexpr auto count_named_args() -> int { return count::value...>(); } template constexpr auto count_static_named_args() -> int { return count::value...>(); } template struct named_arg_info { const Char* name; int id; }; // named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13. template FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, int named_arg_index, basic_string_view arg_name) { for (int i = 0; i < named_arg_index; ++i) { if (named_args[i].name == arg_name) report_error("duplicate named arg"); } } template ::value)> void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { ++arg_index; } template ::value)> void init_named_arg(named_arg_info* named_args, int& arg_index, int& named_arg_index, const T& arg) { check_for_duplicate(named_args, named_arg_index, arg.name); named_args[named_arg_index++] = {arg.name, arg_index++}; } template ::value)> FMT_CONSTEXPR void init_static_named_arg(named_arg_info*, int& arg_index, int&) { ++arg_index; } template ::value)> FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, int& arg_index, int& named_arg_index) { check_for_duplicate(named_args, named_arg_index, T::name); named_args[named_arg_index++] = {T::name, arg_index++}; } // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES }; using long_type = conditional_t; using ulong_type = conditional_t; template using format_as_result = remove_cvref_t()))>; template using format_as_member_result = remove_cvref_t::format_as(std::declval()))>; template struct use_format_as : std::false_type {}; // format_as member is only used to avoid injection into the std namespace. template struct use_format_as_member : std::false_type {}; // Only map owning types because mapping views can be unsafe. template struct use_format_as< T, bool_constant>::value>> : std::true_type {}; template struct use_format_as_member< T, bool_constant>::value>> : std::true_type {}; template > using use_formatter = bool_constant<(std::is_class::value || std::is_enum::value || std::is_union::value || std::is_array::value) && !has_to_string_view::value && !is_named_arg::value && !use_format_as::value && !use_format_as_member::value>; template > auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) -> decltype(formatter().format(*p, *ctx), std::true_type()); template auto has_formatter_impl(...) -> std::false_type; // T can be const-qualified to check if it is const-formattable. template constexpr auto has_formatter() -> bool { return decltype(has_formatter_impl(static_cast(nullptr)))::value; } // Maps formatting argument types to natively supported types or user-defined // types with formatters. Returns void on errors to be SFINAE-friendly. template struct type_mapper { static auto map(signed char) -> int; static auto map(unsigned char) -> unsigned; static auto map(short) -> int; static auto map(unsigned short) -> unsigned; static auto map(int) -> int; static auto map(unsigned) -> unsigned; static auto map(long) -> long_type; static auto map(unsigned long) -> ulong_type; static auto map(long long) -> long long; static auto map(ullong) -> ullong; static auto map(native_int128) -> native_int128; static auto map(native_uint128) -> native_uint128; static auto map(bool) -> bool; template ::value)> static auto map(T) -> conditional_t< std::is_same::value || std::is_same::value, Char, void>; static auto map(float) -> float; static auto map(double) -> double; static auto map(long double) -> long double; static auto map(Char*) -> const Char*; static auto map(const Char*) -> const Char*; template , FMT_ENABLE_IF(!std::is_pointer::value)> static auto map(const T&) -> conditional_t::value, basic_string_view, void>; static auto map(void*) -> const void*; static auto map(const void*) -> const void*; static auto map(volatile void*) -> const void*; static auto map(const volatile void*) -> const void*; static auto map(nullptr_t) -> const void*; template ::value || std::is_member_pointer::value)> static auto map(const T&) -> void; template ::value)> static auto map(const T& x) -> decltype(map(format_as(x))); template ::value)> static auto map(const T& x) -> decltype(map(formatter::format_as(x))); template ::value)> static auto map(T&) -> conditional_t(), T&, void>; template ::value)> static auto map(const T& named_arg) -> decltype(map(named_arg.value)); }; // detail:: is used to workaround a bug in MSVC 2017. template using mapped_t = decltype(detail::type_mapper::map(std::declval())); // A type constant after applying type_mapper. template using mapped_type_constant = type_constant, Char>; template ::value> using stored_type_constant = std::integral_constant< type, Context::builtin_types || TYPE == type::int_type ? TYPE : type::custom_type>; // A parse context with extra data used only in compile-time checks. template class compile_parse_context : public parse_context { private: int num_args_; const type* types_; using base = parse_context; public: constexpr explicit compile_parse_context(basic_string_view fmt, int num_args, const type* types, int next_arg_id = 0) : base(fmt, next_arg_id), num_args_(num_args), types_(types) {} constexpr auto num_args() const -> int { return num_args_; } constexpr auto arg_type(int id) const -> type { return types_[id]; } FMT_CONSTEXPR auto next_arg_id() -> int { int id = base::next_arg_id(); if (id >= num_args_) report_error("argument not found"); return id; } FMT_CONSTEXPR void check_arg_id(int id) { base::check_arg_id(id); if (id >= num_args_) report_error("argument not found"); } using base::check_arg_id; FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) report_error("width/precision is not integer"); } }; // An argument reference. template union arg_ref { FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {} FMT_CONSTEXPR arg_ref(basic_string_view n) : name(n) {} int index; basic_string_view name; }; // Format specifiers with width and precision resolved at formatting rather // than parsing time to allow reusing the same parsed specifiers with // different sets of arguments (precompilation of format strings). template struct dynamic_format_specs : format_specs { arg_ref width_ref; arg_ref precision_ref; }; // Converts a character to ASCII. Returns '\0' on conversion failure. template ::value)> constexpr auto to_ascii(Char c) -> char { return c <= 0xff ? char(c) : '\0'; } // Returns the number of code units in a code point or 1 on error. template FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if FMT_CONSTEXPR20 (sizeof(Char) != 1) return 1; auto c = static_cast(*begin); return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; } // Parses the range [begin, end) as an unsigned integer. This function assumes // that the range is non-empty and the first character is a digit. template FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, int error_value) noexcept -> int { FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); unsigned value = 0, prev = 0; auto p = begin; do { prev = value; value = value * 10 + unsigned(*p - '0'); ++p; } while (p != end && '0' <= *p && *p <= '9'); auto num_digits = p - begin; begin = p; int digits10 = int(sizeof(int) * CHAR_BIT * 3 / 10); if (num_digits <= digits10) return int(value); // Check for overflow. unsigned max = INT_MAX; return num_digits == digits10 + 1 && prev * 10ull + unsigned(p[-1] - '0') <= max ? int(value) : error_value; } FMT_CONSTEXPR inline auto parse_align(char c) -> align { switch (c) { case '<': return align::left; case '>': return align::right; case '^': return align::center; } return align::none; } template constexpr auto is_name_start(Char c) -> bool { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; } template FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, Handler&& handler) -> const Char* { Char c = *begin; if (c >= '0' && c <= '9') { int index = 0; if (c != '0') index = parse_nonnegative_int(begin, end, INT_MAX); else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) report_error("invalid format string"); else handler.on_index(index); return begin; } if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) { report_error("invalid format string"); return begin; } auto it = begin; do { ++it; } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); handler.on_name({begin, to_unsigned(it - begin)}); return it; } template struct dynamic_spec_handler { parse_context& ctx; arg_ref& ref; arg_id_kind& kind; FMT_CONSTEXPR void on_index(int id) { ref = id; kind = arg_id_kind::index; ctx.check_arg_id(id); ctx.check_dynamic_spec(id); } FMT_CONSTEXPR void on_name(basic_string_view id) { ref = id; kind = arg_id_kind::name; ctx.check_arg_id(id); } }; template struct parse_dynamic_spec_result { const Char* end; arg_id_kind kind; }; // Parses integer | "{" [arg_id] "}". template FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, int& value, arg_ref& ref, parse_context& ctx) -> parse_dynamic_spec_result { FMT_ASSERT(begin != end, ""); auto kind = arg_id_kind::none; if ('0' <= *begin && *begin <= '9') { int val = parse_nonnegative_int(begin, end, -1); if (val == -1) report_error("number is too big"); value = val; } else { if (*begin == '{') { ++begin; if (begin != end) { Char c = *begin; if (c == '}' || c == ':') { int id = ctx.next_arg_id(); ref = id; kind = arg_id_kind::index; ctx.check_dynamic_spec(id); } else { begin = parse_arg_id(begin, end, dynamic_spec_handler{ctx, ref, kind}); } } if (begin != end && *begin == '}') return {++begin, kind}; } report_error("invalid format string"); } return {begin, kind}; } template FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, format_specs& specs, arg_ref& width_ref, parse_context& ctx) -> const Char* { auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); specs.set_dynamic_width(result.kind); return result.end; } template FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, format_specs& specs, arg_ref& precision_ref, parse_context& ctx) -> const Char* { ++begin; if (begin == end) { report_error("invalid precision"); return begin; } auto result = parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx); specs.set_dynamic_precision(result.kind); return result.end; } enum class state { start, align, sign, hash, zero, width, precision, locale }; // Parses standard format specifiers. template FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, dynamic_format_specs& specs, parse_context& ctx, type arg_type) -> const Char* { auto c = '\0'; if (end - begin > 1) { auto next = to_ascii(begin[1]); c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; } else { if (begin == end) return begin; c = to_ascii(*begin); } struct { state current_state = state::start; FMT_CONSTEXPR void operator()(state s, bool valid = true) { if (current_state >= s || !valid) report_error("invalid format specifier"); current_state = s; } } enter_state; using pres = presentation_type; constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; struct { const Char*& begin; format_specs& specs; type arg_type; FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { if (!in(arg_type, set)) report_error("invalid format specifier"); specs.set_type(pres_type); return begin + 1; } } parse_presentation_type{begin, specs, arg_type}; for (;;) { switch (c) { case '<': case '>': case '^': enter_state(state::align); specs.set_align(parse_align(c)); ++begin; break; case '+': case ' ': specs.set_sign(c == ' ' ? sign::space : sign::plus); FMT_FALLTHROUGH; case '-': enter_state(state::sign, in(arg_type, sint_set | float_set)); ++begin; break; case '#': enter_state(state::hash, is_arithmetic_type(arg_type)); specs.set_alt(); ++begin; break; case '0': enter_state(state::zero); if (!is_arithmetic_type(arg_type)) report_error("format specifier requires numeric argument"); if (specs.align() == align::none) { // Ignore 0 if align is specified for compatibility with std::format. specs.set_align(align::numeric); specs.set_fill('0'); } ++begin; break; // clang-format off case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '{': // clang-format on enter_state(state::width); begin = parse_width(begin, end, specs, specs.width_ref, ctx); break; case '.': enter_state(state::precision, in(arg_type, float_set | string_set | cstring_set)); begin = parse_precision(begin, end, specs, specs.precision_ref, ctx); break; case 'L': enter_state(state::locale, is_arithmetic_type(arg_type)); specs.set_localized(); ++begin; break; case 'd': return parse_presentation_type(pres::dec, integral_set); case 'X': specs.set_upper(); FMT_FALLTHROUGH; case 'x': return parse_presentation_type(pres::hex, integral_set); case 'o': return parse_presentation_type(pres::oct, integral_set); case 'B': specs.set_upper(); FMT_FALLTHROUGH; case 'b': return parse_presentation_type(pres::bin, integral_set); case 'E': specs.set_upper(); FMT_FALLTHROUGH; case 'e': return parse_presentation_type(pres::exp, float_set); case 'F': specs.set_upper(); FMT_FALLTHROUGH; case 'f': return parse_presentation_type(pres::fixed, float_set); case 'G': specs.set_upper(); FMT_FALLTHROUGH; case 'g': return parse_presentation_type(pres::general, float_set); case 'A': specs.set_upper(); FMT_FALLTHROUGH; case 'a': return parse_presentation_type(pres::hexfloat, float_set); case 'c': if (arg_type == type::bool_type) report_error("invalid format specifier"); return parse_presentation_type(pres::chr, integral_set); case 's': return parse_presentation_type(pres::string, bool_set | string_set | cstring_set); case 'p': return parse_presentation_type(pres::pointer, pointer_set | cstring_set); case '?': return parse_presentation_type(pres::debug, char_set | string_set | cstring_set); case '}': return begin; default: { if (*begin == '}') return begin; // Parse fill and alignment. auto fill_end = begin + code_point_length(begin); if (end - fill_end <= 0) { report_error("invalid format specifier"); return begin; } if (*begin == '{') { report_error("invalid fill character '{'"); return begin; } auto alignment = parse_align(to_ascii(*fill_end)); enter_state(state::align, alignment != align::none); specs.set_fill( basic_string_view(begin, to_unsigned(fill_end - begin))); specs.set_align(alignment); begin = fill_end + 1; } } if (begin == end) return begin; c = to_ascii(*begin); } } template FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, const Char* end, Handler&& handler) -> const Char* { ++begin; if (begin == end) { handler.on_error("invalid format string"); return end; } int arg_id = 0; switch (*begin) { case '}': handler.on_replacement_field(handler.on_arg_id(), begin); return begin + 1; case '{': handler.on_text(begin, begin + 1); return begin + 1; case ':': arg_id = handler.on_arg_id(); break; default: { struct id_adapter { Handler& handler; int arg_id; FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } FMT_CONSTEXPR void on_name(basic_string_view id) { arg_id = handler.on_arg_id(id); } } adapter = {handler, 0}; begin = parse_arg_id(begin, end, adapter); arg_id = adapter.arg_id; Char c = begin != end ? *begin : Char(); if (c == '}') { handler.on_replacement_field(arg_id, begin); return begin + 1; } if (c != ':') { handler.on_error("missing '}' in format string"); return end; } break; } } begin = handler.on_format_specs(arg_id, begin + 1, end); if (begin == end || *begin != '}') return handler.on_error("unknown format specifier"), end; return begin + 1; } template FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, Handler&& handler) { auto begin = fmt.data(), end = begin + fmt.size(); auto p = begin; while (p != end) { auto c = *p++; if (c == '{') { handler.on_text(begin, p - 1); begin = p = parse_replacement_field(p - 1, end, handler); } else if (c == '}') { if (p == end || *p != '}') return handler.on_error("unmatched '}' in format string"); handler.on_text(begin, p); begin = ++p; } } handler.on_text(begin, end); } // Checks char specs and returns true iff the presentation type is char-like. FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { auto type = specs.type(); if (type != presentation_type::none && type != presentation_type::chr && type != presentation_type::debug) { return false; } if (specs.align() == align::numeric || specs.sign() != sign::none || specs.alt()) { report_error("invalid format specifier for char"); } return true; } // A base class for compile-time strings. struct compile_string {}; template FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { using mapped_type = remove_cvref_t>; constexpr bool formattable = std::is_constructible>::value; if (!formattable) return ctx.begin(); // Error is reported in the value ctor. using formatted_type = conditional_t; return formatter().parse(ctx); } template struct arg_pack {}; template class format_string_checker { private: type types_[max_of(1, NUM_ARGS)]; named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; compile_parse_context context_; using parse_func = auto (*)(parse_context&) -> const Char*; parse_func parse_funcs_[max_of(1, NUM_ARGS)]; public: template FMT_CONSTEXPR explicit format_string_checker(basic_string_view fmt, arg_pack) : types_{mapped_type_constant::value...}, named_args_{}, context_(fmt, NUM_ARGS, types_), parse_funcs_{&invoke_parse...} { int arg_index = 0, named_arg_index = 0; FMT_APPLY_VARIADIC( init_static_named_arg(named_args_, arg_index, named_arg_index)); ignore_unused(arg_index, named_arg_index); } FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } FMT_CONSTEXPR auto on_arg_id(int id) -> int { context_.check_arg_id(id); return id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { for (int i = 0; i < NUM_NAMED_ARGS; ++i) { if (named_args_[i].name == id) return named_args_[i].id; } if (!DYNAMIC_NAMES) on_error("argument not found"); return -1; } FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { on_format_specs(id, begin, begin); // Call parse() on empty specs. } FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) -> const Char* { context_.advance_to(begin); if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); // If id is out of range, it means we do not know the type and cannot parse // the format at compile time. Instead, skip over content until we finish // the format spec, accounting for any nested replacements. for (int bracket_count = 0; begin != end && (bracket_count > 0 || *begin != '}'); ++begin) { if (*begin == '{') ++bracket_count; else if (*begin == '}') --bracket_count; } return begin; } FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { report_error(message); } }; /// A contiguous memory buffer with an optional growing ability. It is an /// internal class and shouldn't be used directly, only via `memory_buffer`. template class buffer { private: T* ptr_; size_t size_; size_t capacity_; using grow_fun = void (*)(buffer& buf, size_t capacity); grow_fun grow_; protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept : size_(sz), capacity_(sz), grow_(grow) { if (FMT_MSC_VERSION != 0) ptr_ = nullptr; // Suppress warning 26495. } constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} FMT_CONSTEXPR20 ~buffer() = default; buffer(buffer&&) = default; /// Sets the buffer data and capacity. FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { ptr_ = buf_data; capacity_ = buf_capacity; } public: using value_type = T; using const_reference = const T&; buffer(const buffer&) = delete; void operator=(const buffer&) = delete; auto begin() noexcept -> T* { return ptr_; } auto end() noexcept -> T* { return ptr_ + size_; } auto begin() const noexcept -> const T* { return ptr_; } auto end() const noexcept -> const T* { return ptr_ + size_; } /// Returns the size of this buffer. constexpr auto size() const noexcept -> size_t { return size_; } /// Returns the capacity of this buffer. constexpr auto capacity() const noexcept -> size_t { return capacity_; } /// Returns a pointer to the buffer data (not null-terminated). FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } /// Clears this buffer. FMT_CONSTEXPR void clear() { size_ = 0; } // Tries resizing the buffer to contain `count` elements. If T is a POD type // the new elements may not be initialized. FMT_CONSTEXPR void try_resize(size_t count) { try_reserve(count); size_ = min_of(count, capacity_); } // Tries increasing the buffer capacity to `new_capacity`. It can increase the // capacity by a smaller amount than requested but guarantees there is space // for at least one additional element either by increasing the capacity or by // flushing the buffer if it is full. FMT_CONSTEXPR void try_reserve(size_t new_capacity) { if (new_capacity > capacity_) grow_(*this, new_capacity); } FMT_CONSTEXPR void push_back(const T& value) { try_reserve(size_ + 1); ptr_[size_++] = value; } /// Appends data to the end of the buffer. template FMT_CONSTEXPR20 void append(const U* begin, const U* end) { static_assert(std::is_same() || std::is_same(), ""); while (begin != end) { auto size = size_; auto free_cap = capacity_ - size; auto count = to_unsigned(end - begin); if (free_cap < count) { grow_(*this, size + count); size = size_; free_cap = capacity_ - size; count = count < free_cap ? count : free_cap; } // A loop is faster than memcpy on small sizes. T* out = ptr_ + size; for (size_t i = 0; i < count; ++i) out[i] = static_cast(begin[i]); size_ += count; begin += count; } } template FMT_CONSTEXPR auto operator[](Idx index) -> T& { return ptr_[index]; } template constexpr auto operator[](Idx index) const -> const T& { return ptr_[index]; } }; struct buffer_traits { constexpr explicit buffer_traits(size_t) {} constexpr auto count() const -> size_t { return 0; } constexpr auto limit(size_t size) const -> size_t { return size; } }; class fixed_buffer_traits { private: size_t count_ = 0; size_t limit_; public: constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} constexpr auto count() const -> size_t { return count_; } FMT_CONSTEXPR auto limit(size_t size) -> size_t { size_t n = limit_ > count_ ? limit_ - count_ : 0; count_ += size; return min_of(size, n); } }; // A buffer that writes to an output iterator when flushed. template class iterator_buffer : public Traits, public buffer { private: OutputIt out_; enum { buffer_size = 256 }; T data_[buffer_size]; static FMT_CONSTEXPR void grow(buffer& buf, size_t) { if (buf.size() == buffer_size) static_cast(buf).flush(); } void flush() { auto size = this->size(); this->clear(); const T* begin = data_; const T* end = begin + this->limit(size); while (begin != end) *out_++ = *begin++; } public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} iterator_buffer(iterator_buffer&& other) noexcept : Traits(other), buffer(grow, data_, 0, buffer_size), out_(other.out_) {} ~iterator_buffer() { // Don't crash if flush fails during unwinding. FMT_TRY { flush(); } FMT_CATCH(...) {} } auto out() -> OutputIt { flush(); return out_; } auto count() const -> size_t { return Traits::count() + this->size(); } }; template class iterator_buffer : public fixed_buffer_traits, public buffer { private: T* out_; enum { buffer_size = 256 }; T data_[buffer_size]; static FMT_CONSTEXPR void grow(buffer& buf, size_t) { if (buf.size() == buf.capacity()) static_cast(buf).flush(); } void flush() { size_t n = this->limit(this->size()); if (this->data() == out_) { out_ += n; this->set(data_, buffer_size); } this->clear(); } public: explicit iterator_buffer(T* out, size_t n = buffer_size) : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} iterator_buffer(iterator_buffer&& other) noexcept : fixed_buffer_traits(other), buffer(static_cast(other)), out_(other.out_) { if (this->data() != out_) { this->set(data_, buffer_size); this->clear(); } } ~iterator_buffer() { flush(); } auto out() -> T* { flush(); return out_; } auto count() const -> size_t { return fixed_buffer_traits::count() + this->size(); } }; template class iterator_buffer : public buffer { public: explicit iterator_buffer(T* out, size_t = 0) : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} auto out() -> T* { return &*this->end(); } }; template class container_buffer : public buffer { private: using value_type = typename Container::value_type; static FMT_CONSTEXPR void grow(buffer& buf, size_t capacity) { auto& self = static_cast(buf); self.container.resize(capacity); self.set(&self.container[0], capacity); } public: Container& container; explicit container_buffer(Container& c) : buffer(grow, c.size()), container(c) {} }; // A buffer that writes to a container with the contiguous storage. template class iterator_buffer< OutputIt, enable_if_t::value && is_contiguous::value, typename OutputIt::container_type::value_type>> : public container_buffer { private: using base = container_buffer; public: explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {} explicit iterator_buffer(OutputIt out, size_t = 0) : base(get_container(out)) {} auto out() -> OutputIt { return OutputIt(this->container); } }; // A buffer that counts the number of code units written discarding the output. template class counting_buffer : public buffer { private: enum { buffer_size = 256 }; T data_[buffer_size]; size_t count_ = 0; static FMT_CONSTEXPR void grow(buffer& buf, size_t) { if (buf.size() != buffer_size) return; static_cast(buf).count_ += buf.size(); buf.clear(); } public: constexpr counting_buffer() : buffer(grow, data_, 0, buffer_size) {} constexpr auto count() const noexcept -> size_t { return count_ + this->size(); } }; template struct is_back_insert_iterator> : std::true_type {}; template struct is_buffer_appender : std::false_type {}; template struct is_buffer_appender< It, bool_constant< is_back_insert_iterator::value && std::is_base_of, typename It::container_type>::value>> : std::true_type {}; // Maps an output iterator to a buffer. template ::value)> auto get_buffer(OutputIt out) -> iterator_buffer { return iterator_buffer(out); } template ::value)> auto get_buffer(OutputIt out) -> buffer& { return get_container(out); } template auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { return buf.out(); } template auto get_iterator(buffer&, OutputIt out) -> OutputIt { return out; } // This type is intentionally undefined, only used for errors. template struct type_is_unformattable_for; template struct string_value { const Char* data; size_t size; auto str() const -> basic_string_view { return {data, size}; } }; template struct custom_value { using char_type = typename Context::char_type; void* value; void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); }; template struct named_arg_value { const named_arg_info* data; size_t size; }; struct custom_tag {}; #if !FMT_BUILTIN_TYPES # define FMT_BUILTIN , monostate #else # define FMT_BUILTIN #endif // A formatting argument value. template class value { public: using char_type = typename Context::char_type; union { monostate no_value; int int_value; unsigned uint_value; long long long_long_value; ullong ulong_long_value; native_int128 int128_value; native_uint128 uint128_value; bool bool_value; char_type char_value; float float_value; double double_value; long double long_double_value; const void* pointer; string_value string; custom_value custom; named_arg_value named_args; }; constexpr FMT_INLINE value() : no_value() {} constexpr FMT_INLINE value(signed char x) : int_value(x) {} constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {} constexpr FMT_INLINE value(signed short x) : int_value(x) {} constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {} constexpr FMT_INLINE value(int x) : int_value(x) {} constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {} constexpr FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {} constexpr FMT_INLINE value(unsigned long x FMT_BUILTIN) : value(ulong_type(x)) {} constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {} constexpr FMT_INLINE value(ullong x FMT_BUILTIN) : ulong_long_value(x) {} FMT_INLINE value(native_int128 x FMT_BUILTIN) : int128_value(x) {} FMT_INLINE value(native_uint128 x FMT_BUILTIN) : uint128_value(x) {} constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {} template ::value)> constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { static_assert( std::is_same::value || std::is_same::value, "mixing character types is disallowed"); } constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {} constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {} FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {} FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) { string.data = x; if (is_constant_evaluated()) string.size = 0; } FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) { string.data = x; if (is_constant_evaluated()) string.size = 0; } template , FMT_ENABLE_IF(!std::is_pointer::value)> FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { static_assert(std::is_same::value, "mixing character types is disallowed"); auto sv = to_string_view(x); string.data = sv.data(); string.size = sv.size(); } constexpr FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {} constexpr FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {} constexpr FMT_INLINE value(volatile void* x FMT_BUILTIN) : pointer(const_cast(x)) {} constexpr FMT_INLINE value(const volatile void* x FMT_BUILTIN) : pointer(const_cast(x)) {} constexpr FMT_INLINE value(nullptr_t) : pointer(nullptr) {} template ::value || std::is_member_pointer::value) && !std::is_void::type>::value)> constexpr value(const T&) { // Formatting of arbitrary pointers is disallowed. If you want to format a // pointer cast it to `void*` or `const void*`. In particular, this forbids // formatting of `[const] volatile char*` printed as bool by iostreams. static_assert(sizeof(T) == 0, "formatting of non-void pointers is disallowed"); } template ::value)> constexpr value(const T& x) : value(format_as(x)) {} template ::value)> constexpr value(const T& x) : value(formatter::format_as(x)) {} template ::value)> constexpr value(const T& named_arg) : value(named_arg.value) {} template ::value || !FMT_BUILTIN_TYPES)> FMT_CONSTEXPR FMT_INLINE value(T& x) : value(x, custom_tag()) {} FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} private: template ())> FMT_CONSTEXPR value(T& x, custom_tag) { using value_type = remove_const_t; // T may overload operator& e.g. std::vector::reference in libc++. if (!is_constant_evaluated()) { custom.value = const_cast(&reinterpret_cast(x)); } else { custom.value = nullptr; #if defined(__cpp_if_constexpr) if constexpr (std::is_same*>::value) custom.value = const_cast(&x); #endif } custom.format = format_custom; } template ())> FMT_CONSTEXPR value(const T&, custom_tag) { // Cannot format an argument; to make type T formattable provide a // formatter specialization: https://fmt.dev/latest/api#udt. type_is_unformattable_for _; } // Formats an argument of a custom type, such as a user-defined class. template static void format_custom(void* arg, parse_context& parse_ctx, Context& ctx) { auto f = formatter(); parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = conditional_t(), const T, T>; // format must be const for compatibility with std::format and compilation. const auto& cf = f; ctx.advance_to(cf.format(*static_cast(arg), ctx)); } }; enum { packed_arg_bits = 4 }; // Maximum number of arguments with packed types. enum { max_packed_args = 62 / packed_arg_bits }; enum : ullong { is_unpacked_bit = 1ULL << 63 }; enum : ullong { has_named_args_bit = 1ULL << 62 }; template struct is_output_iterator : std::false_type {}; template <> struct is_output_iterator : std::true_type {}; template struct is_output_iterator< It, T, enable_if_t&>()++), T>::value>> : std::true_type {}; template constexpr auto encode_types() -> ullong { return 0; } template constexpr auto encode_types() -> ullong { return unsigned(stored_type_constant::value) | (encode_types() << packed_arg_bits); } template constexpr auto make_descriptor() -> ullong { return NUM_ARGS <= max_packed_args ? encode_types() : is_unpacked_bit | NUM_ARGS; } template using arg_t = conditional_t, basic_format_arg>; template struct named_arg_store { // args_[0].named_args points to named_args to avoid bloating format_args. arg_t args[NUM_ARGS + 1u]; named_arg_info named_args[NUM_NAMED_ARGS + 0u]; template FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) : args{{named_args, NUM_NAMED_ARGS}, values...} { int arg_index = 0, named_arg_index = 0; FMT_APPLY_VARIADIC( init_named_arg(named_args, arg_index, named_arg_index, values)); } named_arg_store(named_arg_store&& rhs) { args[0] = {named_args, NUM_NAMED_ARGS}; for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i) args[i] = rhs.args[i]; for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) named_args[i] = rhs.named_args[i]; } named_arg_store(const named_arg_store& rhs) = delete; auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete; auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete; operator const arg_t*() const { return args + 1; } }; // An array of references to arguments. It can be implicitly converted to // `basic_format_args` for passing into type-erased formatting functions // such as `vformat`. It is a plain struct to reduce binary size in debug mode. template struct format_arg_store { // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. using type = conditional_t[max_of(1, NUM_ARGS)], named_arg_store>; type args; }; // TYPE can be different from type_constant, e.g. for __float128. template struct native_formatter { private: dynamic_format_specs specs_; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); if FMT_CONSTEXPR20 (TYPE == type::char_type) check_char_specs(specs_); return end; } template FMT_CONSTEXPR void set_debug_format(bool set = true) { specs_.set_type(set ? presentation_type::debug : presentation_type::none); } template FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -> decltype(ctx.out()); }; template constexpr bool enforce_compile_checks() { #ifdef FMT_ENFORCE_COMPILE_STRING static_assert( FMT_USE_CONSTEVAL && B, "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); #endif return true; } template constexpr auto is_locking() -> bool { return locking>::value; } template constexpr auto is_locking() -> bool { return locking>::value || is_locking(); } FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, locale_ref loc = {}); #if FMT_WIN32 FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); #else // format_args is passed by reference since it is defined later. inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} #endif } // namespace detail // The main public API. template using named_arg = detail::named_arg; template FMT_CONSTEXPR void parse_context::do_check_arg_id(int arg_id) { // Argument id is only checked at compile time during parsing because // formatting has its own validation. if (detail::is_constant_evaluated() && use_constexpr_cast) { auto ctx = static_cast*>(this); if (arg_id >= ctx->num_args()) report_error("argument not found"); } } template FMT_CONSTEXPR void parse_context::check_dynamic_spec(int arg_id) { using detail::compile_parse_context; if (detail::is_constant_evaluated() && use_constexpr_cast) static_cast*>(this)->check_dynamic_spec(arg_id); } FMT_BEGIN_EXPORT // An output iterator that appends to a buffer. It is used instead of // back_insert_iterator to reduce symbol sizes and avoid dependency. template class basic_appender { protected: detail::buffer* container; public: using container_type = detail::buffer; constexpr basic_appender(detail::buffer& buf) : container(&buf) {} FMT_CONSTEXPR auto operator=(T c) -> basic_appender& { container->push_back(c); return *this; } FMT_CONSTEXPR auto operator*() -> basic_appender& { return *this; } FMT_CONSTEXPR auto operator++() -> basic_appender& { return *this; } FMT_CONSTEXPR auto operator++(int) -> basic_appender { return *this; } }; // A formatting argument. Context is a template parameter for the compiled API // where output can be unbuffered. template class basic_format_arg { private: detail::value value_; detail::type type_; friend class basic_format_args; using char_type = typename Context::char_type; public: class handle { private: detail::custom_value custom_; public: explicit handle(detail::custom_value custom) : custom_(custom) {} void format(parse_context& parse_ctx, Context& ctx) const { custom_.format(custom_.value, parse_ctx, ctx); } }; constexpr basic_format_arg() : type_(detail::type::none_type) {} basic_format_arg(const detail::named_arg_info* args, size_t size) : value_(args, size) {} template basic_format_arg(T&& val) : value_(val), type_(detail::stored_type_constant::value) {} constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; } auto type() const -> detail::type { return type_; } /** * Visits an argument dispatching to the appropriate visit method based on * the argument type. For example, if the argument type is `double` then * `vis(value)` will be called with the value of type `double`. */ template FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { using detail::map; switch (type_) { case detail::type::none_type: break; case detail::type::int_type: return vis(value_.int_value); case detail::type::uint_type: return vis(value_.uint_value); case detail::type::long_long_type: return vis(value_.long_long_value); case detail::type::ulong_long_type: return vis(value_.ulong_long_value); case detail::type::int128_type: return vis(map(value_.int128_value)); case detail::type::uint128_type: return vis(map(value_.uint128_value)); case detail::type::bool_type: return vis(value_.bool_value); case detail::type::char_type: return vis(value_.char_value); case detail::type::float_type: return vis(value_.float_value); case detail::type::double_type: return vis(value_.double_value); case detail::type::long_double_type: return vis(value_.long_double_value); case detail::type::cstring_type: return vis(value_.string.data); case detail::type::string_type: return vis(value_.string.str()); case detail::type::pointer_type: return vis(value_.pointer); case detail::type::custom_type: return vis(handle(value_.custom)); } return vis(monostate()); } auto format_custom(const char_type* parse_begin, parse_context& parse_ctx, Context& ctx) -> bool { if (type_ != detail::type::custom_type) return false; parse_ctx.advance_to(parse_begin); value_.custom.format(value_.custom.value, parse_ctx, ctx); return true; } }; /** * A view of a collection of formatting arguments. To avoid lifetime issues it * should only be used as a parameter type in type-erased functions such as * `vformat`: * * void vlog(fmt::string_view fmt, fmt::format_args args); // OK * fmt::format_args args = fmt::make_format_args(); // Dangling reference */ template class basic_format_args { private: // A descriptor that contains information about formatting arguments. // If the number of arguments is less or equal to max_packed_args then // argument types are passed in the descriptor. This reduces binary code size // per formatting function call. ullong desc_; union { // If is_packed() returns true then argument values are stored in values_; // otherwise they are stored in args_. This is done to improve cache // locality and reduce compiled code size since storing larger objects // may require more code (at least on x86-64) even if the same amount of // data is actually copied to stack. It saves ~10% on the bloat test. const detail::value* values_; const basic_format_arg* args_; }; constexpr auto is_packed() const -> bool { return (desc_ & detail::is_unpacked_bit) == 0; } constexpr auto has_named_args() const -> bool { return (desc_ & detail::has_named_args_bit) != 0; } FMT_CONSTEXPR auto type(int index) const -> detail::type { int shift = index * detail::packed_arg_bits; unsigned mask = (1 << detail::packed_arg_bits) - 1; return static_cast((desc_ >> shift) & mask); } template using store = detail::format_arg_store; public: using format_arg = basic_format_arg; constexpr basic_format_args() : desc_(0), args_(nullptr) {} /// Constructs a `basic_format_args` object from `format_arg_store`. template constexpr FMT_ALWAYS_INLINE basic_format_args( const store& s) : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), values_(s.args) {} template detail::max_packed_args)> constexpr basic_format_args(const store& s) : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), args_(s.args) {} /// Constructs a `basic_format_args` object from a dynamic list of arguments. constexpr basic_format_args(const format_arg* args, int count, bool has_named = false) : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | (has_named ? +detail::has_named_args_bit : 0)), args_(args) {} /// Returns the argument with the specified id. FMT_CONSTEXPR auto get(int id) const -> format_arg { auto arg = format_arg(); if (!is_packed()) { if (id < max_size()) arg = args_[id]; return arg; } if (unsigned(id) >= detail::max_packed_args) return arg; arg.type_ = type(id); if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; return arg; } template auto get(basic_string_view name) const -> format_arg { int id = get_id(name); return id >= 0 ? get(id) : format_arg(); } template FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { if (!has_named_args()) return -1; const auto& named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args; for (size_t i = 0; i < named_args.size; ++i) { if (named_args.data[i].name == name) return named_args.data[i].id; } return -1; } auto max_size() const -> int { return int(is_packed() ? ullong(detail::max_packed_args) : desc_ & ~detail::is_unpacked_bit); } }; // A formatting context. class context { private: appender out_; format_args args_; FMT_NO_UNIQUE_ADDRESS locale_ref loc_; public: using char_type = char; ///< The character type for the output. using iterator = appender; using format_arg = basic_format_arg; enum { builtin_types = FMT_BUILTIN_TYPES }; /// Constructs a `context` object. References to the arguments are stored /// in the object so make sure they have appropriate lifetimes. constexpr context(iterator out, format_args args, locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} context(context&&) = default; context(const context&) = delete; void operator=(const context&) = delete; FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } inline auto arg(string_view name) const -> format_arg { return args_.get(name); } FMT_CONSTEXPR auto arg_id(string_view name) const -> int { return args_.get_id(name); } auto args() const -> const format_args& { return args_; } // Returns an iterator to the beginning of the output range. constexpr auto out() const -> iterator { return out_; } // Advances the begin iterator to `it`. FMT_CONSTEXPR void advance_to(iterator) {} constexpr auto locale() const -> locale_ref { return loc_; } }; template struct runtime_format_string { basic_string_view str; }; /** * Creates a runtime format string. * * **Example**: * * // Check format string at runtime instead of compile-time. * fmt::print(fmt::runtime("{:d}"), "I am not a number"); */ inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } /// A compile-time format string. Use `format_string` in the public API to /// prevent type deduction. template struct fstring { private: static constexpr int num_static_named_args = detail::count_static_named_args(); using checker = detail::format_string_checker< char, int(sizeof...(T)), num_static_named_args, num_static_named_args != detail::count_named_args()>; using arg_pack = detail::arg_pack; public: string_view str; using t = fstring; // Reports a compile-time error if S is not a valid format string for T. template FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { using namespace detail; 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())); 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())); constexpr bool unused = detail::enforce_compile_checks(); (void)unused; } template ::value&& std::is_same::value)> FMT_ALWAYS_INLINE fstring(const S&) : str(S()) { FMT_CONSTEXPR auto sv = string_view(S()); FMT_CONSTEXPR int x = (parse_format_string(sv, checker(sv, arg_pack())), 0); detail::ignore_unused(x); } fstring(runtime_format_string<> fmt) : str(fmt.str) {} FMT_DEPRECATED operator const string_view&() const { return str; } auto get() const -> string_view { return str; } }; template using format_string = typename fstring::t; template using is_formattable = bool_constant::value, int*, T>, Char>, void>::value>; #if defined(__cpp_concepts) && __cpp_concepts >= 201907L template concept formattable = is_formattable, Char>::value; #endif // A formatter specialization for natively supported types. template struct formatter::value != detail::type::custom_type>> : detail::native_formatter::value> { }; /** * Constructs an object that stores references to arguments and can be * implicitly converted to `format_args`. `Context` can be omitted in which case * it defaults to `context`. See `arg` for lifetime considerations. */ // Take arguments by lvalue references to avoid some lifetime issues, e.g. // auto args = make_format_args(std::string()); template (), ullong DESC = detail::make_descriptor()> constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) -> detail::format_arg_store { return {{args...}}; } template using vargs = detail::format_arg_store(), detail::make_descriptor()>; /** * Returns a named argument to be used in a formatting function. * It should only be used in a call to a formatting function. * * **Example**: * * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); * * Named arguments passed with `fmt::arg` are not supported * in compile-time checks, but `"answer"_a=42` are compile-time checked in * sufficiently new compilers. See `operator""_a()`. */ template inline auto arg(const char* name, const T& arg) -> named_arg { return {name, arg}; } /// Formats a string and writes the output to `out`. template , char>::value)> // DEPRECATED! Passing out as a forwarding reference. auto vformat_to(OutputIt&& out, string_view fmt, format_args args) -> remove_cvref_t { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, fmt, args, {}); return detail::get_iterator(buf, out); } /** * Formats `args` according to specifications in `fmt`, writes the result to * the output iterator `out` and returns the iterator past the end of the output * range. `format_to` does not append a terminating null character. * * **Example**: * * auto out = std::vector(); * fmt::format_to(std::back_inserter(out), "{}", 42); */ template , char>::value)> FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) -> remove_cvref_t { return vformat_to(out, fmt.str, vargs{{args...}}); } template struct format_to_n_result { OutputIt out; ///< Iterator past the end of the output range. size_t size; ///< Total (not truncated) output size. }; template ::value)> auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); detail::vformat_to(buf, fmt, args, {}); return {buf.out(), buf.count()}; } /** * Formats `args` according to specifications in `fmt`, writes up to `n` * characters of the result to the output iterator `out` and returns the total * (not truncated) output size and the iterator past the end of the output * range. `format_to_n` does not append a terminating null character. */ template ::value)> FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, T&&... args) -> format_to_n_result { return vformat_to_n(out, n, fmt.str, vargs{{args...}}); } struct format_to_result { char* out; ///< Pointer to just after the last successful write. bool truncated; ///< Specifies if the output was truncated. FMT_CONSTEXPR operator char*() const { // Report truncation to prevent silent data loss. if (truncated) report_error("output is truncated"); return out; } }; template FMT_DEPRECATED auto vformat_to(char (&out)[N], string_view fmt, format_args args) -> format_to_result { auto result = vformat_to_n(out, N, fmt, args); return {result.out, result.size > N}; } template FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) -> format_to_result { auto result = vformat_to_n(out, N, fmt.str, vargs{{args...}}); return {result.out, result.size > N}; } /// Returns the number of chars in the output of `format(fmt, args...)`. template FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); detail::vformat_to(buf, fmt.str, vargs{{args...}}, {}); return buf.count(); } FMT_API void vprint(string_view fmt, format_args args); FMT_API void vprint(FILE* f, string_view fmt, format_args args); FMT_API void vprintln(FILE* f, string_view fmt, format_args args); FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); /** * Formats `args` according to specifications in `fmt` and writes the output * to `stdout`. * * **Example**: * * fmt::print("The answer is {}.", 42); */ template FMT_INLINE void print(format_string fmt, T&&... args) { vargs va = {{args...}}; if FMT_CONSTEXPR20 (!detail::use_utf8) return detail::vprint_mojibake(stdout, fmt.str, va, false); detail::is_locking() ? vprint_buffered(stdout, fmt.str, va) : vprint(fmt.str, va); } /** * Formats `args` according to specifications in `fmt` and writes the * output to the file `f`. * * **Example**: * * fmt::print(stderr, "Don't {}!", "panic"); */ template FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { vargs va = {{args...}}; if FMT_CONSTEXPR20 (!detail::use_utf8) return detail::vprint_mojibake(f, fmt.str, va, false); detail::is_locking() ? vprint_buffered(f, fmt.str, va) : vprint(f, fmt.str, va); } /// Formats `args` according to specifications in `fmt` and writes the output /// to the file `f` followed by a newline. template FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { vargs va = {{args...}}; if FMT_CONSTEXPR20 (detail::use_utf8) return vprintln(f, fmt.str, va); detail::vprint_mojibake(f, fmt.str, va, true); } /// Formats `args` according to specifications in `fmt` and writes the output /// to `stdout` followed by a newline. template FMT_INLINE void println(format_string fmt, T&&... args) { fmt::println(stdout, fmt, static_cast(args)...); } FMT_PRAGMA_GCC(pop_options) FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY # include "format.h" #endif #endif // FMT_BASE_H_ ================================================ FILE: include/fmt/chrono.h ================================================ // Formatting library for C++ - chrono support // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ #ifndef FMT_MODULE # include # include # include // std::isfinite # include // std::memcpy # include # include # include # include # include #endif #include "format.h" FMT_BEGIN_NAMESPACE // Enable safe chrono durations, unless explicitly disabled. #ifndef FMT_SAFE_DURATION_CAST # define FMT_SAFE_DURATION_CAST 1 #endif #if FMT_SAFE_DURATION_CAST // For conversion between std::chrono::durations without undefined // behaviour or erroneous results. // This is a stripped down version of duration_cast, for inclusion in fmt. // See https://github.com/pauldreik/safe_duration_cast // // Copyright Paul Dreik 2019 namespace safe_duration_cast { // DEPRECATED! template ::value && std::numeric_limits::is_signed == std::numeric_limits::is_signed)> FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; static_assert(F::is_integer, "From must be integral"); static_assert(T::is_integer, "To must be integral"); // A and B are both signed, or both unsigned. if FMT_CONSTEXPR20 (F::digits <= T::digits) { // From fits in To without any problem. } else { // From does not always fit in To, resort to a dynamic check. if (from < (T::min)() || from > (T::max)()) { // outside range. ec = 1; return {}; } } return static_cast(from); } /// Converts From to To, without loss. If the dynamic value of from /// can't be converted to To without loss, ec is set. template ::value && std::numeric_limits::is_signed != std::numeric_limits::is_signed)> FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; static_assert(F::is_integer, "From must be integral"); static_assert(T::is_integer, "To must be integral"); if FMT_CONSTEXPR20 (F::is_signed && !T::is_signed) { // From may be negative, not allowed! if (fmt::detail::is_negative(from)) { ec = 1; return {}; } // From is positive. Can it always fit in To? if (F::digits > T::digits && from > static_cast(detail::max_value())) { ec = 1; return {}; } } if (!F::is_signed && T::is_signed && F::digits >= T::digits && from > static_cast(detail::max_value())) { ec = 1; return {}; } return static_cast(from); // Lossless conversion. } template ::value)> FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) -> To { ec = 0; return from; } // function // clang-format off /** * converts From to To if possible, otherwise ec is set. * * input | output * ---------------------------------|--------------- * NaN | NaN * Inf | Inf * normal, fits in output | converted (possibly lossy) * normal, does not fit in output | ec is set * subnormal | best effort * -Inf | -Inf */ // clang-format on template ::value)> FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; using T = std::numeric_limits; static_assert(std::is_floating_point::value, "From must be floating"); static_assert(std::is_floating_point::value, "To must be floating"); // catch the only happy case if (std::isfinite(from)) { if (from >= T::lowest() && from <= (T::max)()) { return static_cast(from); } // not within range. ec = 1; return {}; } // nan and inf will be preserved return static_cast(from); } // function template ::value)> FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; static_assert(std::is_floating_point::value, "From must be floating"); return from; } /// Safe duration_cast between floating point durations template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> auto safe_duration_cast(std::chrono::duration from, int& ec) -> To { using From = std::chrono::duration; ec = 0; // the basic idea is that we need to convert from count() in the from type // to count() in the To type, by multiplying it with this: struct Factor : std::ratio_divide {}; static_assert(Factor::num > 0, "num must be positive"); static_assert(Factor::den > 0, "den must be positive"); // the conversion is like this: multiply from.count() with Factor::num // /Factor::den and convert it to To::rep, all this without // overflow/underflow. let's start by finding a suitable type that can hold // both To, From and Factor::num using IntermediateRep = typename std::common_type::type; // force conversion of From::rep -> IntermediateRep to be safe, // even if it will never happen be narrowing in this context. IntermediateRep count = safe_float_conversion(from.count(), ec); if (ec) { return {}; } // multiply with Factor::num without overflow or underflow if FMT_CONSTEXPR20 (Factor::num != 1) { constexpr auto max1 = detail::max_value() / static_cast(Factor::num); if (count > max1) { ec = 1; return {}; } constexpr auto min1 = std::numeric_limits::lowest() / static_cast(Factor::num); if (count < min1) { ec = 1; return {}; } count *= static_cast(Factor::num); } // this can't go wrong, right? den>0 is checked earlier. if FMT_CONSTEXPR20 (Factor::den != 1) { using common_t = typename std::common_type::type; count /= static_cast(Factor::den); } // convert to the to type, safely using ToRep = typename To::rep; const ToRep tocount = safe_float_conversion(count, ec); if (ec) { return {}; } return To{tocount}; } } // namespace safe_duration_cast #endif namespace detail { // Check if std::chrono::utc_time is available. #ifdef FMT_USE_UTC_TIME // Use the provided definition. #elif defined(__cpp_lib_chrono) # define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) #else # define FMT_USE_UTC_TIME 0 #endif #if FMT_USE_UTC_TIME using utc_clock = std::chrono::utc_clock; #else struct utc_clock { template void to_sys(T); }; #endif // Check if std::chrono::local_time is available. #ifdef FMT_USE_LOCAL_TIME // Use the provided definition. #elif defined(__cpp_lib_chrono) # define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) #else # define FMT_USE_LOCAL_TIME 0 #endif #if FMT_USE_LOCAL_TIME using local_t = std::chrono::local_t; #else struct local_t {}; #endif } // namespace detail template using sys_time = std::chrono::time_point; template using utc_time = std::chrono::time_point; template using local_time = std::chrono::time_point; namespace detail { // Prevents expansion of a preceding token as a function-style macro. // Usage: f FMT_NOMACRO() #define FMT_NOMACRO template struct null {}; inline auto gmtime_r(...) -> null<> { return null<>(); } inline auto gmtime_s(...) -> null<> { return null<>(); } // It is defined here and not in ostream.h because the latter has expensive // includes. template class formatbuf : public StreamBuf { private: using char_type = typename StreamBuf::char_type; using streamsize = decltype(std::declval().sputn(nullptr, 0)); using int_type = typename StreamBuf::int_type; using traits_type = typename StreamBuf::traits_type; buffer& buffer_; public: explicit formatbuf(buffer& buf) : buffer_(buf) {} protected: // The put area is always empty. This makes the implementation simpler and has // the advantage that the streambuf and the buffer are always in sync and // sputc never writes into uninitialized memory. A disadvantage is that each // call to sputc always results in a (virtual) call to overflow. There is no // disadvantage here for sputn since this always results in a call to xsputn. auto overflow(int_type ch) -> int_type override { if (!traits_type::eq_int_type(ch, traits_type::eof())) buffer_.push_back(static_cast(ch)); return ch; } auto xsputn(const char_type* s, streamsize count) -> streamsize override { buffer_.append(s, s + count); return count; } }; inline auto get_classic_locale() -> const std::locale& { static const auto& locale = std::locale::classic(); return locale; } template struct codecvt_result { static constexpr size_t max_size = 32; CodeUnit buf[max_size]; CodeUnit* end; }; template void write_codecvt(codecvt_result& out, string_view in, const std::locale& loc) { FMT_PRAGMA_CLANG(diagnostic push) FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated") auto& f = std::use_facet>(loc); FMT_PRAGMA_CLANG(diagnostic pop) auto mb = std::mbstate_t(); const char* from_next = nullptr; auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf), std::end(out.buf), out.end); if (result != std::codecvt_base::ok) FMT_THROW(format_error("failed to format time")); } template auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) -> OutputIt { if (detail::use_utf8 && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. #if FMT_MSC_VERSION != 0 || \ (defined(__GLIBCXX__) && \ (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // and newer. using code_unit = wchar_t; #else using code_unit = char32_t; #endif using unit_t = codecvt_result; unit_t unit; write_codecvt(unit, in, loc); // In UTF-8 is used one to four one-byte code units. auto u = to_utf8>(); if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) FMT_THROW(format_error("failed to format time")); return copy(u.c_str(), u.c_str() + u.size(), out); } return copy(in.data(), in.data() + in.size(), out); } template ::value)> auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) -> OutputIt { codecvt_result unit; write_codecvt(unit, sv, loc); return copy(unit.buf, unit.end, out); } template ::value)> auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) -> OutputIt { return write_encoded_tm_str(out, sv, loc); } template inline void do_write(buffer& buf, const std::tm& time, const std::locale& loc, char format, char modifier) { auto&& format_buf = formatbuf>(buf); auto&& os = std::basic_ostream(&format_buf); os.imbue(loc); const auto& facet = std::use_facet>(loc); auto end = facet.put(os, os, Char(' '), &time, format, modifier); if (end.failed()) FMT_THROW(format_error("failed to format time")); } template ::value)> auto write(OutputIt out, const std::tm& time, const std::locale& loc, char format, char modifier = 0) -> OutputIt { auto&& buf = get_buffer(out); do_write(buf, time, loc, format, modifier); return get_iterator(buf, out); } template ::value)> auto write(OutputIt out, const std::tm& time, const std::locale& loc, char format, char modifier = 0) -> OutputIt { auto&& buf = basic_memory_buffer(); do_write(buf, time, loc, format, modifier); return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } template using is_similar_arithmetic_type = bool_constant<(std::is_integral::value && std::is_integral::value) || (std::is_floating_point::value && std::is_floating_point::value)>; FMT_NORETURN inline void throw_duration_error() { FMT_THROW(format_error("cannot format duration")); } // Cast one integral duration to another with an overflow check. template ::value&& std::is_integral::value)> auto duration_cast(std::chrono::duration from) -> To { #if !FMT_SAFE_DURATION_CAST return std::chrono::duration_cast(from); #else // The conversion factor: to.count() == factor * from.count(). using factor = std::ratio_divide; using common_rep = typename std::common_type::type; common_rep count = from.count(); // This conversion is lossless. // Multiply from.count() by factor and check for overflow. if FMT_CONSTEXPR20 (factor::num != 1) { if (count > max_value() / factor::num) throw_duration_error(); const auto min = (std::numeric_limits::min)() / factor::num; if (!std::is_unsigned::value && count < min) throw_duration_error(); count *= factor::num; } if FMT_CONSTEXPR20 (factor::den != 1) count /= factor::den; int ec = 0; auto to = To(safe_duration_cast::lossless_integral_conversion( count, ec)); if (ec) throw_duration_error(); return to; #endif } template ::value&& std::is_floating_point::value)> auto duration_cast(std::chrono::duration from) -> To { #if FMT_SAFE_DURATION_CAST // Preserve infinity and NaN. if (!isfinite(from.count())) return static_cast(from.count()); // Throwing version of safe_duration_cast is only available for // integer to integer or float to float casts. int ec; To to = safe_duration_cast::safe_duration_cast(from, ec); if (ec) throw_duration_error(); return to; #else // Standard duration cast, may overflow. return std::chrono::duration_cast(from); #endif } template ::value)> auto duration_cast(std::chrono::duration from) -> To { // Mixed integer <-> float cast is not supported by safe duration_cast. return std::chrono::duration_cast(from); } template auto to_time_t(sys_time time_point) -> std::time_t { // Cannot use std::chrono::system_clock::to_time_t since this would first // require a cast to std::chrono::system_clock::time_point, which could // overflow. return detail::duration_cast>( time_point.time_since_epoch()) .count(); } } // namespace detail FMT_BEGIN_EXPORT /** * Converts given time since epoch as `std::time_t` value into calendar time, * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this * function is thread-safe on most platforms. */ inline auto gmtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; inline dispatcher(std::time_t t) : time_(t) {} inline auto run() -> bool { using namespace fmt::detail; return handle(gmtime_r(&time_, &tm_)); } inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } inline auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(gmtime_s(&tm_, &time_)); } inline auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION inline auto fallback(detail::null<>) -> bool { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; return tm != nullptr; } #endif }; auto gt = dispatcher(time); // Too big time values may be unsupported. if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); return gt.tm_; } template inline auto gmtime(sys_time time_point) -> std::tm { return gmtime(detail::to_time_t(time_point)); } namespace detail { // Writes two-digit numbers a, b and c separated by sep to buf. // The method by Pavel Novikov based on // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. inline void write_digit2_separated(char* buf, unsigned a, unsigned b, unsigned c, char sep) { ullong digits = a | (b << 24) | (static_cast(c) << 48); // Convert each value to BCD. // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. // The difference is // y - x = a * 6 // a can be found from x: // a = floor(x / 10) // then // y = x + a * 6 = x + floor(x / 10) * 6 // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; // Put low nibbles to high bytes and high nibbles to low bytes. digits = ((digits & 0x00f00000f00000f0) >> 4) | ((digits & 0x000f00000f00000f) << 8); auto usep = static_cast(sep); // Add ASCII '0' to each digit byte and insert separators. digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); constexpr size_t len = 8; if (is_big_endian()) { char tmp[len]; std::memcpy(tmp, &digits, len); std::reverse_copy(tmp, tmp + len, buf); } else { std::memcpy(buf, &digits, len); } } template FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "as"; if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; if (std::is_same::value) return "ns"; if (std::is_same::value) return detail::use_utf8 ? "µs" : "us"; if (std::is_same::value) return "ms"; if (std::is_same::value) return "cs"; if (std::is_same::value) return "ds"; if (std::is_same>::value) return "s"; if (std::is_same::value) return "das"; if (std::is_same::value) return "hs"; if (std::is_same::value) return "ks"; if (std::is_same::value) return "Ms"; if (std::is_same::value) return "Gs"; if (std::is_same::value) return "Ts"; if (std::is_same::value) return "Ps"; if (std::is_same::value) return "Es"; if (std::is_same>::value) return "min"; if (std::is_same>::value) return "h"; if (std::is_same>::value) return "d"; return nullptr; } enum class numeric_system { standard, // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. alternative }; // Glibc extensions for formatting numeric values. enum class pad_type { // Pad a numeric result string with zeros (the default). zero, // Do not pad a numeric result string. none, // Pad a numeric result string with spaces. space, }; template auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { if (pad == pad_type::none) return out; return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); } template auto write_padding(OutputIt out, pad_type pad) -> OutputIt { if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0'; return out; } // Parses a put_time-like format string and invokes handler actions. template FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, Handler&& handler) -> const Char* { if (begin == end || *begin == '}') return begin; if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; while (ptr != end) { pad_type pad = pad_type::zero; auto c = *ptr; if (c == '}') break; if (c != '%') { ++ptr; continue; } if (begin != ptr) handler.on_text(begin, ptr); ++ptr; // consume '%' if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr; switch (c) { case '_': pad = pad_type::space; ++ptr; break; case '-': pad = pad_type::none; ++ptr; break; } if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { case '%': handler.on_text(ptr - 1, ptr); break; case 'n': { const Char newline[] = {'\n'}; handler.on_text(newline, newline + 1); break; } case 't': { const Char tab[] = {'\t'}; handler.on_text(tab, tab + 1); break; } // Year: case 'Y': handler.on_year(numeric_system::standard, pad); break; case 'y': handler.on_short_year(numeric_system::standard); break; case 'C': handler.on_century(numeric_system::standard); break; case 'G': handler.on_iso_week_based_year(); break; case 'g': handler.on_iso_week_based_short_year(); break; // Day of the week: case 'a': handler.on_abbr_weekday(); break; case 'A': handler.on_full_weekday(); break; case 'w': handler.on_dec0_weekday(numeric_system::standard); break; case 'u': handler.on_dec1_weekday(numeric_system::standard); break; // Month: case 'b': case 'h': handler.on_abbr_month(); break; case 'B': handler.on_full_month(); break; case 'm': handler.on_dec_month(numeric_system::standard, pad); break; // Day of the year/month: case 'U': handler.on_dec0_week_of_year(numeric_system::standard, pad); break; case 'W': handler.on_dec1_week_of_year(numeric_system::standard, pad); break; case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break; case 'j': handler.on_day_of_year(pad); break; case 'd': handler.on_day_of_month(numeric_system::standard, pad); break; case 'e': handler.on_day_of_month(numeric_system::standard, pad_type::space); break; // Hour, minute, second: case 'H': handler.on_24_hour(numeric_system::standard, pad); break; case 'I': handler.on_12_hour(numeric_system::standard, pad); break; case 'M': handler.on_minute(numeric_system::standard, pad); break; case 'S': handler.on_second(numeric_system::standard, pad); break; // Other: case 'c': handler.on_datetime(numeric_system::standard); break; case 'x': handler.on_loc_date(numeric_system::standard); break; case 'X': handler.on_loc_time(numeric_system::standard); break; case 'D': handler.on_us_date(); break; case 'F': handler.on_iso_date(); break; case 'r': handler.on_12_hour_time(); break; case 'R': handler.on_24_hour_time(); break; case 'T': handler.on_iso_time(); break; case 'p': handler.on_am_pm(); break; case 'Q': handler.on_duration_value(); break; case 'q': handler.on_duration_unit(); break; case 'z': handler.on_utc_offset(numeric_system::standard); break; case 'Z': handler.on_tz_name(); break; // Alternative representation: case 'E': { if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { case 'Y': handler.on_year(numeric_system::alternative, pad); break; case 'y': handler.on_offset_year(); break; case 'C': handler.on_century(numeric_system::alternative); break; case 'c': handler.on_datetime(numeric_system::alternative); break; case 'x': handler.on_loc_date(numeric_system::alternative); break; case 'X': handler.on_loc_time(numeric_system::alternative); break; case 'z': handler.on_utc_offset(numeric_system::alternative); break; default: FMT_THROW(format_error("invalid format")); } break; } case 'O': if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { case 'y': handler.on_short_year(numeric_system::alternative); break; case 'm': handler.on_dec_month(numeric_system::alternative, pad); break; case 'U': handler.on_dec0_week_of_year(numeric_system::alternative, pad); break; case 'W': handler.on_dec1_week_of_year(numeric_system::alternative, pad); break; case 'V': handler.on_iso_week_of_year(numeric_system::alternative, pad); break; case 'd': handler.on_day_of_month(numeric_system::alternative, pad); break; case 'e': handler.on_day_of_month(numeric_system::alternative, pad_type::space); break; case 'w': handler.on_dec0_weekday(numeric_system::alternative); break; case 'u': handler.on_dec1_weekday(numeric_system::alternative); break; case 'H': handler.on_24_hour(numeric_system::alternative, pad); break; case 'I': handler.on_12_hour(numeric_system::alternative, pad); break; case 'M': handler.on_minute(numeric_system::alternative, pad); break; case 'S': handler.on_second(numeric_system::alternative, pad); break; case 'z': handler.on_utc_offset(numeric_system::alternative); break; default: FMT_THROW(format_error("invalid format")); } break; default: FMT_THROW(format_error("invalid format")); } begin = ptr; } if (begin != ptr) handler.on_text(begin, ptr); return ptr; } template struct null_chrono_spec_handler { FMT_CONSTEXPR void unsupported() { static_cast(this)->unsupported(); } FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_offset_year() { unsupported(); } FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } FMT_CONSTEXPR void on_full_weekday() { unsupported(); } FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); } FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); } FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_us_date() { unsupported(); } FMT_CONSTEXPR void on_iso_date() { unsupported(); } FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } FMT_CONSTEXPR void on_iso_time() { unsupported(); } FMT_CONSTEXPR void on_am_pm() { unsupported(); } FMT_CONSTEXPR void on_duration_value() { unsupported(); } FMT_CONSTEXPR void on_duration_unit() { unsupported(); } FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; class tm_format_checker : public null_chrono_spec_handler { private: bool has_timezone_ = false; public: constexpr explicit tm_format_checker(bool has_timezone) : has_timezone_(has_timezone) {} FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no format")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_CONSTEXPR void on_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_short_year(numeric_system) {} FMT_CONSTEXPR void on_offset_year() {} FMT_CONSTEXPR void on_century(numeric_system) {} FMT_CONSTEXPR void on_iso_week_based_year() {} FMT_CONSTEXPR void on_iso_week_based_short_year() {} FMT_CONSTEXPR void on_abbr_weekday() {} FMT_CONSTEXPR void on_full_weekday() {} FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} FMT_CONSTEXPR void on_abbr_month() {} FMT_CONSTEXPR void on_full_month() {} FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {} FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_day_of_year(pad_type) {} FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} FMT_CONSTEXPR void on_datetime(numeric_system) {} FMT_CONSTEXPR void on_loc_date(numeric_system) {} FMT_CONSTEXPR void on_loc_time(numeric_system) {} FMT_CONSTEXPR void on_us_date() {} FMT_CONSTEXPR void on_iso_date() {} FMT_CONSTEXPR void on_12_hour_time() {} FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_utc_offset(numeric_system) { if (!has_timezone_) FMT_THROW(format_error("no timezone")); } FMT_CONSTEXPR void on_tz_name() { if (!has_timezone_) FMT_THROW(format_error("no timezone")); } }; inline auto tm_wday_full_name(int wday) -> const char* { static constexpr const char* full_name_list[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; } inline auto tm_wday_short_name(int wday) -> const char* { static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; } inline auto tm_mon_full_name(int mon) -> const char* { static constexpr const char* full_name_list[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; } inline auto tm_mon_short_name(int mon) -> const char* { static constexpr const char* short_name_list[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; } template struct has_tm_gmtoff : std::false_type {}; template struct has_tm_gmtoff> : std::true_type {}; template struct has_tm_zone : std::false_type {}; template struct has_tm_zone> : std::true_type {}; template ::value)> auto set_tm_zone(T& time, char* tz) -> bool { time.tm_zone = tz; return true; } template ::value)> auto set_tm_zone(T&, char*) -> bool { return false; } inline auto utc() -> char* { static char tz[] = "UTC"; return tz; } // Converts value to Int and checks that it's in the range [0, upper). template ::value)> inline auto to_nonnegative_int(T value, Int upper) -> Int { if (!std::is_unsigned::value && (value < 0 || to_unsigned(value) > to_unsigned(upper))) { FMT_THROW(format_error("chrono value is out of range")); } return static_cast(value); } 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)) FMT_THROW(format_error("invalid value")); return int_value; } constexpr auto pow10(std::uint32_t n) -> long long { return n == 0 ? 1 : 10 * pow10(n - 1); } // Counts the number of fractional digits in the range [0, 18] according to the // C++20 spec. If more than 18 fractional digits are required then returns 6 for // microseconds precision. template () / 10)> struct count_fractional_digits { static constexpr int value = Num % Den == 0 ? N : count_fractional_digits::value; }; // Base case that doesn't instantiate any more templates // in order to avoid overflow. template struct count_fractional_digits { static constexpr int value = (Num % Den == 0) ? N : 6; }; // Format subseconds which are given as an integer type with an appropriate // number of digits. template void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { constexpr auto num_fractional_digits = count_fractional_digits::value; using subsecond_precision = std::chrono::duration< typename std::common_type::type, std::ratio<1, pow10(num_fractional_digits)>>; const auto fractional = d - detail::duration_cast(d); const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() : detail::duration_cast(fractional).count(); auto n = static_cast>(subseconds); const int num_digits = count_digits(n); int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); if (precision < 0) { FMT_ASSERT(!std::is_floating_point::value, ""); if (std::ratio_less::value) { *out++ = '.'; out = detail::fill_n(out, leading_zeroes, '0'); out = format_decimal(out, n, num_digits); } } else if (precision > 0) { *out++ = '.'; leading_zeroes = min_of(leading_zeroes, precision); int remaining = precision - leading_zeroes; out = detail::fill_n(out, leading_zeroes, '0'); if (remaining < num_digits) { int num_truncated_digits = num_digits - remaining; n /= to_unsigned(pow10(to_unsigned(num_truncated_digits))); if (n != 0) out = format_decimal(out, n, remaining); return; } if (n != 0) { out = format_decimal(out, n, num_digits); remaining -= num_digits; } out = detail::fill_n(out, remaining, '0'); } } // Format subseconds which are given as a floating point type with an // appropriate number of digits. We cannot pass the Duration here, as we // explicitly need to pass the Rep value in the duration_formatter. template void write_floating_seconds(memory_buffer& buf, Duration duration, int num_fractional_digits = -1) { using rep = typename Duration::rep; FMT_ASSERT(std::is_floating_point::value, ""); auto val = duration.count(); if (num_fractional_digits < 0) { // For `std::round` with fallback to `round`: // On some toolchains `std::round` is not available (e.g. GCC 6). using namespace std; num_fractional_digits = count_fractional_digits::value; if (num_fractional_digits < 6 && static_cast(round(val)) != val) num_fractional_digits = 6; } fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), std::fmod(val * static_cast(Duration::period::num) / static_cast(Duration::period::den), static_cast(60)), num_fractional_digits); } template class tm_writer { private: static constexpr int days_per_week = 7; const std::locale& loc_; bool is_classic_; OutputIt out_; const Duration* subsecs_; const std::tm& tm_; auto tm_sec() const noexcept -> int { FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); return tm_.tm_sec; } auto tm_min() const noexcept -> int { FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); return tm_.tm_min; } auto tm_hour() const noexcept -> int { FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); return tm_.tm_hour; } auto tm_mday() const noexcept -> int { FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); return tm_.tm_mday; } auto tm_mon() const noexcept -> int { FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); return tm_.tm_mon; } auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } auto tm_wday() const noexcept -> int { FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); return tm_.tm_wday; } auto tm_yday() const noexcept -> int { FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); return tm_.tm_yday; } auto tm_hour12() const noexcept -> int { auto h = tm_hour(); auto z = h < 12 ? h : h - 12; return z == 0 ? 12 : z; } // POSIX and the C Standard are unclear or inconsistent about what %C and %y // do if the year is negative or exceeds 9999. Use the convention that %C // concatenated with %y yields the same output as %Y, and that %Y contains at // least 4 characters, with more only if necessary. auto split_year_lower(long long year) const noexcept -> int { auto l = year % 100; if (l < 0) l = -l; // l in [0, 99] return static_cast(l); } // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. auto iso_year_weeks(long long curr_year) const noexcept -> int { auto prev_year = curr_year - 1; auto curr_p = (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week; auto prev_p = (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week; return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); } auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / days_per_week; } auto tm_iso_week_year() const noexcept -> long long { auto year = tm_year(); auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return year - 1; if (w > iso_year_weeks(year)) return year + 1; return year; } auto tm_iso_week_of_year() const noexcept -> int { auto year = tm_year(); auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return iso_year_weeks(year - 1); if (w > iso_year_weeks(year)) return 1; return w; } void write1(int value) { *out_++ = static_cast('0' + to_unsigned(value) % 10); } void write2(int value) { const char* d = digits2(to_unsigned(value) % 100); *out_++ = *d++; *out_++ = *d; } void write2(int value, pad_type pad) { unsigned int v = to_unsigned(value) % 100; if (v >= 10) { const char* d = digits2(v); *out_++ = *d++; *out_++ = *d; } else { out_ = detail::write_padding(out_, pad); *out_++ = static_cast('0' + v); } } void write_year_extended(long long year, pad_type pad) { // At least 4 characters. int width = 4; bool negative = year < 0; if (negative) { year = 0 - year; --width; } uint32_or_64_or_128_t n = to_unsigned(year); const int num_digits = count_digits(n); if (negative && pad == pad_type::zero) *out_++ = '-'; if (width > num_digits) out_ = detail::write_padding(out_, pad, width - num_digits); if (negative && pad != pad_type::zero) *out_++ = '-'; out_ = format_decimal(out_, n, num_digits); } void write_year(long long year, pad_type pad) { write_year_extended(year, pad); } void write_utc_offset(long long offset, numeric_system ns) { if (offset < 0) { *out_++ = '-'; offset = -offset; } else { *out_++ = '+'; } offset /= 60; write2(static_cast(offset / 60)); if (ns != numeric_system::standard) *out_++ = ':'; write2(static_cast(offset % 60)); } template ::value)> void format_utc_offset(const T& tm, numeric_system ns) { write_utc_offset(tm.tm_gmtoff, ns); } template ::value)> void format_utc_offset(const T&, numeric_system ns) { write_utc_offset(0, ns); } template ::value)> void format_tz_name(const T& tm) { out_ = write_tm_str(out_, tm.tm_zone, loc_); } template ::value)> void format_tz_name(const T&) { out_ = std::copy_n(utc(), 3, out_); } void format_localized(char format, char modifier = 0) { out_ = write(out_, tm_, loc_, format, modifier); } public: tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm, const Duration* subsecs = nullptr) : loc_(loc), is_classic_(loc_ == get_classic_locale()), out_(out), subsecs_(subsecs), tm_(tm) {} auto out() const -> OutputIt { return out_; } FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { out_ = copy(begin, end, out_); } void on_abbr_weekday() { if (is_classic_) out_ = write(out_, tm_wday_short_name(tm_wday())); else format_localized('a'); } void on_full_weekday() { if (is_classic_) out_ = write(out_, tm_wday_full_name(tm_wday())); else format_localized('A'); } void on_dec0_weekday(numeric_system ns) { if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); format_localized('w', 'O'); } void on_dec1_weekday(numeric_system ns) { if (is_classic_ || ns == numeric_system::standard) { auto wday = tm_wday(); write1(wday == 0 ? days_per_week : wday); } else { format_localized('u', 'O'); } } void on_abbr_month() { if (is_classic_) out_ = write(out_, tm_mon_short_name(tm_mon())); else format_localized('b'); } void on_full_month() { if (is_classic_) out_ = write(out_, tm_mon_full_name(tm_mon())); else format_localized('B'); } void on_datetime(numeric_system ns) { if (is_classic_) { on_abbr_weekday(); *out_++ = ' '; on_abbr_month(); *out_++ = ' '; on_day_of_month(numeric_system::standard, pad_type::space); *out_++ = ' '; on_iso_time(); *out_++ = ' '; on_year(numeric_system::standard, pad_type::space); } else { format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); } } void on_loc_date(numeric_system ns) { if (is_classic_) on_us_date(); else format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); } void on_loc_time(numeric_system ns) { if (is_classic_) on_iso_time(); else format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); } void on_us_date() { char buf[8]; write_digit2_separated(buf, to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(split_year_lower(tm_year())), '/'); out_ = copy(std::begin(buf), std::end(buf), out_); } void on_iso_date() { auto year = tm_year(); char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { write2digits(buf, static_cast(year / 100)); } else { offset = 4; write_year_extended(year, pad_type::zero); year = 0; } write_digit2_separated(buf + 2, static_cast(year % 100), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), '-'); out_ = copy(std::begin(buf) + offset, std::end(buf), out_); } void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); } void on_tz_name() { format_tz_name(tm_); } void on_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write_year(tm_year(), pad); format_localized('Y', 'E'); } void on_short_year(numeric_system ns) { if (is_classic_ || ns == numeric_system::standard) return write2(split_year_lower(tm_year())); format_localized('y', 'O'); } void on_offset_year() { if (is_classic_) return write2(split_year_lower(tm_year())); format_localized('y', 'E'); } void on_century(numeric_system ns) { if (is_classic_ || ns == numeric_system::standard) { auto year = tm_year(); auto upper = year / 100; if (year >= -99 && year < 0) { // Zero upper on negative year. *out_++ = '-'; *out_++ = '0'; } else if (upper >= 0 && upper < 100) { write2(static_cast(upper)); } else { out_ = write(out_, upper); } } else { format_localized('C', 'E'); } } void on_dec_month(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2(tm_mon() + 1, pad); format_localized('m', 'O'); } void on_dec0_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, pad); format_localized('U', 'O'); } void on_dec1_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) { auto wday = tm_wday(); write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / days_per_week, pad); } else { format_localized('W', 'O'); } } void on_iso_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2(tm_iso_week_of_year(), pad); format_localized('V', 'O'); } void on_iso_week_based_year() { write_year(tm_iso_week_year(), pad_type::zero); } void on_iso_week_based_short_year() { write2(split_year_lower(tm_iso_week_year())); } void on_day_of_year(pad_type pad) { auto yday = tm_yday() + 1; auto digit1 = yday / 100; if (digit1 != 0) write1(digit1); else out_ = detail::write_padding(out_, pad); write2(yday % 100, pad); } void on_day_of_month(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday(), pad); format_localized('d', 'O'); } void on_24_hour(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour(), pad); format_localized('H', 'O'); } void on_12_hour(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour12(), pad); format_localized('I', 'O'); } void on_minute(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) return write2(tm_min(), pad); format_localized('M', 'O'); } void on_second(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) { write2(tm_sec(), pad); if (subsecs_) { if (std::is_floating_point::value) { auto buf = memory_buffer(); write_floating_seconds(buf, *subsecs_); if (buf.size() > 1) { // Remove the leading "0", write something like ".123". out_ = copy(buf.begin() + 1, buf.end(), out_); } } else { write_fractional_seconds(out_, *subsecs_); } } } else { // Currently no formatting of subseconds when a locale is set. format_localized('S', 'O'); } } void on_12_hour_time() { if (is_classic_) { char buf[8]; write_digit2_separated(buf, to_unsigned(tm_hour12()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); out_ = copy(std::begin(buf), std::end(buf), out_); *out_++ = ' '; on_am_pm(); } else { format_localized('r'); } } void on_24_hour_time() { write2(tm_hour()); *out_++ = ':'; write2(tm_min()); } void on_iso_time() { on_24_hour_time(); *out_++ = ':'; on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { if (is_classic_) { *out_++ = tm_hour() < 12 ? 'A' : 'P'; *out_++ = 'M'; } else { format_localized('p'); } } // These apply to chrono durations but not tm. void on_duration_value() {} void on_duration_unit() {} }; struct chrono_format_checker : null_chrono_spec_handler { bool has_precision_integral = false; FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_CONSTEXPR void on_day_of_year(pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour_time() {} FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() const { if (has_precision_integral) FMT_THROW(format_error("precision not allowed for this argument type")); } FMT_CONSTEXPR void on_duration_unit() {} }; template ::value&& has_isfinite::value)> inline auto isfinite(T) -> bool { return true; } template ::value)> inline auto mod(T x, int y) -> T { return x % static_cast(y); } template ::value)> inline auto mod(T x, int y) -> T { return std::fmod(x, static_cast(y)); } // If T is an integral type, maps T to its unsigned counterpart, otherwise // leaves it unchanged (unlike std::make_unsigned). template ::value> struct make_unsigned_or_unchanged { using type = T; }; template struct make_unsigned_or_unchanged { using type = typename std::make_unsigned::type; }; template ::value)> inline auto get_milliseconds(std::chrono::duration d) -> std::chrono::duration { // This may overflow and/or the result may not fit in the target type. #if FMT_SAFE_DURATION_CAST using common_seconds_type = typename std::common_type::type; auto d_as_common = detail::duration_cast(d); auto d_as_whole_seconds = detail::duration_cast(d_as_common); // This conversion should be nonproblematic. auto diff = d_as_common - d_as_whole_seconds; auto ms = detail::duration_cast>(diff); return ms; #else auto s = detail::duration_cast(d); return detail::duration_cast(d - s); #endif } template ::value)> auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { return write(out, val); } template ::value)> auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { auto specs = format_specs(); specs.precision = precision; specs.set_type(precision >= 0 ? presentation_type::fixed : presentation_type::general); return write(out, val, specs); } template auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { return copy(unit.begin(), unit.end(), out); } template auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { // This works when wchar_t is UTF-32 because units only contain characters // that have the same representation in UTF-16 and UTF-32. utf8_to_utf16 u(unit); return copy(u.c_str(), u.c_str() + u.size(), out); } template auto format_duration_unit(OutputIt out) -> OutputIt { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); *out++ = '['; out = write(out, Period::num); if FMT_CONSTEXPR20 (Period::den != 1) { *out++ = '/'; out = write(out, Period::den); } *out++ = ']'; *out++ = 's'; return out; } class get_locale { private: union { std::locale locale_; }; bool has_locale_ = false; public: inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { if (!localized) return; ignore_unused(loc); ::new (&locale_) std::locale( #if FMT_USE_LOCALE loc.template get() #endif ); } inline ~get_locale() { if (has_locale_) locale_.~locale(); } inline operator const std::locale&() const { return has_locale_ ? locale_ : get_classic_locale(); } }; template struct duration_formatter { using iterator = basic_appender; iterator out; // rep is unsigned to avoid overflow. using rep = conditional_t::value && sizeof(Rep) < sizeof(int), unsigned, typename make_unsigned_or_unchanged::type>; rep val; int precision; locale_ref locale; bool localized = false; using seconds = std::chrono::duration; seconds s; using milliseconds = std::chrono::duration; bool negative; using tm_writer_type = tm_writer; duration_formatter(iterator o, std::chrono::duration d, locale_ref loc) : out(o), val(static_cast(d.count())), locale(loc), negative(false) { if (d.count() < 0) { val = 0 - val; negative = true; } // this may overflow and/or the result may not fit in the // target type. // might need checked conversion (rep!=Rep) s = detail::duration_cast(std::chrono::duration(val)); } // returns true if nan or inf, writes to out. auto handle_nan_inf() -> bool { if (isfinite(val)) return false; if (isnan(val)) { write_nan(); return true; } // must be +-inf if (val > 0) std::copy_n("inf", 3, out); else std::copy_n("-inf", 4, out); return true; } auto days() const -> Rep { return static_cast(s.count() / 86400); } auto hour() const -> Rep { return static_cast(mod((s.count() / 3600), 24)); } auto hour12() const -> Rep { Rep hour = static_cast(mod((s.count() / 3600), 12)); return hour <= 0 ? 12 : hour; } auto minute() const -> Rep { return static_cast(mod((s.count() / 60), 60)); } auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } auto time() const -> std::tm { auto time = std::tm(); time.tm_hour = to_nonnegative_int(hour(), 24); time.tm_min = to_nonnegative_int(minute(), 60); time.tm_sec = to_nonnegative_int(second(), 60); return time; } void write_sign() { if (!negative) return; *out++ = '-'; negative = false; } void write(Rep value, int width, pad_type pad = pad_type::zero) { write_sign(); if (isnan(value)) return write_nan(); uint32_or_64_or_128_t n = to_unsigned(to_nonnegative_int(value, max_value())); int num_digits = detail::count_digits(n); if (width > num_digits) { out = detail::write_padding(out, pad, width - num_digits); } out = format_decimal(out, n, num_digits); } void write_nan() { std::copy_n("nan", 3, out); } template void format_tm(const tm& time, Callback cb, Args... args) { if (isnan(val)) return write_nan(); get_locale loc(localized, locale); auto w = tm_writer_type(loc, out, time); (w.*cb)(args...); out = w.out(); } void on_text(const Char* begin, const Char* end) { copy(begin, end, out); } // These are not implemented because durations don't have date information. void on_abbr_weekday() {} void on_full_weekday() {} void on_dec0_weekday(numeric_system) {} void on_dec1_weekday(numeric_system) {} void on_abbr_month() {} void on_full_month() {} void on_datetime(numeric_system) {} void on_loc_date(numeric_system) {} void on_loc_time(numeric_system) {} void on_us_date() {} void on_iso_date() {} void on_utc_offset(numeric_system) {} void on_tz_name() {} void on_year(numeric_system, pad_type) {} void on_short_year(numeric_system) {} void on_offset_year() {} void on_century(numeric_system) {} void on_iso_week_based_year() {} void on_iso_week_based_short_year() {} void on_dec_month(numeric_system, pad_type) {} void on_dec0_week_of_year(numeric_system, pad_type) {} void on_dec1_week_of_year(numeric_system, pad_type) {} void on_iso_week_of_year(numeric_system, pad_type) {} void on_day_of_month(numeric_system, pad_type) {} void on_day_of_year(pad_type) { if (handle_nan_inf()) return; write(days(), 0); } void on_24_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; if (ns == numeric_system::standard) return write(hour(), 2, pad); auto time = tm(); time.tm_hour = to_nonnegative_int(hour(), 24); format_tm(time, &tm_writer_type::on_24_hour, ns, pad); } void on_12_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; if (ns == numeric_system::standard) return write(hour12(), 2, pad); auto time = tm(); time.tm_hour = to_nonnegative_int(hour12(), 12); format_tm(time, &tm_writer_type::on_12_hour, ns, pad); } void on_minute(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; if (ns == numeric_system::standard) return write(minute(), 2, pad); auto time = tm(); time.tm_min = to_nonnegative_int(minute(), 60); format_tm(time, &tm_writer_type::on_minute, ns, pad); } void on_second(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; if (ns == numeric_system::standard) { if (std::is_floating_point::value) { auto buf = memory_buffer(); write_floating_seconds(buf, std::chrono::duration(val), precision); if (negative) *out++ = '-'; if (buf.size() < 2 || buf[1] == '.') out = detail::write_padding(out, pad); out = copy(buf.begin(), buf.end(), out); } else { write(second(), 2, pad); write_fractional_seconds( out, std::chrono::duration(val), precision); } return; } auto time = tm(); time.tm_sec = to_nonnegative_int(second(), 60); format_tm(time, &tm_writer_type::on_second, ns, pad); } void on_12_hour_time() { if (handle_nan_inf()) return; format_tm(time(), &tm_writer_type::on_12_hour_time); } void on_24_hour_time() { if (handle_nan_inf()) { *out++ = ':'; handle_nan_inf(); return; } write(hour(), 2); *out++ = ':'; write(minute(), 2); } void on_iso_time() { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { if (handle_nan_inf()) return; format_tm(time(), &tm_writer_type::on_am_pm); } void on_duration_value() { if (handle_nan_inf()) return; write_sign(); out = format_duration_value(out, val, precision); } void on_duration_unit() { out = format_duration_unit(out); } }; } // namespace detail #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 using weekday = std::chrono::weekday; using day = std::chrono::day; using month = std::chrono::month; using year = std::chrono::year; using year_month_day = std::chrono::year_month_day; #else // A fallback version of weekday. class weekday { private: unsigned char value_; public: weekday() = default; constexpr explicit weekday(unsigned wd) noexcept : value_(static_cast(wd != 7 ? wd : 0)) {} constexpr auto c_encoding() const noexcept -> unsigned { return value_; } }; class day { private: unsigned char value_; public: day() = default; constexpr explicit day(unsigned d) noexcept : value_(static_cast(d)) {} constexpr explicit operator unsigned() const noexcept { return value_; } }; class month { private: unsigned char value_; public: month() = default; constexpr explicit month(unsigned m) noexcept : value_(static_cast(m)) {} constexpr explicit operator unsigned() const noexcept { return value_; } }; class year { private: int value_; public: year() = default; constexpr explicit year(int y) noexcept : value_(y) {} constexpr explicit operator int() const noexcept { return value_; } }; class year_month_day { private: fmt::year year_; fmt::month month_; fmt::day day_; public: year_month_day() = default; constexpr year_month_day(const year& y, const month& m, const day& d) noexcept : year_(y), month_(m), day_(d) {} constexpr auto year() const noexcept -> fmt::year { return year_; } constexpr auto month() const noexcept -> fmt::month { return month_; } constexpr auto day() const noexcept -> fmt::day { return day_; } }; #endif // __cpp_lib_chrono >= 201907 template struct formatter : private formatter { private: bool use_tm_formatter_ = false; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == 'L') { ++it; this->set_localized(); } use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); if (use_tm_formatter_) return formatter::format(time, ctx); detail::get_locale loc(this->localized(), ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_weekday(); return w.out(); } }; template struct formatter : private formatter { private: bool use_tm_formatter_ = false; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_mday = static_cast(static_cast(d)); if (use_tm_formatter_) return formatter::format(time, ctx); detail::get_locale loc(false, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); return w.out(); } }; template struct formatter : private formatter { private: bool use_tm_formatter_ = false; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == 'L') { ++it; this->set_localized(); } use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_mon = static_cast(static_cast(m)) - 1; if (use_tm_formatter_) return formatter::format(time, ctx); detail::get_locale loc(this->localized(), ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_month(); return w.out(); } }; template struct formatter : private formatter { private: bool use_tm_formatter_ = false; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_year = static_cast(y) - 1900; if (use_tm_formatter_) return formatter::format(time, ctx); detail::get_locale loc(false, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_year(detail::numeric_system::standard, detail::pad_type::zero); return w.out(); } }; template struct formatter : private formatter { private: bool use_tm_formatter_ = false; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(year_month_day val, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_year = static_cast(val.year()) - 1900; time.tm_mon = static_cast(static_cast(val.month())) - 1; time.tm_mday = static_cast(static_cast(val.day())); if (use_tm_formatter_) return formatter::format(time, ctx); detail::get_locale loc(true, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_iso_date(); return w.out(); } }; template struct formatter, Char> { private: format_specs specs_; detail::arg_ref width_ref_; detail::arg_ref precision_ref_; basic_string_view fmt_; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it == end || *it == '}') return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; Char c = *it; if ((c >= '0' && c <= '9') || c == '{') { it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it == end) return it; } auto checker = detail::chrono_format_checker(); if (*it == '.') { checker.has_precision_integral = !std::is_floating_point::value; it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); } if (it != end && *it == 'L') { specs_.set_localized(); ++it; } end = detail::parse_chrono_format(it, end, checker); fmt_ = {it, detail::to_unsigned(end - it)}; return end; } template auto format(std::chrono::duration d, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; auto precision = specs.precision; specs.precision = -1; auto begin = fmt_.begin(), end = fmt_.end(); // As a possible future optimization, we could avoid extra copying if width // is not specified. auto buf = basic_memory_buffer(); auto out = basic_appender(buf); detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); detail::handle_dynamic_spec(specs.dynamic_precision(), precision, precision_ref_, ctx); if (begin == end || *begin == '}') { out = detail::format_duration_value(out, d.count(), precision); detail::format_duration_unit(out); } else { auto f = detail::duration_formatter(out, d, ctx.locale()); f.precision = precision; f.localized = specs_.localized(); detail::parse_chrono_format(begin, end, f); } return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } }; template struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; basic_string_view fmt_ = detail::string_literal(); protected: auto localized() const -> bool { return specs_.localized(); } FMT_CONSTEXPR void set_localized() { specs_.set_localized(); } FMT_CONSTEXPR auto do_parse(parse_context& ctx, bool has_timezone) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it == end || *it == '}') return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; Char c = *it; if ((c >= '0' && c <= '9') || c == '{') { it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it == end) return it; } if (*it == 'L') { specs_.set_localized(); ++it; } end = detail::parse_chrono_format(it, end, detail::tm_format_checker(has_timezone)); // Replace the default format string only if the new spec is not empty. if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; return end; } template auto do_format(const std::tm& tm, FormatContext& ctx, const Duration* subsecs) const -> decltype(ctx.out()) { auto specs = specs_; auto buf = basic_memory_buffer(); auto out = basic_appender(buf); detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); auto loc_ref = specs.localized() ? ctx.locale() : locale_ref(); detail::get_locale loc(static_cast(loc_ref), loc_ref); auto w = detail::tm_writer, Char, Duration>( loc, out, tm, subsecs); detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return do_parse(ctx, detail::has_tm_gmtoff::value); } template auto format(const std::tm& tm, FormatContext& ctx) const -> decltype(ctx.out()) { return do_format(tm, ctx, nullptr); } }; // DEPRECATED! Reversed order of template parameters. template struct formatter, Char> : private formatter { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return this->do_parse(ctx, true); } template auto format(sys_time val, FormatContext& ctx) const -> decltype(ctx.out()) { std::tm tm = gmtime(val); using period = typename Duration::period; if FMT_CONSTEXPR20 (period::num == 1 && period::den == 1 && !std::is_floating_point< typename Duration::rep>::value) { detail::set_tm_zone(tm, detail::utc()); return formatter::format(tm, ctx); } Duration epoch = val.time_since_epoch(); Duration subsecs = detail::duration_cast( epoch - detail::duration_cast(epoch)); if (subsecs.count() < 0) { auto second = detail::duration_cast(std::chrono::seconds(1)); if (tm.tm_sec != 0) { --tm.tm_sec; } else { tm = gmtime(val - second); detail::set_tm_zone(tm, detail::utc()); } subsecs += second; } return formatter::do_format(tm, ctx, &subsecs); } }; template struct formatter, Char> : formatter, Char> { template auto format(utc_time val, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter, Char>::format( detail::utc_clock::to_sys(val), ctx); } }; template struct formatter, Char> : private formatter { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return this->do_parse(ctx, false); } template auto format(local_time val, FormatContext& ctx) const -> decltype(ctx.out()) { auto time_since_epoch = val.time_since_epoch(); auto seconds_since_epoch = detail::duration_cast(time_since_epoch); // Use gmtime to prevent time zone conversion since local_time has an // unspecified time zone. std::tm t = gmtime(seconds_since_epoch.count()); using period = typename Duration::period; if (period::num == 1 && period::den == 1 && !std::is_floating_point::value) { return formatter::format(t, ctx); } auto subsecs = detail::duration_cast(time_since_epoch - seconds_since_epoch); return formatter::do_format(t, ctx, &subsecs); } }; FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_CHRONO_H_ ================================================ FILE: include/fmt/color.h ================================================ // Formatting library for C++ - color support // // Copyright (c) 2018 - present, Victor Zverovich and fmt contributors // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_COLOR_H_ #define FMT_COLOR_H_ #include "format.h" FMT_BEGIN_NAMESPACE FMT_BEGIN_EXPORT enum class color : uint32_t { alice_blue = 0xF0F8FF, // rgb(240,248,255) antique_white = 0xFAEBD7, // rgb(250,235,215) aqua = 0x00FFFF, // rgb(0,255,255) aquamarine = 0x7FFFD4, // rgb(127,255,212) azure = 0xF0FFFF, // rgb(240,255,255) beige = 0xF5F5DC, // rgb(245,245,220) bisque = 0xFFE4C4, // rgb(255,228,196) black = 0x000000, // rgb(0,0,0) blanched_almond = 0xFFEBCD, // rgb(255,235,205) blue = 0x0000FF, // rgb(0,0,255) blue_violet = 0x8A2BE2, // rgb(138,43,226) brown = 0xA52A2A, // rgb(165,42,42) burly_wood = 0xDEB887, // rgb(222,184,135) cadet_blue = 0x5F9EA0, // rgb(95,158,160) chartreuse = 0x7FFF00, // rgb(127,255,0) chocolate = 0xD2691E, // rgb(210,105,30) coral = 0xFF7F50, // rgb(255,127,80) cornflower_blue = 0x6495ED, // rgb(100,149,237) cornsilk = 0xFFF8DC, // rgb(255,248,220) crimson = 0xDC143C, // rgb(220,20,60) cyan = 0x00FFFF, // rgb(0,255,255) dark_blue = 0x00008B, // rgb(0,0,139) dark_cyan = 0x008B8B, // rgb(0,139,139) dark_golden_rod = 0xB8860B, // rgb(184,134,11) dark_gray = 0xA9A9A9, // rgb(169,169,169) dark_green = 0x006400, // rgb(0,100,0) dark_khaki = 0xBDB76B, // rgb(189,183,107) dark_magenta = 0x8B008B, // rgb(139,0,139) dark_olive_green = 0x556B2F, // rgb(85,107,47) dark_orange = 0xFF8C00, // rgb(255,140,0) dark_orchid = 0x9932CC, // rgb(153,50,204) dark_red = 0x8B0000, // rgb(139,0,0) dark_salmon = 0xE9967A, // rgb(233,150,122) dark_sea_green = 0x8FBC8F, // rgb(143,188,143) dark_slate_blue = 0x483D8B, // rgb(72,61,139) dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) dark_turquoise = 0x00CED1, // rgb(0,206,209) dark_violet = 0x9400D3, // rgb(148,0,211) deep_pink = 0xFF1493, // rgb(255,20,147) deep_sky_blue = 0x00BFFF, // rgb(0,191,255) dim_gray = 0x696969, // rgb(105,105,105) dodger_blue = 0x1E90FF, // rgb(30,144,255) fire_brick = 0xB22222, // rgb(178,34,34) floral_white = 0xFFFAF0, // rgb(255,250,240) forest_green = 0x228B22, // rgb(34,139,34) fuchsia = 0xFF00FF, // rgb(255,0,255) gainsboro = 0xDCDCDC, // rgb(220,220,220) ghost_white = 0xF8F8FF, // rgb(248,248,255) gold = 0xFFD700, // rgb(255,215,0) golden_rod = 0xDAA520, // rgb(218,165,32) gray = 0x808080, // rgb(128,128,128) green = 0x008000, // rgb(0,128,0) green_yellow = 0xADFF2F, // rgb(173,255,47) honey_dew = 0xF0FFF0, // rgb(240,255,240) hot_pink = 0xFF69B4, // rgb(255,105,180) indian_red = 0xCD5C5C, // rgb(205,92,92) indigo = 0x4B0082, // rgb(75,0,130) ivory = 0xFFFFF0, // rgb(255,255,240) khaki = 0xF0E68C, // rgb(240,230,140) lavender = 0xE6E6FA, // rgb(230,230,250) lavender_blush = 0xFFF0F5, // rgb(255,240,245) lawn_green = 0x7CFC00, // rgb(124,252,0) lemon_chiffon = 0xFFFACD, // rgb(255,250,205) light_blue = 0xADD8E6, // rgb(173,216,230) light_coral = 0xF08080, // rgb(240,128,128) light_cyan = 0xE0FFFF, // rgb(224,255,255) light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) light_gray = 0xD3D3D3, // rgb(211,211,211) light_green = 0x90EE90, // rgb(144,238,144) light_pink = 0xFFB6C1, // rgb(255,182,193) light_salmon = 0xFFA07A, // rgb(255,160,122) light_sea_green = 0x20B2AA, // rgb(32,178,170) light_sky_blue = 0x87CEFA, // rgb(135,206,250) light_slate_gray = 0x778899, // rgb(119,136,153) light_steel_blue = 0xB0C4DE, // rgb(176,196,222) light_yellow = 0xFFFFE0, // rgb(255,255,224) lime = 0x00FF00, // rgb(0,255,0) lime_green = 0x32CD32, // rgb(50,205,50) linen = 0xFAF0E6, // rgb(250,240,230) magenta = 0xFF00FF, // rgb(255,0,255) maroon = 0x800000, // rgb(128,0,0) medium_aquamarine = 0x66CDAA, // rgb(102,205,170) medium_blue = 0x0000CD, // rgb(0,0,205) medium_orchid = 0xBA55D3, // rgb(186,85,211) medium_purple = 0x9370DB, // rgb(147,112,219) medium_sea_green = 0x3CB371, // rgb(60,179,113) medium_slate_blue = 0x7B68EE, // rgb(123,104,238) medium_spring_green = 0x00FA9A, // rgb(0,250,154) medium_turquoise = 0x48D1CC, // rgb(72,209,204) medium_violet_red = 0xC71585, // rgb(199,21,133) midnight_blue = 0x191970, // rgb(25,25,112) mint_cream = 0xF5FFFA, // rgb(245,255,250) misty_rose = 0xFFE4E1, // rgb(255,228,225) moccasin = 0xFFE4B5, // rgb(255,228,181) navajo_white = 0xFFDEAD, // rgb(255,222,173) navy = 0x000080, // rgb(0,0,128) old_lace = 0xFDF5E6, // rgb(253,245,230) olive = 0x808000, // rgb(128,128,0) olive_drab = 0x6B8E23, // rgb(107,142,35) orange = 0xFFA500, // rgb(255,165,0) orange_red = 0xFF4500, // rgb(255,69,0) orchid = 0xDA70D6, // rgb(218,112,214) pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) pale_green = 0x98FB98, // rgb(152,251,152) pale_turquoise = 0xAFEEEE, // rgb(175,238,238) pale_violet_red = 0xDB7093, // rgb(219,112,147) papaya_whip = 0xFFEFD5, // rgb(255,239,213) peach_puff = 0xFFDAB9, // rgb(255,218,185) peru = 0xCD853F, // rgb(205,133,63) pink = 0xFFC0CB, // rgb(255,192,203) plum = 0xDDA0DD, // rgb(221,160,221) powder_blue = 0xB0E0E6, // rgb(176,224,230) purple = 0x800080, // rgb(128,0,128) rebecca_purple = 0x663399, // rgb(102,51,153) red = 0xFF0000, // rgb(255,0,0) rosy_brown = 0xBC8F8F, // rgb(188,143,143) royal_blue = 0x4169E1, // rgb(65,105,225) saddle_brown = 0x8B4513, // rgb(139,69,19) salmon = 0xFA8072, // rgb(250,128,114) sandy_brown = 0xF4A460, // rgb(244,164,96) sea_green = 0x2E8B57, // rgb(46,139,87) sea_shell = 0xFFF5EE, // rgb(255,245,238) sienna = 0xA0522D, // rgb(160,82,45) silver = 0xC0C0C0, // rgb(192,192,192) sky_blue = 0x87CEEB, // rgb(135,206,235) slate_blue = 0x6A5ACD, // rgb(106,90,205) slate_gray = 0x708090, // rgb(112,128,144) snow = 0xFFFAFA, // rgb(255,250,250) spring_green = 0x00FF7F, // rgb(0,255,127) steel_blue = 0x4682B4, // rgb(70,130,180) tan = 0xD2B48C, // rgb(210,180,140) teal = 0x008080, // rgb(0,128,128) thistle = 0xD8BFD8, // rgb(216,191,216) tomato = 0xFF6347, // rgb(255,99,71) turquoise = 0x40E0D0, // rgb(64,224,208) violet = 0xEE82EE, // rgb(238,130,238) wheat = 0xF5DEB3, // rgb(245,222,179) white = 0xFFFFFF, // rgb(255,255,255) white_smoke = 0xF5F5F5, // rgb(245,245,245) yellow = 0xFFFF00, // rgb(255,255,0) yellow_green = 0x9ACD32 // rgb(154,205,50) }; // enum class color enum class terminal_color : uint8_t { black = 30, red, green, yellow, blue, magenta, cyan, white, bright_black = 90, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white }; enum class emphasis : uint8_t { bold = 1, faint = 1 << 1, italic = 1 << 2, underline = 1 << 3, blink = 1 << 4, reverse = 1 << 5, conceal = 1 << 6, strikethrough = 1 << 7, }; // rgb is a struct for red, green and blue colors. // Using the name "rgb" makes some editors show the color in a tooltip. struct rgb { constexpr rgb() : r(0), g(0), b(0) {} constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} constexpr rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} constexpr rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), b(uint32_t(hex) & 0xFF) {} uint8_t r; uint8_t g; uint8_t b; }; namespace detail { // A bit-packed variant of an RGB color, a terminal color, or unset color. // see text_style for the bit-packing scheme. struct color_type { constexpr color_type() noexcept = default; constexpr color_type(color rgb_color) noexcept : value_(static_cast(rgb_color) | (1 << 24)) {} constexpr color_type(rgb rgb_color) noexcept : color_type(static_cast( (static_cast(rgb_color.r) << 16) | (static_cast(rgb_color.g) << 8) | rgb_color.b)) {} constexpr color_type(terminal_color term_color) noexcept : value_(static_cast(term_color) | (3 << 24)) {} constexpr auto is_terminal_color() const noexcept -> bool { return (value_ & (1 << 25)) != 0; } constexpr auto value() const noexcept -> uint32_t { return value_ & 0xFFFFFF; } constexpr color_type(uint32_t value) noexcept : value_(value) {} uint32_t value_ = 0; }; } // namespace detail /// A text style consisting of foreground and background colors and emphasis. class text_style { // The information is packed as follows: // ┌──┐ // │ 0│─┐ // │..│ ├── foreground color value // │23│─┘ // ├──┤ // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused) // ├──┤ // │26│──── overflow bit, always zero (see below) // ├──┤ // │27│─┐ // │..│ │ // │50│ │ // ├──┤ │ // │51│ ├── background color (same format as the foreground color) // │52│ │ // ├──┤ │ // │53│─┘ // ├──┤ // │54│─┐ // │..│ ├── emphases // │61│─┘ // ├──┤ // │62│─┬── unused // │63│─┘ // └──┘ // The overflow bits are there to make operator|= efficient. // When ORing, we must throw if, for either the foreground or background, // one style specifies a terminal color and the other specifies any color // (terminal or RGB); in other words, if one discriminator is 11 and the // other is 11 or 01. // // We do that check by adding the styles. Consider what adding does to each // possible pair of discriminators: // 00 + 00 = 000 // 01 + 00 = 001 // 11 + 00 = 011 // 01 + 01 = 010 // 11 + 01 = 100 (!!) // 11 + 11 = 110 (!!) // In the last two cases, the ones we want to catch, the third bit——the // overflow bit——is set. Bingo. // // We must take into account the possible carry bit from the bits // before the discriminator. The only potentially problematic case is // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry // bit is impossible in that case, because 00 (unset color) means the // 24 bits that precede the discriminator are all zero. // // This test can be applied to both colors simultaneously. public: FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept : style_(static_cast(em) << 54) {} FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) report_error("can't OR a terminal color"); style_ |= rhs.style_; return *this; } friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) -> text_style { return lhs |= rhs; } FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool { return style_ == rhs.style_; } FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool { return !(*this == rhs); } FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { return (style_ & (1 << 24)) != 0; } FMT_CONSTEXPR auto has_background() const noexcept -> bool { return (style_ & (1ULL << 51)) != 0; } FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { return (style_ >> 54) != 0; } FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); return style_ & 0x3FFFFFF; } FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_ASSERT(has_background(), "no background specified for this style"); return (style_ >> 27) & 0x3FFFFFF; } FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); return static_cast(style_ >> 54); } private: FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept -> text_style; friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept -> text_style; uint64_t style_ = 0; }; /// Creates a text style from the foreground (text) color. FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept -> text_style { return foreground.value_; } /// Creates a text style from the background color. FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept -> text_style { return static_cast(background.value_) << 27; } FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept -> text_style { return text_style(lhs) | rhs; } namespace detail { template struct ansi_color_escape { FMT_CONSTEXPR ansi_color_escape(color_type text_color, const char* esc) noexcept { // If we have a terminal color, we need to output another escape code // sequence. if (text_color.is_terminal_color()) { bool is_background = esc == string_view("\x1b[48;2;"); uint32_t value = text_color.value(); // Background ASCII codes are the same as the foreground ones but with // 10 more. if (is_background) value += 10u; buffer[size++] = static_cast('\x1b'); buffer[size++] = static_cast('['); if (value >= 100u) { buffer[size++] = static_cast('1'); value %= 100u; } buffer[size++] = static_cast('0' + value / 10u); buffer[size++] = static_cast('0' + value % 10u); buffer[size++] = static_cast('m'); return; } for (int i = 0; i < 7; i++) { buffer[i] = static_cast(esc[i]); } rgb color(text_color.value()); to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); size = 19; } FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { uint8_t em_codes[num_emphases] = {}; if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; buffer[size++] = static_cast('\x1b'); buffer[size++] = static_cast('['); for (size_t i = 0; i < num_emphases; ++i) { if (!em_codes[i]) continue; buffer[size++] = static_cast('0' + em_codes[i]); buffer[size++] = static_cast(';'); } buffer[size - 1] = static_cast('m'); } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } FMT_CONSTEXPR auto end() const noexcept -> const Char* { return buffer + size; } private: static constexpr size_t num_emphases = 8; Char buffer[7u + 4u * num_emphases] = {}; size_t size = 0; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, char delimiter) noexcept { out[0] = static_cast('0' + c / 100); out[1] = static_cast('0' + c / 10 % 10); out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept -> bool { return static_cast(em) & static_cast(mask); } }; template FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept -> ansi_color_escape { return ansi_color_escape(foreground, "\x1b[38;2;"); } template FMT_CONSTEXPR auto make_background_color(color_type background) noexcept -> ansi_color_escape { return ansi_color_escape(background, "\x1b[48;2;"); } template FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept -> ansi_color_escape { return ansi_color_escape(em); } template inline void reset_color(buffer& buffer) { auto reset_color = string_view("\x1b[0m"); buffer.append(reset_color.begin(), reset_color.end()); } template struct styled_arg : view { const T& value; text_style style; styled_arg(const T& v, text_style s) : value(v), style(s) {} }; template void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, basic_format_args> args) { if (ts.has_emphasis()) { auto emphasis = make_emphasis(ts.get_emphasis()); buf.append(emphasis.begin(), emphasis.end()); } if (ts.has_foreground()) { auto foreground = make_foreground_color(ts.get_foreground()); buf.append(foreground.begin(), foreground.end()); } if (ts.has_background()) { auto background = make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } vformat_to(buf, fmt, args); if (ts != text_style()) reset_color(buf); } } // namespace detail inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { auto buf = memory_buffer(); detail::vformat_to(buf, ts, fmt, args); print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); } /** * Formats a string and prints it to the specified file stream using ANSI * escape sequences to specify text formatting. * * **Example**: * * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23); */ template void print(FILE* f, text_style ts, format_string fmt, T&&... args) { vprint(f, ts, fmt.str, vargs{{args...}}); } /** * Formats a string and prints it to stdout using ANSI escape sequences to * specify text formatting. * * **Example**: * * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23); */ template void print(text_style ts, format_string fmt, T&&... args) { return print(stdout, ts, fmt, std::forward(args)...); } inline auto vformat(text_style ts, string_view fmt, format_args args) -> std::string { auto buf = memory_buffer(); detail::vformat_to(buf, ts, fmt, args); return fmt::to_string(buf); } /** * Formats arguments and returns the result as a string using ANSI escape * sequences to specify text formatting. * * **Example**: * * ``` * #include * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), * "The answer is {}", 42); * ``` */ template inline auto format(text_style ts, format_string fmt, T&&... args) -> std::string { return fmt::vformat(ts, fmt.str, vargs{{args...}}); } /// Formats a string with the given text_style and writes the output to `out`. template ::value)> auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, ts, fmt, args); return detail::get_iterator(buf, out); } /** * Formats arguments with the given text style, writes the result to the output * iterator `out` and returns the iterator past the end of the output range. * * **Example**: * * std::vector out; * fmt::format_to(std::back_inserter(out), * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); */ template ::value)> inline auto format_to(OutputIt out, text_style ts, format_string fmt, T&&... args) -> OutputIt { return vformat_to(out, ts, fmt.str, vargs{{args...}}); } template struct formatter, Char> : formatter { template auto format(const detail::styled_arg& arg, FormatContext& ctx) const -> decltype(ctx.out()) { const auto& ts = arg.style; auto out = ctx.out(); bool has_style = false; if (ts.has_emphasis()) { has_style = true; auto emphasis = detail::make_emphasis(ts.get_emphasis()); out = detail::copy(emphasis.begin(), emphasis.end(), out); } if (ts.has_foreground()) { has_style = true; auto foreground = detail::make_foreground_color(ts.get_foreground()); out = detail::copy(foreground.begin(), foreground.end(), out); } if (ts.has_background()) { has_style = true; auto background = detail::make_background_color(ts.get_background()); out = detail::copy(background.begin(), background.end(), out); } out = formatter::format(arg.value, ctx); if (has_style) { auto reset_color = string_view("\x1b[0m"); out = detail::copy(reset_color.begin(), reset_color.end(), out); } return out; } }; /** * Returns an argument that will be formatted using ANSI escape sequences, * to be used in a formatting function. * * **Example**: * * fmt::print("Elapsed time: {0:.2f} seconds", * fmt::styled(1.23, fmt::fg(fmt::color::green) | * fmt::bg(fmt::color::blue))); */ template FMT_CONSTEXPR auto styled(const T& value, text_style ts) -> detail::styled_arg> { return detail::styled_arg>{value, ts}; } FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_COLOR_H_ ================================================ FILE: include/fmt/compile.h ================================================ // Formatting library for C++ - experimental format string compilation // // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_COMPILE_H_ #define FMT_COMPILE_H_ #ifndef FMT_MODULE # include // std::back_inserter #endif #include "format.h" FMT_BEGIN_NAMESPACE FMT_BEGIN_EXPORT // A compile-time string which is compiled into fast formatting code. class compiled_string {}; template struct is_compiled_string : std::is_base_of {}; /** * Converts a string literal `s` into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires C++17 * `constexpr if` compiler support. * * **Example**: * * // Converts 42 into std::string using the most efficient method and no * // runtime format string processing. * std::string s = fmt::format(FMT_COMPILE("{}"), 42); */ #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) #else # define FMT_COMPILE(s) FMT_STRING(s) #endif /** * Converts a string literal into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires support * for class types in constant template parameters (a C++20 feature). * * **Example**: * * // Converts 42 into std::string using the most efficient method and no * // runtime format string processing. * using namespace fmt::literals; * std::string s = fmt::format("{}"_cf, 42); */ #if FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { template constexpr auto operator""_cf() { return FMT_COMPILE(Str.data); } } // namespace literals #endif FMT_END_EXPORT namespace detail { template constexpr auto first(const T& value, const Tail&...) -> const T& { return value; } #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) template struct type_list {}; // Returns a reference to the argument at index N from [first, rest...]. template constexpr auto get([[maybe_unused]] const T& first, [[maybe_unused]] const Args&... rest) -> const auto& { static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); if constexpr (N == 0) return first; else return detail::get(rest...); } # if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (is_static_named_arg()) { if (name == T::name) return N; } if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name(name); (void)name; // Workaround an MSVC bug about "unused" parameter. return -1; } # endif template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { # if FMT_USE_NONTYPE_TEMPLATE_ARGS if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); # endif (void)name; return -1; } template constexpr auto get_arg_index_by_name(basic_string_view name, type_list) -> int { return get_arg_index_by_name(name); } template struct get_type_impl; template struct get_type_impl> { using type = remove_cvref_t(std::declval()...))>; }; template using get_type = typename get_type_impl::type; template struct is_compiled_format : std::false_type {}; template struct text { basic_string_view data; using char_type = Char; template constexpr auto format(OutputIt out, const T&...) const -> OutputIt { return write(out, data); } }; template struct is_compiled_format> : std::true_type {}; template constexpr auto make_text(basic_string_view s, size_t pos, size_t size) -> text { return {{&s[pos], size}}; } template struct code_unit { Char value; using char_type = Char; template constexpr auto format(OutputIt out, const T&...) const -> OutputIt { *out++ = value; return out; } }; // This ensures that the argument type is convertible to `const T&`. template constexpr auto get_arg_checked(const Args&... args) -> const T& { const auto& arg = detail::get(args...); if constexpr (detail::is_named_arg>()) { return arg.value; } else { return arg; } } template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N. template struct field { using char_type = Char; template constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { const V& arg = get_arg_checked(args...); if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); return copy(s.begin(), s.end(), out); } else { return write(out, arg); } } }; template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument with name. template struct runtime_named_field { using char_type = Char; basic_string_view name; template constexpr static auto try_format_argument( OutputIt& out, // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 [[maybe_unused]] basic_string_view arg_name, const T& arg) -> bool { if constexpr (is_named_arg::type>::value) { if (arg_name == arg.name) { out = write(out, arg.value); return true; } } return false; } template constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { bool found = (try_format_argument(out, name, args) || ...); if (!found) { FMT_THROW(format_error("argument with specified name is not found")); } return out; } }; template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N and has format specifiers. template struct spec_field { using char_type = Char; formatter fmt; template constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const -> OutputIt { const auto& vargs = fmt::make_format_args>(args...); basic_format_context ctx(out, vargs); return fmt.format(get_arg_checked(args...), ctx); } }; template struct is_compiled_format> : std::true_type {}; template struct concat { L lhs; R rhs; using char_type = typename L::char_type; template constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { out = lhs.format(out, args...); return rhs.format(out, args...); } }; template struct is_compiled_format> : std::true_type {}; template constexpr auto make_concat(L lhs, R rhs) -> concat { return {lhs, rhs}; } struct unknown_format {}; template constexpr auto parse_text(basic_string_view str, size_t pos) -> size_t { for (size_t size = str.size(); pos != size; ++pos) { if (str[pos] == '{' || str[pos] == '}') break; } return pos; } template constexpr auto compile_format_string(S fmt); template constexpr auto parse_tail(T head, S fmt) { if constexpr (POS != basic_string_view(fmt).size()) { constexpr auto tail = compile_format_string(fmt); if constexpr (std::is_same, unknown_format>()) return tail; else return make_concat(head, tail); } else { return head; } } template struct parse_specs_result { formatter fmt; size_t end; int next_arg_id; }; enum { manual_indexing_id = -1 }; template constexpr auto parse_specs(basic_string_view str, size_t pos, int next_arg_id) -> parse_specs_result { str.remove_prefix(pos); auto ctx = compile_parse_context(str, max_value(), nullptr, next_arg_id); auto f = formatter(); auto end = f.parse(ctx); return {f, pos + fmt::detail::to_unsigned(end - str.data()), next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; } template struct arg_id_handler { arg_id_kind kind; arg_ref arg_id; constexpr auto on_auto() -> int { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } constexpr auto on_index(int id) -> int { kind = arg_id_kind::index; arg_id = arg_ref(id); return 0; } constexpr auto on_name(basic_string_view id) -> int { kind = arg_id_kind::name; arg_id = arg_ref(id); return 0; } }; template struct parse_arg_id_result { arg_id_kind kind; arg_ref arg_id; const Char* arg_id_end; }; template constexpr auto parse_arg_id(const Char* begin, const Char* end) { auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; auto arg_id_end = parse_arg_id(begin, end, handler); return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; } template struct field_type { using type = remove_cvref_t; }; template struct field_type::value>> { using type = remove_cvref_t; }; template constexpr auto parse_replacement_field_then_tail(S fmt) { using char_type = typename S::char_type; constexpr auto str = basic_string_view(fmt); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); if constexpr (c == '}') { return parse_tail( field::type, ARG_INDEX>(), fmt); } else if constexpr (c != ':') { FMT_THROW(format_error("expected ':'")); } else { constexpr auto result = parse_specs::type>( str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); if constexpr (result.end >= str.size() || str[result.end] != '}') { FMT_THROW(format_error("expected '}'")); return 0; } else { return parse_tail( spec_field::type, ARG_INDEX>{ result.fmt}, fmt); } } } // Compiles a non-empty format string and returns the compiled representation // or unknown_format() on unrecognized input. template constexpr auto compile_format_string(S fmt) { using char_type = typename S::char_type; constexpr auto str = basic_string_view(fmt); if constexpr (str[POS] == '{') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '{' in format string")); if constexpr (str[POS + 1] == '{') { return parse_tail( make_text(str, POS, 1), fmt); } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { static_assert(ID != manual_indexing_id, "cannot switch from manual to automatic argument indexing"); constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail< get_type, Args, POS + 1, ID, next_id, DYNAMIC_NAMES>(fmt); } else { constexpr auto arg_id_result = parse_arg_id(str.data() + POS + 1, str.data() + str.size()); constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); constexpr char_type c = arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); static_assert(c == '}' || c == ':', "missing '}' in format string"); if constexpr (arg_id_result.kind == arg_id_kind::index) { static_assert( ID == manual_indexing_id || ID == 0, "cannot switch from automatic to manual argument indexing"); constexpr auto arg_index = arg_id_result.arg_id.index; return parse_replacement_field_then_tail< get_type, Args, arg_id_end_pos, arg_index, manual_indexing_id, DYNAMIC_NAMES>(fmt); } else if constexpr (arg_id_result.kind == arg_id_kind::name) { constexpr auto arg_index = get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); static_assert(arg_index >= 0 || DYNAMIC_NAMES, "named argument not found"); if constexpr (arg_index >= 0) { constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail< decltype(get_type::value), Args, arg_id_end_pos, arg_index, next_id, DYNAMIC_NAMES>(fmt); } else if constexpr (c == '}') { return parse_tail( runtime_named_field{arg_id_result.arg_id.name}, fmt); } else if constexpr (c == ':') { return unknown_format(); // no type info for specs parsing } } } } else if constexpr (str[POS] == '}') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '}' in format string")); return parse_tail(make_text(str, POS, 1), fmt); } else { constexpr auto end = parse_text(str, POS + 1); if constexpr (end - POS > 1) { return parse_tail( make_text(str, POS, end - POS), fmt); } else { return parse_tail( code_unit{str[POS]}, fmt); } } } template ::value)> constexpr auto compile(S fmt) { constexpr auto str = basic_string_view(fmt); if constexpr (str.size() == 0) { return detail::make_text(str, 0, 0); } else { constexpr int num_static_named_args = detail::count_static_named_args(); constexpr auto result = detail::compile_format_string< detail::type_list, 0, 0, num_static_named_args != detail::count_named_args()>(fmt); return result; } } #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) } // namespace detail FMT_BEGIN_EXPORT #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) template ::value)> FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf, const T&... args) -> std::basic_string { auto s = std::basic_string(); cf.format(std::back_inserter(s), args...); return s; } template ::value)> constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf, const T&... args) -> OutputIt { return cf.format(out, args...); } template ::value)> FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args) -> std::basic_string { if constexpr (std::is_same::value) { constexpr auto str = basic_string_view(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { const auto& first = detail::first(args...); if constexpr (detail::is_named_arg< remove_cvref_t>::value) { return fmt::to_string(first.value); } else { return fmt::to_string(first); } } } constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format( static_cast>(S()), std::forward(args)...); } else { return fmt::format(compiled, std::forward(args)...); } } template ::value)> FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt { constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format_to( out, static_cast>(S()), std::forward(args)...); } else { return fmt::format_to(out, compiled, std::forward(args)...); } } #endif template ::value)> auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); fmt::format_to(appender(buf), fmt, std::forward(args)...); return {buf.out(), buf.count()}; } template ::value)> FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); fmt::format_to(appender(buf), fmt, std::forward(args)...); return buf.count(); } template ::value)> void print(std::FILE* f, const S& fmt, T&&... args) { auto buf = memory_buffer(); fmt::format_to(appender(buf), fmt, std::forward(args)...); detail::print(f, {buf.data(), buf.size()}); } template ::value)> void print(const S& fmt, T&&... args) { print(stdout, fmt, std::forward(args)...); } template class static_format_result { private: char data[N]; public: template ::value)> explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) { *fmt::format_to(data, fmt, std::forward(args)...) = '\0'; } FMT_CONSTEXPR auto str() const -> fmt::string_view { return {data, N - 1}; } FMT_CONSTEXPR auto c_str() const -> const char* { return data; } }; /** * Formats arguments according to the format string `fmt_str` and produces * a string of the exact required size at compile time. Both the format string * and the arguments must be compile-time expressions. * * The resulting string can be accessed as a C string via `c_str()` or as * a `fmt::string_view` via `str()`. * * **Example**: * * // Produces the static string "42" at compile time. * static constexpr auto result = FMT_STATIC_FORMAT("{}", 42); * const char* s = result.c_str(); */ #define FMT_STATIC_FORMAT(fmt_str, ...) \ fmt::static_format_result< \ fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \ FMT_COMPILE(fmt_str), __VA_ARGS__) FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_COMPILE_H_ ================================================ FILE: include/fmt/core.h ================================================ #include "base.h" // Using fmt::format via fmt/core.h has been deprecated since version 11 // and now requires an explicit opt in. #ifdef FMT_DEPRECATED_HEAVY_CORE # include "format.h" #endif ================================================ FILE: include/fmt/fmt-c.h ================================================ // Formatting library for C++ - the C API // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_C_H_ #define FMT_C_H_ #include // bool #include // size_t #ifdef __cplusplus extern "C" { #endif typedef enum { fmt_int = 1, fmt_uint, fmt_bool = 7, fmt_char, fmt_float, fmt_double, fmt_long_double, fmt_cstring, fmt_pointer = 14 } fmt_type; typedef struct { fmt_type type; union { long long int_value; unsigned long long uint_value; // Used for FMT_PTR and custom data bool bool_value; char char_value; float float_value; double double_value; long double long_double_value; const char* cstring; const void* pointer; } value; } fmt_arg; 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); #ifdef __cplusplus } #endif #ifndef __cplusplus static inline fmt_arg fmt_from_int(long long x) { return (fmt_arg){.type = fmt_int, .value.int_value = x}; } static inline fmt_arg fmt_from_uint(unsigned long long x) { return (fmt_arg){.type = fmt_uint, .value.uint_value = x}; } static inline fmt_arg fmt_from_bool(bool x) { return (fmt_arg){.type = fmt_bool, .value.bool_value = x}; } static inline fmt_arg fmt_from_char(char x) { return (fmt_arg){.type = fmt_char, .value.char_value = x}; } static inline fmt_arg fmt_from_float(float x) { return (fmt_arg){.type = fmt_float, .value.float_value = x}; } static inline fmt_arg fmt_from_double(double x) { return (fmt_arg){.type = fmt_double, .value.double_value = x}; } static inline fmt_arg fmt_from_long_double(long double x) { return (fmt_arg){.type = fmt_long_double, .value.long_double_value = x}; } static inline fmt_arg fmt_from_str(const char* x) { return (fmt_arg){.type = fmt_cstring, .value.cstring = x}; } static inline fmt_arg fmt_from_ptr(const void* x) { return (fmt_arg){.type = fmt_pointer, .value.pointer = x}; } void fmt_unsupported_type(void); # if !defined(_MSC_VER) || defined(__clang__) typedef signed char fmt_signed_char; # else typedef enum {} fmt_signed_char; # endif // Require modern MSVC with conformant preprocessor. # if defined(_MSC_VER) && !defined(__clang__) && \ (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) # error "C API requires MSVC 2019+ with /Zc:preprocessor flag." # endif # define FMT_MAKE_ARG(x) \ _Generic((x), \ fmt_signed_char: fmt_from_int, \ unsigned char: fmt_from_uint, \ short: fmt_from_int, \ unsigned short: fmt_from_uint, \ int: fmt_from_int, \ unsigned int: fmt_from_uint, \ long: fmt_from_int, \ unsigned long: fmt_from_uint, \ long long: fmt_from_int, \ unsigned long long: fmt_from_uint, \ bool: fmt_from_bool, \ char: fmt_from_char, \ float: fmt_from_float, \ double: fmt_from_double, \ long double: fmt_from_long_double, \ char*: fmt_from_str, \ const char*: fmt_from_str, \ void*: fmt_from_ptr, \ const void*: fmt_from_ptr, \ default: fmt_unsupported_type)(x) # define FMT_CAT(a, b) FMT_CAT_(a, b) # define FMT_CAT_(a, b) a##b # define FMT_NARG_(_unused, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \ _12, _13, _14, _15, _16, N, ...) \ N # define FMT_NARG(_unused, ...) \ FMT_NARG_(, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, \ 3, 2, 1, 0) # define FMT_MAP_0(...) # define FMT_MAP_1(f, a) f(a) # define FMT_MAP_2(f, a, b) f(a), f(b) # define FMT_MAP_3(f, a, b, c) f(a), f(b), f(c) # define FMT_MAP_4(f, a, b, c, d) f(a), f(b), f(c), f(d) # define FMT_MAP_5(f, a, b, c, d, e) f(a), f(b), f(c), f(d), f(e) # define FMT_MAP_6(f, a, b, c, d, e, g) f(a), f(b), f(c), f(d), f(e), f(g) # define FMT_MAP_7(f, a, b, c, d, e, g, h) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h) # define FMT_MAP_8(f, a, b, c, d, e, g, h, i) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i) # define FMT_MAP_9(f, a, b, c, d, e, g, h, i, j) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j) # define FMT_MAP_10(f, a, b, c, d, e, g, h, i, j, k) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k) # define FMT_MAP_11(f, a, b, c, d, e, g, h, i, j, k, l) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l) # define FMT_MAP_12(f, a, b, c, d, e, g, h, i, j, k, l, m) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m) # define FMT_MAP_13(f, a, b, c, d, e, g, h, i, j, k, l, m, n) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), f(n) # define FMT_MAP_14(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \ f(n), f(o) # define FMT_MAP_15(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o, p) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \ f(n), f(o), f(p) # define FMT_MAP_16(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o, p, q) \ f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \ f(n), f(o), f(p), f(q) # define FMT_MAP(f, ...) \ FMT_CAT(FMT_MAP_, FMT_NARG(, ##__VA_ARGS__))(f, ##__VA_ARGS__) // select between two expressions depending on whether __VA_ARGS__ is empty // expands to e if __VA_ARGS__ is empty and n otherwise # define FMT_VA_SELECT(e, n, ...) \ FMT_NARG_(, ##__VA_ARGS__, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, \ e) # define FMT_MAKE_NULL(...) NULL # define FMT_MAKE_ARGLIST(...) \ (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__)) #endif // __cplusplus #endif // FMT_C_H_ ================================================ FILE: include/fmt/format-inl.h ================================================ // Formatting library for C++ - implementation // // Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ #ifndef FMT_MODULE # include // ptrdiff_t # include # include // errno # include # include # include # include // std::bad_alloc #endif #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) # include // _isatty #endif #include "format.h" #if FMT_USE_LOCALE && !defined(FMT_MODULE) # include #endif #ifndef FMT_FUNC # define FMT_FUNC #endif #if defined(FMT_USE_FULL_CACHE_DRAGONBOX) // Use the provided definition. #elif defined(__OPTIMIZE_SIZE__) # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #else # define FMT_USE_FULL_CACHE_DRAGONBOX 1 #endif FMT_BEGIN_NAMESPACE #ifndef FMT_CUSTOM_ASSERT_FAIL FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // Use unchecked std::fprintf to avoid triggering another assertion when // writing to stderr fails. std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); abort(); } #endif #if FMT_USE_LOCALE namespace detail { using std::locale; using std::numpunct; using std::use_facet; } // namespace detail #else namespace detail { struct locale {}; template struct numpunct { auto grouping() const -> std::string { return "\03"; } auto thousands_sep() const -> Char { return ','; } auto decimal_point() const -> Char { return '.'; } }; template Facet use_facet(locale) { return {}; } } // namespace detail #endif // FMT_USE_LOCALE template auto locale_ref::get() const -> Locale { using namespace detail; static_assert(std::is_same::value, ""); #if FMT_USE_LOCALE if (locale_) return *static_cast(locale_); #endif return locale(); } namespace detail { FMT_FUNC auto allocate(size_t size) -> void* { void* p = malloc(size); if (!p) FMT_THROW(std::bad_alloc()); return p; } FMT_FUNC void format_error_code(detail::buffer& out, int error_code, string_view message) noexcept { // Report error code making sure that the output fits into inline_buffer_size // to avoid dynamic memory allocation and potential bad_alloc. out.try_resize(0); static constexpr char SEP[] = ": "; static constexpr char ERROR_STR[] = "error "; // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; auto abs_value = static_cast>(error_code); if (detail::is_negative(error_code)) { abs_value = 0 - abs_value; ++error_code_size; } error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); auto it = appender(out); if (message.size() <= inline_buffer_size - error_code_size) fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); FMT_ASSERT(out.size() <= inline_buffer_size, ""); } FMT_FUNC void do_report_error(format_func func, int error_code, const char* message) noexcept { memory_buffer full_message; func(full_message, error_code, message); // Don't use fwrite_all because the latter may throw. if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) std::fputc('\n', stderr); } // A wrapper around fwrite that throws on error. inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { size_t written = std::fwrite(ptr, 1, count, stream); if (written < count) FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } template FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { auto&& facet = use_facet>(loc.get()); auto grouping = facet.grouping(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } template FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { return use_facet>(loc.get()).decimal_point(); } #if FMT_USE_LOCALE FMT_FUNC auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool { auto locale = loc.get(); // We cannot use the num_put facet because it may produce output in // a wrong encoding. using facet = format_facet; if (std::has_facet(locale)) return use_facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs); } #endif } // namespace detail FMT_FUNC void report_error(const char* message) { #if FMT_MSC_VERSION || defined(__NVCC__) // Silence unreachable code warnings in MSVC and NVCC because these // are nearly impossible to fix in a generic code. volatile bool b = true; if (!b) return; #endif FMT_THROW(format_error(message)); } template typename Locale::id format_facet::id; template format_facet::format_facet(Locale& loc) { auto& np = detail::use_facet>(loc); grouping_ = np.grouping(); if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); } #if FMT_USE_LOCALE template <> FMT_API FMT_FUNC auto format_facet::do_put( appender out, loc_value val, const format_specs& specs) const -> bool { return val.visit( detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); } #endif FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args) -> std::system_error { auto ec = std::error_code(error_code, std::generic_category()); return std::system_error(ec, vformat(fmt, args)); } namespace detail { template inline auto operator==(basic_fp x, basic_fp y) -> bool { return x.f == y.f && x.e == y.e; } // Compilers should be able to optimize this into the ror instruction. FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. namespace dragonbox { // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t { return umul128_upper64(static_cast(x) << 32, y); } // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. inline auto umul192_lower128(uint64_t x, uint128 y) noexcept -> uint128 { uint64_t high = x * y.high(); uint128 high_low = umul128(x, y.low()); return {high + high_low.high(), high_low.low()}; } // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t { return x * y; } // Various fast log computations. inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); return (e * 631305 - 261663) >> 21; } FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { uint32_t divisor; int shift_amount; } div_small_pow10_infos[] = {{10, 16}, {100, 16}}; // Replaces n by floor(n / pow(10, N)) returning true if and only if n is // divisible by pow(10, N). // Precondition: n <= pow(10, N + 1). template auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool { // The numbers below are chosen such that: // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, // 2. nm mod 2^k < m if and only if n is divisible by d, // where m is magic_number, k is shift_amount // and d is divisor. // // Item 1 is a common technique of replacing division by a constant with // multiplication, see e.g. "Division by Invariant Integers Using // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set // to ceil(2^k/d) for large enough k. // The idea for item 2 originates from Schubfach. constexpr auto info = div_small_pow10_infos[N - 1]; FMT_ASSERT(n <= info.divisor * 10, "n is too large"); constexpr uint32_t magic_number = (1u << info.shift_amount) / info.divisor + 1; n *= magic_number; const uint32_t comparison_mask = (1u << info.shift_amount) - 1; bool result = (n & comparison_mask) < magic_number; n >>= info.shift_amount; return result; } // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). template auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t { constexpr auto info = div_small_pow10_infos[N - 1]; FMT_ASSERT(n <= info.divisor * 10, "n is too large"); constexpr uint32_t magic_number = (1u << info.shift_amount) / info.divisor + 1; return (n * magic_number) >> info.shift_amount; } // Computes floor(n / 10^(kappa + 1)) (float) inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t { // 1374389535 = ceil(2^37/100) return static_cast((static_cast(n) * 1374389535) >> 37); } // Computes floor(n / 10^(kappa + 1)) (double) inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t { // 2361183241434822607 = ceil(2^(64+7)/1000) return umul128_upper64(n, 2361183241434822607ull) >> 7; } // Various subroutines using pow10 cache template struct cache_accessor; template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; static auto get_cached_power(int k) noexcept -> uint64_t { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); static constexpr uint64_t pow10_significands[] = { 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985, 0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, 0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7, 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, 0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a, 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f}; return pow10_significands[k - float_info::min_k]; } struct compute_mul_result { carrier_uint result; bool is_integer; }; struct compute_mul_parity_result { bool parity; bool is_integer; }; static auto compute_mul(carrier_uint u, const cache_entry_type& cache) noexcept -> compute_mul_result { auto r = umul96_upper64(u, cache); return {static_cast(r >> 32), static_cast(r) == 0}; } static auto compute_delta(const cache_entry_type& cache, int beta) noexcept -> uint32_t { return static_cast(cache >> (64 - 1 - beta)); } static auto compute_mul_parity(carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); auto r = umul96_lower64(two_f, cache); return {((r >> (64 - beta)) & 1) != 0, static_cast(r >> (32 - beta)) == 0}; } static auto compute_left_endpoint_for_shorter_interval_case( const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache - (cache >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta)); } static auto compute_right_endpoint_for_shorter_interval_case( const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache + (cache >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta)); } static auto compute_round_up_for_shorter_interval_case( const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (static_cast( cache >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } }; template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint128; static auto get_cached_power(int k) noexcept -> uint128 { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); static constexpr uint128 pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, {0x9becce62836ac577, 0x4ee367f9430aec33}, {0xc2e801fb244576d5, 0x229c41f793cda740}, {0xf3a20279ed56d48a, 0x6b43527578c11110}, {0x9845418c345644d6, 0x830a13896b78aaaa}, {0xbe5691ef416bd60c, 0x23cc986bc656d554}, {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, {0x91376c36d99995be, 0x23100809b9c21fa2}, {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, {0xdd95317f31c7fa1d, 0x40405643d711d584}, {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, {0xad1c8eab5ee43b66, 0xda3243650005eed0}, {0xd863b256369d4a40, 0x90bed43e40076a83}, {0x873e4f75e2224e68, 0x5a7744a6e804a292}, {0xa90de3535aaae202, 0x711515d0a205cb37}, {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, {0x8412d9991ed58091, 0xe858790afe9486c3}, {0xa5178fff668ae0b6, 0x626e974dbe39a873}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, {0xc987434744ac874e, 0xa327ffb266b56221}, {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, {0xf6019da07f549b2b, 0x7e2a53a146606a49}, {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, {0xc0314325637a1939, 0xfa911155fefb5309}, {0xf03d93eebc589f88, 0x793555ab7eba27cb}, {0x96267c7535b763b5, 0x4bc1558b2f3458df}, {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, {0x92a1958a7675175f, 0x0bfacd89ec191eca}, {0xb749faed14125d36, 0xcef980ec671f667c}, {0xe51c79a85916f484, 0x82b7e12780e7401b}, {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, {0xaecc49914078536d, 0x58fae9f773886e19}, {0xda7f5bf590966848, 0xaf39a475506a899f}, {0x888f99797a5e012d, 0x6d8406c952429604}, {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, {0xd0601d8efc57b08b, 0xf13b94daf124da27}, {0x823c12795db6ce57, 0x76c53d08d6b70859}, {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, {0xc21094364dfb5636, 0x985915fc12f542e5}, {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, {0xbd8430bd08277231, 0x50c6ff782a838354}, {0xece53cec4a314ebd, 0xa4f8bf5635246429}, {0x940f4613ae5ed136, 0x871b7795e136be9a}, {0xb913179899f68584, 0x28e2557b59846e40}, {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, {0xb4bca50b065abe63, 0x0fed077a756b53aa}, {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, {0x89e42caaf9491b60, 0xf41686c49db57245}, {0xac5d37d5b79b6239, 0x311c2875c522ced6}, {0xd77485cb25823ac7, 0x7d633293366b828c}, {0x86a8d39ef77164bc, 0xae5dff9c02033198}, {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, {0xd267caa862a12d66, 0xd072df63c324fd7c}, {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, {0xa46116538d0deb78, 0x52d9be85f074e609}, {0xcd795be870516656, 0x67902e276c921f8c}, {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, {0xef340a98172aace4, 0x86fb897116c87c35}, {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, {0xbae0a846d2195712, 0x8974836059cca10a}, {0xe998d258869facd7, 0x2bd1a438703fc94c}, {0x91ff83775423cc06, 0x7b6306a34627ddd0}, {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, {0x8e938662882af53e, 0x547eb47b7282ee9d}, {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, {0xae0b158b4738705e, 0x9624ab50b148d446}, {0xd98ddaee19068c76, 0x3badd624dd9b0958}, {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, {0xd47487cc8470652b, 0x7647c32000696720}, {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, {0xa5fb0a17c777cf09, 0xf468107100525891}, {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, {0x81ac1fe293d599bf, 0xc6f14cd848405531}, {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, {0xfd442e4688bd304a, 0x908f4a166d1da664}, {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, {0xf7549530e188c128, 0xd12bee59e68ef47d}, {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, {0xebdf661791d60f56, 0x111b495b3464ad22}, {0x936b9fcebb25c995, 0xcab10dd900beec35}, {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, {0xb3f4e093db73a093, 0x59ed216765690f57}, {0xe0f218b8d25088b8, 0x306869c13ec3532d}, {0x8c974f7383725573, 0x1e414218c73a13fc}, {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, {0x894bc396ce5da772, 0x6b8bba8c328eb784}, {0xab9eb47c81f5114f, 0x066ea92f3f326565}, {0xd686619ba27255a2, 0xc80a537b0efefebe}, {0x8613fd0145877585, 0xbd06742ce95f5f37}, {0xa798fc4196e952e7, 0x2c48113823b73705}, {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, {0x82ef85133de648c4, 0x9a984d73dbe722fc}, {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, {0xcc963fee10b7d1b3, 0x318df905079926a9}, {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, {0x9c1661a651213e2d, 0x06bea10ca65c084f}, {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, {0xbe89523386091465, 0xf6bbb397f1135824}, {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, {0xba121a4650e4ddeb, 0x92f34d62616ce414}, {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, {0x87625f056c7c4a8b, 0x11471cd764ad4973}, {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, {0xd389b47879823479, 0x4aff1d108d4ec2c4}, {0x843610cb4bf160cb, 0xcedf722a585139bb}, {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, {0xce947a3da6a9273e, 0x733d226229feea33}, {0x811ccc668829b887, 0x0806357d5a3f5260}, {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, {0xc5029163f384a931, 0x0a9e795e65d4df12}, {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, {0x964e858c91ba2655, 0x3a6a07f8d510f870}, {0xbbe226efb628afea, 0x890489f70a55368c}, {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, {0xe55990879ddcaabd, 0xcc420a6a101d0516}, {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, {0xb32df8e9f3546564, 0x47939822dc96abfa}, {0xdff9772470297ebd, 0x59787e2b93bc56f8}, {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, {0xaefae51477a06b03, 0xede622920b6b23f2}, {0xdab99e59958885c4, 0xe95fab368e45ecee}, {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, {0xd59944a37c0752a2, 0x4be76d3346f04960}, {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, {0x825ecc24c873782f, 0x8ed400668c0c28c9}, {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, {0xc24452da229b021b, 0xfbe85badce996169}, {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, {0xed246723473e3813, 0x290123e9aab23b69}, {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, {0x8d590723948a535f, 0x579c487e5a38ad0f}, {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, {0xdcdb1b2798182244, 0xf8e431456cf88e66}, {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, {0xa87fea27a539e9a5, 0x3f2398d747b36225}, {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, {0x83a3eeeef9153e89, 0x1953cf68300424ad}, {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, {0xcdb02555653131b6, 0x3792f412cb06794e}, {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, {0xc8de047564d20a8b, 0xf245825a5a445276}, {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, {0x9ced737bb6c4183d, 0x55464dd69685606c}, {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, {0xf53304714d9265df, 0xd53dd99f4b3066a9}, {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, {0xbf8fdb78849a5f96, 0xde98520472bdd034}, {0xef73d256a5c0f77c, 0x963e66858f6d4441}, {0x95a8637627989aad, 0xdde7001379a44aa9}, {0xbb127c53b17ec159, 0x5560c018580d5d53}, {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, {0x9226712162ab070d, 0xcab3961304ca70e9}, {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, {0xb267ed1940f1c61c, 0x55f038b237591ed4}, {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, {0xd9c7dced53c72255, 0x96e7bd358c904a22}, {0x881cea14545c7575, 0x7e50d64177da2e55}, {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, {0xcfb11ead453994ba, 0x67de18eda5814af3}, {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, {0xa2425ff75e14fc31, 0xa1258379a94d028e}, {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, {0x9e74d1b791e07e48, 0x775ea264cf55347e}, {0xc612062576589dda, 0x95364afe032a819e}, {0xf79687aed3eec551, 0x3a83ddbd83f52205}, {0x9abe14cd44753b52, 0xc4926a9672793543}, {0xc16d9a0095928a27, 0x75b7053c0f178294}, {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, {0xb877aa3236a4b449, 0x09befeb9fad487c3}, {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, {0xb424dc35095cd80f, 0x538484c19ef38c95}, {0xe12e13424bb40e13, 0x2865a5f206b06fba}, {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, {0xafebff0bcb24aafe, 0xf78f69a51539d749}, {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, {0x89705f4136b4a597, 0x31680a88f8953031}, {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, {0xd1b71758e219652b, 0xd3c36113404ea4a9}, {0x83126e978d4fdf3b, 0x645a1cac083126ea}, {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, {0xcccccccccccccccc, 0xcccccccccccccccd}, {0x8000000000000000, 0x0000000000000000}, {0xa000000000000000, 0x0000000000000000}, {0xc800000000000000, 0x0000000000000000}, {0xfa00000000000000, 0x0000000000000000}, {0x9c40000000000000, 0x0000000000000000}, {0xc350000000000000, 0x0000000000000000}, {0xf424000000000000, 0x0000000000000000}, {0x9896800000000000, 0x0000000000000000}, {0xbebc200000000000, 0x0000000000000000}, {0xee6b280000000000, 0x0000000000000000}, {0x9502f90000000000, 0x0000000000000000}, {0xba43b74000000000, 0x0000000000000000}, {0xe8d4a51000000000, 0x0000000000000000}, {0x9184e72a00000000, 0x0000000000000000}, {0xb5e620f480000000, 0x0000000000000000}, {0xe35fa931a0000000, 0x0000000000000000}, {0x8e1bc9bf04000000, 0x0000000000000000}, {0xb1a2bc2ec5000000, 0x0000000000000000}, {0xde0b6b3a76400000, 0x0000000000000000}, {0x8ac7230489e80000, 0x0000000000000000}, {0xad78ebc5ac620000, 0x0000000000000000}, {0xd8d726b7177a8000, 0x0000000000000000}, {0x878678326eac9000, 0x0000000000000000}, {0xa968163f0a57b400, 0x0000000000000000}, {0xd3c21bcecceda100, 0x0000000000000000}, {0x84595161401484a0, 0x0000000000000000}, {0xa56fa5b99019a5c8, 0x0000000000000000}, {0xcecb8f27f4200f3a, 0x0000000000000000}, {0x813f3978f8940984, 0x4000000000000000}, {0xa18f07d736b90be5, 0x5000000000000000}, {0xc9f2c9cd04674ede, 0xa400000000000000}, {0xfc6f7c4045812296, 0x4d00000000000000}, {0x9dc5ada82b70b59d, 0xf020000000000000}, {0xc5371912364ce305, 0x6c28000000000000}, {0xf684df56c3e01bc6, 0xc732000000000000}, {0x9a130b963a6c115c, 0x3c7f400000000000}, {0xc097ce7bc90715b3, 0x4b9f100000000000}, {0xf0bdc21abb48db20, 0x1e86d40000000000}, {0x96769950b50d88f4, 0x1314448000000000}, {0xbc143fa4e250eb31, 0x17d955a000000000}, {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, {0xb7abc627050305ad, 0xf14a3d9e40000000}, {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, {0xe0352f62a19e306e, 0xd50b2037ad200000}, {0x8c213d9da502de45, 0x4526f422cc340000}, {0xaf298d050e4395d6, 0x9670b12b7f410000}, {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, {0xab0e93b6efee0053, 0x8eea0d047a457a00}, {0xd5d238a4abe98068, 0x72a4904598d6d880}, {0x85a36366eb71f041, 0x47a6da2b7f864750}, {0xa70c3c40a64e6c51, 0x999090b65f67d924}, {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, {0x82818f1281ed449f, 0xbff8f10e7a8921a5}, {0xa321f2d7226895c7, 0xaff72d52192b6a0e}, {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491}, {0xfee50b7025c36a08, 0x02f236d04753d5b5}, {0x9f4f2726179a2245, 0x01d762422c946591}, {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6}, {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3}, {0x9b934c3b330c8577, 0x63cc55f49f88eb30}, {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc}, {0xf316271c7fc3908a, 0x8bef464e3945ef7b}, {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad}, {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318}, {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde}, {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b}, {0xb975d6b6ee39e436, 0xb3e2fd538e122b45}, {0xe7d34c64a9c85d44, 0x60dbbca87196b617}, {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce}, {0xb51d13aea4a488dd, 0x6babab6398bdbe42}, {0xe264589a4dcdab14, 0xc696963c7eed2dd2}, {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3}, {0xb0de65388cc8ada8, 0x3b25a55f43294bcc}, {0xdd15fe86affad912, 0x49ef0eb713f39ebf}, {0x8a2dbf142dfcc7ab, 0x6e3569326c784338}, {0xacb92ed9397bf996, 0x49c2c37f07965405}, {0xd7e77a8f87daf7fb, 0xdc33745ec97be907}, {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4}, {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d}, {0xd2d80db02aabd62b, 0xf50a3fa490c30191}, {0x83c7088e1aab65db, 0x792667c6da79e0fb}, {0xa4b8cab1a1563f52, 0x577001b891185939}, {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, {0x80b05e5ac60b6178, 0x544f8158315b05b5}, {0xa0dc75f1778e39d6, 0x696361ae3db1c722}, {0xc913936dd571c84c, 0x03bc3a19cd1e38ea}, {0xfb5878494ace3a5f, 0x04ab48a04065c724}, {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77}, {0xc45d1df942711d9a, 0x3ba5d0bd324f8395}, {0xf5746577930d6500, 0xca8f44ec7ee3647a}, {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc}, {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f}, {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f}, {0x95d04aee3b80ece5, 0xbba1f1d158724a13}, {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98}, {0xea1575143cf97226, 0xf52d09d71a3293be}, {0x924d692ca61be758, 0x593c2626705f9c57}, {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d}, {0xe498f455c38b997a, 0x0b6dfb9c0f956448}, {0x8edf98b59a373fec, 0x4724bd4189bd5ead}, {0xb2977ee300c50fe7, 0x58edec91ec2cb658}, {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee}, {0x8b865b215899f46c, 0xbd79e0d20082ee75}, {0xae67f1e9aec07187, 0xecd8590680a3aa12}, {0xda01ee641a708de9, 0xe80e6f4820cc9496}, {0x884134fe908658b2, 0x3109058d147fdcde}, {0xaa51823e34a7eede, 0xbd4b46f0599fd416}, {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b}, {0x850fadc09923329e, 0x03e2cf6bc604ddb1}, {0xa6539930bf6bff45, 0x84db8346b786151d}, {0xcfe87f7cef46ff16, 0xe612641865679a64}, {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f}, {0xa26da3999aef7749, 0xe3be5e330f38f09e}, {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6}, {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7}, {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb}, {0xc646d63501a1511d, 0xb281e1fd541501b9}, {0xf7d88bc24209a565, 0x1f225a7ca91a4227}, {0x9ae757596946075f, 0x3375788de9b06959}, {0xc1a12d2fc3978937, 0x0052d6b1641c83af}, {0xf209787bb47d6b84, 0xc0678c5dbd23a49b}, {0x9745eb4d50ce6332, 0xf840b7ba963646e1}, {0xbd176620a501fbff, 0xb650e5a93bc3d899}, {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf}, {0x93ba47c980e98cdf, 0xc66f336c36b10138}, {0xb8a8d9bbe123f017, 0xb80b0047445d4185}, {0xe6d3102ad96cec1d, 0xa60dc059157491e6}, {0x9043ea1ac7e41392, 0x87c89837ad68db30}, {0xb454e4a179dd1877, 0x29babe4598c311fc}, {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b}, {0x8ce2529e2734bb1d, 0x1899e4a65f58660d}, {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90}, {0xdc21a1171d42645d, 0x76707543f4fa1f74}, {0x899504ae72497eba, 0x6a06494a791c53a9}, {0xabfa45da0edbde69, 0x0487db9d17636893}, {0xd6f8d7509292d603, 0x45a9d2845d3c42b7}, {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, {0xa7f26836f282b732, 0x8e6cac7768d7141f}, {0xd1ef0244af2364ff, 0x3207d795430cd927}, {0x8335616aed761f1f, 0x7f44e6bd49e807b9}, {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7}, {0xcd036837130890a1, 0x36dba887c37a8c10}, {0x802221226be55a64, 0xc2494954da2c978a}, {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d}, {0xc83553c5c8965d3d, 0x6f92829494e5acc8}, {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa}, {0x9c69a97284b578d7, 0xff2a760414536efc}, {0xc38413cf25e2d70d, 0xfef5138519684abb}, {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a}, {0x98bf2f79d5993802, 0xef2f773ffbd97a62}, {0xbeeefb584aff8603, 0xaafb550ffacfd8fb}, {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39}, {0x952ab45cfa97a0b2, 0xdd945a747bf26184}, {0xba756174393d88df, 0x94f971119aeef9e5}, {0xe912b9d1478ceb17, 0x7a37cd5601aab85e}, {0x91abb422ccb812ee, 0xac62e055c10ab33b}, {0xb616a12b7fe617aa, 0x577b986b314d600a}, {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c}, {0x8e41ade9fbebc27d, 0x14588f13be847308}, {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9}, {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc}, {0x8aec23d680043bee, 0x25de7bb9480d5855}, {0xada72ccc20054ae9, 0xaf561aa79a10ae6b}, {0xd910f7ff28069da4, 0x1b2ba1518094da05}, {0x87aa9aff79042286, 0x90fb44d2f05d0843}, {0xa99541bf57452b28, 0x353a1607ac744a54}, {0xd3fa922f2d1675f2, 0x42889b8997915ce9}, {0x847c9b5d7c2e09b7, 0x69956135febada12}, {0xa59bc234db398c25, 0x43fab9837e699096}, {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc}, {0x8161afb94b44f57d, 0x1d1be0eebac278f6}, {0xa1ba1ba79e1632dc, 0x6462d92a69731733}, {0xca28a291859bbf93, 0x7d7b8f7503cfdcff}, {0xfcb2cb35e702af78, 0x5cda735244c3d43f}, {0x9defbf01b061adab, 0x3a0888136afa64a8}, {0xc56baec21c7a1916, 0x088aaa1845b8fdd1}, {0xf6c69a72a3989f5b, 0x8aad549e57273d46}, {0x9a3c2087a63f6399, 0x36ac54e2f678864c}, {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de}, {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6}, {0x969eb7c47859e743, 0x9f644ae5a4b1b326}, {0xbc4665b596706114, 0x873d5d9f0dde1fef}, {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb}, {0x9316ff75dd87cbd8, 0x09a7f12442d588f3}, {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30}, {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb}, {0x8fa475791a569d10, 0xf96e017d694487bd}, {0xb38d92d760ec4455, 0x37c981dcc395a9ad}, {0xe070f78d3927556a, 0x85bbe253f47b1418}, {0x8c469ab843b89562, 0x93956d7478ccec8f}, {0xaf58416654a6babb, 0x387ac8d1970027b3}, {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f}, {0x88fcf317f22241e2, 0x441fece3bdf81f04}, {0xab3c2fddeeaad25a, 0xd527e81cad7626c4}, {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075}, {0x85c7056562757456, 0xf6872d5667844e4a}, {0xa738c6bebb12d16c, 0xb428f8ac016561dc}, {0xd106f86e69d785c7, 0xe13336d701beba53}, {0x82a45b450226b39c, 0xecc0024661173474}, {0xa34d721642b06084, 0x27f002d7f95d0191}, {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5}, {0xff290242c83396ce, 0x7e67047175a15272}, {0x9f79a169bd203e41, 0x0f0062c6e984d387}, {0xc75809c42c684dd1, 0x52c07b78a3e60869}, {0xf92e0c3537826145, 0xa7709a56ccdf8a83}, {0x9bbcc7a142b17ccb, 0x88a66076400bb692}, {0xc2abf989935ddbfe, 0x6acff893d00ea436}, {0xf356f7ebf83552fe, 0x0583f6b8c4124d44}, {0x98165af37b2153de, 0xc3727a337a8b704b}, {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d}, {0xeda2ee1c7064130c, 0x1162def06f79df74}, {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9}, {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693}, {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438}, {0x910ab1d4db9914a0, 0x1d9c9892400a22a3}, {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c}, {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e}, {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, {0xb10d8e1456105dad, 0x7425a83e872c5f48}, {0xdd50f1996b947518, 0xd12f124e28f7771a}, {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70}, {0xace73cbfdc0bfb7b, 0x636cc64d1001550c}, {0xd8210befd30efa5a, 0x3c47f7e05401aa4f}, {0x8714a775e3e95c78, 0x65acfaec34810a72}, {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e}, {0xd31045a8341ca07c, 0x1ede48111209a051}, {0x83ea2b892091e44d, 0x934aed0aab460433}, {0xa4e4b66b68b65d60, 0xf81da84d56178540}, {0xce1de40642e3f4b9, 0x36251260ab9d668f}, {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a}, {0xa1075a24e4421730, 0xb24cf65b8612f820}, {0xc94930ae1d529cfc, 0xdee033f26797b628}, {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2}, {0x9d412e0806e88aa5, 0x8e1f289560ee864f}, {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3}, {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc}, {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a}, {0xbff610b0cc6edd3f, 0x17fd090a58d32af4}, {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1}, {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f}, {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2}, {0xea53df5fd18d5513, 0x84c86189216dc5ee}, {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5}, {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2}, {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f}, {0xb2c71d5bca9023f8, 0x743e20e9ef511013}, {0xdf78e4b2bd342cf6, 0x914da9246b255417}, {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f}, {0xae9672aba3d0c320, 0xa184ac2473b529b2}, {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f}, {0x8865899617fb1871, 0x7e2fa67c7a658893}, {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8}, {0xd51ea6fa85785631, 0x552a74227f3ea566}, {0x8533285c936b35de, 0xd53a88958f872760}, {0xa67ff273b8460356, 0x8a892abaf368f138}, {0xd01fef10a657842c, 0x2d2b7569b0432d86}, {0x8213f56a67f6b29b, 0x9c3b29620e29fc74}, {0xa298f2c501f45f42, 0x8349f3ba91b47b90}, {0xcb3f2f7642717713, 0x241c70a936219a74}, {0xfe0efb53d30dd4d7, 0xed238cd383aa0111}, {0x9ec95d1463e8a506, 0xf4363804324a40ab}, {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6}, {0xf81aa16fdc1b81da, 0xdd94b7868e94050b}, {0x9b10a4e5e9913128, 0xca7cf2b4191c8327}, {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1}, {0xf24a01a73cf2dccf, 0xbc633b39673c8ced}, {0x976e41088617ca01, 0xd5be0503e085d814}, {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19}, {0xec9c459d51852ba2, 0xddf8e7d60ed1219f}, {0x93e1ab8252f33b45, 0xcabb90e5c942b504}, {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, {0xe7109bfba19c0c9d, 0x0cc512670a783ad5}, {0x906a617d450187e2, 0x27fb2b80668b24c6}, {0xb484f9dc9641e9da, 0xb1f9f660802dedf7}, {0xe1a63853bbd26451, 0x5e7873f8a0396974}, {0x8d07e33455637eb2, 0xdb0b487b6423e1e9}, {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63}, {0xdc5c5301c56b75f7, 0x7641a140cc7810fc}, {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e}, {0xac2820d9623bf429, 0x546345fa9fbdcd45}, {0xd732290fbacaf133, 0xa97c177947ad4096}, {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e}, {0xa81f301449ee8c70, 0x5c68f256bfff5a75}, {0xd226fc195c6a2f8c, 0x73832eec6fff3112}, {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac}, {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56}, {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec}, {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4}, {0xa0555e361951c366, 0xd7e105bcc3326220}, {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8}, {0xfa856334878fc150, 0xb14f98f6f0feb952}, {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4}, {0xc3b8358109e84f07, 0x0a862f80ec4700c9}, {0xf4a642e14c6262c8, 0xcd27bb612758c0fb}, {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d}, {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4}, {0xeeea5d5004981478, 0x1858ccfce06cac75}, {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, {0xbaa718e68396cffd, 0xd30560258f54e6bb}, {0xe950df20247c83fd, 0x47c6b82ef32a206a}, {0x91d28b7416cdd27e, 0x4cdc331d57fa5442}, {0xb6472e511c81471d, 0xe0133fe4adf8e953}, {0xe3d8f9e563a198e5, 0x58180fddd97723a7}, {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649}, {0xb201833b35d63f73, 0x2cd2cc6551e513db}, {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2}, {0x8b112e86420f6191, 0xfb04afaf27faf783}, {0xadd57a27d29339f6, 0x79c5db9af1f9b564}, {0xd94ad8b1c7380874, 0x18375281ae7822bd}, {0x87cec76f1c830548, 0x8f2293910d0b15b6}, {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23}, {0xd433179d9c8cb841, 0x5fa60692a46151ec}, {0x849feec281d7f328, 0xdbc7c41ba6bcd334}, {0xa5c7ea73224deff3, 0x12b9b522906c0801}, {0xcf39e50feae16bef, 0xd768226b34870a01}, {0x81842f29f2cce375, 0xe6a1158300d46641}, {0xa1e53af46f801c53, 0x60495ae3c1097fd1}, {0xca5e89b18b602368, 0x385bb19cb14bdfc5}, {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, {0xc5a05277621be293, 0xc7098b7305241886}, {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8}, {0x9a65406d44a5c903, 0x737f74f1dc043329}, {0xc0fe908895cf3b44, 0x505f522e53053ff3}, {0xf13e34aabb430a15, 0x647726b9e7c68ff0}, {0x96c6e0eab509e64d, 0x5eca783430dc19f6}, {0xbc789925624c5fe0, 0xb67d16413d132073}, {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890}, {0x933e37a534cbaae7, 0x8e91b962f7b6f15a}, {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1}, {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d}, {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2}, {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e}, {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}, #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, {0x86a8d39ef77164bc, 0xae5dff9c02033198}, {0xd98ddaee19068c76, 0x3badd624dd9b0958}, {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, {0xe55990879ddcaabd, 0xcc420a6a101d0516}, {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, {0x95a8637627989aad, 0xdde7001379a44aa9}, {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, {0xc350000000000000, 0x0000000000000000}, {0x9dc5ada82b70b59d, 0xf020000000000000}, {0xfee50b7025c36a08, 0x02f236d04753d5b5}, {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, {0xa6539930bf6bff45, 0x84db8346b786151d}, {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, {0xd910f7ff28069da4, 0x1b2ba1518094da05}, {0xaf58416654a6babb, 0x387ac8d1970027b3}, {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, {0xf13e34aabb430a15, 0x647726b9e7c68ff0} #endif }; #if FMT_USE_FULL_CACHE_DRAGONBOX return pow10_significands[k - float_info::min_k]; #else static constexpr uint64_t powers_of_5_64[] = { 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; static const int compression_ratio = 27; // Compute base index. int cache_index = (k - float_info::min_k) / compression_ratio; int kb = cache_index * compression_ratio + float_info::min_k; int offset = k - kb; // Get base cache. uint128 base_cache = pow10_significands[cache_index]; if (offset == 0) return base_cache; // Compute the required amount of bit-shift. int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); // Try to recover the real cache. uint64_t pow5 = powers_of_5_64[offset]; uint128 recovered_cache = umul128(base_cache.high(), pow5); uint128 middle_low = umul128(base_cache.low(), pow5); recovered_cache += middle_low.high(); uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); recovered_cache = uint128{(recovered_cache.low() >> alpha) | high_to_middle, ((middle_low.low() >> alpha) | middle_to_low)}; FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); return {recovered_cache.high(), recovered_cache.low() + 1}; #endif } struct compute_mul_result { carrier_uint result; bool is_integer; }; struct compute_mul_parity_result { bool parity; bool is_integer; }; static auto compute_mul(carrier_uint u, const cache_entry_type& cache) noexcept -> compute_mul_result { auto r = umul192_upper128(u, cache); return {r.high(), r.low() == 0}; } static auto compute_delta(const cache_entry_type& cache, int beta) noexcept -> uint32_t { return static_cast(cache.high() >> (64 - 1 - beta)); } static auto compute_mul_parity(carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); auto r = umul192_lower128(two_f, cache); return {((r.high() >> (64 - beta)) & 1) != 0, ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; } static auto compute_left_endpoint_for_shorter_interval_case( const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() - (cache.high() >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta); } static auto compute_right_endpoint_for_shorter_interval_case( const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() + (cache.high() >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta); } static auto compute_round_up_for_shorter_interval_case( const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } }; FMT_FUNC auto get_cached_power(int k) noexcept -> uint128 { return cache_accessor::get_cached_power(k); } // Various integer checks template auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_upper_threshold = 3; return exponent >= case_shorter_interval_left_endpoint_lower_threshold && exponent <= case_shorter_interval_left_endpoint_upper_threshold; } // Remove trailing zeros from n and return the number of zeros removed (float). FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int { FMT_ASSERT(n != 0, ""); // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. constexpr uint32_t mod_inv_5 = 0xcccccccd; constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 while (true) { auto q = rotr(n * mod_inv_25, 2); if (q > max_value() / 100) break; n = q; s += 2; } auto q = rotr(n * mod_inv_5, 1); if (q <= max_value() / 10) { n = q; s |= 1; } return s; } // Removes trailing zeros and returns the number of zeros removed (double). FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int { FMT_ASSERT(n != 0, ""); // Is n is divisible by 10^8? constexpr uint32_t ten_pow_8 = 100000000u; if ((n % ten_pow_8) == 0) { // If yes, work with the quotient... auto n32 = static_cast(n / ten_pow_8); // ... and use the 32 bit variant of the function int num_zeros = remove_trailing_zeros(n32, 8); n = n32; return num_zeros; } // If n is not divisible by 10^8, work with n itself. constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd; constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5 int s = 0; while (true) { auto q = rotr(n * mod_inv_25, 2); if (q > max_value() / 100) break; n = q; s += 2; } auto q = rotr(n * mod_inv_5, 1); if (q <= max_value() / 10) { n = q; s |= 1; } return s; } // The main algorithm for shorter interval case template FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); const int beta = exponent + floor_log2_pow10(-minus_k); // Compute xi and zi using cache_entry_type = typename cache_accessor::cache_entry_type; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( cache, beta); auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( cache, beta); // If the left endpoint is not an integer, increase it if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; // Try bigger divisor ret_value.significand = zi / 10; // If succeed, remove trailing zeros if necessary and return if (ret_value.significand * 10 >= xi) { ret_value.exponent = minus_k + 1; ret_value.exponent += remove_trailing_zeros(ret_value.significand); return ret_value; } // Otherwise, compute the round-up of y ret_value.significand = cache_accessor::compute_round_up_for_shorter_interval_case(cache, beta); ret_value.exponent = minus_k; // When tie occurs, choose one of them according to the rule if (exponent >= float_info::shorter_interval_tie_lower_threshold && exponent <= float_info::shorter_interval_tie_upper_threshold) { ret_value.significand = ret_value.significand % 2 == 0 ? ret_value.significand : ret_value.significand - 1; } else if (ret_value.significand < xi) { ++ret_value.significand; } return ret_value; } template auto to_decimal(T x) noexcept -> decimal_fp { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; using cache_entry_type = typename cache_accessor::cache_entry_type; auto br = bit_cast(x); // Extract significand bits and exponent bits. const carrier_uint significand_mask = (static_cast(1) << num_significand_bits()) - 1; carrier_uint significand = (br & significand_mask); int exponent = static_cast((br & exponent_mask()) >> num_significand_bits()); if (exponent != 0) { // Check if normal. exponent -= exponent_bias() + num_significand_bits(); // Shorter interval case; proceed like Schubfach. // In fact, when exponent == 1 and significand == 0, the interval is // regular. However, it can be shown that the end-results are anyway same. if (significand == 0) return shorter_interval_case(exponent); significand |= (static_cast(1) << num_significand_bits()); } else { // Subnormal case; the interval is always regular. if (significand == 0) return {0, 0}; exponent = std::numeric_limits::min_exponent - num_significand_bits() - 1; } const bool include_left_endpoint = (significand % 2 == 0); const bool include_right_endpoint = include_left_endpoint; // Compute k and beta. const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); const int beta = exponent + floor_log2_pow10(-minus_k); // Compute zi and deltai. // 10^kappa <= deltai < 10^(kappa + 1) const uint32_t deltai = cache_accessor::compute_delta(cache, beta); const carrier_uint two_fc = significand << 1; // For the case of binary32, the result of integer check is not correct for // 29711844 * 2^-82 // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18 // and 29711844 * 2^-81 // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17, // and they are the unique counterexamples. However, since 29711844 is even, // this does not cause any problem for the endpoints calculations; it can only // cause a problem when we need to perform integer check for the center. // Fortunately, with these inputs, that branch is never executed, so we are // fine. const typename cache_accessor::compute_mul_result z_mul = cache_accessor::compute_mul((two_fc | 1) << beta, cache); // Step 2: Try larger divisor; remove trailing zeros if necessary. // Using an upper bound on zi, we might be able to optimize the division // better than the compiler; we are computing zi / big_divisor here. decimal_fp ret_value; ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); uint32_t r = static_cast(z_mul.result - float_info::big_divisor * ret_value.significand); if (r < deltai) { // Exclude the right endpoint if necessary. if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) { --ret_value.significand; r = float_info::big_divisor; goto small_divisor_case_label; } } else if (r > deltai) { goto small_divisor_case_label; } else { // r == deltai; compare fractional parts. const typename cache_accessor::compute_mul_parity_result x_mul = cache_accessor::compute_mul_parity(two_fc - 1, cache, beta); if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) goto small_divisor_case_label; } ret_value.exponent = minus_k + float_info::kappa + 1; // We may need to remove trailing zeros. ret_value.exponent += remove_trailing_zeros(ret_value.significand); return ret_value; // Step 3: Find the significand with the smaller divisor. small_divisor_case_label: ret_value.significand *= 10; ret_value.exponent = minus_k + float_info::kappa; uint32_t dist = r - (deltai / 2) + (float_info::small_divisor / 2); const bool approx_y_parity = ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; // Is dist divisible by 10^kappa? const bool divisible_by_small_divisor = check_divisibility_and_divide_by_pow10::kappa>(dist); // Add dist / 10^kappa to the significand. ret_value.significand += dist; if (!divisible_by_small_divisor) return ret_value; // Check z^(f) >= epsilon^(f). // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f). // Since there are only 2 possibilities, we only need to care about the // parity. Also, zi and r should have the same parity since the divisor // is an even number. const auto y_mul = cache_accessor::compute_mul_parity(two_fc, cache, beta); // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f), // or equivalently, when y is an integer. if (y_mul.parity != approx_y_parity) --ret_value.significand; else if (y_mul.is_integer & (ret_value.significand % 2 != 0)) --ret_value.significand; return ret_value; } } // namespace dragonbox } // namespace detail template <> struct formatter { FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> format_parse_context::iterator { return ctx.begin(); } auto format(const detail::bigint& n, format_context& ctx) const -> format_context::iterator { auto out = ctx.out(); bool first = true; for (auto i = n.bigits_.size(); i > 0; --i) { auto value = n.bigits_[i - 1u]; if (first) { out = fmt::format_to(out, FMT_STRING("{:x}"), value); first = false; continue; } out = fmt::format_to(out, FMT_STRING("{:08x}"), value); } if (n.exp_ > 0) out = fmt::format_to(out, FMT_STRING("p{}"), n.exp_ * detail::bigint::bigit_bits); return out; } }; FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { for_each_codepoint(s, [this](uint32_t cp, string_view) { if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); if (cp <= 0xFFFF) { buffer_.push_back(static_cast(cp)); } else { cp -= 0x10000; buffer_.push_back(static_cast(0xD800 + (cp >> 10))); buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); } return true; }); buffer_.push_back(0); } FMT_FUNC void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept { FMT_TRY { auto ec = std::error_code(error_code, std::generic_category()); detail::write(appender(out), std::system_error(ec, message).what()); return; } FMT_CATCH(...) {} format_error_code(out, error_code, message); } FMT_FUNC void report_system_error(int error_code, const char* message) noexcept { do_report_error(format_system_error, error_code, message); } FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); return to_string(buffer); } namespace detail { FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, locale_ref loc) { auto out = appender(buf); if (fmt.size() == 2 && equal2(fmt.data(), "{}")) return args.get(0).visit(default_arg_formatter{out}); parse_format_string(fmt, format_handler<>{parse_context<>(fmt), {out, args, loc}}); } template struct span { T* data; size_t size; }; template auto flockfile(F* f) -> decltype(_lock_file(f)) { _lock_file(f); } template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { _unlock_file(f); } #ifndef getc_unlocked template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { return _fgetc_nolock(f); } #endif #ifndef FMT_USE_FLOCKFILE # define FMT_USE_FLOCKFILE 1 #endif template struct has_flockfile : std::false_type {}; template struct has_flockfile()))>> : bool_constant {}; // A FILE wrapper. F is FILE defined as a template parameter to make system API // detection work. template class file_base { public: F* file_; public: file_base(F* file) : file_(file) {} operator F*() const { return file_; } // Reads a code unit from the stream. auto get() -> int { int result = getc_unlocked(file_); if (result == EOF && ferror(file_) != 0) FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); return result; } // Puts the code unit back into the stream buffer. void unget(char c) { if (ungetc(c, file_) == EOF) FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); } void flush() { fflush(this->file_); } }; // A FILE wrapper for glibc. template class glibc_file : public file_base { private: enum { line_buffered = 0x200, // _IO_LINE_BUF unbuffered = 2 // _IO_UNBUFFERED }; public: using file_base::file_base; auto is_buffered() const -> bool { return (this->file_->_flags & unbuffered) == 0; } void init_buffer() { if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return; // Force buffer initialization by placing and removing a char in a buffer. putc_unlocked(0, this->file_); --this->file_->_IO_write_ptr; } // Returns the file's read buffer. auto get_read_buffer() const -> span { auto ptr = this->file_->_IO_read_ptr; return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; } // Returns the file's write buffer. auto get_write_buffer() const -> span { auto ptr = this->file_->_IO_write_ptr; return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; } void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; char* end = this->file_->_IO_write_end; auto size = max_of(this->file_->_IO_write_ptr - end, 0); return memchr(end, '\n', static_cast(size)); } void flush() { fflush_unlocked(this->file_); } }; // A FILE wrapper for Apple's libc. template class apple_file : public file_base { private: enum { line_buffered = 1, // __SNBF unbuffered = 2 // __SLBF }; public: using file_base::file_base; auto is_buffered() const -> bool { return (this->file_->_flags & unbuffered) == 0; } void init_buffer() { if (this->file_->_p) return; // Force buffer initialization by placing and removing a char in a buffer. if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_); --this->file_->_p; ++this->file_->_w; } auto get_read_buffer() const -> span { return {reinterpret_cast(this->file_->_p), to_unsigned(this->file_->_r)}; } auto get_write_buffer() const -> span { return {reinterpret_cast(this->file_->_p), to_unsigned(this->file_->_bf._base + this->file_->_bf._size - this->file_->_p)}; } void advance_write_buffer(size_t size) { this->file_->_p += size; this->file_->_w -= size; } auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; return memchr(this->file_->_p + this->file_->_w, '\n', to_unsigned(-this->file_->_w)); } }; // A fallback FILE wrapper. template class fallback_file : public file_base { private: char next_; // The next unconsumed character in the buffer. bool has_next_ = false; public: using file_base::file_base; auto is_buffered() const -> bool { return false; } auto needs_flush() const -> bool { return false; } void init_buffer() {} auto get_read_buffer() const -> span { return {&next_, has_next_ ? 1u : 0u}; } auto get_write_buffer() const -> span { return {nullptr, 0}; } void advance_write_buffer(size_t) {} auto get() -> int { has_next_ = false; return file_base::get(); } void unget(char c) { file_base::unget(c); next_ = c; has_next_ = true; } }; #ifndef FMT_USE_FALLBACK_FILE # define FMT_USE_FALLBACK_FILE 0 #endif template auto get_file(F* f, int) -> apple_file { return f; } template inline auto get_file(F* f, int) -> glibc_file { return f; } inline auto get_file(FILE* f, ...) -> fallback_file { return f; } using file_ref = decltype(get_file(static_cast(nullptr), 0)); template class file_print_buffer : public buffer { public: explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} }; template class file_print_buffer::value>> : public buffer { private: file_ref file_; static void grow(buffer& base, size_t) { auto& self = static_cast(base); self.file_.advance_write_buffer(self.size()); if (self.file_.get_write_buffer().size == 0) self.file_.flush(); auto buf = self.file_.get_write_buffer(); FMT_ASSERT(buf.size > 0, ""); self.set(buf.data, buf.size); self.clear(); } public: explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { flockfile(f); file_.init_buffer(); auto buf = file_.get_write_buffer(); set(buf.data, buf.size); } ~file_print_buffer() { file_.advance_write_buffer(size()); bool flush = file_.needs_flush(); F* f = file_; // Make funlockfile depend on the template parameter F funlockfile(f); // for the system API detection to work. if (flush) fflush(file_); } }; #if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) FMT_FUNC auto write_console(int, string_view) -> bool { return false; } #else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); FMT_FUNC bool write_console(int fd, string_view text) { auto u16 = utf8_to_utf16(text); return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), nullptr, nullptr) != 0; } #endif #ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, bool newline) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); if (newline) buffer.push_back('\n'); fwrite_all(buffer.data(), buffer.size(), f); } #endif FMT_FUNC void print(std::FILE* f, string_view text) { #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) int fd = _fileno(f); if (_isatty(fd)) { std::fflush(f); if (write_console(fd, text)) return; } #endif fwrite_all(text.data(), text.size(), f); } } // namespace detail FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); detail::print(f, {buffer.data(), buffer.size()}); } FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) return vprint_buffered(f, fmt, args); auto&& buffer = detail::file_print_buffer<>(f); return detail::vformat_to(buffer, fmt, args); } FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); buffer.push_back('\n'); detail::print(f, {buffer.data(), buffer.size()}); } FMT_FUNC void vprint(string_view fmt, format_args args) { vprint(stdout, fmt, args); } namespace detail { struct singleton { unsigned char upper; unsigned char lower_count; }; inline auto is_printable(uint16_t x, const singleton* singletons, size_t singletons_size, const unsigned char* singleton_lowers, const unsigned char* normal, size_t normal_size) -> bool { auto upper = x >> 8; auto lower_start = 0; for (size_t i = 0; i < singletons_size; ++i) { auto s = singletons[i]; auto lower_end = lower_start + s.lower_count; if (upper < s.upper) break; if (upper == s.upper) { for (auto j = lower_start; j < lower_end; ++j) { if (singleton_lowers[j] == (x & 0xff)) return false; } } lower_start = lower_end; } auto xsigned = static_cast(x); auto current = true; for (size_t i = 0; i < normal_size; ++i) { auto v = static_cast(normal[i]); auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; xsigned -= len; if (xsigned < 0) break; current = !current; } return current; } // This code is generated by support/printable.py. FMT_FUNC auto is_printable(uint32_t cp) -> bool { static constexpr singleton singletons0[] = { {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, }; static constexpr unsigned char singletons0_lower[] = { 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, 0xfe, 0xff, }; static constexpr singleton singletons1[] = { {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, {0xfa, 2}, {0xfb, 1}, }; static constexpr unsigned char singletons1_lower[] = { 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, }; static constexpr unsigned char normal0[] = { 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, }; static constexpr unsigned char normal1[] = { 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, }; auto lower = static_cast(cp); if (cp < 0x10000) { return is_printable(lower, singletons0, sizeof(singletons0) / sizeof(*singletons0), singletons0_lower, normal0, sizeof(normal0)); } if (cp < 0x20000) { return is_printable(lower, singletons1, sizeof(singletons1) / sizeof(*singletons1), singletons1_lower, normal1, sizeof(normal1)); } if (0x2a6de <= cp && cp < 0x2a700) return false; if (0x2b735 <= cp && cp < 0x2b740) return false; if (0x2b81e <= cp && cp < 0x2b820) return false; if (0x2cea2 <= cp && cp < 0x2ceb0) return false; if (0x2ebe1 <= cp && cp < 0x2f800) return false; if (0x2fa1e <= cp && cp < 0x30000) return false; if (0x3134b <= cp && cp < 0xe0100) return false; if (0xe01f0 <= cp && cp < 0x110000) return false; return cp < 0x110000; } } // namespace detail FMT_END_NAMESPACE #endif // FMT_FORMAT_INL_H_ ================================================ FILE: include/fmt/format.h ================================================ /* Formatting library for C++ Copyright (c) 2012 - present, Victor Zverovich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- Optional exception to the license --- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the above copyright and permission notices. */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ #ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES # define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES # define FMT_REMOVE_TRANSITIVE_INCLUDES #endif #include "base.h" // libc++ supports string_view in pre-c++17. #if FMT_HAS_INCLUDE() && \ (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # define FMT_USE_STRING_VIEW #endif #ifndef FMT_MODULE # include // uint32_t # include // malloc, free # include // memcpy # include // std::signbit # include // std::numeric_limits # if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI) // Workaround for pre gcc 5 libstdc++. # include // std::allocator_traits # endif # include // std::runtime_error # include // std::string # include // std::system_error // Check FMT_CPLUSPLUS to avoid a warning in MSVC. # if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L # include // std::bit_cast # endif # if defined(FMT_USE_STRING_VIEW) # include # endif # if FMT_MSC_VERSION # include // _BitScanReverse[64], _umul128 # endif #endif // FMT_MODULE #if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS) // Use the provided definition. #elif defined(__NVCOMPILER) # define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 #elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L # define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 #elif defined(__cpp_nontype_template_args) && \ __cpp_nontype_template_args >= 201911L # define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 #elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L # define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 #else # define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 #endif #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L # define FMT_INLINE_VARIABLE inline #else # define FMT_INLINE_VARIABLE #endif // Check if RTTI is disabled. #ifdef FMT_USE_RTTI // Use the provided definition. #elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ defined(__INTEL_RTTI__) || defined(__RTTI) // __RTTI is for EDG compilers. _CPPRTTI is for MSVC. # define FMT_USE_RTTI 1 #else # define FMT_USE_RTTI 0 #endif // Visibility when compiled as a shared library/object. #if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) # define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value) #else # define FMT_SO_VISIBILITY(value) #endif #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_NOINLINE __attribute__((noinline)) #else # define FMT_NOINLINE #endif // Detect constexpr std::string. #if !FMT_USE_CONSTEVAL # define FMT_USE_CONSTEXPR_STRING 0 #elif defined(__cpp_lib_constexpr_string) && \ __cpp_lib_constexpr_string >= 201907L # if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE // clang + libstdc++ are able to work only starting with gcc13.3 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294 # if FMT_GLIBCXX_RELEASE < 13 # define FMT_USE_CONSTEXPR_STRING 0 # elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521 # define FMT_USE_CONSTEXPR_STRING 0 # else # define FMT_USE_CONSTEXPR_STRING 1 # endif # else # define FMT_USE_CONSTEXPR_STRING 1 # endif #else # define FMT_USE_CONSTEXPR_STRING 0 #endif #if FMT_USE_CONSTEXPR_STRING # define FMT_CONSTEXPR_STRING constexpr #else # define FMT_CONSTEXPR_STRING #endif // GCC 4.9 doesn't support qualified names in specializations. namespace std { template struct iterator_traits> { using iterator_category = output_iterator_tag; using value_type = T; using difference_type = decltype(static_cast(nullptr) - static_cast(nullptr)); using pointer = void; using reference = void; }; } // namespace std #ifdef FMT_THROW // Use the provided definition. #elif FMT_USE_EXCEPTIONS # define FMT_THROW(x) throw x #else # define FMT_THROW(x) ::fmt::assert_fail(__FILE__, __LINE__, (x).what()) #endif #ifdef __clang_analyzer__ # define FMT_CLANG_ANALYZER 1 #else # define FMT_CLANG_ANALYZER 0 #endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the // largest integer type. This results in a reduction in binary size but will // cause a decrease in integer formatting performance. #if !defined(FMT_REDUCE_INT_INSTANTIATIONS) # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif FMT_BEGIN_NAMESPACE template struct is_contiguous> : std::true_type {}; namespace detail { // __builtin_clz is broken in clang with Microsoft codegen: // https://github.com/fmtlib/fmt/issues/519. #if !FMT_MSC_VERSION # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) # endif # if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION # define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) # endif #endif // Some compilers masquerade as both MSVC and GCC but otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. #if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. # ifndef __clang__ # pragma intrinsic(_BitScanReverse) # ifdef _WIN64 # pragma intrinsic(_BitScanReverse64) # endif # endif inline auto clz(uint32_t x) -> int { FMT_ASSERT(x != 0, ""); unsigned long r = 0; _BitScanReverse(&r, x); return 31 ^ static_cast(r); } # define FMT_BUILTIN_CLZ(n) detail::clz(n) inline auto clzll(uint64_t x) -> int { FMT_ASSERT(x != 0, ""); unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); # else // Scan the high 32 bits. if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ static_cast(r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif return 63 ^ static_cast(r); } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) #endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { ignore_unused(condition); #ifdef FMT_FUZZ if (condition) throw std::runtime_error("fuzzing limit reached"); #endif } #if defined(FMT_USE_STRING_VIEW) template using std_string_view = std::basic_string_view; #else template struct std_string_view { operator basic_string_view() const; }; #endif template struct string_literal { static constexpr Char value[sizeof...(C)] = {C...}; constexpr operator basic_string_view() const { return {value, sizeof...(C)}; } }; #if FMT_CPLUSPLUS < 201703L template constexpr Char string_literal::value[sizeof...(C)]; #endif // Implementation of std::bit_cast for pre-C++20. template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { #ifdef __cpp_lib_bit_cast if (is_constant_evaluated()) return std::bit_cast(from); #endif auto to = To(); // The cast suppresses a bogus -Wclass-memaccess on GCC. memcpy(static_cast(&to), &from, sizeof(to)); return to; } inline auto is_big_endian() -> bool { #ifdef _WIN32 return false; #elif defined(__BIG_ENDIAN__) return true; #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; #else struct bytes { char data[sizeof(int)]; }; return bit_cast(1).data[0] == 0; #endif } class uint128 { private: uint64_t lo_, hi_; public: constexpr uint128(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} constexpr uint128(uint64_t value = 0) : lo_(value), hi_(0) {} constexpr auto high() const noexcept -> uint64_t { return hi_; } constexpr auto low() const noexcept -> uint64_t { return lo_; } template ::value)> constexpr explicit operator T() const { return static_cast(lo_); } friend constexpr auto operator==(const uint128& lhs, const uint128& rhs) -> bool { return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; } friend constexpr auto operator!=(const uint128& lhs, const uint128& rhs) -> bool { return !(lhs == rhs); } friend constexpr auto operator>(const uint128& lhs, const uint128& rhs) -> bool { return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_; } friend constexpr auto operator|(const uint128& lhs, const uint128& rhs) -> uint128 { return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; } friend constexpr auto operator&(const uint128& lhs, const uint128& rhs) -> uint128 { return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; } friend FMT_CONSTEXPR auto operator+(const uint128& lhs, const uint128& rhs) -> uint128 { auto result = uint128(lhs); result += rhs; return result; } friend FMT_CONSTEXPR auto operator*(const uint128& lhs, uint32_t rhs) -> uint128 { FMT_ASSERT(lhs.hi_ == 0, ""); uint64_t hi = (lhs.lo_ >> 32) * rhs; uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs; uint64_t new_lo = (hi << 32) + lo; return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; } friend constexpr auto operator-(const uint128& lhs, uint64_t rhs) -> uint128 { return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; } FMT_CONSTEXPR auto operator>>(int shift) const -> uint128 { if (shift == 64) return {0, hi_}; if (shift > 64) return uint128(0, hi_) >> (shift - 64); return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; } FMT_CONSTEXPR auto operator<<(int shift) const -> uint128 { if (shift == 64) return {lo_, 0}; if (shift > 64) return uint128(lo_, 0) << (shift - 64); return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; } FMT_CONSTEXPR auto operator>>=(int shift) -> uint128& { return *this = *this >> shift; } FMT_CONSTEXPR void operator+=(uint128 n) { uint64_t new_lo = lo_ + n.lo_; uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0); FMT_ASSERT(new_hi >= hi_, ""); lo_ = new_lo; hi_ = new_hi; } FMT_CONSTEXPR void operator&=(uint128 n) { lo_ &= n.lo_; hi_ &= n.hi_; } FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128& { if (is_constant_evaluated()) { lo_ += n; hi_ += (lo_ < n ? 1 : 0); return *this; } #if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__) ullong carry; lo_ = __builtin_addcll(lo_, n, 0, &carry); hi_ += carry; #elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) ullong result; auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); lo_ = result; hi_ += carry; #elif defined(_MSC_VER) && defined(_M_AMD64) auto carry = _addcarry_u64(0, lo_, n, &lo_); _addcarry_u64(carry, hi_, 0, &hi_); #else lo_ += n; hi_ += (lo_ < n ? 1 : 0); #endif return *this; } }; using uint128_t = conditional_t; #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; #else using uintptr_t = uint128_t; #endif // Returns the largest possible value for type T. Same as // std::numeric_limits::max() but shorter and not affected by the max macro. template constexpr auto max_value() -> T { return (std::numeric_limits::max)(); } template constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. template <> constexpr auto num_bits() -> int { return 128; } template <> constexpr auto num_bits() -> int { return 128; } template <> constexpr auto num_bits() -> int { return 128; } // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t // and 128-bit pointers to uint128. template sizeof(From))> inline auto bit_cast(const From& from) -> To { constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned short)); struct data_t { unsigned short value[static_cast(size)]; } data = bit_cast(from); auto result = To(); if (is_big_endian()) { for (int i = 0; i < size; ++i) result = (result << num_bits()) | data.value[i]; } else { for (int i = size - 1; i >= 0; --i) result = (result << num_bits()) | data.value[i]; } return result; } template FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int { int lz = 0; constexpr UInt msb_mask = static_cast(1) << (num_bits() - 1); for (; (n & msb_mask) == 0; n <<= 1) lz++; return lz; } FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int { #ifdef FMT_BUILTIN_CLZ if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n); #endif return countl_zero_fallback(n); } FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n); #endif return countl_zero_fallback(n); } FMT_INLINE void assume(bool condition) { (void)condition; #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION __builtin_assume(condition); #elif FMT_GCC_VERSION if (!condition) __builtin_unreachable(); #endif } // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to it. template ::value&& is_contiguous::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif FMT_CONSTEXPR20 inline auto reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { auto& c = get_container(it); size_t size = c.size(); c.resize(size + n); return &c[size]; } template FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) -> basic_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; } template constexpr auto reserve(Iterator& it, size_t) -> Iterator& { return it; } template using reserve_iterator = remove_reference_t(), 0))>; template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } template FMT_CONSTEXPR auto to_pointer(T*& ptr, size_t n) -> T* { T* begin = ptr; ptr += n; return begin; } template FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; buf.try_resize(size + n); return buf.data() + size; } template ::value&& is_contiguous::value)> inline auto base_iterator(OutputIt it, typename OutputIt::container_type::value_type*) -> OutputIt { return it; } template constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { return it; } // is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) -> OutputIt { for (Size i = 0; i < count; ++i) *out++ = value; return out; } template FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { if (is_constant_evaluated()) return fill_n(out, count, value); static_assert(sizeof(T) == 1, "sizeof(T) must be 1 to use char for initialization"); memset(out, value, to_unsigned(count)); 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); } template FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, OutputIt out) -> OutputIt { return copy(begin, end, out); } // A public domain branchless UTF-8 decoder by Christopher Wellons: // https://github.com/skeeto/branchless-utf8 /* Decode the next character, c, from s, reporting errors in e. * * Since this is a branchless decoder, four bytes will be read from the * buffer regardless of the actual length of the next character. This * means the buffer _must_ have at least three bytes of zero padding * following the end of the data stream. * * Errors are reported in e, which will be non-zero if the parsed * character was somehow invalid: invalid byte sequence, non-canonical * encoding, or a surrogate half. * * The function returns a pointer to the next character. When an error * occurs, this pointer will be a guess that depends on the particular * error, but it will always advance at least one byte. */ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) -> const char* { constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; constexpr int shiftc[] = {0, 18, 12, 6, 0}; constexpr int shifte[] = {0, 6, 4, 2, 0}; int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" [static_cast(*s) >> 3]; // Compute the pointer to the next character early so that the next // iteration can start working on the next character. Neither Clang // nor GCC figure out this reordering on their own. const char* next = s + len + !len; using uchar = unsigned char; // Assume a four-byte character and load four bytes. Unused bits are // shifted out. *c = uint32_t(uchar(s[0]) & masks[len]) << 18; *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; *c >>= shiftc[len]; // Accumulate the various error conditions. *e = (*c < mins[len]) << 6; // non-canonical encoding *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? *e |= (*c > 0x10FFFF) << 8; // out of range? *e |= (uchar(s[1]) & 0xc0) >> 2; *e |= (uchar(s[2]) & 0xc0) >> 4; *e |= uchar(s[3]) >> 6; *e ^= 0x2a; // top two bits of each tail byte correct? *e >>= shifte[len]; return next; } constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t(); // Invokes f(cp, sv) for every code point cp in s with sv being the string view // corresponding to the code point. cp is invalid_code_point on error. template FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { auto decode = [f](const char* buf_ptr, const char* ptr) { auto cp = uint32_t(); auto error = 0; auto end = utf8_decode(buf_ptr, &cp, &error); bool result = f(error ? invalid_code_point : cp, string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); return result ? (error ? buf_ptr + 1 : end) : nullptr; }; auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. if (s.size() >= block_size) { for (auto end = p + s.size() - block_size + 1; p < end;) { p = decode(p, p); if (!p) return; } } auto num_chars_left = to_unsigned(s.data() + s.size() - p); if (num_chars_left == 0) return; // Suppress bogus -Wstringop-overflow. if (FMT_GCC_VERSION) num_chars_left &= 3; char buf[2 * block_size - 1] = {}; copy(p, p + num_chars_left, buf); const char* buf_ptr = buf; do { auto end = decode(buf_ptr, p); if (!end) return; p += end - buf_ptr; buf_ptr = end; } while (buf_ptr < buf + num_chars_left); } FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t { return to_unsigned( 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms (cp >= 0x20000 && cp <= 0x2fffd) || // CJK (cp >= 0x30000 && cp <= 0x3fffd) || // Miscellaneous Symbols and Pictographs + Emoticons: (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: (cp >= 0x1f900 && cp <= 0x1f9ff)))); } template struct is_integral : std::is_integral {}; template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; template using is_signed = std::integral_constant::is_signed || std::is_same::value>; template using is_integer = bool_constant::value && !std::is_same::value && !std::is_same::value && !std::is_same::value>; #if defined(FMT_USE_FLOAT128) // Use the provided definition. #elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE() # define FMT_USE_FLOAT128 1 #elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ !defined(__STRICT_ANSI__) # define FMT_USE_FLOAT128 1 #else # define FMT_USE_FLOAT128 0 #endif #if FMT_USE_FLOAT128 using float128 = __float128; #else struct float128 {}; #endif template using is_float128 = std::is_same; template struct is_floating_point : std::is_floating_point {}; template <> struct is_floating_point : std::true_type {}; template ::value> struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; template using fast_float_t = conditional_t; template using is_double_double = bool_constant::digits == 106>; FMT_API auto allocate(size_t size) -> void*; // An allocator that uses malloc/free to allow removing dependency on the C++ // standard library runtime. std::decay is used for back_inserter to be found by // ADL when applied to memory_buffer. template struct allocator : private std::decay { using value_type = T; auto allocate(size_t n) -> T* { FMT_ASSERT(n <= max_value() / sizeof(T), ""); return static_cast(detail::allocate(n * sizeof(T))); } void deallocate(T* p, size_t) { free(p); } constexpr friend auto operator==(allocator, allocator) noexcept -> bool { return true; // All instances of this allocator are equivalent. } constexpr friend auto operator!=(allocator, allocator) noexcept -> bool { return false; } }; template FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) -> decltype(f.set_debug_format(set)) { f.set_debug_format(set); } template FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} } // namespace detail FMT_BEGIN_EXPORT // The number of characters to store in the basic_memory_buffer object itself // to avoid dynamic memory allocation. enum { inline_buffer_size = 500 }; /** * A dynamically growing memory buffer for trivially copyable/constructible * types with the first `SIZE` elements stored in the object itself. Most * commonly used via the `memory_buffer` alias for `char`. * * **Example**: * * auto out = fmt::memory_buffer(); * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); * * This will append "The answer is 42." to `out`. The buffer content can be * converted to `std::string` with `to_string(out)`. */ template > class basic_memory_buffer : public detail::buffer { private: T store_[SIZE]; // Don't inherit from Allocator to avoid generating type_info for it. FMT_NO_UNIQUE_ADDRESS Allocator alloc_; // Deallocate memory allocated by the buffer. FMT_CONSTEXPR20 void deallocate() { T* data = this->data(); if (data != store_) alloc_.deallocate(data, this->capacity()); } static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { detail::abort_fuzzing_if(size > 5000); auto& self = static_cast(buf); const size_t max_size = std::allocator_traits::max_size(self.alloc_); size_t old_capacity = buf.capacity(); size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; else if (new_capacity > max_size) new_capacity = max_of(size, max_size); T* old_data = buf.data(); T* new_data = self.alloc_.allocate(new_capacity); // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). detail::assume(buf.size() <= new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. memcpy(new_data, old_data, buf.size() * sizeof(T)); self.set(new_data, new_capacity); // deallocate must not throw according to the standard, but even if it does, // the buffer already uses the new storage and will deallocate it in // destructor. if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity); } public: using value_type = T; using const_reference = const T&; FMT_CONSTEXPR explicit basic_memory_buffer( const Allocator& alloc = Allocator()) : detail::buffer(grow), alloc_(alloc) { this->set(store_, SIZE); if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: template :: propagate_on_container_move_assignment::value)> FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { alloc_ = std::move(other.alloc_); return true; } // If the allocator does not propagate then copy the data from other. template :: propagate_on_container_move_assignment::value)> FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { T* data = other.data(); if (alloc_ == other.alloc_ || data == other.store_) return true; size_t size = other.size(); // Perform copy operation, allocators are different. this->resize(size); detail::copy(data, data + size, this->data()); return false; } // Move data from other to this buffer. FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); if (!move_alloc(other)) return; if (data == other.store_) { this->set(store_, capacity); detail::copy(other.store_, other.store_ + size, store_); } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called // when deallocating. other.set(other.store_, 0); other.clear(); } this->resize(size); } public: /// Constructs a `basic_memory_buffer` object moving the content of the other /// object to it. FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept : detail::buffer(grow) { move(other); } /// Moves the content of the other `basic_memory_buffer` object to this one. auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); return *this; } // Returns a copy of the allocator associated with this buffer. auto get_allocator() const -> Allocator { return alloc_; } /// Resizes the buffer to contain `count` elements. If T is a POD type new /// elements may not be initialized. FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); } /// Increases the buffer capacity to `new_capacity`. void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } using detail::buffer::append; template FMT_CONSTEXPR20 void append(const ContiguousRange& range) { append(range.data(), range.data() + range.size()); } }; using memory_buffer = basic_memory_buffer; template FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) -> std::string { auto size = buf.size(); detail::assume(size < std::string().max_size()); return {buf.data(), size}; } // A writer to a buffered stream. It doesn't own the underlying stream. class writer { private: detail::buffer* buf_; // We cannot create a file buffer in advance because any write to a FILE may // invalidate it. FILE* file_; public: inline writer(FILE* f) : buf_(nullptr), file_(f) {} inline writer(detail::buffer& buf) : buf_(&buf) {} /// Formats `args` according to specifications in `fmt` and writes the /// output to the file. template void print(format_string fmt, T&&... args) { if (buf_) fmt::format_to(appender(*buf_), fmt, std::forward(args)...); else fmt::print(file_, fmt, std::forward(args)...); } }; class string_buffer { private: std::string str_; detail::container_buffer buf_; public: inline string_buffer() : buf_(str_) {} inline operator writer() { return buf_; } inline auto str() -> std::string& { return str_; } }; template struct is_contiguous> : std::true_type { }; // Suppress a misleading warning in older versions of clang. FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") /// An error reported from a formatting function. class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; }; class loc_value; FMT_END_EXPORT namespace detail { FMT_API auto write_console(int fd, string_view text) -> bool; FMT_API void print(FILE*, string_view); } // namespace detail namespace detail { template struct fixed_string { FMT_CONSTEXPR fixed_string(const Char (&s)[N]) { detail::copy(static_cast(s), s + N, data); } Char data[N] = {}; }; // Converts a compile-time string to basic_string_view. FMT_EXPORT template constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view { // Remove trailing NUL character if needed. Won't be present if this is used // with a raw character array (i.e. not defined as a string). return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; } FMT_EXPORT template constexpr auto compile_string_to_view(basic_string_view s) -> basic_string_view { return s; } // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> constexpr auto is_negative(T value) -> bool { return value < 0; } template ::value)> constexpr auto is_negative(T) -> bool { return false; } // Smallest of uint32_t, uint64_t, uint128_t that is large enough to // represent all values of an integral type T. template using uint32_or_64_or_128_t = conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, conditional_t() <= 64, uint64_t, uint128_t>>; template using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \ (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \ (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. GCC generates a bit better // code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc). inline auto digits2(size_t value) noexcept -> const char* { // Align data since unaligned access may be slower when crossing a // hardware-specific boundary. alignas(2) static constexpr char data[] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; return &data[value * 2]; } // Given i in [0, 100), let x be the first 7 digits after // the decimal point of i / 100 in base 2, the first 2 bytes // after digits2_i(x) is the string representation of i. inline auto digits2_i(size_t value) noexcept -> const char* { alignas(2) static constexpr char data[] = "00010203 0405060707080910 1112" "131414151617 18192021 222324 " "25262728 2930313232333435 3637" "383939404142 43444546 474849 " "50515253 5455565757585960 6162" "636464656667 68697071 727374 " "75767778 7980818282838485 8687" "888989909192 93949596 979899 "; return &data[value * 2]; } template constexpr auto getsign(sign s) -> Char { return static_cast(static_cast( ((' ' << 24) | ('+' << 16) | ('-' << 8)) >> (static_cast(s) * 8))); } template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. if (n < 10) return count; if (n < 100) return count + 1; if (n < 1000) return count + 2; if (n < 10000) return count + 3; n /= 10000u; 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. inline auto do_count_digits(uint64_t n) -> int { // This has comparable performance to the version by Kendall Willets // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) // but uses smaller tables. // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). static constexpr uint8_t bsr2log10[] = { 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; static constexpr uint64_t zero_or_powers_of_10[] = { 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; return t - (n < zero_or_powers_of_10[t]); } #endif // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } // Counts the number of digits in n. BITS = log2(radix). template FMT_CONSTEXPR auto count_digits(UInt n) -> int { #ifdef FMT_BUILTIN_CLZ if (!is_constant_evaluated() && num_bits() == 32) return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; #endif // Lambda avoids unreachable code warnings from NVHPC. return [](UInt m) { int num_digits = 0; do { ++num_digits; } while ((m >>= BITS) != 0); return num_digits; }(n); } #ifdef FMT_BUILTIN_CLZ // It is a separate function rather than a part of count_digits to workaround // the lack of static constexpr in constexpr functions. FMT_INLINE auto do_count_digits(uint32_t n) -> int { // An optimization by Kendall Willets from https://bit.ly/3uOIQrB. // This increments the upper 32 bits (log10(T) - 1) when >= T is added. # define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) static constexpr uint64_t table[] = { FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M FMT_INC(1000000000), FMT_INC(1000000000) // 4B }; auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; return static_cast((n + inc) >> 32); } #endif // Optional version of count_digits for better performance on 32-bit platforms. FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { #ifdef FMT_BUILTIN_CLZ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } template constexpr auto digits10() noexcept -> int { return std::numeric_limits::digits10; } template <> constexpr auto digits10() noexcept -> int { return 38; } template <> constexpr auto digits10() noexcept -> int { return 38; } template struct thousands_sep_result { std::string grouping; Char thousands_sep; }; template FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; template inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { auto result = thousands_sep_impl(loc); return {std::move(result.grouping), Char(result.thousands_sep)}; } template <> inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { return thousands_sep_impl(loc); } template FMT_API auto decimal_point_impl(locale_ref loc) -> Char; template inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } #ifndef FMT_HEADER_ONLY FMT_BEGIN_EXPORT extern template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; extern template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; extern template FMT_API auto decimal_point_impl(locale_ref) -> char; extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; FMT_END_EXPORT #endif // FMT_HEADER_ONLY // Compares two characters for equality. template auto equal2(const Char* lhs, const char* rhs) -> bool { return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); } inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } // Writes a two-digit value to out. template FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { if (!is_constant_evaluated() && std::is_same::value && !FMT_OPTIMIZE_SIZE) { memcpy(out, digits2(value), 2); return; } *out++ = static_cast('0' + value / 10); *out = static_cast('0' + value % 10); } template FMT_INLINE void write2digits_i(Char* out, size_t value) { if (std::is_same::value && !FMT_OPTIMIZE_SIZE) { memcpy(out, digits2_i(value), 2); return; } *out++ = static_cast(digits2_i(value)[0]); *out = static_cast(digits2_i(value)[1]); } // Formats a decimal unsigned integer value writing to out pointing to a buffer // of specified size. The caller must ensure that the buffer is large enough. template FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) -> Char* { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); unsigned n = to_unsigned(size); while (value >= 100) { n -= 2; if (!is_constant_evaluated() && sizeof(UInt) == 4) { auto p = value * static_cast((1ull << 39) / 100 + 1); write2digits_i(out + n, p >> (39 - 7) & ((1 << 7) - 1)); value = static_cast(p >> 39) + (static_cast(value >= (100u << 25)) << 25); } else { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. write2digits(out + n, static_cast(value % 100)); value /= 100; } } if (value >= 10) { n -= 2; write2digits(out + n, static_cast(value)); } else { out[--n] = static_cast('0' + value); } return out + n; } template FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, int num_digits) -> Char* { do_format_decimal(out, value, num_digits); return out + num_digits; } template >::value)> FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { do_format_decimal(ptr, value, num_digits); return out; } // Buffer is large enough to hold all digits (digits10 + 1). char buffer[digits10() + 1]; if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); do_format_decimal(buffer, value, num_digits); return copy_noinline(buffer, buffer + num_digits, out); } template FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, int size, bool upper = false) -> Char* { out += size; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; unsigned digit = static_cast(value & ((1u << base_bits) - 1)); *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= base_bits) != 0); return out; } // Formats an unsigned integer in the power of two base (binary, octal, hex). template FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, int num_digits, bool upper = false) -> Char* { do_format_base2e(base_bits, out, value, num_digits, upper); return out + num_digits; } template ::value)> FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, int num_digits, bool upper = false) -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { format_base2e(base_bits, ptr, value, num_digits, upper); return out; } // Make buffer large enough for any base. char buffer[num_bits()]; if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); format_base2e(base_bits, buffer, value, num_digits, upper); return detail::copy_noinline(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. class utf8_to_utf16 { private: basic_memory_buffer buffer_; public: FMT_API explicit utf8_to_utf16(string_view s); inline operator basic_string_view() const { return {&buffer_[0], size()}; } inline auto size() const -> size_t { return buffer_.size() - 1; } inline auto c_str() const -> const wchar_t* { return &buffer_[0]; } inline auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; enum class to_utf8_error_policy { abort, replace, wtf }; inline void to_utf8_3bytes(buffer& buf, uint32_t cp) { buf.push_back(static_cast(0xe0 | (cp >> 12))); buf.push_back(static_cast(0x80 | ((cp & 0xfff) >> 6))); buf.push_back(static_cast(0x80 | (cp & 0x3f))); } // A converter from UTF-16/UTF-32 (host endian) to UTF-8. template class to_utf8 { private: Buffer buffer_; public: to_utf8() {} explicit to_utf8(basic_string_view s, to_utf8_error_policy policy = to_utf8_error_policy::abort) { static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, "expected utf16 or utf32"); if (!convert(s, policy)) { FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" : "invalid utf32")); } } operator string_view() const { return string_view(&buffer_[0], size()); } auto size() const -> size_t { return buffer_.size() - 1; } auto c_str() const -> const char* { return &buffer_[0]; } auto str() const -> std::string { return std::string(&buffer_[0], size()); } // Performs conversion returning a bool instead of throwing exception on // conversion error. This method may still throw in case of memory allocation // error. auto convert(basic_string_view s, to_utf8_error_policy policy = to_utf8_error_policy::abort) -> bool { if (!convert(buffer_, s, policy)) return false; buffer_.push_back(0); return true; } static auto convert(Buffer& buf, basic_string_view s, to_utf8_error_policy policy = to_utf8_error_policy::abort) -> bool { for (auto p = s.begin(); p != s.end(); ++p) { uint32_t c = static_cast(*p); if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { // Handle a surrogate pair. ++p; if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { switch (policy) { case to_utf8_error_policy::abort: return false; case to_utf8_error_policy::replace: buf.append(string_view("\xEF\xBF\xBD")); break; case to_utf8_error_policy::wtf: to_utf8_3bytes(buf, c); break; } --p; continue; } c = (c << 10) + static_cast(*p) - 0x35fdc00; } if (c < 0x80) { buf.push_back(static_cast(c)); } else if (c < 0x800) { buf.push_back(static_cast(0xc0 | (c >> 6))); buf.push_back(static_cast(0x80 | (c & 0x3f))); } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { to_utf8_3bytes(buf, c); } else if (c >= 0x10000 && c <= 0x10ffff) { buf.push_back(static_cast(0xf0 | (c >> 18))); buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); buf.push_back(static_cast(0x80 | (c & 0x3f))); } else { return false; } } return true; } }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128 { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return {static_cast(p >> 64), static_cast(p)}; #elif defined(_MSC_VER) && defined(_M_AMD64) auto hi = uint64_t(); auto lo = _umul128(x, y, &hi); return {hi, lo}; #else const uint64_t mask = static_cast(max_value()); uint64_t a = x >> 32; uint64_t b = x & mask; uint64_t c = y >> 32; uint64_t d = y & mask; uint64_t ac = a * c; uint64_t bc = b * c; uint64_t ad = a * d; uint64_t bd = b * d; uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), (intermediate << 32) + (bd & mask)}; #endif } namespace dragonbox { // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. inline auto floor_log10_pow2(int e) noexcept -> int { FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); return (e * 315653) >> 20; } inline auto floor_log2_pow10(int e) noexcept -> int { FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); return (e * 1741647) >> 19; } // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); #elif defined(_MSC_VER) && defined(_M_AMD64) return __umulh(x, y); #else return umul128(x, y).high(); #endif } // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. inline auto umul192_upper128(uint64_t x, uint128 y) noexcept -> uint128 { uint128 r = umul128(x, y.high()); r += umul128_upper64(x, y.low()); return r; } FMT_API auto get_cached_power(int k) noexcept -> uint128; // Type-specific information that Dragonbox uses. template struct float_info; template <> struct float_info { using carrier_uint = uint32_t; static constexpr int exponent_bits = 8; static constexpr int kappa = 1; static constexpr int big_divisor = 100; static constexpr int small_divisor = 10; static constexpr int min_k = -31; enum { max_k = 46 }; static constexpr int shorter_interval_tie_lower_threshold = -35; static constexpr int shorter_interval_tie_upper_threshold = -35; }; template <> struct float_info { using carrier_uint = uint64_t; static constexpr int exponent_bits = 11; static constexpr int kappa = 2; static constexpr int big_divisor = 1000; static constexpr int small_divisor = 100; static constexpr int min_k = -292; enum { max_k = 341 }; static constexpr int shorter_interval_tie_lower_threshold = -77; static constexpr int shorter_interval_tie_upper_threshold = -77; }; // An 80- or 128-bit floating point number. template struct float_info::digits == 64 || std::numeric_limits::digits == 113 || is_float128::value>> { using carrier_uint = detail::uint128_t; static const int exponent_bits = 15; }; // A double-double floating point number. template struct float_info::value>> { using carrier_uint = detail::uint128_t; }; template struct decimal_fp { using significand_type = typename float_info::carrier_uint; significand_type significand; int exponent; }; template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox // Returns true iff Float has the implicit bit which is not stored. template constexpr auto has_implicit_bit() -> bool { // An 80-bit FP number has a 64-bit significand an no implicit bit. return std::numeric_limits::digits != 64; } // Returns the number of significand bits stored in Float. The implicit bit is // not counted since it is not stored. template constexpr auto num_significand_bits() -> int { // std::numeric_limits may not support __float128. return is_float128() ? 112 : (std::numeric_limits::digits - (has_implicit_bit() ? 1 : 0)); } template constexpr auto exponent_mask() -> typename dragonbox::float_info::carrier_uint { using float_uint = typename dragonbox::float_info::carrier_uint; return ((float_uint(1) << dragonbox::float_info::exponent_bits) - 1) << num_significand_bits(); } template constexpr auto exponent_bias() -> int { // std::numeric_limits may not support __float128. return is_float128() ? 16383 : std::numeric_limits::max_exponent - 1; } FMT_CONSTEXPR inline auto compute_exp_size(int exp) -> int { auto prefix_size = 2; // sign + 'e' auto abs_exp = exp >= 0 ? exp : -exp; if (abs_exp < 100) return prefix_size + 2; return prefix_size + (abs_exp >= 1000 ? 4 : 3); } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *out++ = static_cast('-'); exp = -exp; } else { *out++ = static_cast('+'); } auto uexp = static_cast(exp); if (is_constant_evaluated()) { if (uexp < 10) *out++ = '0'; return format_decimal(out, uexp, count_digits(uexp)); } if (uexp >= 100u) { const char* top = digits2(uexp / 100); if (uexp >= 1000u) *out++ = static_cast(top[0]); *out++ = static_cast(top[1]); uexp %= 100; } const char* d = digits2(uexp); *out++ = static_cast(d[0]); *out++ = static_cast(d[1]); return out; } // A floating-point number f * pow(2, e) where F is an unsigned type. template struct basic_fp { F f; int e; static constexpr int num_significand_bits = static_cast(sizeof(F) * num_bits()); constexpr basic_fp() : f(0), e(0) {} constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} // Constructs fp from an IEEE754 floating-point number. template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } // Assigns n to this and return true iff predecessor is closer than successor. template ::value)> FMT_CONSTEXPR auto assign(Float n) -> bool { static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); // Assume Float is in the format [sign][exponent][significand]. using carrier_uint = typename dragonbox::float_info::carrier_uint; const auto num_float_significand_bits = detail::num_significand_bits(); const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; const auto significand_mask = implicit_bit - 1; auto u = bit_cast(n); f = static_cast(u & significand_mask); auto biased_e = static_cast((u & exponent_mask()) >> num_float_significand_bits); // The predecessor is closer if n is a normalized power of 2 (f == 0) // other than the smallest normalized number (biased_e > 1). auto is_predecessor_closer = f == 0 && biased_e > 1; if (biased_e == 0) biased_e = 1; // Subnormals use biased exponent 1 (min exponent). else if (has_implicit_bit()) f += static_cast(implicit_bit); e = biased_e - exponent_bias() - num_float_significand_bits; if (!has_implicit_bit()) ++e; return is_predecessor_closer; } template ::value)> FMT_CONSTEXPR auto assign(Float n) -> bool { static_assert(std::numeric_limits::is_iec559, "unsupported FP"); return assign(static_cast(n)); } }; using fp = basic_fp; // Normalizes the value converted from double and multiplied by (1 << SHIFT). template FMT_CONSTEXPR auto normalize(basic_fp value) -> basic_fp { // Handle subnormals. const auto implicit_bit = F(1) << num_significand_bits(); const auto shifted_implicit_bit = implicit_bit << SHIFT; while ((value.f & shifted_implicit_bit) == 0) { value.f <<= 1; --value.e; } // Subtract 1 to account for hidden bit. const auto offset = basic_fp::num_significand_bits - num_significand_bits() - SHIFT - 1; value.f <<= offset; value.e -= offset; return value; } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t { #if FMT_USE_INT128 auto product = static_cast<__uint128_t>(lhs) * rhs; auto f = static_cast(product >> 64); return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; #else // Multiply 32-bit parts of significands. uint64_t mask = (1ULL << 32) - 1; uint64_t a = lhs >> 32, b = lhs & mask; uint64_t c = rhs >> 32, d = rhs & mask; uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; // Compute mid 64-bit of result and round. uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); #endif } FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp { return {multiply(x.f, y.f), x.e + y.e + 64}; } template () == num_bits()> using convert_float_result = conditional_t::value || doublish, double, T>; template constexpr auto convert_float(T value) -> convert_float_result { return static_cast>(value); } template auto select(T true_value, F) -> T { return true_value; } template auto select(T, F false_value) -> F { return false_value; } template FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, const basic_specs& specs) -> OutputIt { auto fill_size = specs.fill_size(); if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); if (const Char* data = specs.fill()) { for (size_t i = 0; i < n; ++i) it = copy(data, data + fill_size, it); } return it; } // Writes the output of f, padded according to format specifications in specs. // size: output size in code units. // width: output display width in (terminal) column positions. template FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, size_t size, size_t width, F&& f) -> OutputIt { static_assert(default_align == align::left || default_align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; // Shifts are encoded as string literals because static constexpr is not // supported in constexpr functions. auto* shifts = default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; size_t left_padding = padding >> shifts[static_cast(specs.align())]; size_t right_padding = padding - left_padding; auto it = reserve(out, size + padding * specs.fill_size()); if (left_padding != 0) it = fill(it, left_padding, specs); it = f(it); if (right_padding != 0) it = fill(it, right_padding, specs); return base_iterator(out, it); } template constexpr auto write_padded(OutputIt out, const format_specs& specs, size_t size, F&& f) -> OutputIt { return write_padded(out, specs, size, size, f); } template FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, const format_specs& specs = {}) -> OutputIt { return write_padded( out, specs, bytes.size(), [bytes](reserve_iterator it) { const char* data = bytes.data(); return copy(data, data + bytes.size(), it); }); } template auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); return format_base2e(4, it, value, num_digits); }; return specs ? write_padded(out, *specs, size, write) : base_iterator(out, write(reserve(out, size))); } // Returns true iff the code point cp is printable. FMT_API auto is_printable(uint32_t cp) -> bool; inline auto needs_escape(uint32_t cp) -> bool { if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; if FMT_CONSTEXPR20 (FMT_OPTIMIZE_SIZE > 1) return false; return !is_printable(cp); } template struct find_escape_result { const Char* begin; const Char* end; uint32_t cp; }; template auto find_escape(const Char* begin, const Char* end) -> find_escape_result { for (; begin != end; ++begin) { uint32_t cp = static_cast>(*begin); if (sizeof(Char) == 1 && cp >= 0x80) continue; if (needs_escape(cp)) return {begin, begin + 1, cp}; } return {begin, nullptr, 0}; } inline auto find_escape(const char* begin, const char* end) -> find_escape_result { if FMT_CONSTEXPR20 (!use_utf8) return find_escape(begin, end); auto result = find_escape_result{end, nullptr, 0}; for_each_codepoint(string_view(begin, to_unsigned(end - begin)), [&](uint32_t cp, string_view sv) { if (needs_escape(cp)) { result = {sv.begin(), sv.end(), cp}; return false; } return true; }); return result; } template auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { *out++ = static_cast('\\'); *out++ = static_cast(prefix); Char buf[width]; fill_n(buf, width, static_cast('0')); format_base2e(4, buf, cp, width); return copy(buf, buf + width, out); } template auto write_escaped_cp(OutputIt out, const find_escape_result& escape) -> OutputIt { auto c = static_cast(escape.cp); switch (escape.cp) { case '\n': *out++ = static_cast('\\'); c = static_cast('n'); break; case '\r': *out++ = static_cast('\\'); c = static_cast('r'); break; case '\t': *out++ = static_cast('\\'); c = static_cast('t'); break; case '"': FMT_FALLTHROUGH; case '\'': FMT_FALLTHROUGH; case '\\': *out++ = static_cast('\\'); break; default: if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); if (escape.cp < 0x10000) return write_codepoint<4, Char>(out, 'u', escape.cp); if (escape.cp < 0x110000) return write_codepoint<8, Char>(out, 'U', escape.cp); for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { out = write_codepoint<2, Char>(out, 'x', static_cast(escape_char) & 0xFF); } return out; } *out++ = c; return out; } template auto write_escaped_string(OutputIt out, basic_string_view str) -> OutputIt { *out++ = static_cast('"'); auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); out = copy(begin, escape.begin, out); begin = escape.end; if (!begin) break; out = write_escaped_cp(out, escape); } while (begin != end); *out++ = static_cast('"'); return out; } template auto write_escaped_char(OutputIt out, Char v) -> OutputIt { Char v_array[1] = {v}; *out++ = static_cast('\''); if ((needs_escape(static_cast(v)) && v != static_cast('"')) || v == static_cast('\'')) { out = write_escaped_cp(out, find_escape_result{v_array, v_array + 1, static_cast(v)}); } else { *out++ = v; } *out++ = static_cast('\''); return out; } template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, const format_specs& specs) -> OutputIt { bool is_debug = specs.type() == presentation_type::debug; return write_padded(out, specs, 1, [=](reserve_iterator it) { if (is_debug) return write_escaped_char(it, value); *it++ = value; return it; }); } template class digit_grouping { private: std::string grouping_; std::basic_string thousands_sep_; struct next_state { std::string::const_iterator group; int pos; }; auto initial_state() const -> next_state { return {grouping_.begin(), 0}; } // Returns the next digit group separator position. auto next(next_state& state) const -> int { if (thousands_sep_.empty()) return max_value(); if (state.group == grouping_.end()) return state.pos += grouping_.back(); if (*state.group <= 0 || *state.group == max_value()) return max_value(); state.pos += *state.group++; return state.pos; } public: explicit digit_grouping(locale_ref loc, bool localized = true) { if (!localized) return; auto sep = thousands_sep(loc); grouping_ = std::move(sep.grouping); if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep); } digit_grouping(std::string grouping, std::basic_string sep) : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} auto has_separator() const -> bool { return !thousands_sep_.empty(); } auto count_separators(int num_digits) const -> int { int count = 0; auto state = initial_state(); while (num_digits > next(state)) ++count; return count; } // Applies grouping to digits and writes the output to out. template auto apply(Out out, basic_string_view digits) const -> Out { auto num_digits = static_cast(digits.size()); auto separators = basic_memory_buffer(); separators.push_back(0); auto state = initial_state(); while (int i = next(state)) { if (i >= num_digits) break; separators.push_back(i); } for (int i = 0, sep_index = static_cast(separators.size() - 1); i < num_digits; ++i) { if (num_digits - i == separators[sep_index]) { out = copy(thousands_sep_.data(), thousands_sep_.data() + thousands_sep_.size(), out); --sep_index; } *out++ = static_cast(digits[to_unsigned(i)]); } return out; } }; FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { prefix |= prefix != 0 ? value << 8 : value; prefix += (1u + (value > 0xff ? 1 : 0)) << 24; } // Writes a decimal integer with digit grouping. template auto write_int(OutputIt out, UInt value, unsigned prefix, const format_specs& specs, const digit_grouping& grouping) -> OutputIt { static_assert(std::is_same, UInt>::value, ""); int num_digits = 0; auto buffer = memory_buffer(); switch (specs.type()) { default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: case presentation_type::dec: num_digits = count_digits(value); format_decimal(appender(buffer), value, num_digits); break; case presentation_type::hex: if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); num_digits = count_digits<4>(value); format_base2e(4, appender(buffer), value, num_digits, specs.upper()); break; case presentation_type::oct: num_digits = count_digits<3>(value); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. if (specs.alt() && specs.precision <= num_digits && value != 0) prefix_append(prefix, '0'); format_base2e(3, appender(buffer), value, num_digits); break; case presentation_type::bin: if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); num_digits = count_digits<1>(value); format_base2e(1, appender(buffer), value, num_digits); break; case presentation_type::chr: return write_char(out, static_cast(value), specs); } unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + to_unsigned(grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) *it++ = static_cast(p & 0xff); return grouping.apply(it, string_view(buffer.data(), buffer.size())); }); } #if FMT_USE_LOCALE // Writes a localized value. FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; auto write_loc(basic_appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; #endif template inline auto write_loc(OutputIt, const loc_value&, const format_specs&, locale_ref) -> bool { return false; } template struct write_int_arg { UInt abs_value; unsigned prefix; }; template FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) -> write_int_arg> { auto prefix = 0u; auto abs_value = static_cast>(value); if (is_negative(value)) { prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; prefix = prefixes[static_cast(s)]; } return {abs_value, prefix}; } template struct loc_writer { basic_appender out; const format_specs& specs; std::basic_string sep; std::string grouping; std::basic_string decimal_point; template ::value)> auto operator()(T value) -> bool { auto arg = make_write_int_arg(value, specs.sign()); write_int(out, static_cast>(arg.abs_value), arg.prefix, specs, digit_grouping(grouping, sep)); return true; } template ::value)> auto operator()(T) -> bool { return false; } }; // Size and padding computation separate from write_int to avoid template bloat. struct size_padding { unsigned size; unsigned padding; FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix, const format_specs& specs) : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { if (specs.align() == align::numeric) { auto width = to_unsigned(specs.width); if (width > size) { padding = width - size; size = width; } } else if (specs.precision > num_digits) { size = (prefix >> 24) + to_unsigned(specs.precision); padding = to_unsigned(specs.precision - num_digits); } } }; template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); constexpr size_t buffer_size = num_bits(); char buffer[buffer_size]; if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); const char* begin = nullptr; const char* end = buffer + buffer_size; auto abs_value = arg.abs_value; auto prefix = arg.prefix; switch (specs.type()) { default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: case presentation_type::dec: begin = do_format_decimal(buffer, abs_value, buffer_size); break; case presentation_type::hex: begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); break; case presentation_type::oct: { begin = do_format_base2e(3, buffer, abs_value, buffer_size); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. auto num_digits = end - begin; if (specs.alt() && specs.precision <= num_digits && abs_value != 0) prefix_append(prefix, '0'); break; } case presentation_type::bin: begin = do_format_base2e(1, buffer, abs_value, buffer_size); if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); break; case presentation_type::chr: return write_char(out, static_cast(abs_value), specs); } // Write an integer in the format // // prefix contains chars in three lower bytes and the size in the fourth byte. int num_digits = static_cast(end - begin); // Slightly faster check for specs.width == 0 && specs.precision == -1. if ((specs.width | (specs.precision + 1)) == 0) { auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) *it++ = static_cast(p & 0xff); return base_iterator(out, copy(begin, end, it)); } auto sp = size_padding(num_digits, prefix, specs); unsigned padding = sp.padding; return write_padded( out, specs, sp.size, [=](reserve_iterator it) { for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) *it++ = static_cast(p & 0xff); it = detail::fill_n(it, padding, static_cast('0')); return copy(begin, end, it); }); } template FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, write_int_arg arg, const format_specs& specs) -> OutputIt { 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)> 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); } template FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, locale_ref loc = {}) -> OutputIt { // char is formatted as unsigned char for consistency across platforms. using unsigned_type = conditional_t::value, unsigned char, unsigned>; return check_char_specs(specs) ? write_char(out, value, specs) : write(out, static_cast(value), specs, loc); } template ::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs) -> OutputIt { bool is_debug = specs.type() == presentation_type::debug; if (specs.precision < 0 && specs.width == 0) { auto&& it = reserve(out, s.size()); return is_debug ? write_escaped_string(it, s) : copy(s, it); } size_t display_width_limit = specs.precision < 0 ? SIZE_MAX : to_unsigned(specs.precision); size_t display_width = !is_debug || specs.precision == 0 ? 0 : 1; // Account for opening '"'. size_t size = !is_debug || specs.precision == 0 ? 0 : 1; for_each_codepoint(s, [&](uint32_t cp, string_view sv) { if (is_debug && needs_escape(cp)) { counting_buffer buf; write_escaped_cp(basic_appender(buf), find_escape_result{sv.begin(), sv.end(), cp}); // We're reinterpreting bytes as display width. That's okay // because write_escaped_cp() only writes ASCII characters. size_t cp_width = buf.count(); if (display_width + cp_width <= display_width_limit) { display_width += cp_width; size += cp_width; // If this is the end of the string, account for closing '"'. if (display_width < display_width_limit && sv.end() == s.end()) { ++display_width; ++size; } return true; } size += display_width_limit - display_width; display_width = display_width_limit; return false; } size_t cp_width = display_width_of(cp); if (cp_width + display_width <= display_width_limit) { display_width += cp_width; size += sv.size(); // If this is the end of the string, account for closing '"'. if (is_debug && display_width < display_width_limit && sv.end() == s.end()) { ++display_width; ++size; } return true; } return false; }); struct bounded_output_iterator { reserve_iterator underlying_iterator; size_t bound; FMT_CONSTEXPR auto operator*() -> bounded_output_iterator& { return *this; } FMT_CONSTEXPR auto operator++() -> bounded_output_iterator& { return *this; } FMT_CONSTEXPR auto operator++(int) -> bounded_output_iterator& { return *this; } FMT_CONSTEXPR auto operator=(char c) -> bounded_output_iterator& { if (bound > 0) { *underlying_iterator++ = c; --bound; } return *this; } }; return write_padded( out, specs, size, display_width, [=](reserve_iterator it) { return is_debug ? write_escaped_string(bounded_output_iterator{it, size}, s) .underlying_iterator : copy(s.data(), s.data() + size, it); }); } template ::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = to_unsigned(specs.precision); bool is_debug = specs.type() == presentation_type::debug; if (is_debug) { auto buf = counting_buffer(); write_escaped_string(basic_appender(buf), s); size = buf.count(); } return write_padded( out, specs, size, [=](reserve_iterator it) { return is_debug ? write_escaped_string(it, s) : copy(data, data + size, it); }); } template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs, locale_ref) -> OutputIt { return write(out, s, specs); } template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, locale_ref) -> OutputIt { if (specs.type() == presentation_type::pointer) return write_ptr(out, bit_cast(s), &specs); if (!s) report_error("string pointer is null"); return write(out, basic_string_view(s), specs, {}); } template ::value && !std::is_same::value && !std::is_same::value)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { auto abs_value = static_cast>(value); bool negative = is_negative(value); // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. if (negative) abs_value = ~abs_value + 1; int num_digits = count_digits(abs_value); auto size = (negative ? 1 : 0) + static_cast(num_digits); if (auto ptr = to_pointer(out, size)) { if (negative) *ptr++ = static_cast('-'); format_decimal(ptr, abs_value, num_digits); return out; } if (negative) *out++ = static_cast('-'); return format_decimal(out, abs_value, num_digits); } template FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, format_specs& specs) -> const Char* { FMT_ASSERT(begin != end, ""); auto alignment = align::none; auto p = begin + code_point_length(begin); if (end - p <= 0) p = begin; for (;;) { switch (to_ascii(*p)) { case '<': alignment = align::left; break; case '>': alignment = align::right; break; case '^': alignment = align::center; break; } if (alignment != align::none) { if (p != begin) { auto c = *begin; if (c == '}') return begin; if (c == '{') { report_error("invalid fill character '{'"); return begin; } specs.set_fill(basic_string_view(begin, to_unsigned(p - begin))); begin = p + 1; } else { ++begin; } break; } if (p == begin) break; p = begin; } specs.set_align(alignment); return begin; } template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, format_specs specs, sign s) -> OutputIt { auto str = isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf"); constexpr size_t str_size = 3; auto size = str_size + (s != sign::none ? 1 : 0); // Replace '0'-padding with space for non-finite values. const bool is_zero_fill = specs.fill_size() == 1 && specs.fill_unit() == '0'; if (is_zero_fill) specs.set_fill(' '); return write_padded(out, specs, size, [=](reserve_iterator it) { if (s != sign::none) *it++ = detail::getsign(s); return copy(str, str + str_size, it); }); } // A decimal floating-point number significand * pow(10, exp). struct big_decimal_fp { const char* significand; int significand_size; int exponent; }; constexpr auto get_significand_size(const big_decimal_fp& f) -> int { return f.significand_size; } template inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { return count_digits(f.significand); } template constexpr auto write_significand(OutputIt out, const char* significand, int significand_size) -> OutputIt { return copy(significand, significand + significand_size, out); } template inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt { return format_decimal(out, significand, significand_size); } template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, int significand_size, int exponent, const Grouping& grouping) -> OutputIt { if (!grouping.has_separator()) { out = write_significand(out, significand, significand_size); return detail::fill_n(out, exponent, static_cast('0')); } auto buffer = memory_buffer(); write_significand(appender(buffer), significand, significand_size); detail::fill_n(appender(buffer), exponent, '0'); return grouping.apply(out, string_view(buffer.data(), buffer.size())); } template ::value)> inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { if (!decimal_point) return format_decimal(out, significand, significand_size); out += significand_size + 1; Char* end = out; int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { *--out = static_cast('0' + significand % 10); significand /= 10; } *--out = decimal_point; format_decimal(out - integral_size, significand, integral_size); return end; } template >::value)> inline auto write_significand(OutputIt out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> OutputIt { // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. Char buffer[digits10() + 2]; auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point); return detail::copy_noinline(buffer, end, out); } template FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, int significand_size, int integral_size, Char decimal_point) -> OutputIt { out = detail::copy_noinline(significand, significand + integral_size, out); if (!decimal_point) return out; *out++ = decimal_point; return detail::copy_noinline(significand + integral_size, significand + significand_size, out); } template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, int significand_size, int integral_size, Char decimal_point, const Grouping& grouping) -> OutputIt { if (!grouping.has_separator()) { return write_significand(out, significand, significand_size, integral_size, decimal_point); } auto buffer = basic_memory_buffer(); write_significand(basic_appender(buffer), significand, significand_size, integral_size, decimal_point); grouping.apply( out, basic_string_view(buffer.data(), to_unsigned(integral_size))); return detail::copy_noinline(buffer.data() + integral_size, buffer.end(), out); } // Numbers with exponents greater or equal to the returned value will use // the exponential notation. template FMT_CONSTEVAL auto exp_upper() -> int { return std::numeric_limits::digits10 != 0 ? min_of(16, std::numeric_limits::digits10 + 1) : 16; } // Use the fixed notation if the exponent is in [-4, exp_upper), // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. constexpr auto use_fixed(int exp, int exp_upper) -> bool { return exp >= -4 && exp < exp_upper; } template class fallback_digit_grouping { public: constexpr fallback_digit_grouping(locale_ref, bool) {} constexpr auto has_separator() const -> bool { return false; } constexpr auto count_separators(int) const -> int { return 0; } template constexpr auto apply(Out out, basic_string_view) const -> Out { return out; } }; template FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f, int significand_size, Char decimal_point, const format_specs& specs, sign s, locale_ref loc = {}) -> OutputIt { using iterator = reserve_iterator; int exp = f.exponent + significand_size; long long size = significand_size + (s != sign::none ? 1 : 0); if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] size += f.exponent; int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); if (specs.alt()) { ++size; if (num_zeros <= 0 && specs.type() != presentation_type::fixed) num_zeros = 0; if (num_zeros > 0) size += num_zeros; } auto grouping = Grouping(loc, specs.localized()); size += grouping.count_separators(exp); return write_padded( out, specs, static_cast(size), [&](iterator it) { if (s != sign::none) *it++ = detail::getsign(s); it = write_significand(it, f.significand, significand_size, f.exponent, grouping); if (!specs.alt()) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; }); } if (exp > 0) { // 1234e-2 -> 12.34[0+] int num_zeros = specs.alt() ? specs.precision - significand_size : 0; size += 1 + max_of(num_zeros, 0); auto grouping = Grouping(loc, specs.localized()); size += grouping.count_separators(exp); return write_padded( out, specs, static_cast(size), [&](iterator it) { if (s != sign::none) *it++ = detail::getsign(s); it = write_significand(it, f.significand, significand_size, exp, decimal_point, grouping); return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; }); } // 1234e-6 -> 0.001234 int num_zeros = -exp; if (significand_size == 0 && specs.precision >= 0 && specs.precision < num_zeros) { num_zeros = specs.precision; } bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); size += 1 + (pointy ? 1 : 0) + num_zeros; return write_padded( out, specs, static_cast(size), [&](iterator it) { if (s != sign::none) *it++ = detail::getsign(s); *it++ = Char('0'); if (!pointy) return it; *it++ = decimal_point; it = detail::fill_n(it, num_zeros, Char('0')); return write_significand(it, f.significand, significand_size); }); } template FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, const format_specs& specs, sign s, int exp_upper, locale_ref loc) -> OutputIt { Char point = specs.localized() ? detail::decimal_point(loc) : Char('.'); int significand_size = get_significand_size(f); int exp = f.exponent + significand_size - 1; if (specs.type() == presentation_type::fixed || (specs.type() != presentation_type::exp && use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) { return write_fixed(out, f, significand_size, point, specs, s, loc); } // Write value in the exponential format. int num_zeros = 0; long long size = significand_size + (s != sign::none ? 1 : 0); if (specs.alt()) { num_zeros = max_of(specs.precision - significand_size, 0); size += num_zeros; } else if (significand_size == 1) { point = Char(); } size += (point ? 1 : 0) + compute_exp_size(exp); char exp_char = specs.upper() ? 'E' : 'e'; auto write = [=](reserve_iterator it) { if (s != sign::none) *it++ = detail::getsign(s); // Insert a decimal point after the first digit and add an exponent. it = write_significand(it, f.significand, significand_size, 1, point); if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0')); *it++ = Char(exp_char); return write_exponent(exp, it); }; size_t usize = static_cast(size); return specs.width > 0 ? write_padded(out, specs, usize, write) : base_iterator(out, write(reserve(out, usize))); } template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, const format_specs& specs, sign s, int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { return do_write_float>(out, f, specs, s, exp_upper, loc); } else { return do_write_float>(out, f, specs, s, exp_upper, loc); } } template constexpr auto isnan(T value) -> bool { return value != value; // std::isnan doesn't support __float128. } template struct has_isfinite : std::false_type {}; template struct has_isfinite> : std::true_type {}; template ::value&& has_isfinite::value)> FMT_CONSTEXPR20 auto isfinite(T value) -> bool { constexpr T inf = T(std::numeric_limits::infinity()); if (is_constant_evaluated()) return !detail::isnan(value) && value < inf && value > -inf; return std::isfinite(value); } template ::value)> FMT_CONSTEXPR auto isfinite(T value) -> bool { T inf = T(std::numeric_limits::infinity()); // std::isfinite doesn't support __float128. return !detail::isnan(value) && value < inf && value > -inf; } template ::value)> FMT_INLINE FMT_CONSTEXPR auto signbit(T value) -> bool { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits::is_iec559) { auto bits = detail::bit_cast(static_cast(value)); return (bits >> (num_bits() - 1)) != 0; } #endif } return std::signbit(static_cast(value)); } inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { // Adjust fixed precision by exponent because it is relative to decimal // point. if (exp10 > 0 && precision > max_value() - exp10) FMT_THROW(format_error("number is too big")); precision += exp10; } class bigint { private: // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_. using bigit = uint32_t; // A big digit. using double_bigit = uint64_t; enum { bigit_bits = num_bits() }; enum { bigits_capacity = 32 }; basic_memory_buffer bigits_; int exp_; friend struct formatter; FMT_CONSTEXPR auto get_bigit(int i) const -> bigit { return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0; } FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) { auto result = double_bigit(bigits_[index]) - other - borrow; bigits_[index] = static_cast(result); borrow = static_cast(result >> (bigit_bits * 2 - 1)); } FMT_CONSTEXPR void remove_leading_zeros() { int num_bigits = static_cast(bigits_.size()) - 1; while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; bigits_.resize(to_unsigned(num_bigits + 1)); } // Computes *this -= other assuming aligned bigints and *this >= other. FMT_CONSTEXPR void subtract_aligned(const bigint& other) { FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); FMT_ASSERT(compare(*this, other) >= 0, ""); bigit borrow = 0; int i = other.exp_ - exp_; for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) subtract_bigits(i, other.bigits_[j], borrow); if (borrow != 0) subtract_bigits(i, 0, borrow); FMT_ASSERT(borrow == 0, ""); remove_leading_zeros(); } FMT_CONSTEXPR void multiply(uint32_t value) { bigit carry = 0; const double_bigit wide_value = value; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { double_bigit result = bigits_[i] * wide_value + carry; bigits_[i] = static_cast(result); carry = static_cast(result >> bigit_bits); } if (carry != 0) bigits_.push_back(carry); } template ::value || std::is_same::value)> FMT_CONSTEXPR void multiply(UInt value) { using half_uint = conditional_t::value, uint64_t, uint32_t>; const int shift = num_bits() - bigit_bits; const UInt lower = static_cast(value); const UInt upper = value >> num_bits(); UInt carry = 0; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { UInt result = lower * bigits_[i] + static_cast(carry); carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + (carry >> bigit_bits); bigits_[i] = static_cast(result); } while (carry != 0) { bigits_.push_back(static_cast(carry)); carry >>= bigit_bits; } } template ::value || std::is_same::value)> FMT_CONSTEXPR void assign(UInt n) { size_t num_bigits = 0; do { bigits_[num_bigits++] = static_cast(n); n >>= bigit_bits; } while (n != 0); bigits_.resize(num_bigits); exp_ = 0; } public: FMT_CONSTEXPR bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; FMT_CONSTEXPR void assign(const bigint& other) { auto size = other.bigits_.size(); bigits_.resize(size); auto data = other.bigits_.data(); copy(data, data + size, bigits_.data()); exp_ = other.exp_; } template FMT_CONSTEXPR void operator=(Int n) { FMT_ASSERT(n > 0, ""); assign(uint64_or_128_t(n)); } FMT_CONSTEXPR auto num_bigits() const -> int { return static_cast(bigits_.size()) + exp_; } FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; if (shift == 0) return *this; bigit carry = 0; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { bigit c = bigits_[i] >> (bigit_bits - shift); bigits_[i] = (bigits_[i] << shift) + carry; carry = c; } if (carry != 0) bigits_.push_back(carry); return *this; } template FMT_CONSTEXPR auto operator*=(Int value) -> bigint& { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int { int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits(); if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1; int i = static_cast(b1.bigits_.size()) - 1; int j = static_cast(b2.bigits_.size()) - 1; int end = i - j; if (end < 0) end = 0; for (; i >= end; --i, --j) { bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j]; if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1; } if (i != j) return i > j ? 1 : -1; return 0; } // Returns compare(lhs1 + lhs2, rhs). friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2, const bigint& rhs) -> int { int max_lhs_bigits = max_of(lhs1.num_bigits(), lhs2.num_bigits()); int num_rhs_bigits = rhs.num_bigits(); if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; if (max_lhs_bigits > num_rhs_bigits) return 1; double_bigit borrow = 0; int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_); for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i); bigit rhs_bigit = rhs.get_bigit(i); if (sum > rhs_bigit + borrow) return 1; borrow = rhs_bigit + borrow - sum; if (borrow > 1) return -1; borrow <<= bigit_bits; } return borrow != 0 ? -1 : 0; } // Assigns pow(10, exp) to this bigint. FMT_CONSTEXPR20 void assign_pow10(int exp) { FMT_ASSERT(exp >= 0, ""); if (exp == 0) return *this = 1; int bitmask = 1 << (num_bits() - countl_zero(static_cast(exp)) - 1); // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by // repeated squaring and multiplication. *this = 5; bitmask >>= 1; while (bitmask != 0) { square(); if ((exp & bitmask) != 0) *this *= 5; bitmask >>= 1; } *this <<= exp; // Multiply by pow(2, exp) by shifting. } FMT_CONSTEXPR20 void square() { int num_bigits = static_cast(bigits_.size()); int num_result_bigits = 2 * num_bigits; basic_memory_buffer n(std::move(bigits_)); bigits_.resize(to_unsigned(num_result_bigits)); auto sum = uint128_t(); for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { // Compute bigit at position bigit_index of the result by adding // cross-product terms n[i] * n[j] such that i + j == bigit_index. for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { // Most terms are multiplied twice which can be optimized in the future. sum += double_bigit(n[i]) * n[j]; } bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); // Compute the carry. } // Do the same for the top half. for (int bigit_index = num_bigits; bigit_index < num_result_bigits; ++bigit_index) { for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) sum += double_bigit(n[i++]) * n[j--]; bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); } remove_leading_zeros(); exp_ *= 2; } // If this bigint has a bigger exponent than other, adds trailing zero to make // exponents equal. This simplifies some operations such as subtraction. FMT_CONSTEXPR void align(const bigint& other) { int exp_difference = exp_ - other.exp_; if (exp_difference <= 0) return; int num_bigits = static_cast(bigits_.size()); bigits_.resize(to_unsigned(num_bigits + exp_difference)); for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) bigits_[j] = bigits_[i]; fill_n(bigits_.data(), to_unsigned(exp_difference), 0U); exp_ -= exp_difference; } // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); align(divisor); int quotient = 0; do { subtract_aligned(divisor); ++quotient; } while (compare(*this, divisor) >= 0); return quotient; } }; // format_dragon flags. enum dragon { predecessor_closer = 1, fixup = 2, // Run fixup to correct exp10 which can be off by one. fixed = 4, }; // Formats a floating-point number using a variation of the Fixed-Precision // Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: // https://fmt.dev/papers/p372-steele.pdf. FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, unsigned flags, int num_digits, buffer& buf, int& exp10) { bigint numerator; // 2 * R in (FPP)^2. bigint denominator; // 2 * S in (FPP)^2. // lower and upper are differences between value and corresponding boundaries. bigint lower; // (M^- in (FPP)^2). bigint upper_store; // upper's value if different from lower. bigint* upper = nullptr; // (M^+ in (FPP)^2). // Shift numerator and denominator by an extra bit or two (if lower boundary // is closer) to make lower and upper integers. This eliminates multiplication // by 2 during later computations. bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; int shift = is_predecessor_closer ? 2 : 1; if (value.e >= 0) { numerator = value.f; numerator <<= value.e + shift; lower = 1; lower <<= value.e; if (is_predecessor_closer) { upper_store = 1; upper_store <<= value.e + 1; upper = &upper_store; } denominator.assign_pow10(exp10); denominator <<= shift; } else if (exp10 < 0) { numerator.assign_pow10(-exp10); lower.assign(numerator); if (is_predecessor_closer) { upper_store.assign(numerator); upper_store <<= 1; upper = &upper_store; } numerator *= value.f; numerator <<= shift; denominator = 1; denominator <<= shift - value.e; } else { numerator = value.f; numerator <<= shift; denominator.assign_pow10(exp10); denominator <<= shift - value.e; lower = 1; if (is_predecessor_closer) { upper_store = 1ULL << 1; upper = &upper_store; } } int even = static_cast((value.f & 1) == 0); if (!upper) upper = &lower; bool shortest = num_digits < 0; if ((flags & dragon::fixup) != 0) { if (add_compare(numerator, *upper, denominator) + even <= 0) { --exp10; numerator *= 10; if (num_digits < 0) { lower *= 10; if (upper != &lower) *upper *= 10; } } if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); } // Invariant: value == (numerator / denominator) * pow(10, exp10). if (shortest) { // Generate the shortest representation. num_digits = 0; char* data = buf.data(); for (;;) { int digit = numerator.divmod_assign(denominator); bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. // numerator + upper >[=] pow10: bool high = add_compare(numerator, *upper, denominator) + even > 0; data[num_digits++] = static_cast('0' + digit); if (low || high) { if (!low) { ++data[num_digits - 1]; } else if (high) { int result = add_compare(numerator, numerator, denominator); // Round half to even. if (result > 0 || (result == 0 && (digit % 2) != 0)) ++data[num_digits - 1]; } buf.try_resize(to_unsigned(num_digits)); exp10 -= num_digits - 1; return; } numerator *= 10; lower *= 10; if (upper != &lower) *upper *= 10; } } // Generate the given number of digits. exp10 -= num_digits - 1; if (num_digits <= 0) { auto digit = '0'; if (num_digits == 0) { denominator *= 10; digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; } buf.push_back(digit); return; } buf.try_resize(to_unsigned(num_digits)); for (int i = 0; i < num_digits - 1; ++i) { int digit = numerator.divmod_assign(denominator); buf[i] = static_cast('0' + digit); numerator *= 10; } int digit = numerator.divmod_assign(denominator); auto result = add_compare(numerator, numerator, denominator); if (result > 0 || (result == 0 && (digit % 2) != 0)) { if (digit == 9) { const auto overflow = '0' + 10; buf[num_digits - 1] = overflow; // Propagate the carry. for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { buf[i] = '0'; ++buf[i - 1]; } if (buf[0] == overflow) { buf[0] = '1'; if ((flags & dragon::fixed) != 0) buf.push_back('0'); else ++exp10; } return; } ++digit; } buf[num_digits - 1] = static_cast('0' + digit); } // Formats a floating-point number using the hexfloat format. template ::value)> FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, buffer& buf) { // float is passed as double to reduce the number of instantiations and to // simplify implementation. static_assert(!std::is_same::value, ""); using info = dragonbox::float_info; // Assume Float is in the format [sign][exponent][significand]. using carrier_uint = typename info::carrier_uint; const auto num_float_significand_bits = detail::num_significand_bits(); basic_fp f(value); f.e += num_float_significand_bits; if (!has_implicit_bit()) --f.e; const auto num_fraction_bits = num_float_significand_bits + (has_implicit_bit() ? 1 : 0); const auto num_xdigits = (num_fraction_bits + 3) / 4; const auto leading_shift = ((num_xdigits - 1) * 4); const auto leading_mask = carrier_uint(0xF) << leading_shift; const auto leading_xdigit = static_cast((f.f & leading_mask) >> leading_shift); if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); int print_xdigits = num_xdigits - 1; if (specs.precision >= 0 && print_xdigits > specs.precision) { const int shift = ((print_xdigits - specs.precision - 1) * 4); const auto mask = carrier_uint(0xF) << shift; const auto v = static_cast((f.f & mask) >> shift); if (v >= 8) { const auto inc = carrier_uint(1) << (shift + 4); f.f += inc; f.f &= ~(inc - 1); } // Check long double overflow if (!has_implicit_bit()) { const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; if ((f.f & implicit_bit) == implicit_bit) { f.f >>= 4; f.e += 4; } } print_xdigits = specs.precision; } char xdigits[num_bits() / 4]; detail::fill_n(xdigits, sizeof(xdigits), '0'); format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); // Remove zero tail while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; buf.push_back('0'); buf.push_back(specs.upper() ? 'X' : 'x'); buf.push_back(xdigits[0]); if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision) buf.push_back('.'); buf.append(xdigits + 1, xdigits + 1 + print_xdigits); for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0'); buf.push_back(specs.upper() ? 'P' : 'p'); uint32_t abs_e; if (f.e < 0) { buf.push_back('-'); abs_e = static_cast(-f.e); } else { buf.push_back('+'); abs_e = static_cast(f.e); } format_decimal(appender(buf), abs_e, detail::count_digits(abs_e)); } template ::value)> FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, buffer& buf) { format_hexfloat(static_cast(value), specs, buf); } constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { // For checking rounding thresholds. // The kth entry is chosen to be the smallest integer such that the // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. // 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]; } template FMT_CONSTEXPR20 auto format_float(Float value, int precision, const format_specs& specs, bool binary32, buffer& buf) -> int { // float is passed as double to reduce the number of instantiations. static_assert(!std::is_same::value, ""); auto converted_value = convert_float(value); const bool fixed = specs.type() == presentation_type::fixed; if (value == 0) { if (precision <= 0 || !fixed) { buf.push_back('0'); return 0; } buf.try_resize(to_unsigned(precision)); fill_n(buf.data(), precision, '0'); return -precision; } int exp = 0; bool use_dragon = true; unsigned dragon_flags = 0; if (!is_fast_float() || is_constant_evaluated()) { const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) using info = dragonbox::float_info; const auto f = basic_fp(converted_value); // Compute exp, an approximate power of 10, such that // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). // This is based on log10(value) == log2(value) / log2(10) and approximation // of log2(value) by e + num_fraction_bits idea from double-conversion. auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10; exp = static_cast(e); if (e > exp) ++exp; // Compute ceil. dragon_flags = dragon::fixup; } else { // Extract significand bits and exponent bits. using info = dragonbox::float_info; auto br = bit_cast(static_cast(value)); const uint64_t significand_mask = (static_cast(1) << num_significand_bits()) - 1; uint64_t significand = (br & significand_mask); int exponent = static_cast((br & exponent_mask()) >> num_significand_bits()); if (exponent != 0) { // Check if normal. exponent -= exponent_bias() + num_significand_bits(); significand |= (static_cast(1) << num_significand_bits()); significand <<= 1; } else { // Normalize subnormal inputs. FMT_ASSERT(significand != 0, "zeros should not appear here"); int shift = countl_zero(significand); FMT_ASSERT(shift >= num_bits() - num_significand_bits(), ""); shift -= (num_bits() - num_significand_bits() - 2); exponent = (std::numeric_limits::min_exponent - num_significand_bits()) - shift; significand <<= shift; } // Compute the first several nonzero decimal significand digits. // We call the number we get the first segment. const int k = info::kappa - dragonbox::floor_log10_pow2(exponent); exp = -k; const int beta = exponent + dragonbox::floor_log2_pow10(k); uint64_t first_segment; bool has_more_segments; int digits_in_the_first_segment; { const auto r = dragonbox::umul192_upper128( significand << beta, dragonbox::get_cached_power(k)); first_segment = r.high(); has_more_segments = r.low() != 0; // The first segment can have 18 ~ 19 digits. if (first_segment >= 1000000000000000000ULL) { digits_in_the_first_segment = 19; } else { // When it is of 18-digits, we align it to 19-digits by adding a bogus // zero at the end. digits_in_the_first_segment = 18; first_segment *= 10; } } // Compute the actual number of decimal digits to print. if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment); // Use Dragon4 only when there might be not enough digits in the first // segment. if (digits_in_the_first_segment > precision) { use_dragon = false; if (precision <= 0) { exp += digits_in_the_first_segment; if (precision < 0) { // Nothing to do, since all we have are just leading zeros. buf.try_resize(0); } else { // We may need to round-up. buf.try_resize(1); if ((first_segment | static_cast(has_more_segments)) > 5000000000000000000ULL) { buf[0] = '1'; } else { buf[0] = '0'; } } } // precision <= 0 else { exp += digits_in_the_first_segment - precision; // When precision > 0, we divide the first segment into three // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits // in 32-bits which usually allows faster calculation than in // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize // division-by-constant for large 64-bit divisors, we do it here // manually. The magic number 7922816251426433760 below is equal to // ceil(2^(64+32) / 10^10). const uint32_t first_subsegment = static_cast( dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >> 32); const uint64_t second_third_subsegments = first_segment - first_subsegment * 10000000000ULL; uint64_t prod; uint32_t digits; bool should_round_up; int number_of_digits_to_print = min_of(precision, 9); // Print a 9-digits subsegment, either the first or the second. auto print_subsegment = [&](uint32_t subsegment, char* buffer) { int number_of_digits_printed = 0; // If we want to print an odd number of digits from the subsegment, if ((number_of_digits_to_print & 1) != 0) { // Convert to 64-bit fixed-point fractional form with 1-digit // integer part. The magic number 720575941 is a good enough // approximation of 2^(32 + 24) / 10^8; see // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case // for details. prod = ((subsegment * static_cast(720575941)) >> 24) + 1; digits = static_cast(prod >> 32); *buffer = static_cast('0' + digits); number_of_digits_printed++; } // If we want to print an even number of digits from the // first_subsegment, else { // Convert to 64-bit fixed-point fractional form with 2-digits // integer part. The magic number 450359963 is a good enough // approximation of 2^(32 + 20) / 10^7; see // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case // for details. prod = ((subsegment * static_cast(450359963)) >> 20) + 1; digits = static_cast(prod >> 32); write2digits(buffer, digits); number_of_digits_printed += 2; } // Print all digit pairs. while (number_of_digits_printed < number_of_digits_to_print) { prod = static_cast(prod) * static_cast(100); digits = static_cast(prod >> 32); write2digits(buffer + number_of_digits_printed, digits); number_of_digits_printed += 2; } }; // Print first subsegment. print_subsegment(first_subsegment, buf.data()); // Perform rounding if the first subsegment is the last subsegment to // print. if (precision <= 9) { // Rounding inside the subsegment. // We round-up if: // - either the fractional part is strictly larger than 1/2, or // - the fractional part is exactly 1/2 and the last digit is odd. // We rely on the following observations: // - If fractional_part >= threshold, then the fractional part is // strictly larger than 1/2. // - If the MSB of fractional_part is set, then the fractional part // must be at least 1/2. // - When the MSB of fractional_part is set, either // second_third_subsegments being nonzero or has_more_segments // being true means there are further digits not printed, so the // fractional part is strictly larger than 1/2. if (precision < 9) { uint32_t fractional_part = static_cast(prod); should_round_up = fractional_part >= fractional_part_rounding_thresholds( 8 - number_of_digits_to_print) || ((fractional_part >> 31) & ((digits & 1) | (second_third_subsegments != 0) | has_more_segments)) != 0; } // Rounding at the subsegment boundary. // In this case, the fractional part is at least 1/2 if and only if // second_third_subsegments >= 5000000000ULL, and is strictly larger // than 1/2 if we further have either second_third_subsegments > // 5000000000ULL or has_more_segments == true. else { should_round_up = second_third_subsegments > 5000000000ULL || (second_third_subsegments == 5000000000ULL && ((digits & 1) != 0 || has_more_segments)); } } // Otherwise, print the second subsegment. else { // Compilers are not aware of how to leverage the maximum value of // second_third_subsegments to find out a better magic number which // allows us to eliminate an additional shift. 1844674407370955162 = // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))). const uint32_t second_subsegment = static_cast(dragonbox::umul128_upper64( second_third_subsegments, 1844674407370955162ULL)); const uint32_t third_subsegment = static_cast(second_third_subsegments) - second_subsegment * 10; number_of_digits_to_print = precision - 9; print_subsegment(second_subsegment, buf.data() + 9); // Rounding inside the subsegment. if (precision < 18) { // The condition third_subsegment != 0 implies that the segment was // of 19 digits, so in this case the third segment should be // consisting of a genuine digit from the input. uint32_t fractional_part = static_cast(prod); should_round_up = fractional_part >= fractional_part_rounding_thresholds( 8 - number_of_digits_to_print) || ((fractional_part >> 31) & ((digits & 1) | (third_subsegment != 0) | has_more_segments)) != 0; } // Rounding at the subsegment boundary. else { // In this case, the segment must be of 19 digits, thus // the third subsegment should be consisting of a genuine digit from // the input. should_round_up = third_subsegment > 5 || (third_subsegment == 5 && ((digits & 1) != 0 || has_more_segments)); } } // Round-up if necessary. if (should_round_up) { ++buf[precision - 1]; for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) { buf[i] = '0'; ++buf[i - 1]; } if (buf[0] > '9') { buf[0] = '1'; if (fixed) buf[precision++] = '0'; else ++exp; } } buf.try_resize(to_unsigned(precision)); } } // if (digits_in_the_first_segment > precision) else { // Adjust the exponent for its use in Dragon4. exp += digits_in_the_first_segment - 1; } } if (use_dragon) { auto f = basic_fp(); bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) : f.assign(converted_value); if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; if (fixed) dragon_flags |= dragon::fixed; // Limit precision to the maximum possible number of significant digits in // an IEEE754 double because we don't need to generate zeros. const int max_double_digits = 767; if (precision > max_double_digits) precision = max_double_digits; format_dragon(f, dragon_flags, precision, buf, exp); } if (!fixed && !specs.alt()) { // Remove trailing zeros. auto num_digits = buf.size(); while (num_digits > 0 && buf[num_digits - 1] == '0') { --num_digits; ++exp; } buf.try_resize(num_digits); } return exp; } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, locale_ref loc = {}) -> OutputIt { if (specs.localized() && write_loc(out, value, specs, loc)) return out; // Use signbit because value < 0 is false for NaN. sign s = detail::signbit(value) ? sign::minus : specs.sign(); if (!detail::isfinite(value)) return write_nonfinite(out, detail::isnan(value), specs, s); if (specs.align() == align::numeric && s != sign::none) { *out++ = detail::getsign(s); s = sign::none; if (specs.width != 0) --specs.width; } const int exp_upper = detail::exp_upper(); int precision = specs.precision; if (precision < 0) { if (specs.type() != presentation_type::none) { precision = 6; } else if (is_fast_float::value && !is_constant_evaluated()) { // Use Dragonbox for the shortest format. auto dec = dragonbox::to_decimal(static_cast>(value)); return write_float(out, dec, specs, s, exp_upper, loc); } } memory_buffer buffer; if (specs.type() == presentation_type::hexfloat) { if (s != sign::none) buffer.push_back(detail::getsign(s)); format_hexfloat(convert_float(value), specs, buffer); return write_bytes(out, {buffer.data(), buffer.size()}, specs); } if (specs.type() == presentation_type::exp) { if (precision == max_value()) report_error("number is too big"); else ++precision; if (specs.precision != 0) specs.set_alt(); } else if (specs.type() == presentation_type::fixed) { if (specs.precision != 0) specs.set_alt(); } else if (precision == 0) { precision = 1; } int exp = format_float(convert_float(value), precision, specs, std::is_same(), buffer); specs.precision = precision; auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; return write_float(out, f, specs, s, exp_upper, loc); } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { if (is_constant_evaluated()) return write(out, value, format_specs()); auto s = detail::signbit(value) ? sign::minus : sign::none; auto mask = exponent_mask>(); if ((bit_cast(value) & mask) == mask) return write_nonfinite(out, std::isnan(value), {}, s); auto dec = dragonbox::to_decimal(static_cast>(value)); auto significand = dec.significand; int significand_size = count_digits(significand); int exponent = dec.exponent + significand_size - 1; if (use_fixed(exponent, detail::exp_upper())) { return write_fixed>( out, dec, significand_size, Char('.'), {}, s); } // Write value in the exponential format. const char* prefix = "e+"; int abs_exponent = exponent; if (exponent < 0) { abs_exponent = -exponent; prefix = "e-"; } auto has_decimal_point = significand_size != 1; size_t size = std::is_pointer::value ? 0u : to_unsigned((s != sign::none ? 1 : 0) + significand_size + (has_decimal_point ? 1 : 0) + (abs_exponent >= 100 ? 5 : 4)); if (auto ptr = to_pointer(out, size)) { if (s != sign::none) *ptr++ = Char('-'); if (has_decimal_point) { auto begin = ptr; ptr = format_decimal(ptr, significand, significand_size + 1); *begin = begin[1]; begin[1] = '.'; } else { *ptr++ = static_cast('0' + significand); } if (std::is_same::value) { memcpy(ptr, prefix, 2); ptr += 2; } else { *ptr++ = static_cast(prefix[0]); *ptr++ = static_cast(prefix[1]); } if (abs_exponent >= 100) { *ptr++ = static_cast('0' + abs_exponent / 100); abs_exponent %= 100; } write2digits(ptr, static_cast(abs_exponent)); return select::value>(ptr + 2, out); } auto it = reserve(out, size); if (s != sign::none) *it++ = Char('-'); // Insert a decimal point after the first digit and add an exponent. it = write_significand(it, significand, significand_size, 1, has_decimal_point ? Char('.') : Char()); *it++ = Char('e'); it = write_exponent(exponent, it); return base_iterator(out, it); } template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, {}); } template auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; } template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -> OutputIt { return copy_noinline(value.begin(), value.end(), out); } template ::value)> constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, bool check = std::is_enum::value && !std::is_same::value && mapped_type_constant::value != type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return write(out, static_cast>(value)); } template ::value)> FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {}, locale_ref = {}) -> OutputIt { return specs.type() != presentation_type::none && specs.type() != presentation_type::string ? write(out, value ? 1 : 0, specs, {}) : write_bytes(out, value ? "true" : "false", specs); } template FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { auto it = reserve(out, 1); *it++ = value; return base_iterator(out, it); } template FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { if (value) return write(out, basic_string_view(value)); report_error("string pointer is null"); return out; } template ::value)> auto write(OutputIt out, const T* value, const format_specs& specs = {}, locale_ref = {}) -> OutputIt { return write_ptr(out, bit_cast(value), &specs); } template ::value == type::custom_type && !std::is_fundamental::value)> FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt { auto f = formatter(); auto parse_ctx = parse_context({}); f.parse(parse_ctx); auto ctx = basic_format_context(out, {}, {}); return f.format(value, ctx); } template using is_builtin = bool_constant::value || FMT_BUILTIN_TYPES>; // An argument visitor that formats the argument and writes it via the output // iterator. It's a class and not a generic lambda for compatibility with C++11. template struct default_arg_formatter { using context = buffered_context; basic_appender out; void operator()(monostate) { report_error("argument not found"); } template ::value)> void operator()(T value) { write(out, value); } template ::value)> void operator()(T) { FMT_ASSERT(false, ""); } void operator()(typename basic_format_arg::handle h) { // Use a null locale since the default format must be unlocalized. auto parse_ctx = parse_context({}); auto format_ctx = context(out, {}, {}); h.format(parse_ctx, format_ctx); } }; template struct arg_formatter { basic_appender out; const format_specs& specs; FMT_NO_UNIQUE_ADDRESS locale_ref locale; template ::value)> FMT_CONSTEXPR FMT_INLINE void operator()(T value) { detail::write(out, value, specs, locale); } template ::value)> void operator()(T) { FMT_ASSERT(false, ""); } void operator()(typename basic_format_arg>::handle) { // User-defined types are handled separately because they require access // to the parse context. } }; struct dynamic_spec_getter { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> ullong { return is_negative(value) ? ~0ull : static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> ullong { report_error("width/precision is not integer"); return 0; } }; template FMT_CONSTEXPR void handle_dynamic_spec( arg_id_kind kind, int& value, const arg_ref& ref, Context& ctx) { if (kind == arg_id_kind::none) return; auto arg = kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); if (!arg) report_error("argument not found"); ullong result = arg.visit(dynamic_spec_getter()); if (result > to_unsigned(max_value())) report_error("width/precision is out of range"); value = static_cast(result); } #if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct static_named_arg : view { static constexpr auto name = Str.data; const T& value; static_named_arg(const T& v) : value(v) {} }; template Str> struct is_named_arg> : std::true_type {}; template Str> struct is_static_named_arg> : std::true_type { }; template Str> struct udl_arg { template auto operator=(T&& value) const { return static_named_arg(std::forward(value)); } }; #else template struct udl_arg { const Char* str; template auto operator=(T&& value) const -> named_arg { return {str, std::forward(value)}; } }; #endif // FMT_USE_NONTYPE_TEMPLATE_ARGS template struct format_handler { parse_context parse_ctx; buffered_context ctx; void on_text(const Char* begin, const Char* end) { copy_noinline(begin, end, ctx.out()); } FMT_CONSTEXPR auto on_arg_id() -> int { return parse_ctx.next_arg_id(); } FMT_CONSTEXPR auto on_arg_id(int id) -> int { parse_ctx.check_arg_id(id); return id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { parse_ctx.check_arg_id(id); int arg_id = ctx.arg_id(id); if (arg_id < 0) report_error("argument not found"); return arg_id; } FMT_INLINE void on_replacement_field(int id, const Char*) { ctx.arg(id).visit(default_arg_formatter{ctx.out()}); } auto on_format_specs(int id, const Char* begin, const Char* end) -> const Char* { auto arg = ctx.arg(id); if (!arg) report_error("argument not found"); // Not using a visitor for custom types gives better codegen. if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); auto specs = dynamic_format_specs(); begin = parse_format_specs(begin, end, specs, parse_ctx, arg.type()); if (specs.dynamic()) { handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, ctx); handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs.precision_ref, ctx); } arg.visit(arg_formatter{ctx.out(), specs, ctx.locale()}); return begin; } FMT_NORETURN void on_error(const char* message) { report_error(message); } }; // It is used in format-inl.h and os.cc. using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void do_report_error(format_func func, int error_code, const char* message) noexcept; FMT_API void format_error_code(buffer& out, int error_code, string_view message) noexcept; template template FMT_CONSTEXPR auto native_formatter::format( const T& val, FormatContext& ctx) const -> decltype(ctx.out()) { if (!specs_.dynamic()) return write(ctx.out(), val, specs_, ctx.locale()); auto specs = format_specs(specs_); handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref, ctx); handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs_.precision_ref, ctx); return write(ctx.out(), val, specs, ctx.locale()); } } // namespace detail FMT_BEGIN_EXPORT // A generic formatting context with custom output iterator and character // (code unit) support. Char is the format string code unit type which can be // different from OutputIt::value_type. template class generic_context { private: OutputIt out_; basic_format_args args_; locale_ref loc_; public: using char_type = Char; using iterator = OutputIt; enum { builtin_types = FMT_BUILTIN_TYPES }; constexpr generic_context(OutputIt out, basic_format_args args, locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} generic_context(generic_context&&) = default; generic_context(const generic_context&) = delete; void operator=(const generic_context&) = delete; constexpr auto arg(int id) const -> basic_format_arg { return args_.get(id); } auto arg(basic_string_view name) const -> basic_format_arg { return args_.get(name); } constexpr auto arg_id(basic_string_view name) const -> int { return args_.get_id(name); } constexpr auto out() const -> iterator { return out_; } void advance_to(iterator it) { if (!detail::is_back_insert_iterator()) out_ = it; } constexpr auto locale() const -> locale_ref { return loc_; } }; class loc_value { private: basic_format_arg value_; public: template ::value)> loc_value(T value) : value_(value) {} template ::value)> loc_value(T) {} template auto visit(Visitor&& vis) -> decltype(vis(0)) { return value_.visit(vis); } }; // A locale facet that formats values in UTF-8. // It is parameterized on the locale to avoid the heavy include. template class format_facet : public Locale::facet { private: std::string separator_; std::string grouping_; std::string decimal_point_; protected: virtual auto do_put(appender out, loc_value val, const format_specs& specs) const -> bool; public: static FMT_API typename Locale::id id; explicit format_facet(Locale& loc); explicit format_facet(string_view sep = "", std::string grouping = "\3", std::string decimal_point = ".") : separator_(sep.data(), sep.size()), grouping_(std::move(grouping)), decimal_point_(std::move(decimal_point)) {} auto put(appender out, loc_value val, const format_specs& specs) const -> bool { return do_put(out, val, specs); } }; #define FMT_FORMAT_AS(Type, Base) \ template \ struct formatter : formatter { \ template \ FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \ -> decltype(ctx.out()) { \ return formatter::format(value, ctx); \ } \ } FMT_FORMAT_AS(signed char, int); FMT_FORMAT_AS(unsigned char, unsigned); FMT_FORMAT_AS(short, int); FMT_FORMAT_AS(unsigned short, unsigned); FMT_FORMAT_AS(long, detail::long_type); FMT_FORMAT_AS(unsigned long, detail::ulong_type); FMT_FORMAT_AS(Char*, const Char*); FMT_FORMAT_AS(detail::std_string_view, basic_string_view); FMT_FORMAT_AS(std::nullptr_t, const void*); FMT_FORMAT_AS(void*, const void*); template struct formatter : formatter, Char> {}; template class formatter, Char> : public formatter, Char> {}; template struct formatter : detail::native_formatter {}; template struct formatter>> : formatter, Char> { template FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto&& val = format_as(value); // Make an lvalue reference for format. return formatter, Char>::format(val, ctx); } }; /** * Converts `p` to `const void*` for pointer formatting. * * **Example**: * * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { static_assert(std::is_pointer::value, "fmt::ptr used with non-pointer"); return detail::bit_cast(p); } /** * Converts `e` to the underlying type. * * **Example**: * * enum class color { red, green, blue }; * auto s = fmt::format("{}", fmt::underlying(color::red)); // s == "0" */ template constexpr auto underlying(Enum e) noexcept -> underlying_t { return static_cast>(e); } namespace enums { template ::value)> constexpr auto format_as(Enum e) noexcept -> underlying_t { return static_cast>(e); } } // namespace enums struct bytes { string_view data; inline explicit bytes(string_view s) : data(s) {} }; template <> struct formatter { private: detail::dynamic_format_specs<> specs_; public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type::string_type); } template auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, ctx); detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs.precision_ref, ctx); return detail::write_bytes(ctx.out(), b.data, specs); } }; // group_digits_view is not derived from view because it copies the argument. template struct group_digits_view { T value; }; /** * Returns a view that formats an integer value using ',' as a * locale-independent thousands separator. * * **Example**: * * fmt::print("{}", fmt::group_digits(12345)); * // Output: "12,345" */ template auto group_digits(T value) -> group_digits_view { return {value}; } template struct formatter> : formatter { private: detail::dynamic_format_specs<> specs_; public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type::int_type); } template auto format(group_digits_view view, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, ctx); detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs.precision_ref, ctx); auto arg = detail::make_write_int_arg(view.value, specs.sign()); return detail::write_int( ctx.out(), static_cast>(arg.abs_value), arg.prefix, specs, detail::digit_grouping("\3", ",")); } }; template struct nested_view { const formatter* fmt; const T* value; }; template struct formatter, Char> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(nested_view view, FormatContext& ctx) const -> decltype(ctx.out()) { return view.fmt->format(*view.value, ctx); } }; template struct nested_formatter { private: basic_specs specs_; int width_; formatter formatter_; public: constexpr nested_formatter() : width_(0) {} FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it == end) return it; auto specs = format_specs(); it = detail::parse_align(it, end, specs); specs_ = specs; Char c = *it; auto width_ref = detail::arg_ref(); if ((c >= '0' && c <= '9') || c == '{') { it = detail::parse_width(it, end, specs, width_ref, ctx); width_ = specs.width; } ctx.advance_to(it); return formatter_.parse(ctx); } template auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) { if (width_ == 0) return write(ctx.out()); auto buf = basic_memory_buffer(); write(basic_appender(buf)); auto specs = format_specs(); specs.width = width_; specs.copy_fill_from(specs_); specs.set_align(specs_.align()); return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } auto nested(const T& value) const -> nested_view { return nested_view{&formatter_, &value}; } }; inline namespace literals { #if FMT_USE_NONTYPE_TEMPLATE_ARGS /** * User-defined literal equivalent of `fmt::arg`, but with compile-time checks. * * **Example**: * * using namespace fmt::literals; * fmt::print("The answer is {answer}.", "answer"_a=42); */ template constexpr auto operator""_a() { using char_t = remove_cvref_t; return detail::udl_arg(); } #else /** * User-defined literal equivalent of `fmt::arg`. * * **Example**: * * using namespace fmt::literals; * fmt::print("The answer is {answer}.", "answer"_a=42); */ constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { return {s}; } #endif // FMT_USE_NONTYPE_TEMPLATE_ARGS } // namespace literals /// A fast integer formatter. class format_int { private: // Buffer should be large enough to hold all digits (digits10 + 1), // a sign and a null character. enum { buffer_size = std::numeric_limits::digits10 + 3 }; mutable char buffer_[buffer_size]; char* str_; template FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* { auto n = static_cast>(value); return detail::do_format_decimal(buffer_, n, buffer_size - 1); } template FMT_CONSTEXPR20 auto format_signed(Int value) -> char* { auto abs_value = static_cast>(value); bool negative = value < 0; if (negative) abs_value = 0 - abs_value; auto begin = format_unsigned(abs_value); if (negative) *--begin = '-'; return begin; } public: FMT_CONSTEXPR20 explicit format_int(int value) : str_(format_signed(value)) {} FMT_CONSTEXPR20 explicit format_int(long value) : str_(format_signed(value)) {} FMT_CONSTEXPR20 explicit format_int(long long value) : str_(format_signed(value)) {} FMT_CONSTEXPR20 explicit format_int(unsigned value) : str_(format_unsigned(value)) {} FMT_CONSTEXPR20 explicit format_int(unsigned long value) : str_(format_unsigned(value)) {} FMT_CONSTEXPR20 explicit format_int(ullong value) : str_(format_unsigned(value)) {} /// Returns the number of characters written to the output buffer. FMT_CONSTEXPR20 auto size() const -> size_t { return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); } /// Returns a pointer to the output buffer content. No terminating null /// character is appended. FMT_CONSTEXPR20 auto data() const -> const char* { return str_; } /// Returns a pointer to the output buffer content with terminating null /// character appended. FMT_CONSTEXPR20 auto c_str() const -> const char* { buffer_[buffer_size - 1] = '\0'; return str_; } /// Returns the content of the output buffer as an `std::string`. inline auto str() const -> std::string { return {str_, size()}; } }; #if FMT_CLANG_ANALYZER # define FMT_STRING_IMPL(s, base) s #else # define FMT_STRING_IMPL(s, base) \ [] { \ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ /* Use a macro-like name to avoid shadowing warnings. */ \ struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ using char_type = fmt::remove_cvref_t; \ constexpr explicit operator fmt::basic_string_view() \ const { \ return fmt::detail::compile_string_to_view(s); \ } \ }; \ using FMT_STRING_VIEW = \ fmt::basic_string_view; \ fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ return FMT_COMPILE_STRING(); \ }() #endif // FMT_CLANG_ANALYZER /** * Constructs a legacy compile-time format string from a string literal `s`. * * **Example**: * * // A compile-time error because 'd' is an invalid specifier for strings. * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); */ #if FMT_USE_CONSTEVAL # define FMT_STRING(s) s #else # define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) #endif // FMT_USE_CONSTEVAL FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args) -> std::system_error; /** * Constructs `std::system_error` with a message formatted with * `fmt::format(fmt, args...)`. * `error_code` is a system error code as given by `errno`. * * **Example**: * * // This throws std::system_error with the description * // cannot open file 'madeup': No such file or directory * // or similar (system message may vary). * const char* filename = "madeup"; * FILE* file = fopen(filename, "r"); * if (!file) * throw fmt::system_error(errno, "cannot open file '{}'", filename); */ template auto system_error(int error_code, format_string fmt, T&&... args) -> std::system_error { return vsystem_error(error_code, fmt.str, vargs{{args...}}); } /** * Formats an error message for an error returned by an operating system or a * language runtime, for example a file opening error, and writes it to `out`. * The format is the same as the one used by `std::system_error(ec, message)` * where `ec` is `std::error_code(error_code, std::generic_category())`. * It is implementation-defined but normally looks like: * * : * * where `` is the passed message and `` is the system * message corresponding to the error code. * `error_code` is a system error code as given by `errno`. */ FMT_API void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, const char* message) noexcept; inline auto vformat(locale_ref loc, string_view fmt, format_args args) -> std::string { auto buf = memory_buffer(); detail::vformat_to(buf, fmt, args, loc); return {buf.data(), buf.size()}; } template FMT_INLINE auto format(locale_ref loc, format_string fmt, T&&... args) -> std::string { return vformat(loc, fmt.str, vargs{{args...}}); } template ::value)> auto vformat_to(OutputIt out, locale_ref loc, string_view fmt, format_args args) -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, fmt, args, loc); return detail::get_iterator(buf, out); } template ::value)> FMT_INLINE auto format_to(OutputIt out, locale_ref loc, format_string fmt, T&&... args) -> OutputIt { return fmt::vformat_to(out, loc, fmt.str, vargs{{args...}}); } template FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc, format_string fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); detail::vformat_to(buf, fmt.str, vargs{{args...}}, loc); return buf.count(); } FMT_API auto vformat(string_view fmt, format_args args) -> std::string; /** * Formats `args` according to specifications in `fmt` and returns the result * as a string. * * **Example**: * * #include * std::string message = fmt::format("The answer is {}.", 42); */ template FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) -> std::string { return vformat(fmt.str, vargs{{args...}}); } /** * Converts `value` to `std::string` using the default format for type `T`. * * **Example**: * * std::string answer = fmt::to_string(42); */ template ::value)> FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string { // The buffer should be large enough to store the number including the sign // or "false" for bool. char buffer[max_of(detail::digits10() + 2, 5)]; return {buffer, detail::write(buffer, value)}; } template ::value)> FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) -> std::string { return to_string(format_as(value)); } template ::value && !detail::use_format_as::value)> FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) -> std::string { auto buffer = memory_buffer(); detail::write(appender(buffer), value); return {buffer.data(), buffer.size()}; } FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format-inl.h" #endif // Restore _LIBCPP_REMOVE_TRANSITIVE_INCLUDES. #ifdef FMT_REMOVE_TRANSITIVE_INCLUDES # undef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES #endif #endif // FMT_FORMAT_H_ ================================================ FILE: include/fmt/os.h ================================================ // Formatting library for C++ - optional OS-specific functionality // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_OS_H_ #define FMT_OS_H_ #include "format.h" #ifndef FMT_MODULE # include # include # include # include // std::system_error # if FMT_HAS_INCLUDE() # include // LC_NUMERIC_MASK on macOS # endif #endif // FMT_MODULE #ifndef FMT_USE_FCNTL // UWP doesn't provide _pipe. # if FMT_HAS_INCLUDE("winapifamily.h") # include # endif # if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ defined(__linux__)) && \ (!defined(WINAPI_FAMILY) || \ (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \ !defined(__wasm__) # include // for O_RDONLY # define FMT_USE_FCNTL 1 # else # define FMT_USE_FCNTL 0 # endif #endif #ifndef FMT_POSIX # if defined(_WIN32) && !defined(__MINGW32__) // Fix warnings about deprecated symbols. # define FMT_POSIX(call) _##call # else # define FMT_POSIX(call) call # endif #endif // Calls to system functions are wrapped in FMT_SYSTEM for testability. #ifdef FMT_SYSTEM # define FMT_HAS_SYSTEM # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) #else # define FMT_SYSTEM(call) ::call # ifdef _WIN32 // Fix warnings about deprecated symbols. # define FMT_POSIX_CALL(call) ::_##call # else # define FMT_POSIX_CALL(call) ::call # endif #endif // Retries the expression while it evaluates to error_result and errno // equals to EINTR. #ifndef _WIN32 # define FMT_RETRY_VAL(result, expression, error_result) \ do { \ (result) = (expression); \ } while ((result) == (error_result) && errno == EINTR) #else # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) #endif #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) FMT_BEGIN_NAMESPACE FMT_BEGIN_EXPORT /** * A reference to a null-terminated string. It can be constructed from a C * string or `std::string`. * * You can use one of the following type aliases for common character types: * * +---------------+-----------------------------+ * | Type | Definition | * +===============+=============================+ * | cstring_view | basic_cstring_view | * +---------------+-----------------------------+ * | wcstring_view | basic_cstring_view | * +---------------+-----------------------------+ * * This class is most useful as a parameter type for functions that wrap C APIs. */ template class basic_cstring_view { private: const Char* data_; public: /// Constructs a string reference object from a C string. basic_cstring_view(const Char* s) : data_(s) {} /// Constructs a string reference from an `std::string` object. basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} /// Returns the pointer to a C string. auto c_str() const -> const Char* { return data_; } }; using cstring_view = basic_cstring_view; using wcstring_view = basic_cstring_view; #ifdef _WIN32 FMT_API const std::error_category& system_category() noexcept; namespace detail { FMT_API void format_windows_error(buffer& out, int error_code, const char* message) noexcept; } FMT_API std::system_error vwindows_error(int error_code, string_view fmt, format_args args); /** * Constructs a `std::system_error` object with the description of the form * * : * * where `` is the formatted message and `` is the * system message corresponding to the error code. * `error_code` is a Windows error code as given by `GetLastError`. * If `error_code` is not a valid error code such as -1, the system message * will look like "error -1". * * **Example**: * * // This throws a system_error with the description * // cannot open file 'foo': The system cannot find the file specified. * // or similar (system message may vary) if the file doesn't exist. * const char *filename = "foo"; * LPOFSTRUCT of = LPOFSTRUCT(); * HFILE file = OpenFile(filename, &of, OF_READ); * if (file == HFILE_ERROR) { * throw fmt::windows_error(GetLastError(), * "cannot open file '{}'", filename); * } */ template auto windows_error(int error_code, string_view message, const T&... args) -> std::system_error { return vwindows_error(error_code, message, vargs{{args...}}); } // Reports a Windows error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_windows_error(int error_code, const char* message) noexcept; #else inline auto system_category() noexcept -> const std::error_category& { return std::system_category(); } #endif // _WIN32 // A buffered file. class buffered_file { private: FILE* file_; friend class file; inline explicit buffered_file(FILE* f) : file_(f) {} public: buffered_file(const buffered_file&) = delete; void operator=(const buffered_file&) = delete; // Constructs a buffered_file object which doesn't represent any file. inline buffered_file() noexcept : file_(nullptr) {} // Destroys the object closing the file it represents if any. FMT_API ~buffered_file() noexcept; public: inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) { other.file_ = nullptr; } inline auto operator=(buffered_file&& other) -> buffered_file& { close(); file_ = other.file_; other.file_ = nullptr; return *this; } // Opens a file. FMT_API buffered_file(cstring_view filename, cstring_view mode); // Closes the file. FMT_API void close(); // Returns the pointer to a FILE object representing this file. inline auto get() const noexcept -> FILE* { return file_; } FMT_API auto descriptor() const -> int; template inline void print(string_view fmt, const T&... args) { fmt::vargs vargs = {{args...}}; detail::is_locking() ? fmt::vprint_buffered(file_, fmt, vargs) : fmt::vprint(file_, fmt, vargs); } }; #if FMT_USE_FCNTL // A file. Closed file is represented by a file object with descriptor -1. // Methods that are not declared with noexcept may throw // fmt::system_error in case of failure. Note that some errors such as // closing the file multiple times will cause a crash on Windows rather // than an exception. You can get standard behavior by overriding the // invalid parameter handler with _set_invalid_parameter_handler. class FMT_API file { private: int fd_; // File descriptor. // Constructs a file object with a given descriptor. explicit file(int fd) : fd_(fd) {} friend struct pipe; public: // Possible values for the oflag argument to the constructor. enum { RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. APPEND = FMT_POSIX(O_APPEND), // Open in append mode. TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. }; // Constructs a file object which doesn't represent any file. inline file() noexcept : fd_(-1) {} // Opens a file and constructs a file object representing this file. file(cstring_view path, int oflag); public: file(const file&) = delete; void operator=(const file&) = delete; inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } // Move assignment is not noexcept because close may throw. inline auto operator=(file&& other) -> file& { close(); fd_ = other.fd_; other.fd_ = -1; return *this; } // Destroys the object closing the file it represents if any. ~file() noexcept; // Returns the file descriptor. inline auto descriptor() const noexcept -> int { return fd_; } // Closes the file. void close(); // Returns the file size. The size has signed type for consistency with // stat::st_size. auto size() const -> long long; // Attempts to read count bytes from the file into the specified buffer. auto read(void* buffer, size_t count) -> size_t; // Attempts to write count bytes from the specified buffer to the file. auto write(const void* buffer, size_t count) -> size_t; // Duplicates a file descriptor with the dup function and returns // the duplicate as a file object. static auto dup(int fd) -> file; // Makes fd be the copy of this file descriptor, closing fd first if // necessary. void dup2(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. void dup2(int fd, std::error_code& ec) noexcept; // Creates a buffered_file object associated with this file and detaches // this file object from the file. auto fdopen(const char* mode) -> buffered_file; # if defined(_WIN32) && !defined(__MINGW32__) // Opens a file and constructs a file object representing this file by // wcstring_view filename. Windows only. static file open_windows_file(wcstring_view path, int oflag); # endif }; struct FMT_API pipe { file read_end; file write_end; // Creates a pipe setting up read_end and write_end file objects for reading // and writing respectively. pipe(); }; // Returns the memory page size. auto getpagesize() -> long; namespace detail { struct buffer_size { constexpr buffer_size() = default; size_t value = 0; FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size { auto bs = buffer_size(); bs.value = val; return bs; } }; struct ostream_params { int oflag = file::WRONLY | file::CREATE | file::TRUNC; size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; constexpr ostream_params() {} template ostream_params(T... params, int new_oflag) : ostream_params(params...) { oflag = new_oflag; } template ostream_params(T... params, detail::buffer_size bs) : ostream_params(params...) { this->buffer_size = bs.value; } // Intel has a bug that results in failure to deduce a constructor // for empty parameter packs. # if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 ostream_params(int new_oflag) : oflag(new_oflag) {} ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} # endif }; } // namespace detail FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); /// A fast buffered output stream for writing from a single thread. Writing from /// multiple threads without external synchronization may result in a data race. class ostream : private detail::buffer { private: file file_; FMT_API ostream(cstring_view path, const detail::ostream_params& params); FMT_API static void grow(buffer& buf, size_t); public: FMT_API ostream(ostream&& other) noexcept; FMT_API ~ostream(); operator writer() { detail::buffer& buf = *this; return buf; } inline void flush() { if (size() == 0) return; file_.write(data(), size() * sizeof(data()[0])); clear(); } template friend auto output_file(cstring_view path, T... params) -> ostream; inline void close() { flush(); file_.close(); } /// Formats `args` according to specifications in `fmt` and writes the /// output to the file. template void print(format_string fmt, T&&... args) { vformat_to(appender(*this), fmt.str, vargs{{args...}}); } }; /** * Opens a file for writing. Supported parameters passed in `params`: * * - ``: Flags passed to [open]( * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) * (`file::WRONLY | file::CREATE | file::TRUNC` by default) * - `buffer_size=`: Output buffer size * * **Example**: * * auto out = fmt::output_file("guide.txt"); * out.print("Don't {}", "Panic"); */ template inline auto output_file(cstring_view path, T... params) -> ostream { return {path, detail::ostream_params(params...)}; } #endif // FMT_USE_FCNTL FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_OS_H_ ================================================ FILE: include/fmt/ostream.h ================================================ // Formatting library for C++ - std::ostream support // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ #ifndef FMT_MODULE # include // std::filebuf #endif #ifdef _WIN32 # ifdef __GLIBCXX__ # include # include # endif # include #endif #include "chrono.h" // formatbuf #ifdef _MSVC_STL_UPDATE # define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE #elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5 # define FMT_MSVC_STL_UPDATE _MSVC_LANG #else # define FMT_MSVC_STL_UPDATE 0 #endif FMT_BEGIN_NAMESPACE namespace detail { // Generate a unique explicit instantiation in every translation unit using a // tag type in an anonymous namespace. namespace { struct file_access_tag {}; } // namespace template class file_access { friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } }; #if FMT_MSVC_STL_UPDATE template class file_access; auto get_file(std::filebuf&) -> FILE*; #endif // Write the content of buf to os. // It is a separate function rather than a part of vprint to simplify testing. template void write_buffer(std::basic_ostream& os, buffer& buf) { const Char* buf_data = buf.data(); using unsigned_streamsize = make_unsigned_t; unsigned_streamsize size = buf.size(); unsigned_streamsize max_size = to_unsigned(max_value()); do { unsigned_streamsize n = size <= max_size ? size : max_size; os.write(buf_data, static_cast(n)); buf_data += n; size -= n; } while (size != 0); } template struct streamed_view { const T& value; }; } // namespace detail // Formats an object of type T that has an overloaded ostream operator<<. template struct basic_ostream_formatter : formatter, Char> { void set_debug_format() = delete; template auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { auto buffer = basic_memory_buffer(); auto&& formatbuf = detail::formatbuf>(buffer); auto&& output = std::basic_ostream(&formatbuf); output.imbue(std::locale::classic()); // The default is always unlocalized. output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); return formatter, Char>::format( {buffer.data(), buffer.size()}, ctx); } }; using ostream_formatter = basic_ostream_formatter; template struct formatter, Char> : basic_ostream_formatter { template auto format(detail::streamed_view view, Context& ctx) const -> decltype(ctx.out()) { return basic_ostream_formatter::format(view.value, ctx); } }; /** * Returns a view that formats `value` via an ostream `operator<<`. * * **Example**: * * fmt::print("Current thread id: {}\n", * fmt::streamed(std::this_thread::get_id())); */ template constexpr auto streamed(const T& value) -> detail::streamed_view { return {value}; } inline void vprint(std::ostream& os, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); FILE* f = nullptr; #if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI if (auto* buf = dynamic_cast(os.rdbuf())) f = detail::get_file(*buf); #elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI auto* rdbuf = os.rdbuf(); if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) f = sfbuf->file(); else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) f = fbuf->file(); #endif #ifdef _WIN32 if (f) { int fd = _fileno(f); if (_isatty(fd)) { os.flush(); if (detail::write_console(fd, {buffer.data(), buffer.size()})) return; } } #endif detail::ignore_unused(f); detail::write_buffer(os, buffer); } /** * Prints formatted data to the stream `os`. * * **Example**: * * fmt::print(cerr, "Don't {}!", "panic"); */ FMT_EXPORT template void print(std::ostream& os, format_string fmt, T&&... args) { fmt::vargs vargs = {{args...}}; if FMT_CONSTEXPR20 (detail::use_utf8) return vprint(os, fmt.str, vargs); auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt.str, vargs); detail::write_buffer(os, buffer); } FMT_EXPORT template void println(std::ostream& os, format_string fmt, T&&... args) { fmt::print(os, FMT_STRING("{}\n"), fmt::format(fmt, std::forward(args)...)); } FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_ ================================================ FILE: include/fmt/printf.h ================================================ // Formatting library for C++ - legacy printf implementation // // Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_PRINTF_H_ #define FMT_PRINTF_H_ #ifndef FMT_MODULE # include // std::find # include // std::numeric_limits #endif #include "format.h" FMT_BEGIN_NAMESPACE FMT_BEGIN_EXPORT template class basic_printf_context { private: basic_appender out_; basic_format_args args_; static_assert(std::is_same::value || std::is_same::value, "Unsupported code unit type."); public: using char_type = Char; enum { builtin_types = 1 }; /// Constructs a `printf_context` object. References to the arguments are /// stored in the context object so make sure they have appropriate lifetimes. basic_printf_context(basic_appender out, basic_format_args args) : out_(out), args_(args) {} auto out() -> basic_appender { return out_; } void advance_to(basic_appender) {} auto locale() -> locale_ref { return {}; } auto arg(int id) const -> basic_format_arg { return args_.get(id); } }; namespace detail { // Return the result via the out param to workaround gcc bug 77539. template FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { for (out = first; out != last; ++out) { if (*out == value) return true; } return false; } template <> inline auto find(const char* first, const char* last, char value, const char*& out) -> bool { out = static_cast(memchr(first, value, to_unsigned(last - first))); return out != nullptr; } // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct int_checker { template static auto fits_in_int(T value) -> bool { return value <= to_unsigned(max_value()); } inline static auto fits_in_int(bool) -> bool { return true; } }; template <> struct int_checker { template static auto fits_in_int(T value) -> bool { return value >= (std::numeric_limits::min)() && value <= max_value(); } inline static auto fits_in_int(int) -> bool { return true; } }; struct printf_precision_handler { template ::value)> auto operator()(T value) -> int { if (!int_checker::is_signed>::fits_in_int(value)) report_error("number is too big"); return max_of(static_cast(value), 0); } template ::value)> auto operator()(T) -> int { report_error("precision is not integer"); return 0; } }; // An argument visitor that returns true iff arg is a zero integer. struct is_zero_int { template ::value)> auto operator()(T value) -> bool { return value == 0; } template ::value)> auto operator()(T) -> bool { return false; } }; template struct make_unsigned_or_bool : std::make_unsigned {}; template <> struct make_unsigned_or_bool { using type = bool; }; template class arg_converter { private: using char_type = typename Context::char_type; basic_format_arg& arg_; char_type type_; public: arg_converter(basic_format_arg& arg, char_type type) : arg_(arg), type_(type) {} void operator()(bool value) { if (type_ != 's') operator()(value); } template ::value)> void operator()(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; using target_type = conditional_t::value, U, T>; if FMT_CONSTEXPR20 (sizeof(target_type) <= sizeof(int)) { // Extra casts are used to silence warnings. using unsigned_type = typename make_unsigned_or_bool::type; if (is_signed) arg_ = static_cast(static_cast(value)); else arg_ = static_cast(static_cast(value)); } else { // glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB. if (is_signed) arg_ = static_cast(value); else arg_ = static_cast::type>(value); } } template ::value)> void operator()(U) {} // No conversion needed for non-integral types. }; // Converts an integer argument to T for printf, if T is an integral type. // If T is void, the argument is converted to corresponding signed or unsigned // type depending on the type specifier: 'd' and 'i' - signed, other - // unsigned). template void convert_arg(basic_format_arg& arg, Char type) { arg.visit(arg_converter(arg, type)); } // Converts an integer argument to char for printf. template class char_converter { private: basic_format_arg& arg_; public: explicit char_converter(basic_format_arg& arg) : arg_(arg) {} template ::value)> void operator()(T value) { arg_ = static_cast(value); } template ::value)> void operator()(T) {} // No conversion needed for non-integral types. }; // An argument visitor that return a pointer to a C string if argument is a // string or null otherwise. template struct get_cstring { template auto operator()(T) -> const Char* { return nullptr; } auto operator()(const Char* s) -> const Char* { return s; } }; // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. class printf_width_handler { private: format_specs& specs_; public: inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {} template ::value)> auto operator()(T value) -> unsigned { auto width = static_cast>(value); if (detail::is_negative(value)) { specs_.set_align(align::left); width = 0 - width; } unsigned int_max = to_unsigned(max_value()); if (width > int_max) report_error("number is too big"); return static_cast(width); } template ::value)> auto operator()(T) -> unsigned { report_error("width is not integer"); return 0; } }; // Workaround for a bug with the XL compiler when initializing // printf_arg_formatter's base class. template auto make_arg_formatter(basic_appender iter, format_specs& s) -> arg_formatter { return {iter, s, locale_ref()}; } // The `printf` argument formatter. template class printf_arg_formatter : public arg_formatter { private: using base = arg_formatter; using context_type = basic_printf_context; context_type& context_; void write_null_pointer(bool is_string = false) { auto s = this->specs; s.set_type(presentation_type::none); write_bytes(this->out, is_string ? "(null)" : "(nil)", s); } template void write(T value) { detail::write(this->out, value, this->specs, this->locale); } public: printf_arg_formatter(basic_appender iter, format_specs& s, context_type& ctx) : base(make_arg_formatter(iter, s)), context_(ctx) {} void operator()(monostate value) { write(value); } template ::value)> void operator()(T value) { // MSVC2013 fails to compile separate overloads for bool and Char so use // std::is_same instead. if (!std::is_same::value) { write(value); return; } format_specs s = this->specs; if (s.type() != presentation_type::none && s.type() != presentation_type::chr) { return (*this)(static_cast(value)); } s.set_sign(sign::none); s.clear_alt(); s.set_fill(' '); // Ignore '0' flag for char types. // align::numeric needs to be overwritten here since the '0' flag is // ignored for non-numeric types if (s.align() == align::none || s.align() == align::numeric) s.set_align(align::right); detail::write(this->out, static_cast(value), s); } template ::value)> void operator()(T value) { write(value); } void operator()(const char* value) { if (value) write(value); else write_null_pointer(this->specs.type() != presentation_type::pointer); } void operator()(const wchar_t* value) { if (value) write(value); else write_null_pointer(this->specs.type() != presentation_type::pointer); } void operator()(basic_string_view value) { write(value); } void operator()(const void* value) { if (value) write(value); else write_null_pointer(); } void operator()(typename basic_format_arg::handle handle) { auto parse_ctx = parse_context({}); handle.format(parse_ctx, context_); } }; template void parse_flags(format_specs& specs, const Char*& it, const Char* end) { for (; it != end; ++it) { switch (*it) { case '-': specs.set_align(align::left); break; case '+': specs.set_sign(sign::plus); break; case '0': specs.set_fill('0'); break; case ' ': if (specs.sign() != sign::plus) specs.set_sign(sign::space); break; case '#': specs.set_alt(); break; default: return; } } } template auto parse_header(const Char*& it, const Char* end, format_specs& specs, GetArg get_arg) -> int { int arg_index = -1; Char c = *it; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). int value = parse_nonnegative_int(it, end, -1); if (it != end && *it == '$') { // value is an argument index ++it; arg_index = value != -1 ? value : max_value(); } else { if (c == '0') specs.set_fill('0'); if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. if (value == -1) report_error("number is too big"); specs.width = value; return arg_index; } } } parse_flags(specs, it, end); // Parse width. if (it != end) { if (*it >= '0' && *it <= '9') { specs.width = parse_nonnegative_int(it, end, -1); if (specs.width == -1) report_error("number is too big"); } else if (*it == '*') { ++it; // Check for positional width argument like *1$ if (it != end && *it >= '0' && *it <= '9') { int width_index = parse_nonnegative_int(it, end, -1); if (it != end && *it == '$') { ++it; specs.width = static_cast( get_arg(width_index).visit(detail::printf_width_handler(specs))); } else { // Invalid format, rewind and treat as non-positional report_error("invalid format specifier"); } } else { specs.width = static_cast( get_arg(-1).visit(detail::printf_width_handler(specs))); } } } return arg_index; } inline auto parse_printf_presentation_type(char c, type t, bool& upper) -> presentation_type { using pt = presentation_type; constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; switch (c) { case 'd': return in(t, integral_set) ? pt::dec : pt::none; case 'o': return in(t, integral_set) ? pt::oct : pt::none; case 'X': upper = true; FMT_FALLTHROUGH; case 'x': return in(t, integral_set) ? pt::hex : pt::none; case 'E': upper = true; FMT_FALLTHROUGH; case 'e': return in(t, float_set) ? pt::exp : pt::none; case 'F': upper = true; FMT_FALLTHROUGH; case 'f': return in(t, float_set) ? pt::fixed : pt::none; case 'G': upper = true; FMT_FALLTHROUGH; case 'g': return in(t, float_set) ? pt::general : pt::none; case 'A': upper = true; FMT_FALLTHROUGH; case 'a': return in(t, float_set) ? pt::hexfloat : pt::none; case 'c': return in(t, integral_set) ? pt::chr : pt::none; case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none; case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; default: return pt::none; } } template void vprintf(buffer& buf, basic_string_view format, basic_format_args args) { using iterator = basic_appender; auto out = iterator(buf); auto context = basic_printf_context(out, args); auto parse_ctx = parse_context(format); // Returns the argument with specified index or, if arg_index is -1, the next // argument. auto get_arg = [&](int arg_index) { if (arg_index < 0) arg_index = parse_ctx.next_arg_id(); else parse_ctx.check_arg_id(--arg_index); auto arg = context.arg(arg_index); if (!arg) report_error("argument not found"); return arg; }; const Char* start = parse_ctx.begin(); const Char* end = parse_ctx.end(); auto it = start; while (it != end) { if (!find(it, end, '%', it)) { it = end; // find leaves it == nullptr if it doesn't find '%'. break; } Char c = *it++; if (it != end && *it == c) { write(out, basic_string_view(start, to_unsigned(it - start))); start = ++it; continue; } write(out, basic_string_view(start, to_unsigned(it - 1 - start))); auto specs = format_specs(); specs.set_align(align::right); // Parse argument index, flags and width. int arg_index = parse_header(it, end, specs, get_arg); if (arg_index == 0) report_error("argument not found"); // Parse precision. if (it != end && *it == '.') { ++it; c = it != end ? *it : 0; if ('0' <= c && c <= '9') { specs.precision = parse_nonnegative_int(it, end, 0); } else if (c == '*') { ++it; // Check for positional precision argument like .*1$ if (it != end && *it >= '0' && *it <= '9') { int precision_index = parse_nonnegative_int(it, end, -1); if (it != end && *it == '$') { ++it; specs.precision = static_cast( get_arg(precision_index).visit(printf_precision_handler())); } else { // Invalid format, rewind and treat as non-positional report_error("invalid format specifier"); } } else { specs.precision = static_cast(get_arg(-1).visit(printf_precision_handler())); } } else { specs.precision = 0; } } auto arg = get_arg(arg_index); // For d, i, o, u, x and X conversion specifiers, if a precision is // specified, the '0' flag is ignored if (specs.precision >= 0 && is_integral_type(arg.type())) { // Ignore '0' for non-numeric types or if '-' present. specs.set_fill(' '); } if (specs.precision >= 0 && arg.type() == type::cstring_type) { auto str = arg.visit(get_cstring()); auto str_end = str + specs.precision; auto nul = std::find(str, str_end, Char()); auto sv = basic_string_view( str, to_unsigned(nul != str_end ? nul - str : specs.precision)); arg = sv; } if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt(); if (specs.fill_unit() == '0') { if (is_arithmetic_type(arg.type()) && specs.align() != align::left) { specs.set_align(align::numeric); } else { // Ignore '0' flag for non-numeric types or if '-' flag is also present. specs.set_fill(' '); } } // Parse length and convert the argument to the required type. c = it != end ? *it++ : 0; Char t = it != end ? *it : 0; switch (c) { case 'h': if (t == 'h') { ++it; t = it != end ? *it : 0; convert_arg(arg, t); } else { convert_arg(arg, t); } break; case 'l': if (t == 'l') { ++it; t = it != end ? *it : 0; convert_arg(arg, t); } else { convert_arg(arg, t); } break; case 'j': convert_arg(arg, t); break; case 'z': convert_arg(arg, t); break; case 't': convert_arg(arg, t); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; default: --it; convert_arg(arg, c); } // Parse type. if (it == end) report_error("invalid format string"); char type = static_cast(*it++); if (is_integral_type(arg.type())) { // Normalize type. switch (type) { case 'i': case 'u': type = 'd'; break; case 'c': arg.visit(char_converter>(arg)); break; } } bool upper = false; specs.set_type(parse_printf_presentation_type(type, arg.type(), upper)); if (specs.type() == presentation_type::none) report_error("invalid format specifier"); if (upper) specs.set_upper(); start = it; // Format argument. arg.visit(printf_arg_formatter(out, specs, context)); } write(out, basic_string_view(start, to_unsigned(it - start))); } } // namespace detail using printf_context = basic_printf_context; using wprintf_context = basic_printf_context; using printf_args = basic_format_args; using wprintf_args = basic_format_args; /// Constructs an `format_arg_store` object that contains references to /// arguments and can be implicitly converted to `printf_args`. template inline auto make_printf_args(T&... args) -> decltype(fmt::make_format_args>(args...)) { return fmt::make_format_args>(args...); } template struct vprintf_args { using type = basic_format_args>; }; template inline auto vsprintf(basic_string_view fmt, typename vprintf_args::type args) -> std::basic_string { auto buf = basic_memory_buffer(); detail::vprintf(buf, fmt, args); return {buf.data(), buf.size()}; } /** * Formats `args` according to specifications in `fmt` and returns the result * as string. * * **Example**: * * std::string message = fmt::sprintf("The answer is %d", 42); */ template inline auto sprintf(string_view fmt, const T&... args) -> std::string { return vsprintf(fmt, make_printf_args(args...)); } template FMT_DEPRECATED auto sprintf(basic_string_view fmt, const T&... args) -> std::wstring { return vsprintf(fmt, make_printf_args(args...)); } template auto vfprintf(std::FILE* f, basic_string_view fmt, typename vprintf_args::type args) -> int { auto buf = basic_memory_buffer(); detail::vprintf(buf, fmt, args); size_t size = buf.size(); return std::fwrite(buf.data(), sizeof(Char), size, f) < size ? -1 : static_cast(size); } /** * Formats `args` according to specifications in `fmt` and writes the output * to `f`. * * **Example**: * * fmt::fprintf(stderr, "Don't %s!", "panic"); */ template inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int { return vfprintf(f, fmt, make_printf_args(args...)); } template FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view fmt, const T&... args) -> int { return vfprintf(f, fmt, make_printf_args(args...)); } /** * Formats `args` according to specifications in `fmt` and writes the output * to `stdout`. * * **Example**: * * fmt::printf("Elapsed time: %.2f seconds", 1.23); */ template inline auto printf(string_view fmt, const T&... args) -> int { return vfprintf(stdout, fmt, make_printf_args(args...)); } FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_PRINTF_H_ ================================================ FILE: include/fmt/ranges.h ================================================ // Formatting library for C++ - range and tuple support // // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_RANGES_H_ #define FMT_RANGES_H_ #ifndef FMT_MODULE # include # include # include # include # include #endif #include "format.h" #if FMT_HAS_CPP_ATTRIBUTE(clang::lifetimebound) # define FMT_LIFETIMEBOUND [[clang::lifetimebound]] #else # define FMT_LIFETIMEBOUND #endif FMT_PRAGMA_CLANG(diagnostic error "-Wreturn-stack-address") FMT_BEGIN_NAMESPACE FMT_EXPORT enum class range_format { disabled, map, set, sequence, string, debug_string }; namespace detail { template class is_map { template static auto check(U*) -> typename U::mapped_type; template static void check(...); public: static constexpr bool value = !std::is_void(nullptr))>::value; }; template class is_set { template static auto check(U*) -> typename U::key_type; template static void check(...); public: static constexpr bool value = !std::is_void(nullptr))>::value && !is_map::value; }; // C array overload template auto range_begin(const T (&arr)[N]) -> const T* { return arr; } template auto range_end(const T (&arr)[N]) -> const T* { return arr + N; } template struct has_member_fn_begin_end_t : std::false_type {}; template struct has_member_fn_begin_end_t().begin()), decltype(std::declval().end())>> : std::true_type {}; // Member function overloads. template 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()) { return static_cast(rng).end(); } // ADL overloads. Only participate in overload resolution if member functions // are not found. template auto range_begin(T&& rng) -> enable_if_t::value, decltype(begin(static_cast(rng)))> { return begin(static_cast(rng)); } template auto range_end(T&& rng) -> enable_if_t::value, decltype(end(static_cast(rng)))> { return end(static_cast(rng)); } template struct has_const_begin_end : std::false_type {}; template struct has_mutable_begin_end : std::false_type {}; template struct has_const_begin_end< T, void_t&>())), decltype(detail::range_end( std::declval&>()))>> : std::true_type {}; template struct has_mutable_begin_end< T, void_t())), decltype(detail::range_end(std::declval())), // the extra int here is because older versions of MSVC don't // SFINAE properly unless there are distinct types int>> : std::true_type {}; template struct is_range_ : std::false_type {}; template struct is_range_ : std::integral_constant::value || has_mutable_begin_end::value)> {}; // tuple_size and tuple_element check. template class is_tuple_like_ { template ::type> static auto check(U* p) -> decltype(std::tuple_size::value, 0); template static void check(...); public: static constexpr bool value = !std::is_void(nullptr))>::value; }; // Check for integer_sequence #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 template using integer_sequence = std::integer_sequence; template using index_sequence = std::index_sequence; template using make_index_sequence = std::make_index_sequence; #else template struct integer_sequence { using value_type = T; static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); } }; template using index_sequence = integer_sequence; template struct make_integer_sequence : make_integer_sequence {}; template struct make_integer_sequence : integer_sequence {}; template using make_index_sequence = make_integer_sequence; #endif template using tuple_index_sequence = make_index_sequence::value>; template ::value> class is_tuple_formattable_ { public: static constexpr bool value = false; }; template class is_tuple_formattable_ { template static auto all_true(index_sequence, integer_sequence= 0)...>) -> std::true_type; static auto all_true(...) -> std::false_type; template static auto check(index_sequence) -> decltype(all_true( index_sequence{}, integer_sequence::type, C>::value)...>{})); public: static constexpr bool value = decltype(check(tuple_index_sequence{}))::value; }; template FMT_CONSTEXPR void for_each(index_sequence, Tuple&& t, F&& f) { using std::get; // Using a free function get(Tuple) now. const int unused[] = {0, ((void)f(get(t)), 0)...}; ignore_unused(unused); } template FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) { for_each(tuple_index_sequence>(), std::forward(t), std::forward(f)); } template void for_each2(index_sequence, Tuple1&& t1, Tuple2&& t2, F&& f) { using std::get; const int unused[] = {0, ((void)f(get(t1), get(t2)), 0)...}; ignore_unused(unused); } template void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) { for_each2(tuple_index_sequence>(), std::forward(t1), std::forward(t2), std::forward(f)); } namespace tuple { // Workaround a bug in MSVC 2019 (v140). template using result_t = std::tuple, Char>...>; using std::get; template auto get_formatters(index_sequence) -> result_t(std::declval()))...>; } // namespace tuple #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 // Older MSVC doesn't get the reference type correctly for arrays. template struct range_reference_type_impl { using type = decltype(*detail::range_begin(std::declval())); }; template struct range_reference_type_impl { using type = T&; }; template using range_reference_type = typename range_reference_type_impl::type; #else template using range_reference_type = decltype(*detail::range_begin(std::declval())); #endif // We don't use the Range's value_type for anything, but we do need the Range's // reference type, with cv-ref stripped. template using uncvref_type = remove_cvref_t>; template struct range_format_kind_ : std::integral_constant, T>::value ? range_format::disabled : is_map::value ? range_format::map : is_set::value ? range_format::set : range_format::sequence> {}; template using range_format_constant = std::integral_constant; // These are not generic lambdas for compatibility with C++11. template struct parse_empty_specs { template FMT_CONSTEXPR void operator()(Formatter& f) { f.parse(ctx); detail::maybe_set_debug_format(f, true); } parse_context& ctx; }; template struct format_tuple_element { using char_type = typename FormatContext::char_type; template void operator()(const formatter& f, const T& v) { if (i > 0) ctx.advance_to(detail::copy(separator, ctx.out())); ctx.advance_to(f.format(v, ctx)); ++i; } int i; FormatContext& ctx; basic_string_view separator; }; } // namespace detail FMT_EXPORT template struct is_tuple_like { static constexpr bool value = detail::is_tuple_like_::value && !detail::is_range_::value; }; FMT_EXPORT template struct is_tuple_formattable { static constexpr bool value = detail::is_tuple_formattable_::value; }; template struct formatter::value && fmt::is_tuple_formattable::value>> { private: decltype(detail::tuple::get_formatters( detail::tuple_index_sequence())) formatters_; basic_string_view separator_ = detail::string_literal{}; basic_string_view opening_bracket_ = detail::string_literal{}; basic_string_view closing_bracket_ = detail::string_literal{}; public: FMT_CONSTEXPR formatter() {} FMT_CONSTEXPR void set_separator(basic_string_view sep) { separator_ = sep; } FMT_CONSTEXPR void set_brackets(basic_string_view open, basic_string_view close) { opening_bracket_ = open; closing_bracket_ = close; } FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); if (it != end && detail::to_ascii(*it) == 'n') { ++it; set_brackets({}, {}); set_separator({}); } if (it != end && *it != '}') report_error("invalid format specifier"); ctx.advance_to(it); detail::for_each(formatters_, detail::parse_empty_specs{ctx}); return it; } template auto format(const Tuple& value, FormatContext& ctx) const -> decltype(ctx.out()) { ctx.advance_to(detail::copy(opening_bracket_, ctx.out())); detail::for_each2( formatters_, value, detail::format_tuple_element{0, ctx, separator_}); return detail::copy(closing_bracket_, ctx.out()); } }; FMT_EXPORT template struct is_range { static constexpr bool value = detail::is_range_::value && !detail::has_to_string_view::value; }; namespace detail { template using range_formatter_type = formatter, Char>; template using maybe_const_range = conditional_t::value, const R, R>; template struct is_formattable_delayed : is_formattable>, Char> {}; } // namespace detail template struct conjunction : std::true_type {}; template struct conjunction

: P {}; template struct conjunction : conditional_t, P1> {}; FMT_EXPORT template struct range_formatter; template struct range_formatter< T, Char, enable_if_t>, is_formattable>::value>> { private: detail::range_formatter_type underlying_; basic_string_view separator_ = detail::string_literal{}; basic_string_view opening_bracket_ = detail::string_literal{}; basic_string_view closing_bracket_ = detail::string_literal{}; bool is_debug = false; template ::value)> auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { auto buf = basic_memory_buffer(); for (; it != end; ++it) buf.push_back(*it); auto specs = format_specs(); specs.set_type(presentation_type::debug); return detail::write( out, basic_string_view(buf.data(), buf.size()), specs); } template ::value)> auto write_debug_string(Output& out, It, Sentinel) const -> Output { return out; } public: FMT_CONSTEXPR range_formatter() {} FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type& { return underlying_; } FMT_CONSTEXPR void set_separator(basic_string_view sep) { separator_ = sep; } FMT_CONSTEXPR void set_brackets(basic_string_view open, basic_string_view close) { opening_bracket_ = open; closing_bracket_ = close; } FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); detail::maybe_set_debug_format(underlying_, true); if (it == end) return underlying_.parse(ctx); switch (detail::to_ascii(*it)) { case 'n': set_brackets({}, {}); ++it; break; case '?': is_debug = true; set_brackets({}, {}); ++it; if (it == end || *it != 's') report_error("invalid format specifier"); FMT_FALLTHROUGH; case 's': if (!std::is_same::value) report_error("invalid format specifier"); if (!is_debug) { set_brackets(detail::string_literal{}, detail::string_literal{}); set_separator({}); detail::maybe_set_debug_format(underlying_, false); } ++it; return it; } if (it != end && *it != '}') { if (*it != ':') report_error("invalid format specifier"); detail::maybe_set_debug_format(underlying_, false); ++it; } ctx.advance_to(it); return underlying_.parse(ctx); } template 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); if (is_debug) return write_debug_string(out, std::move(it), end); out = detail::copy(opening_bracket_, out); int i = 0; for (; it != end; ++it) { if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); auto&& item = *it; // Need an lvalue out = underlying_.format(item, ctx); ++i; } out = detail::copy(closing_bracket_, out); return out; } }; FMT_EXPORT template struct range_format_kind : conditional_t< is_range::value, detail::range_format_kind_, std::integral_constant> {}; template struct formatter< R, Char, enable_if_t::value != range_format::disabled && range_format_kind::value != range_format::map && range_format_kind::value != range_format::string && range_format_kind::value != range_format::debug_string>, detail::is_formattable_delayed>::value>> { private: using range_type = detail::maybe_const_range; range_formatter, Char> range_formatter_; public: using nonlocking = void; FMT_CONSTEXPR formatter() { if FMT_CONSTEXPR20 (range_format_kind::value != range_format::set) return; range_formatter_.set_brackets(detail::string_literal{}, detail::string_literal{}); } FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return range_formatter_.parse(ctx); } template auto format(range_type& range, FormatContext& ctx) const -> decltype(ctx.out()) { return range_formatter_.format(range, ctx); } }; // A map formatter. template struct formatter< R, Char, enable_if_t::value == range_format::map>, detail::is_formattable_delayed>::value>> { private: using map_type = detail::maybe_const_range; using element_type = detail::uncvref_type; decltype(detail::tuple::get_formatters( detail::tuple_index_sequence())) formatters_; bool no_delimiters_ = false; public: FMT_CONSTEXPR formatter() {} FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); if (it != end) { if (detail::to_ascii(*it) == 'n') { no_delimiters_ = true; ++it; } if (it != end && *it != '}') { if (*it != ':') report_error("invalid format specifier"); ++it; } ctx.advance_to(it); } detail::for_each(formatters_, detail::parse_empty_specs{ctx}); return it; } template auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); basic_string_view open = detail::string_literal{}; if (!no_delimiters_) out = detail::copy(open, out); int i = 0; basic_string_view sep = detail::string_literal{}; for (auto&& value : map) { if (i > 0) out = detail::copy(sep, out); ctx.advance_to(out); detail::for_each2(formatters_, value, detail::format_tuple_element{ 0, ctx, detail::string_literal{}}); ++i; } basic_string_view close = detail::string_literal{}; if (!no_delimiters_) out = detail::copy(close, out); return out; } }; // A (debug_)string formatter. template struct formatter< R, Char, enable_if_t::value == range_format::string || range_format_kind::value == range_format::debug_string>> { private: using range_type = detail::maybe_const_range; using string_type = conditional_t, decltype(detail::range_begin(std::declval())), decltype(detail::range_end(std::declval()))>::value, detail::std_string_view, std::basic_string>; formatter underlying_; public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return underlying_.parse(ctx); } template auto format(range_type& range, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); if FMT_CONSTEXPR20 (range_format_kind::value == range_format::debug_string) { *out++ = '"'; } out = underlying_.format( string_type{detail::range_begin(range), detail::range_end(range)}, ctx); if FMT_CONSTEXPR20 (range_format_kind::value == range_format::debug_string) *out++ = '"'; return out; } }; template struct join_view : detail::view { It begin; Sentinel end; basic_string_view sep; join_view(It b, Sentinel e, basic_string_view s) : begin(std::move(b)), end(e), sep(s) {} }; template struct formatter, Char> { private: using value_type = #ifdef __cpp_lib_ranges std::iter_value_t; #else typename std::iterator_traits::value_type; #endif formatter, Char> value_formatter_; using view = conditional_t::value, const join_view, join_view>; public: using nonlocking = void; FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return value_formatter_.parse(ctx); } template auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) { using iter = conditional_t::value, It, It&>; iter it = value.begin; auto out = ctx.out(); if (it == value.end) return out; out = value_formatter_.format(*it, ctx); ++it; while (it != value.end) { out = detail::copy(value.sep.begin(), value.sep.end(), out); ctx.advance_to(out); out = value_formatter_.format(*it, ctx); ++it; } return out; } }; FMT_EXPORT template struct tuple_join_view : detail::view { const Tuple& tuple; basic_string_view sep; tuple_join_view(const Tuple& t, basic_string_view s) : tuple(t), sep{s} {} }; // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers // support in tuple_join. It is disabled by default because of issues with // the dynamic width and precision. #ifndef FMT_TUPLE_JOIN_SPECIFIERS # define FMT_TUPLE_JOIN_SPECIFIERS 0 #endif template struct formatter, Char, enable_if_t::value>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return do_parse(ctx, std::tuple_size()); } template auto format(const tuple_join_view& value, FormatContext& ctx) const -> typename FormatContext::iterator { return do_format(value, ctx, std::tuple_size()); } private: decltype(detail::tuple::get_formatters( detail::tuple_index_sequence())) formatters_; FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) -> const Char* { return ctx.begin(); } template FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) -> const Char* { auto end = ctx.begin(); #if FMT_TUPLE_JOIN_SPECIFIERS end = std::get::value - N>(formatters_).parse(ctx); if (N > 1) { auto end1 = do_parse(ctx, std::integral_constant()); if (end != end1) report_error("incompatible format specs for tuple elements"); } #endif return end; } template 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 -> typename FormatContext::iterator { using std::get; auto out = std::get::value - N>(formatters_) .format(get::value - N>(value.tuple), ctx); if (N <= 1) return out; out = detail::copy(value.sep, out); ctx.advance_to(out); return do_format(value, ctx, std::integral_constant()); } }; namespace detail { template struct all { const Container& c; auto begin() const -> typename Container::const_iterator { return c.begin(); } auto end() const -> typename Container::const_iterator { return c.end(); } }; } // namespace detail /** * Specifies if `T` is a container adaptor (like `std::stack`) that should be * formatted as the underlying container. */ FMT_EXPORT template struct is_container_adaptor { private: template static auto check(U* p) -> typename U::container_type; template static void check(...); public: static constexpr bool value = !std::is_void(nullptr))>::value; }; template struct formatter< T, Char, enable_if_t, bool_constant::value == range_format::disabled>>::value>> : formatter, Char> { using all = detail::all; template auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { struct getter : T { static auto get(const T& v) -> all { return {v.*(&getter::c)}; // Access c through the derived class. } }; return formatter::format(getter::get(value), ctx); } }; FMT_BEGIN_EXPORT /// Returns a view that formats the iterator range `[begin, end)` with elements /// separated by `sep`. template auto join(It begin, Sentinel end, string_view sep) -> join_view { return {std::move(begin), end, sep}; } /** * Returns a view that formats `range` with elements separated by `sep`. * * **Example**: * * auto v = std::vector{1, 2, 3}; * fmt::print("{}", fmt::join(v, ", ")); * // Output: 1, 2, 3 * * `fmt::join` applies passed format specifiers to the range elements: * * fmt::print("{:02}", fmt::join(v, ", ")); * // Output: 01, 02, 03 */ template ::value)> auto join(Range&& r, string_view sep) -> join_view { return {detail::range_begin(r), detail::range_end(r), sep}; } /** * Returns an object that formats `std::tuple` with elements separated by `sep`. * * **Example**: * * auto t = std::tuple(1, 'a'); * fmt::print("{}", fmt::join(t, ", ")); * // Output: 1, a */ template ::value)> FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep) -> tuple_join_view { return {tuple, sep}; } /** * Returns an object that formats `std::initializer_list` with elements * separated by `sep`. * * **Example**: * * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); * // Output: "1, 2, 3" */ template FMT_DEPRECATED auto join(std::initializer_list list, string_view sep) -> join_view { return join(std::begin(list), std::end(list), sep); } FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_RANGES_H_ ================================================ FILE: include/fmt/std.h ================================================ // Formatting library for C++ - formatters for standard library types // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_STD_H_ #define FMT_STD_H_ #include "format.h" #include "ostream.h" #ifndef FMT_MODULE # include # include # include # include // std::byte # include // std::exception # include // std::reference_wrapper # include # include # include # include // std::type_info # include // std::make_index_sequence // Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. # if FMT_CPLUSPLUS >= 201703L # if FMT_HAS_INCLUDE() && \ (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0) # include # endif # if FMT_HAS_INCLUDE() # include # endif # if FMT_HAS_INCLUDE() # include # endif # endif // Use > instead of >= in the version check because may be // available after C++17 but before C++20 is marked as implemented. # if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() # include # endif # if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE() # include # endif #endif // FMT_MODULE #if FMT_HAS_INCLUDE() # include #endif // GCC 4 does not support FMT_HAS_INCLUDE. #if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) # include // Android NDK with gabi++ library on some architectures does not implement // abi::__cxa_demangle(). # ifndef __GABIXX_CXXABI_H__ # define FMT_HAS_ABI_CXA_DEMANGLE # endif #endif #ifdef FMT_CPP_LIB_FILESYSTEM // Use the provided definition. #elif defined(__cpp_lib_filesystem) # define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem #else # define FMT_CPP_LIB_FILESYSTEM 0 #endif #ifdef FMT_CPP_LIB_VARIANT // Use the provided definition. #elif defined(__cpp_lib_variant) # define FMT_CPP_LIB_VARIANT __cpp_lib_variant #else # define FMT_CPP_LIB_VARIANT 0 #endif FMT_BEGIN_NAMESPACE namespace detail { #ifdef FMT_USE_BITINT // Use the provided definition. #elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__) # define FMT_USE_BITINT 1 #else # define FMT_USE_BITINT 0 #endif #if FMT_USE_BITINT FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension") template using bitint = _BitInt(N); template using ubitint = unsigned _BitInt(N); #else template struct bitint {}; template struct ubitint {}; #endif // FMT_USE_BITINT #if FMT_CPP_LIB_FILESYSTEM template auto get_path_string(const std::filesystem::path& p, const std::basic_string& native) { if constexpr (std::is_same_v && std::is_same_v) { return to_utf8(native, to_utf8_error_policy::wtf); } else { return p.string(); } } template void write_escaped_path(basic_memory_buffer& quoted, const std::filesystem::path& p, const std::basic_string& native) { if constexpr (std::is_same_v && std::is_same_v) { auto buf = basic_memory_buffer(); write_escaped_string(std::back_inserter(buf), native); bool valid = to_utf8::convert(quoted, {buf.data(), buf.size()}); FMT_ASSERT(valid, "invalid utf16"); } else if constexpr (std::is_same_v) { write_escaped_string( std::back_inserter(quoted), native); } else { write_escaped_string(std::back_inserter(quoted), p.string()); } } #endif // FMT_CPP_LIB_FILESYSTEM #if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT template 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); formatter, Char> underlying; maybe_set_debug_format(underlying, true); return underlying.format(v, ctx); } #endif #if FMT_CPP_LIB_VARIANT template struct is_variant_like_ : std::false_type {}; template struct is_variant_like_> : std::true_type {}; template class is_variant_formattable { template static auto check(std::index_sequence) -> std::conjunction< is_formattable, Char>...>; public: static constexpr bool value = decltype(check( std::make_index_sequence::value>()))::value; }; #endif // FMT_CPP_LIB_VARIANT #if FMT_USE_RTTI inline auto normalize_libcxx_inline_namespaces(string_view demangled_name_view, char* begin) -> string_view { // Normalization of stdlib inline namespace names. // libc++ inline namespaces. // std::__1::* -> std::* // std::__1::__fs::* -> std::* // libstdc++ inline namespaces. // std::__cxx11::* -> std::* // std::filesystem::__cxx11::* -> std::filesystem::* if (demangled_name_view.starts_with("std::")) { char* to = begin + 5; // std:: for (const char *from = to, *end = begin + demangled_name_view.size(); from < end;) { // This is safe, because demangled_name is NUL-terminated. if (from[0] == '_' && from[1] == '_') { const char* next = from + 1; while (next < end && *next != ':') next++; if (next[0] == ':' && next[1] == ':') { from = next + 2; continue; } } *to++ = *from++; } demangled_name_view = {begin, detail::to_unsigned(to - begin)}; } return demangled_name_view; } template auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out) -> OutputIt { const string_view demangled_name(abi_name_view); for (size_t i = 0; i < demangled_name.size(); ++i) { auto sub = demangled_name; sub.remove_prefix(i); if (sub.starts_with("enum ")) { i += 4; continue; } if (sub.starts_with("class ") || sub.starts_with("union ")) { i += 5; continue; } if (sub.starts_with("struct ")) { i += 6; continue; } if (*sub.begin() != ' ') *out++ = *sub.begin(); } return out; } template auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { # ifdef FMT_HAS_ABI_CXA_DEMANGLE int status = 0; size_t size = 0; std::unique_ptr demangled_name_ptr( abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &free); string_view demangled_name_view; if (demangled_name_ptr) { demangled_name_view = normalize_libcxx_inline_namespaces( demangled_name_ptr.get(), demangled_name_ptr.get()); } else { demangled_name_view = string_view(ti.name()); } return detail::write_bytes(out, demangled_name_view); # elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE) return normalize_msvc_abi_name(ti.name(), out); # elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION) const string_view demangled_name = ti.name(); std::string name_copy(demangled_name.size(), '\0'); // normalize_msvc_abi_name removes class, struct, union etc that MSVC has in // front of types name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()), name_copy.end()); // normalize_libcxx_inline_namespaces removes the inline __1, __2, etc // namespaces libc++ uses for ABI versioning On MSVC ABI + libc++ // environments, we need to eliminate both of them. const string_view normalized_name = normalize_libcxx_inline_namespaces(name_copy, name_copy.data()); return detail::write_bytes(out, normalized_name); # else return detail::write_bytes(out, string_view(ti.name())); # endif } #endif // FMT_USE_RTTI template struct has_flip : std::false_type {}; template struct has_flip().flip())>> : std::true_type {}; template struct is_bit_reference_like { static constexpr bool value = std::is_convertible::value && std::is_nothrow_assignable::value && has_flip::value; }; // Workaround for libc++ incompatibility with C++ standard. // According to the Standard, `bitset::operator[] const` returns bool. #if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD) template struct is_bit_reference_like> { static constexpr bool value = true; }; #endif template struct has_format_as : std::false_type {}; template struct has_format_as()))>> : std::true_type {}; template struct has_format_as_member : std::false_type {}; template struct has_format_as_member< T, void_t::format_as(std::declval()))>> : std::true_type {}; } // namespace detail template auto ptr(const std::unique_ptr& p) -> const void* { return p.get(); } template auto ptr(const std::shared_ptr& p) -> const void* { return p.get(); } #if FMT_CPP_LIB_FILESYSTEM template struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; char path_type_ = 0; public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } FMT_CONSTEXPR auto parse(parse_context& ctx) { auto it = ctx.begin(), end = ctx.end(); if (it == end) return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; Char c = *it; if ((c >= '0' && c <= '9') || c == '{') it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; } if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); return it; } template auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto specs = specs_; auto path_string = !path_type_ ? p.native() : p.generic_string(); detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); if (!debug_) { auto s = detail::get_path_string(p, path_string); return detail::write(ctx.out(), basic_string_view(s), specs); } auto quoted = basic_memory_buffer(); detail::write_escaped_path(quoted, p, path_string); return detail::write(ctx.out(), basic_string_view(quoted.data(), quoted.size()), specs); } }; class path : public std::filesystem::path { public: auto display_string() const -> std::string { const std::filesystem::path& base = *this; return fmt::format(FMT_STRING("{}"), base); } auto system_string() const -> std::string { return string(); } auto generic_display_string() const -> std::string { const std::filesystem::path& base = *this; return fmt::format(FMT_STRING("{:g}"), base); } auto generic_system_string() const -> std::string { return generic_string(); } }; #endif // FMT_CPP_LIB_FILESYSTEM template struct formatter, Char> : nested_formatter, Char> { private: // This is a functor because C++11 doesn't support generic lambdas. struct writer { const std::bitset& bs; template FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { for (auto pos = N; pos > 0; --pos) out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); return out; } }; public: template auto format(const std::bitset& bs, FormatContext& ctx) const -> decltype(ctx.out()) { return this->write_padded(ctx, writer{bs}); } }; template struct formatter : basic_ostream_formatter {}; #ifdef __cpp_lib_optional template struct formatter, Char, std::enable_if_t::value>> { private: formatter, Char> underlying_; static constexpr basic_string_view optional = detail::string_literal{}; static constexpr basic_string_view none = detail::string_literal{}; public: FMT_CONSTEXPR auto parse(parse_context& ctx) { detail::maybe_set_debug_format(underlying_, true); return underlying_.parse(ctx); } template auto format(const std::optional& opt, FormatContext& ctx) const -> decltype(ctx.out()) { if (!opt) return detail::write(ctx.out(), none); auto out = ctx.out(); out = detail::write(out, optional); ctx.advance_to(out); out = underlying_.format(*opt, ctx); return detail::write(out, ')'); } }; #endif // __cpp_lib_optional #ifdef __cpp_lib_expected template struct formatter, Char, std::enable_if_t<(std::is_void::value || is_formattable::value) && is_formattable::value>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const std::expected& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); if (value.has_value()) { out = detail::write(out, "expected("); if constexpr (!std::is_void::value) out = detail::write_escaped_alternative(out, *value, ctx); } else { out = detail::write(out, "unexpected("); out = detail::write_escaped_alternative(out, value.error(), ctx); } *out++ = ')'; return out; } }; template struct formatter, Char, std::enable_if_t::value>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const std::unexpected& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); out = detail::write(out, "unexpected("); out = detail::write_escaped_alternative(out, value.error(), ctx); *out++ = ')'; return out; } }; #endif // __cpp_lib_expected #ifdef __cpp_lib_source_location template <> struct formatter { FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } template auto format(const std::source_location& loc, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); out = detail::write(out, loc.file_name()); out = detail::write(out, ':'); out = detail::write(out, loc.line()); out = detail::write(out, ':'); out = detail::write(out, loc.column()); out = detail::write(out, ": "); out = detail::write(out, loc.function_name()); return out; } }; #endif #if FMT_CPP_LIB_VARIANT template struct is_variant_like { static constexpr bool value = detail::is_variant_like_::value; }; template struct formatter { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const std::monostate&, FormatContext& ctx) const -> decltype(ctx.out()) { return detail::write(ctx.out(), "monostate"); } }; template struct formatter, detail::is_variant_formattable>>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const Variant& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); out = detail::write(out, "variant("); FMT_TRY { std::visit( [&](const auto& v) { out = detail::write_escaped_alternative(out, v, ctx); }, value); } FMT_CATCH(const std::bad_variant_access&) { detail::write(out, "valueless by exception"); } *out++ = ')'; return out; } }; #endif // FMT_CPP_LIB_VARIANT template <> struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { auto it = ctx.begin(), end = ctx.end(); if (it == end) return it; it = detail::parse_align(it, end, specs_); char c = *it; if (it != end && ((c >= '0' && c <= '9') || c == '{')) it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; } if (it != end && *it == 's') { specs_.set_type(presentation_type::string); ++it; } return it; } template FMT_CONSTEXPR20 auto format(const std::error_code& ec, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); auto buf = memory_buffer(); if (specs_.type() == presentation_type::string) { buf.append(ec.message()); } else { buf.append(string_view(ec.category().name())); buf.push_back(':'); detail::write(appender(buf), ec.value()); } auto quoted = memory_buffer(); auto str = string_view(buf.data(), buf.size()); if (debug_) { detail::write_escaped_string(std::back_inserter(quoted), str); str = string_view(quoted.data(), quoted.size()); } return detail::write(ctx.out(), str, specs); } }; #if FMT_USE_RTTI template <> struct formatter { public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return ctx.begin(); } template auto format(const std::type_info& ti, Context& ctx) const -> decltype(ctx.out()) { return detail::write_demangled_name(ctx.out(), ti); } }; #endif // FMT_USE_RTTI template struct formatter< T, char, typename std::enable_if::value>::type> { private: bool with_typename_ = false; public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { auto it = ctx.begin(); auto end = ctx.end(); if (it == end || *it == '}') return it; if (*it == 't') { ++it; with_typename_ = FMT_USE_RTTI != 0; } return it; } template auto format(const std::exception& ex, Context& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); #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())); } }; template struct formatter, Char> : formatter { static_assert(N <= 64, "unsupported _BitInt"); static auto format_as(detail::bitint x) -> long long { return static_cast(x); } template auto format(detail::bitint x, Context& ctx) const -> decltype(ctx.out()) { return formatter::format(format_as(x), ctx); } }; template struct formatter, Char> : formatter { static_assert(N <= 64, "unsupported _BitInt"); static auto format_as(detail::ubitint x) -> ullong { return static_cast(x); } template auto format(detail::ubitint x, Context& ctx) const -> decltype(ctx.out()) { return formatter::format(format_as(x), ctx); } }; // We can't use std::vector::reference and // std::bitset::reference because the compiler can't deduce Allocator and N // in partial specialization. template struct formatter::value>> : formatter { template FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(v, ctx); } }; #ifdef __cpp_lib_byte template struct formatter : formatter { static auto format_as(std::byte b) -> unsigned char { return static_cast(b); } template auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { return formatter::format(format_as(b), ctx); } }; #endif template struct formatter, Char, enable_if_t::value>> : formatter { template auto format(const std::atomic& v, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(v.load(), ctx); } }; #ifdef __cpp_lib_atomic_flag_test template struct formatter : formatter { template auto format(const std::atomic_flag& v, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(v.test(), ctx); } }; #endif // __cpp_lib_atomic_flag_test template struct is_tuple_like; template struct is_tuple_like> : std::false_type {}; template struct formatter, Char> { private: detail::dynamic_format_specs specs_; template FMT_CONSTEXPR auto do_format(const std::complex& c, detail::dynamic_format_specs& specs, FormatContext& ctx, OutputIt out) const -> OutputIt { if (c.real() != 0) { *out++ = Char('('); out = detail::write(out, c.real(), specs, ctx.locale()); specs.set_sign(sign::plus); out = detail::write(out, c.imag(), specs, ctx.locale()); if (!detail::isfinite(c.imag())) *out++ = Char(' '); *out++ = Char('i'); *out++ = Char(')'); return out; } out = detail::write(out, c.imag(), specs, ctx.locale()); if (!detail::isfinite(c.imag())) *out++ = Char(' '); *out++ = Char('i'); return out; } public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type_constant::value); } template auto format(const std::complex& c, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; if (specs.dynamic()) { detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, ctx); detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs.precision_ref, ctx); } if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); auto buf = basic_memory_buffer(); auto outer_specs = format_specs(); outer_specs.width = specs.width; outer_specs.copy_fill_from(specs); outer_specs.set_align(specs.align()); specs.width = 0; specs.set_fill({}); specs.set_align(align::none); do_format(c, specs, ctx, basic_appender(buf)); return detail::write(ctx.out(), basic_string_view(buf.data(), buf.size()), outer_specs); } }; template struct formatter, Char, // Guard against format_as because reference_wrapper is // implicitly convertible to T&. enable_if_t, Char>::value && !detail::has_format_as::value && !detail::has_format_as_member::value>> : formatter, Char> { template auto format(std::reference_wrapper ref, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter, Char>::format(ref.get(), ctx); } }; FMT_END_NAMESPACE #endif // FMT_STD_H_ ================================================ FILE: include/fmt/xchar.h ================================================ // Formatting library for C++ - optional wchar_t and exotic character support // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_XCHAR_H_ #define FMT_XCHAR_H_ #include "color.h" #include "format.h" #include "ostream.h" #include "ranges.h" #ifndef FMT_MODULE # include # if FMT_USE_LOCALE # include # endif #endif FMT_BEGIN_NAMESPACE namespace detail { template using is_exotic_char = bool_constant::value>; template struct format_string_char {}; template struct format_string_char< S, void_t())))>> { using type = char_t; }; template struct format_string_char< S, enable_if_t::value>> { using type = typename S::char_type; }; template using format_string_char_t = typename format_string_char::type; inline auto write_loc(basic_appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool { #if FMT_USE_LOCALE auto& numpunct = std::use_facet>(loc.get()); auto separator = std::wstring(); auto grouping = numpunct.grouping(); if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep()); return value.visit(loc_writer{out, specs, separator, grouping, {}}); #endif return false; } template void vformat_to(buffer& buf, basic_string_view fmt, basic_format_args> args, locale_ref loc = {}) { static_assert(!std::is_same::value, ""); auto out = basic_appender(buf); parse_format_string( fmt, format_handler{parse_context(fmt), {out, args, loc}}); } } // namespace detail FMT_BEGIN_EXPORT using wstring_view = basic_string_view; using wformat_parse_context = parse_context; using wformat_context = buffered_context; using wformat_args = basic_format_args; using wmemory_buffer = basic_memory_buffer; template struct basic_fstring { private: basic_string_view str_; static constexpr int num_static_named_args = detail::count_static_named_args(); using checker = detail::format_string_checker< Char, static_cast(sizeof...(T)), num_static_named_args, num_static_named_args != detail::count_named_args()>; using arg_pack = detail::arg_pack; public: using t = basic_fstring; template >::value)> FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) { if (FMT_USE_CONSTEVAL) detail::parse_format_string(s, checker(s, arg_pack())); } template ::value&& std::is_same::value)> FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) { FMT_CONSTEXPR auto sv = basic_string_view(S()); FMT_CONSTEXPR int ignore = (parse_format_string(sv, checker(sv, arg_pack())), 0); detail::ignore_unused(ignore); } basic_fstring(runtime_format_string fmt) : str_(fmt.str) {} FMT_DEPRECATED operator basic_string_view() const { return str_; } auto get() const -> basic_string_view { return str_; } }; template using basic_format_string = basic_fstring; template using wformat_string = typename basic_format_string::t; inline auto runtime(wstring_view s) -> runtime_format_string { return {{s}}; } template constexpr auto make_wformat_args(T&... args) -> decltype(fmt::make_format_args(args...)) { return fmt::make_format_args(args...); } #if !FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg { return {s}; } } // namespace literals #endif template auto arg(const wchar_t* name, const T& arg) -> named_arg { return {name, arg}; } template ()))::value_type, FMT_ENABLE_IF(detail::is_exotic_char::value)> auto join(It begin, Sentinel end, S&& sep) -> join_view { return {begin, end, detail::to_string_view(sep)}; } template ()))::value_type, FMT_ENABLE_IF(detail::is_exotic_char::value && !is_tuple_like::value)> auto join(Range&& range, S&& sep) -> join_view { return {std::begin(range), std::end(range), detail::to_string_view(sep)}; } template FMT_DEPRECATED auto join(std::initializer_list list, wstring_view sep) -> join_view { return join(std::begin(list), std::end(list), sep); } template ()))::value_type, FMT_ENABLE_IF(detail::is_exotic_char::value&& is_tuple_like::value)> auto join(const Tuple& tuple, S&& sep) -> tuple_join_view { return {tuple, detail::to_string_view(sep)}; } template ::value)> auto vformat(basic_string_view fmt, basic_format_args> args) -> std::basic_string { auto buf = basic_memory_buffer(); detail::vformat_to(buf, fmt, args); return {buf.data(), buf.size()}; } template auto format(wformat_string fmt, T&&... args) -> std::wstring { return vformat(fmt.get(), fmt::make_wformat_args(args...)); } template auto format_to(OutputIt out, wformat_string fmt, T&&... args) -> OutputIt { return vformat_to(out, fmt.get(), fmt::make_wformat_args(args...)); } // Pass char_t as a default template parameter instead of using // std::basic_string> to reduce the symbol size. template , FMT_ENABLE_IF(!std::is_same::value && !std::is_same::value)> auto format(const S& fmt, T&&... args) -> std::basic_string { return vformat(detail::to_string_view(fmt), fmt::make_format_args>(args...)); } template , FMT_ENABLE_IF(detail::is_exotic_char::value)> inline auto vformat(locale_ref loc, const S& fmt, basic_format_args> args) -> std::basic_string { auto buf = basic_memory_buffer(); detail::vformat_to(buf, detail::to_string_view(fmt), args, loc); return {buf.data(), buf.size()}; } template , FMT_ENABLE_IF(detail::is_exotic_char::value)> inline auto format(locale_ref loc, const S& fmt, T&&... args) -> std::basic_string { return vformat(loc, detail::to_string_view(fmt), fmt::make_format_args>(args...)); } template , FMT_ENABLE_IF(detail::is_output_iterator::value&& detail::is_exotic_char::value)> auto vformat_to(OutputIt out, const S& fmt, basic_format_args> args) -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, detail::to_string_view(fmt), args); return detail::get_iterator(buf, out); } template , FMT_ENABLE_IF(detail::is_output_iterator::value && !std::is_same::value && !std::is_same::value)> inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { return vformat_to(out, detail::to_string_view(fmt), fmt::make_format_args>(args...)); } template , FMT_ENABLE_IF(detail::is_output_iterator::value&& detail::is_exotic_char::value)> inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt, basic_format_args> args) -> OutputIt { auto&& buf = detail::get_buffer(out); vformat_to(buf, detail::to_string_view(fmt), args, loc); return detail::get_iterator(buf, out); } template , bool enable = detail::is_output_iterator::value && detail::is_exotic_char::value> inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args) -> typename std::enable_if::type { return vformat_to(out, loc, detail::to_string_view(fmt), fmt::make_format_args>(args...)); } template ::value&& detail::is_exotic_char::value)> inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view fmt, basic_format_args> args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); detail::vformat_to(buf, fmt, args); return {buf.out(), buf.count()}; } template , FMT_ENABLE_IF(detail::is_output_iterator::value&& detail::is_exotic_char::value)> inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) -> format_to_n_result { return vformat_to_n(out, n, fmt::basic_string_view(fmt), fmt::make_format_args>(args...)); } template , FMT_ENABLE_IF(detail::is_exotic_char::value)> inline auto formatted_size(const S& fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer(); detail::vformat_to(buf, detail::to_string_view(fmt), fmt::make_format_args>(args...)); return buf.count(); } inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { auto buf = wmemory_buffer(); detail::vformat_to(buf, fmt, args); buf.push_back(L'\0'); if (std::fputws(buf.data(), f) == -1) FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } inline void vprint(wstring_view fmt, wformat_args args) { vprint(stdout, fmt, args); } template void print(std::FILE* f, wformat_string fmt, T&&... args) { return vprint(f, fmt.get(), fmt::make_wformat_args(args...)); } template void print(wformat_string fmt, T&&... args) { return vprint(fmt.get(), fmt::make_wformat_args(args...)); } template void println(std::FILE* f, wformat_string fmt, T&&... args) { return print(f, L"{}\n", fmt::format(fmt, std::forward(args)...)); } template void println(wformat_string fmt, T&&... args) { return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); } inline auto vformat(text_style ts, wstring_view fmt, wformat_args args) -> std::wstring { auto buf = wmemory_buffer(); detail::vformat_to(buf, ts, fmt, args); return {buf.data(), buf.size()}; } template inline auto format(text_style ts, wformat_string fmt, T&&... args) -> std::wstring { return fmt::vformat(ts, fmt.get(), fmt::make_wformat_args(args...)); } inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { auto buffer = basic_memory_buffer(); detail::vformat_to(buffer, fmt, args); detail::write_buffer(os, buffer); } template void print(std::wostream& os, wformat_string fmt, T&&... args) { vprint(os, fmt.get(), fmt::make_format_args>(args...)); } template void println(std::wostream& os, wformat_string fmt, T&&... args) { print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); } /// Converts `value` to `std::wstring` using the default format for type `T`. template inline auto to_wstring(const T& value) -> std::wstring { return format(FMT_STRING(L"{}"), value); } FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_XCHAR_H_ ================================================ FILE: src/fmt-c.cc ================================================ // Formatting library for C++ - the C API // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #include "fmt/fmt-c.h" #include extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt, const fmt_arg* args, size_t num_args) { constexpr size_t max_args = 16; if (num_args > max_args) return fmt_error_invalid_arg; fmt::basic_format_arg format_args[max_args]; for (size_t i = 0; i < num_args; ++i) { switch (args[i].type) { case fmt_int: format_args[i] = args[i].value.int_value; break; case fmt_uint: format_args[i] = args[i].value.uint_value; break; case fmt_bool: format_args[i] = args[i].value.bool_value; break; case fmt_char: format_args[i] = args[i].value.char_value; break; case fmt_float: format_args[i] = args[i].value.float_value; break; case fmt_double: format_args[i] = args[i].value.double_value; break; case fmt_long_double: format_args[i] = args[i].value.long_double_value; break; case fmt_cstring: format_args[i] = args[i].value.cstring; break; case fmt_pointer: format_args[i] = args[i].value.pointer; break; default: return fmt_error_invalid_arg; } } try { auto result = fmt::vformat_to_n( buffer, size, fmt, fmt::format_args(format_args, static_cast(num_args))); return static_cast(result.size); } catch (...) { } return fmt_error; } ================================================ FILE: src/fmt.cc ================================================ module; #define FMT_MODULE #ifdef _MSVC_LANG # define FMT_CPLUSPLUS _MSVC_LANG #else # define FMT_CPLUSPLUS __cplusplus #endif // Put all implementation-provided headers into the global module fragment // to prevent attachment to this module. #ifndef FMT_IMPORT_STD # include # include # include # include # include # include # include # include # include # include # include # include # if FMT_CPLUSPLUS > 202002L # include # endif # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #else # include # include # include # include # include # include #endif #include #include #include #if __has_include() # include #endif #if defined(_MSC_VER) || defined(__MINGW32__) # include #endif #if defined __APPLE__ || defined(__FreeBSD__) # include #endif #if __has_include() # include #endif #if (__has_include() || defined(__APPLE__) || \ defined(__linux__)) && \ (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) # include # include # include # ifndef _WIN32 # include # else # include # endif #endif #ifdef _WIN32 # if defined(__GLIBCXX__) # include # include # endif # define WIN32_LEAN_AND_MEAN # include #endif export module fmt; #ifdef FMT_IMPORT_STD import std; #endif #define FMT_EXPORT export #define FMT_BEGIN_EXPORT export { #define FMT_END_EXPORT } // If you define FMT_ATTACH_TO_GLOBAL_MODULE // - all declarations are detached from module 'fmt' // - the module behaves like a traditional static library, too // - all library symbols are mangled traditionally // - you can mix TUs with either importing or #including the {fmt} API #ifdef FMT_ATTACH_TO_GLOBAL_MODULE extern "C++" { #endif #ifndef FMT_OS # define FMT_OS 1 #endif // All library-provided declarations and definitions must be in the module // purview to be exported. #include "fmt/args.h" #include "fmt/chrono.h" #include "fmt/color.h" #include "fmt/compile.h" #include "fmt/format.h" #if FMT_OS # include "fmt/os.h" #endif #include "fmt/ostream.h" #include "fmt/printf.h" #include "fmt/ranges.h" #include "fmt/std.h" #include "fmt/xchar.h" #ifdef FMT_ATTACH_TO_GLOBAL_MODULE } #endif // gcc doesn't yet implement private module fragments #if !FMT_GCC_VERSION module :private; #endif #ifdef FMT_ATTACH_TO_GLOBAL_MODULE extern "C++" { #endif #if FMT_HAS_INCLUDE("format.cc") # include "format.cc" #endif #if FMT_OS && FMT_HAS_INCLUDE("os.cc") # include "os.cc" #endif #ifdef FMT_ATTACH_TO_GLOBAL_MODULE } #endif ================================================ FILE: src/format.cc ================================================ // Formatting library for C++ // // Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #include "fmt/format-inl.h" FMT_BEGIN_NAMESPACE #if FMT_USE_LOCALE template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED! template FMT_API auto locale_ref::get() const -> std::locale; #endif namespace detail { template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; // Explicit instantiations for char. template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API auto decimal_point_impl(locale_ref) -> char; // DEPRECATED! template FMT_API void buffer::append(const char*, const char*); // Explicit instantiations for wchar_t. template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; // DEPRECATED! template FMT_API void buffer::append(const wchar_t*, const wchar_t*); } // namespace detail FMT_END_NAMESPACE ================================================ FILE: src/os.cc ================================================ // Formatting library for C++ - optional OS-specific functionality // // Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. // Disable bogus MSVC warnings. #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) # define _CRT_SECURE_NO_WARNINGS #endif #include "fmt/os.h" #ifndef FMT_MODULE # include # if FMT_USE_FCNTL # include # include # ifdef _WRS_KERNEL // VxWorks7 kernel # include // getpagesize # endif # ifndef _WIN32 # include # else # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include # endif // _WIN32 # endif // FMT_USE_FCNTL # ifdef _WIN32 # include # endif #endif #ifdef _WIN32 # ifndef S_IRUSR # define S_IRUSR _S_IREAD # endif # ifndef S_IWUSR # define S_IWUSR _S_IWRITE # endif # ifndef S_IRGRP # define S_IRGRP 0 # endif # ifndef S_IWGRP # define S_IWGRP 0 # endif # ifndef S_IROTH # define S_IROTH 0 # endif # ifndef S_IWOTH # define S_IWOTH 0 # endif #endif namespace { #ifdef _WIN32 // Return type of read and write functions. using rwresult = int; // On Windows the count argument to read and write is unsigned, so convert // it from size_t preventing integer overflow. inline unsigned convert_rwcount(size_t count) { return count <= UINT_MAX ? static_cast(count) : UINT_MAX; } class system_message { system_message(const system_message&) = delete; void operator=(const system_message&) = delete; unsigned long result_; wchar_t* message_; static bool is_whitespace(wchar_t c) noexcept { return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; } public: explicit system_message(unsigned long error_code) : result_(0), message_(nullptr) { result_ = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&message_), 0, nullptr); if (result_ != 0) { while (result_ != 0 && is_whitespace(message_[result_ - 1])) { --result_; } } } ~system_message() { LocalFree(message_); } explicit operator bool() const noexcept { return result_ != 0; } operator fmt::basic_string_view() const noexcept { return fmt::basic_string_view(message_, result_); } }; class utf8_system_category final : public std::error_category { public: const char* name() const noexcept override { return "system"; } std::string message(int error_code) const override { auto&& msg = system_message(error_code); if (msg) { auto utf8_message = fmt::detail::to_utf8(); if (utf8_message.convert(msg)) { return utf8_message.str(); } } return "unknown error"; } }; #elif FMT_USE_FCNTL // Return type of read and write functions. using rwresult = ssize_t; inline auto convert_rwcount(size_t count) -> size_t { return count; } #endif } // namespace FMT_BEGIN_NAMESPACE #ifdef _WIN32 FMT_API const std::error_category& system_category() noexcept { static const utf8_system_category category; return category; } std::system_error vwindows_error(int err_code, string_view format_str, format_args args) { auto ec = std::error_code(err_code, system_category()); return std::system_error(ec, vformat(format_str, args)); } void detail::format_windows_error(detail::buffer& out, int error_code, const char* message) noexcept { FMT_TRY { auto&& msg = system_message(error_code); if (msg) { auto utf8_message = to_utf8(); if (utf8_message.convert(msg)) { fmt::format_to(appender(out), FMT_STRING("{}: {}"), message, string_view(utf8_message)); return; } } } FMT_CATCH(...) {} format_error_code(out, error_code, message); } void report_windows_error(int error_code, const char* message) noexcept { do_report_error(detail::format_windows_error, error_code, message); } #endif // _WIN32 buffered_file::~buffered_file() noexcept { if (file_ && FMT_SYSTEM(fclose(file_)) != 0) report_system_error(errno, "cannot close file"); } buffered_file::buffered_file(cstring_view filename, cstring_view mode) { FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), nullptr); if (!file_) FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"), filename.c_str())); } void buffered_file::close() { if (!file_) return; int result = FMT_SYSTEM(fclose(file_)); file_ = nullptr; if (result != 0) FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); } auto buffered_file::descriptor() const -> int { #ifdef FMT_HAS_SYSTEM // fileno is a macro on OpenBSD. # ifdef fileno # undef fileno # endif int fd = FMT_POSIX_CALL(fileno(file_)); #elif defined(_WIN32) int fd = _fileno(file_); #else int fd = fileno(file_); #endif if (fd == -1) FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor"))); return fd; } #if FMT_USE_FCNTL # ifdef _WIN32 using mode_t = int; # endif constexpr mode_t default_open_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; file::file(cstring_view path, int oflag) { # if defined(_WIN32) && !defined(__MINGW32__) fd_ = -1; auto converted = detail::utf8_to_utf16(string_view(path.c_str())); *this = file::open_windows_file(converted.c_str(), oflag); # else FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode))); if (fd_ == -1) FMT_THROW( system_error(errno, FMT_STRING("cannot open file {}"), path.c_str())); # endif } file::~file() noexcept { // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) report_system_error(errno, "cannot close file"); } void file::close() { if (fd_ == -1) return; // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html int result = FMT_POSIX_CALL(close(fd_)); fd_ = -1; if (result != 0) FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); } auto file::size() const -> long long { # ifdef _WIN32 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT // is less than 0x0500 as is the case with some default MinGW builds. // Both functions support large file sizes. DWORD size_upper = 0; HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); if (size_lower == INVALID_FILE_SIZE) { DWORD error = GetLastError(); if (error != NO_ERROR) FMT_THROW(windows_error(error, "cannot get file size")); } unsigned long long long_size = size_upper; return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; # else using Stat = struct stat; Stat file_stat = Stat(); if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes"))); static_assert(sizeof(long long) >= sizeof(file_stat.st_size), "return type of file::size is not large enough"); return file_stat.st_size; # endif } auto file::read(void* buffer, size_t count) -> size_t { rwresult result = 0; FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); if (result < 0) FMT_THROW(system_error(errno, FMT_STRING("cannot read from file"))); return detail::to_unsigned(result); } auto file::write(const void* buffer, size_t count) -> size_t { rwresult result = 0; FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); if (result < 0) FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); return detail::to_unsigned(result); } auto file::dup(int fd) -> file { // Don't retry as dup doesn't return EINTR. // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html int new_fd = FMT_POSIX_CALL(dup(fd)); if (new_fd == -1) FMT_THROW(system_error( errno, FMT_STRING("cannot duplicate file descriptor {}"), fd)); return file(new_fd); } void file::dup2(int fd) { int result = 0; FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) { FMT_THROW(system_error( errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_, fd)); } } void file::dup2(int fd, std::error_code& ec) noexcept { int result = 0; FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) ec = std::error_code(errno, std::generic_category()); } auto file::fdopen(const char* mode) -> buffered_file { // Don't retry as fdopen doesn't return EINTR. # if defined(__MINGW32__) && defined(_POSIX_) FILE* f = ::fdopen(fd_, mode); # else FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); # endif if (!f) { FMT_THROW(system_error( errno, FMT_STRING("cannot associate stream with file descriptor"))); } buffered_file bf(f); fd_ = -1; return bf; } # if defined(_WIN32) && !defined(__MINGW32__) file file::open_windows_file(wcstring_view path, int oflag) { int fd = -1; auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode); if (fd == -1) { FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"), detail::to_utf8(path.c_str()).c_str())); } return file(fd); } # endif pipe::pipe() { int fds[2] = {}; # ifdef _WIN32 // Make the default pipe capacity same as on Linux 2.6.11+. enum { DEFAULT_CAPACITY = 65536 }; int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); # else // Don't retry as the pipe function doesn't return EINTR. // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html int result = FMT_POSIX_CALL(pipe(fds)); # endif if (result != 0) FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe"))); // The following assignments don't throw. read_end = file(fds[0]); write_end = file(fds[1]); } # if !defined(__MSDOS__) auto getpagesize() -> long { # ifdef _WIN32 SYSTEM_INFO si; GetSystemInfo(&si); return si.dwPageSize; # else # ifdef _WRS_KERNEL long size = FMT_POSIX_CALL(getpagesize()); # else long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); # endif if (size < 0) FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size"))); return size; # endif } # endif void ostream::grow(buffer& buf, size_t) { if (buf.size() == buf.capacity()) static_cast(buf).flush(); } ostream::ostream(cstring_view path, const detail::ostream_params& params) : buffer(grow), file_(path, params.oflag) { set(new char[params.buffer_size], params.buffer_size); } ostream::ostream(ostream&& other) noexcept : buffer(grow, other.data(), other.size(), other.capacity()), file_(std::move(other.file_)) { other.clear(); other.set(nullptr, 0); } ostream::~ostream() { flush(); delete[] data(); } #endif // FMT_USE_FCNTL FMT_END_NAMESPACE ================================================ FILE: support/Android.mk ================================================ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := fmt_static LOCAL_MODULE_FILENAME := libfmt LOCAL_SRC_FILES := ../src/format.cc LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS += -std=c++11 -fexceptions include $(BUILD_STATIC_LIBRARY) ================================================ FILE: support/AndroidManifest.xml ================================================ ================================================ FILE: support/C++.sublime-syntax ================================================ %YAML 1.2 --- # http://www.sublimetext.com/docs/3/syntax.html name: C++ (fmt) comment: I don't think anyone uses .hp. .cp tends to be paired with .h. (I could be wrong. :) -- chris file_extensions: - cpp - cc - cp - cxx - c++ - C - h - hh - hpp - hxx - h++ - inl - ipp first_line_match: '-\*- C\+\+ -\*-' scope: source.c++ variables: identifier: \b[[:alpha:]_][[:alnum:]_]*\b # upper and lowercase macro_identifier: \b[[:upper:]_][[:upper:][:digit:]_]{2,}\b # only uppercase, at least 3 chars path_lookahead: '(?:::\s*)?(?:{{identifier}}\s*::\s*)*(?:template\s+)?{{identifier}}' operator_method_name: '\boperator\s*(?:[-+*/%^&|~!=<>]|[-+*/%^&|=!<>]=|<<=?|>>=?|&&|\|\||\+\+|--|,|->\*?|\(\)|\[\]|""\s*{{identifier}})' casts: 'const_cast|dynamic_cast|reinterpret_cast|static_cast' operator_keywords: 'and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|xor|xor_eq|noexcept' control_keywords: 'break|case|catch|continue|default|do|else|for|goto|if|_Pragma|return|switch|throw|try|while' memory_operators: 'new|delete' basic_types: 'asm|__asm__|auto|bool|_Bool|char|_Complex|double|float|_Imaginary|int|long|short|signed|unsigned|void' before_tag: 'struct|union|enum\s+class|enum\s+struct|enum|class' declspec: '__declspec\(\s*\w+(?:\([^)]+\))?\s*\)' storage_classes: 'static|export|extern|friend|explicit|virtual|register|thread_local' type_qualifier: 'const|constexpr|mutable|typename|volatile' compiler_directive: 'inline|restrict|__restrict__|__restrict' visibility_modifiers: 'private|protected|public' other_keywords: 'typedef|nullptr|{{visibility_modifiers}}|static_assert|sizeof|using|typeid|alignof|alignas|namespace|template' modifiers: '{{storage_classes}}|{{type_qualifier}}|{{compiler_directive}}' non_angle_brackets: '(?=<<|<=)' regular: '[^(){}&;*^%=<>-]*' paren_open: (?:\( paren_close: '\))?' generic_open: (?:< generic_close: '>)?' balance_parentheses: '{{regular}}{{paren_open}}{{regular}}{{paren_close}}{{regular}}' generic_lookahead: <{{regular}}{{generic_open}}{{regular}}{{generic_open}}{{regular}}{{generic_close}}\s*{{generic_close}}{{balance_parentheses}}> data_structures_forward_decl_lookahead: '(\s+{{macro_identifier}})*\s*(:\s*({{path_lookahead}}|{{visibility_modifiers}}|,|\s|<[^;]*>)+)?;' non_func_keywords: 'if|for|switch|while|decltype|sizeof|__declspec|__attribute__|typeid|alignof|alignas|static_assert' format_spec: |- (?x: (?:.? [<>=^])? # fill align [ +-]? # sign \#? # alternate form # technically, octal and hexadecimal integers are also supported as 'width', but rarely used \d* # width ,? # thousands separator (?:\.\d+)? # precision [bcdeEfFgGnosxX%]? # type ) contexts: main: - include: preprocessor-global - include: global ############################################################################# # Reusable contexts # # The follow contexts are currently constructed to be reused in the # Objetive-C++ syntax. They are specifically constructed to not push into # sub-contexts, which ensures that Objective-C++ code isn't accidentally # lexed as plain C++. # # The "unique-*" contexts are additions that C++ makes over C, and thus can # be directly reused in Objective-C++ along with contexts from Objective-C # and C. ############################################################################# unique-late-expressions: # This is highlighted after all of the other control keywords # to allow operator overloading to be lexed properly - match: \boperator\b scope: keyword.control.c++ unique-modifiers: - match: \b({{modifiers}})\b scope: storage.modifier.c++ unique-variables: - match: \bthis\b scope: variable.language.c++ # common C++ instance var naming idiom -- fMemberName - match: '\b(f|m)[[:upper:]]\w*\b' scope: variable.other.readwrite.member.c++ # common C++ instance var naming idiom -- m_member_name - match: '\bm_[[:alnum:]_]+\b' scope: variable.other.readwrite.member.c++ unique-constants: - match: \bnullptr\b scope: constant.language.c++ unique-keywords: - match: \busing\b scope: keyword.control.c++ - match: \bbreak\b scope: keyword.control.flow.break.c++ - match: \bcontinue\b scope: keyword.control.flow.continue.c++ - match: \bgoto\b scope: keyword.control.flow.goto.c++ - match: \breturn\b scope: keyword.control.flow.return.c++ - match: \bthrow\b scope: keyword.control.flow.throw.c++ - match: \b({{control_keywords}})\b scope: keyword.control.c++ - match: '\bdelete\b(\s*\[\])?|\bnew\b(?!])' scope: keyword.control.c++ - match: \b({{operator_keywords}})\b scope: keyword.operator.word.c++ unique-types: - match: \b(char16_t|char32_t|wchar_t|nullptr_t)\b scope: storage.type.c++ - match: \bclass\b scope: storage.type.c++ unique-strings: - match: '((?:L|u8|u|U)?R)("([^\(\)\\ ]{0,16})\()' captures: 1: storage.type.string.c++ 2: punctuation.definition.string.begin.c++ push: - meta_scope: string.quoted.double.c++ - match: '\)\3"' scope: punctuation.definition.string.end.c++ pop: true - match: '\{\{|\}\}' scope: constant.character.escape.c++ - include: formatting-syntax unique-numbers: - match: |- (?x) (?: # floats (?: (?:\b\d(?:[\d']*\d)?\.\d(?:[\d']*\d)?|\B\.\d(?:[\d']*\d)?)(?:[Ee][+-]?\d(?:[\d']*\d)?)?(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b | (?:\b\d(?:[\d']*\d)?\.)(?:\B|(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))\b|(?:[Ee][+-]?\d(?:[\d']*\d)?)(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b) | \b\d(?:[\d']*\d)?(?:[Ee][+-]?\d(?:[\d']*\d)?)(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b ) | # ints \b(?: (?: # dec [1-9](?:[\d']*\d)? | # oct 0(?:[0-7']*[0-7])? | # hex 0[Xx][\da-fA-F](?:[\da-fA-F']*[\da-fA-F])? | # bin 0[Bb][01](?:[01']*[01])? ) # int suffixes (?:(?:l{1,2}|L{1,2})[uU]?|[uU](?:l{0,2}|L{0,2})|(?:i[fl]?|h|min|[mun]?s|_\w*))?)\b ) (?!\.) # Number must not be followed by a decimal point scope: constant.numeric.c++ identifiers: - match: '{{identifier}}\s*(::)\s*' captures: 1: punctuation.accessor.c++ - match: '(?:(::)\s*)?{{identifier}}' captures: 1: punctuation.accessor.c++ function-specifiers: - match: \b(const|final|noexcept|override)\b scope: storage.modifier.c++ ############################################################################# # The following are C++-specific contexts that should not be reused. This is # because they push into subcontexts and use variables that are C++-specific. ############################################################################# ## Common context layout global: - match: '(?=\btemplate\b)' push: - include: template - match: (?=\S) set: global-modifier - include: namespace - include: keywords-angle-brackets - match: '(?={{path_lookahead}}\s*<)' push: global-modifier # Take care of comments just before a function definition. - match: /\* scope: punctuation.definition.comment.c push: - - match: \s*(?=\w) set: global-modifier - match: "" pop: true - - meta_scope: comment.block.c - match: \*/ scope: punctuation.definition.comment.c pop: true - include: early-expressions - match: ^\s*\b(extern)(?=\s+"C(\+\+)?") scope: storage.modifier.c++ push: - include: comments - include: strings - match: '\{' scope: punctuation.section.block.begin.c++ set: - meta_scope: meta.extern-c.c++ - match: '^\s*(#\s*ifdef)\s*__cplusplus\s*' scope: meta.preprocessor.c++ captures: 1: keyword.control.import.c++ set: - match: '\}' scope: punctuation.section.block.end.c++ pop: true - include: preprocessor-global - include: global - match: '\}' scope: punctuation.section.block.end.c++ pop: true - include: preprocessor-global - include: global - match: (?=\S) set: global-modifier - match: ^\s*(?=\w) push: global-modifier - include: late-expressions statements: - include: preprocessor-statements - include: scope:source.c#label - include: expressions expressions: - include: early-expressions - include: late-expressions early-expressions: - include: early-expressions-before-generic-type - include: generic-type - include: early-expressions-after-generic-type early-expressions-before-generic-type: - include: preprocessor-expressions - include: comments - include: case-default - include: typedef - include: keywords-angle-brackets - include: keywords-parens - include: keywords - include: numbers # Prevent a '<' from getting scoped as the start of another template # parameter list, if in reality a less-than-or-equals sign is meant. - match: <= scope: keyword.operator.comparison.c early-expressions-after-generic-type: - include: members-arrow - include: operators - include: members-dot - include: strings - include: parens - include: brackets - include: block - include: variables - include: constants - match: ',' scope: punctuation.separator.c++ - match: '\)|\}' scope: invalid.illegal.stray-bracket-end.c++ expressions-minus-generic-type: - include: early-expressions-before-generic-type - include: angle-brackets - include: early-expressions-after-generic-type - include: late-expressions expressions-minus-generic-type-function-call: - include: early-expressions-before-generic-type - include: angle-brackets - include: early-expressions-after-generic-type - include: late-expressions-before-function-call - include: identifiers - match: ';' scope: punctuation.terminator.c++ late-expressions: - include: late-expressions-before-function-call - include: function-call - include: identifiers - match: ';' scope: punctuation.terminator.c++ late-expressions-before-function-call: - include: unique-late-expressions - include: modifiers-parens - include: modifiers - include: types expressions-minus-function-call: - include: early-expressions - include: late-expressions-before-function-call - include: identifiers - match: ';' scope: punctuation.terminator.c++ comments: - include: scope:source.c#comments operators: - include: scope:source.c#operators modifiers: - include: unique-modifiers - include: scope:source.c#modifiers variables: - include: unique-variables - include: scope:source.c#variables constants: - include: unique-constants - include: scope:source.c#constants keywords: - include: unique-keywords - include: scope:source.c#keywords types: - include: unique-types - include: types-parens - include: scope:source.c#types strings: - include: unique-strings - match: '(L|u8|u|U)?(")' captures: 1: storage.type.string.c++ 2: punctuation.definition.string.begin.c++ push: - meta_scope: string.quoted.double.c++ - match: '"' scope: punctuation.definition.string.end.c++ pop: true - include: scope:source.c#string_escaped_char - match: |- (?x)% (\d+\$)? # field (argument #) [#0\- +']* # flags [,;:_]? # separator character (AltiVec) ((-?\d+)|\*(-?\d+\$)?)? # minimum field width (\.((-?\d+)|\*(-?\d+\$)?)?)? # precision (hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)? # length modifier (\[[^\]]+\]|[am]s|[diouxXDOUeEfFgGaACcSspn%]) # conversion type scope: constant.other.placeholder.c++ - match: '\{\{|\}\}' scope: constant.character.escape.c++ - include: formatting-syntax - include: scope:source.c#strings formatting-syntax: # https://docs.python.org/3.6/library/string.html#formatstrings - match: |- # simple form (?x) (\{) (?: [\w.\[\]]+)? # field_name ( ! [ars])? # conversion ( : (?:{{format_spec}}| # format_spec OR [^}%]*%.[^}]*) # any format-like string )? (\}) scope: constant.other.placeholder.c++ captures: 1: punctuation.definition.placeholder.begin.c++ 2: storage.modifier.c++onversion.c++ 3: constant.other.format-spec.c++ 4: punctuation.definition.placeholder.end.c++ - match: \{(?=[^\}"']+\{[^"']*\}) # complex (nested) form scope: punctuation.definition.placeholder.begin.c++ push: - meta_scope: constant.other.placeholder.c++ - match: \} scope: punctuation.definition.placeholder.end.c++ pop: true - match: '[\w.\[\]]+' - match: '![ars]' scope: storage.modifier.conversion.c++ - match: ':' push: - meta_scope: meta.format-spec.c++ constant.other.format-spec.c++ - match: (?=\}) pop: true - include: formatting-syntax numbers: - include: unique-numbers - include: scope:source.c#numbers ## C++-specific contexts case-default: - match: '\b(default|case)\b' scope: keyword.control.c++ push: - match: (?=[);,]) pop: true - match: ':' scope: punctuation.separator.c++ pop: true - include: expressions modifiers-parens: - match: '\b(alignas)\b\s*(\()' captures: 1: storage.modifier.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions - match: \b(__attribute__)\s*(\(\() captures: 1: storage.modifier.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push : - meta_scope: meta.attribute.c++ - meta_content_scope: meta.group.c++ - include: parens - include: strings - match: \)\) scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - match: \b(__declspec)(\() captures: 1: storage.modifier.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - match: '\b(align|allocate|code_seg|deprecated|property|uuid)\b\s*(\()' captures: 1: storage.modifier.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: numbers - include: strings - match: \b(get|put)\b scope: variable.parameter.c++ - match: ',' scope: punctuation.separator.c++ - match: '=' scope: keyword.operator.assignment.c++ - match: '\b(appdomain|deprecated|dllimport|dllexport|jintrinsic|naked|noalias|noinline|noreturn|nothrow|novtable|process|restrict|safebuffers|selectany|thread)\b' scope: constant.other.c++ types-parens: - match: '\b(decltype)\b\s*(\()' captures: 1: storage.type.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions keywords-angle-brackets: - match: \b({{casts}})\b\s* scope: keyword.operator.word.cast.c++ push: - match: '>' scope: punctuation.section.generic.end.c++ pop: true - match: '<' scope: punctuation.section.generic.begin.c++ push: - match: '(?=>)' pop: true - include: expressions-minus-generic-type-function-call keywords-parens: - match: '\b(alignof|typeid|static_assert|sizeof)\b\s*(\()' captures: 1: keyword.operator.word.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions namespace: - match: '\b(using)\s+(namespace)\s+(?={{path_lookahead}})' captures: 1: keyword.control.c++ 2: keyword.control.c++ push: - include: identifiers - match: '' pop: true - match: '\b(namespace)\s+(?=({{path_lookahead}})?(?!\s*[;,]))' scope: meta.namespace.c++ captures: 1: keyword.control.c++ push: - meta_content_scope: meta.namespace.c++ entity.name.namespace.c++ - include: identifiers - match: '' set: - meta_scope: meta.namespace.c++ - include: comments - match: '=' scope: keyword.operator.alias.c++ - match: '(?=;)' pop: true - match: '\}' scope: meta.block.c++ punctuation.section.block.end.c++ pop: true - match: '\{' scope: punctuation.section.block.begin.c++ push: - meta_scope: meta.block.c++ - match: '(?=\})' pop: true - include: preprocessor-global - include: global - include: expressions template-common: # Exit the template scope if we hit some basic invalid characters. This # helps when a user is in the middle of typing their template types and # prevents re-highlighting the whole file until the next > is found. - match: (?=[{};]) pop: true - include: expressions template: - match: \btemplate\b scope: storage.type.template.c++ push: - meta_scope: meta.template.c++ # Explicitly include comments here at the top, in order to NOT match the # \S lookahead in the case of comments. - include: comments - match: < scope: punctuation.section.generic.begin.c++ set: - meta_content_scope: meta.template.c++ - match: '>' scope: meta.template.c++ punctuation.section.generic.end.c++ pop: true - match: \.{3} scope: keyword.operator.variadic.c++ - match: \b(typename|{{before_tag}})\b scope: storage.type.c++ - include: template # include template here for nested templates - include: template-common - match: (?=\S) set: - meta_content_scope: meta.template.c++ - match: \b({{before_tag}})\b scope: storage.type.c++ - include: template-common generic-type: - match: '(?=(?!template){{path_lookahead}}\s*{{generic_lookahead}}\s*\()' push: - meta_scope: meta.function-call.c++ - match: \btemplate\b scope: storage.type.template.c++ - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' captures: 1: punctuation.accessor.double-colon.c++ 2: punctuation.accessor.double-colon.c++ - match: (?:(::)\s*)?({{identifier}})\s*(<) captures: 1: punctuation.accessor.double-colon.c++ 2: variable.function.c++ 3: punctuation.section.generic.begin.c++ push: - match: '>' scope: punctuation.section.generic.end.c++ pop: true - include: expressions-minus-generic-type-function-call - match: (?:(::)\s*)?({{identifier}})\s*(\() captures: 1: punctuation.accessor.double-colon.c++ 2: variable.function.c++ 3: punctuation.section.group.begin.c++ set: - meta_scope: meta.function-call.c++ - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions - include: angle-brackets - match: '\(' scope: meta.group.c++ punctuation.section.group.begin.c++ set: - meta_scope: meta.function-call.c++ - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions - match: '(?=(?!template){{path_lookahead}}\s*{{generic_lookahead}})' push: - include: identifiers - match: '<' scope: punctuation.section.generic.begin.c++ set: - match: '>' scope: punctuation.section.generic.end.c++ pop: true - include: expressions-minus-generic-type-function-call angle-brackets: - match: '<(?!<)' scope: punctuation.section.generic.begin.c++ push: - match: '>' scope: punctuation.section.generic.end.c++ pop: true - include: expressions-minus-generic-type-function-call block: - match: '\{' scope: punctuation.section.block.begin.c++ push: - meta_scope: meta.block.c++ - match: (?=^\s*#\s*(elif|else|endif)\b) pop: true - match: '\}' scope: punctuation.section.block.end.c++ pop: true - include: statements function-call: - match: (?={{path_lookahead}}\s*\() push: - meta_scope: meta.function-call.c++ - include: scope:source.c#c99 - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' scope: variable.function.c++ captures: 1: punctuation.accessor.c++ 2: punctuation.accessor.c++ - match: '(?:(::)\s*)?{{identifier}}' scope: variable.function.c++ captures: 1: punctuation.accessor.c++ - match: '\(' scope: meta.group.c++ punctuation.section.group.begin.c++ set: - meta_content_scope: meta.function-call.c++ meta.group.c++ - match: '\)' scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions members-inside-function-call: - meta_content_scope: meta.method-call.c++ meta.group.c++ - match: \) scope: meta.method-call.c++ meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions members-after-accessor-junction: # After we've seen an accessor (dot or arrow), this context decides what # kind of entity we're accessing. - include: comments - match: \btemplate\b scope: meta.method-call.c++ storage.type.template.c++ # Guaranteed to be a template member function call after we match this set: - meta_content_scope: meta.method-call.c++ - include: comments - match: '{{identifier}}' scope: variable.function.member.c++ set: - meta_content_scope: meta.method-call.c++ - match: \( scope: meta.group.c++ punctuation.section.group.begin.c++ set: members-inside-function-call - include: comments - include: angle-brackets - match: (?=\S) # safety pop pop: true - match: (?=\S) # safety pop pop: true # Operator overloading - match: '({{operator_method_name}})\s*(\()' captures: 0: meta.method-call.c++ 1: variable.function.member.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ set: members-inside-function-call # Non-templated member function call - match: (~?{{identifier}})\s*(\() captures: 0: meta.method-call.c++ 1: variable.function.member.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ set: members-inside-function-call # Templated member function call - match: (~?{{identifier}})\s*(?={{generic_lookahead}}) captures: 1: variable.function.member.c++ set: - meta_scope: meta.method-call.c++ - match: < scope: punctuation.section.generic.begin.c++ set: - meta_content_scope: meta.method-call.c++ - match: '>' scope: punctuation.section.generic.end.c++ set: - meta_content_scope: meta.method-call.c++ - include: comments - match: \( scope: punctuation.section.group.begin.c++ set: members-inside-function-call - match: (?=\S) # safety pop pop: true - include: expressions # Explicit base-class access - match: ({{identifier}})\s*(::) captures: 1: variable.other.base-class.c++ 2: punctuation.accessor.double-colon.c++ set: members-after-accessor-junction # reset # Just a regular member variable - match: '{{identifier}}' scope: variable.other.readwrite.member.c++ pop: true members-dot: - include: scope:source.c#access-illegal # No lookahead required because members-dot goes after operators in the # early-expressions-after-generic-type context. This means triple dots # (i.e. "..." or "variadic") is attempted first. - match: \. scope: punctuation.accessor.dot.c++ push: members-after-accessor-junction members-arrow: # This needs to be before operators in the # early-expressions-after-generic-type context because otherwise the "->" # from the C language will match. - match: -> scope: punctuation.accessor.arrow.c++ push: members-after-accessor-junction typedef: - match: \btypedef\b scope: storage.type.c++ push: - match: ({{identifier}})?\s*(?=;) captures: 1: entity.name.type.typedef.c++ pop: true - match: \b(struct)\s+({{identifier}})\b captures: 1: storage.type.c++ - include: expressions-minus-generic-type parens: - match: \( scope: punctuation.section.group.begin.c++ push: - meta_scope: meta.group.c++ - match: \) scope: punctuation.section.group.end.c++ pop: true - include: expressions brackets: - match: \[ scope: punctuation.section.brackets.begin.c++ push: - meta_scope: meta.brackets.c++ - match: \] scope: punctuation.section.brackets.end.c++ pop: true - include: expressions function-trailing-return-type: - match: '{{non_angle_brackets}}' pop: true - include: angle-brackets - include: types - include: modifiers-parens - include: modifiers - include: identifiers - match: \*|& scope: keyword.operator.c++ - include: function-trailing-return-type-parens - match: '(?=\S)' pop: true function-trailing-return-type-parens: - match: \( scope: punctuation.section.group.begin.c++ push: - meta_scope: meta.group.c++ - match: \) scope: punctuation.section.group.end.c++ pop: true - include: function-trailing-return-type ## Detection of function and data structure definitions at the global level global-modifier: - include: comments - include: modifiers-parens - include: modifiers # Constructors and destructors don't have a type - match: '(?={{path_lookahead}}\s*::\s*{{identifier}}\s*(\(|$))' set: - meta_content_scope: meta.function.c++ entity.name.function.constructor.c++ - include: identifiers - match: '(?=[^\w\s])' set: function-definition-params - match: '(?={{path_lookahead}}\s*::\s*~{{identifier}}\s*(\(|$))' set: - meta_content_scope: meta.function.c++ entity.name.function.destructor.c++ - include: identifiers - match: '~{{identifier}}' - match: '(?=[^\w\s])' set: function-definition-params # If we see a path ending in :: before a newline, we don't know if it is # a constructor or destructor, or a long return type, so we are just going # to treat it like a regular function. Most likely it is a constructor, # since it doesn't seem most developers would create such a long typename. - match: '(?={{path_lookahead}}\s*::\s*$)' set: - meta_content_scope: meta.function.c++ entity.name.function.c++ - include: identifiers - match: '~{{identifier}}' - match: '(?=[^\w\s])' set: function-definition-params - include: unique-strings - match: '(?=\S)' set: global-type global-type: - include: comments - match: \*|& scope: keyword.operator.c++ - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}}|operator)\b)' pop: true - match: '(?=\s)' set: global-maybe-function # If a class/struct/enum followed by a name that is not a macro or declspec # then this is likely a return type of a function. This is uncommon. - match: |- (?x: ({{before_tag}}) \s+ (?= (?![[:upper:][:digit:]_]+\b|__declspec|{{before_tag}}) {{path_lookahead}} (\s+{{identifier}}\s*\(|\s*[*&]) ) ) captures: 1: storage.type.c++ set: - include: identifiers - match: '' set: global-maybe-function # The previous match handles return types of struct/enum/etc from a func, # there this one exits the context to allow matching an actual struct/class - match: '(?=\b({{before_tag}})\b)' set: data-structures - match: '(?=\b({{casts}})\b\s*<)' pop: true - match: '{{non_angle_brackets}}' pop: true - include: angle-brackets - include: types # Allow a macro call - match: '({{identifier}})\s*(\()(?=[^\)]+\))' captures: 1: variable.function.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_scope: meta.function-call.c++ - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions - match: '(?={{path_lookahead}}\s*\()' set: - include: function-call - match: '' pop: true - include: variables - include: constants - include: identifiers - match: (?=\W) pop: true global-maybe-function: - include: comments # Consume pointer info, macros and any type info that was offset by macros - match: \*|& scope: keyword.operator.c++ - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}})\b)' pop: true - match: '\b({{type_qualifier}})\b' scope: storage.modifier.c++ - match: '{{non_angle_brackets}}' pop: true - include: angle-brackets - include: types - include: modifiers-parens - include: modifiers # All uppercase identifier just before a newline is most likely a macro - match: '[[:upper:][:digit:]_]+\s*$' # Operator overloading - match: '(?=({{path_lookahead}}\s*(?:{{generic_lookahead}})?::\s*)?{{operator_method_name}}\s*(\(|$))' set: - meta_content_scope: meta.function.c++ entity.name.function.c++ - include: identifiers - match: '(?=\s*(\(|$))' set: function-definition-params # Identifier that is not the function name - likely a macro or type - match: '(?={{path_lookahead}}([ \t]+|[*&])(?!\s*(<|::|\(|$)))' push: - include: identifiers - match: '' pop: true # Real function definition - match: '(?={{path_lookahead}}({{generic_lookahead}}({{path_lookahead}})?)\s*(\(|$))' set: [function-definition-params, global-function-identifier-generic] - match: '(?={{path_lookahead}}\s*(\(|$))' set: [function-definition-params, global-function-identifier] - match: '(?={{path_lookahead}}\s*::\s*$)' set: [function-definition-params, global-function-identifier] - match: '(?=\S)' pop: true global-function-identifier-generic: - include: angle-brackets - match: '::' scope: punctuation.accessor.c++ - match: '(?={{identifier}}<.*>\s*\()' push: - meta_content_scope: entity.name.function.c++ - include: identifiers - match: '(?=<)' pop: true - match: '(?={{identifier}}\s*\()' push: - meta_content_scope: entity.name.function.c++ - include: identifiers - match: '' pop: true - match: '(?=\()' pop: true global-function-identifier: - meta_content_scope: entity.name.function.c++ - include: identifiers - match: '(?=\S)' pop: true function-definition-params: - meta_content_scope: meta.function.c++ - include: comments - match: '(?=\()' set: - match: \( scope: meta.function.parameters.c++ meta.group.c++ punctuation.section.group.begin.c++ set: - meta_content_scope: meta.function.parameters.c++ meta.group.c++ - match : \) scope: punctuation.section.group.end.c++ set: function-definition-continue - match: '\bvoid\b' scope: storage.type.c++ - match: '{{identifier}}(?=\s*(\[|,|\)|=))' scope: variable.parameter.c++ - match: '=' scope: keyword.operator.assignment.c++ push: - match: '(?=,|\))' pop: true - include: expressions-minus-generic-type - include: scope:source.c#preprocessor-line-continuation - include: expressions-minus-generic-type - include: scope:source.c#preprocessor-line-continuation - match: (?=\S) pop: true function-definition-continue: - meta_content_scope: meta.function.c++ - include: comments - match: '(?=;)' pop: true - match: '->' scope: punctuation.separator.c++ set: function-definition-trailing-return - include: function-specifiers - match: '=' scope: keyword.operator.assignment.c++ - match: '&' scope: keyword.operator.c++ - match: \b0\b scope: constant.numeric.c++ - match: \b(default|delete)\b scope: storage.modifier.c++ - match: '(?=\{)' set: function-definition-body - match: '(?=\S)' pop: true function-definition-trailing-return: - include: comments - match: '(?=;)' pop: true - match: '(?=\{)' set: function-definition-body - include: function-specifiers - include: function-trailing-return-type function-definition-body: - meta_content_scope: meta.function.c++ meta.block.c++ - match: '\{' scope: punctuation.section.block.begin.c++ set: - meta_content_scope: meta.function.c++ meta.block.c++ - match: '\}' scope: meta.function.c++ meta.block.c++ punctuation.section.block.end.c++ pop: true - match: (?=^\s*#\s*(elif|else|endif)\b) pop: true - match: '(?=({{before_tag}})([^(;]+$|.*\{))' push: data-structures - include: statements ## Data structures including classes, structs, unions and enums data-structures: - match: '\bclass\b' scope: storage.type.c++ set: data-structures-class-definition # Detect variable type definitions using struct/enum/union followed by a tag - match: '\b({{before_tag}})(?=\s+{{path_lookahead}}\s+{{path_lookahead}}\s*[=;\[])' scope: storage.type.c++ - match: '\bstruct\b' scope: storage.type.c++ set: data-structures-struct-definition - match: '\benum(\s+(class|struct))?\b' scope: storage.type.c++ set: data-structures-enum-definition - match: '\bunion\b' scope: storage.type.c++ set: data-structures-union-definition - match: '(?=\S)' pop: true preprocessor-workaround-eat-macro-before-identifier: # Handle macros so they aren't matched as the class name - match: ({{macro_identifier}})(?=\s+~?{{identifier}}) captures: 1: meta.assumed-macro.c data-structures-class-definition: - meta_scope: meta.class.c++ - include: data-structures-definition-common-begin - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' scope: entity.name.class.forward-decl.c++ set: data-structures-class-definition-after-identifier - match: '{{identifier}}' scope: entity.name.class.c++ set: data-structures-class-definition-after-identifier - match: '(?=[:{])' set: data-structures-class-definition-after-identifier - match: '(?=;)' pop: true data-structures-class-definition-after-identifier: - meta_content_scope: meta.class.c++ - include: data-structures-definition-common-begin # No matching of identifiers since they should all be macros at this point - include: data-structures-definition-common-end - match: '\{' scope: meta.block.c++ punctuation.section.block.begin.c++ set: - meta_content_scope: meta.class.c++ meta.block.c++ - match: '\}' scope: meta.class.c++ meta.block.c++ punctuation.section.block.end.c++ pop: true - include: data-structures-body data-structures-struct-definition: - meta_scope: meta.struct.c++ - include: data-structures-definition-common-begin - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' scope: entity.name.struct.forward-decl.c++ set: data-structures-struct-definition-after-identifier - match: '{{identifier}}' scope: entity.name.struct.c++ set: data-structures-struct-definition-after-identifier - match: '(?=[:{])' set: data-structures-struct-definition-after-identifier - match: '(?=;)' pop: true data-structures-struct-definition-after-identifier: - meta_content_scope: meta.struct.c++ - include: data-structures-definition-common-begin # No matching of identifiers since they should all be macros at this point - include: data-structures-definition-common-end - match: '\{' scope: meta.block.c++ punctuation.section.block.begin.c++ set: - meta_content_scope: meta.struct.c++ meta.block.c++ - match: '\}' scope: meta.struct.c++ meta.block.c++ punctuation.section.block.end.c++ pop: true - include: data-structures-body data-structures-enum-definition: - meta_scope: meta.enum.c++ - include: data-structures-definition-common-begin - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' scope: entity.name.enum.forward-decl.c++ set: data-structures-enum-definition-after-identifier - match: '{{identifier}}' scope: entity.name.enum.c++ set: data-structures-enum-definition-after-identifier - match: '(?=[:{])' set: data-structures-enum-definition-after-identifier - match: '(?=;)' pop: true data-structures-enum-definition-after-identifier: - meta_content_scope: meta.enum.c++ - include: data-structures-definition-common-begin # No matching of identifiers since they should all be macros at this point - include: data-structures-definition-common-end - match: '\{' scope: meta.block.c++ punctuation.section.block.begin.c++ set: - meta_content_scope: meta.enum.c++ meta.block.c++ # Enums don't support methods so we have a simplified body - match: '\}' scope: meta.enum.c++ meta.block.c++ punctuation.section.block.end.c++ pop: true - include: statements data-structures-union-definition: - meta_scope: meta.union.c++ - include: data-structures-definition-common-begin - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' scope: entity.name.union.forward-decl.c++ set: data-structures-union-definition-after-identifier - match: '{{identifier}}' scope: entity.name.union.c++ set: data-structures-union-definition-after-identifier - match: '(?=[{])' set: data-structures-union-definition-after-identifier - match: '(?=;)' pop: true data-structures-union-definition-after-identifier: - meta_content_scope: meta.union.c++ - include: data-structures-definition-common-begin # No matching of identifiers since they should all be macros at this point # Unions don't support base classes - include: angle-brackets - match: '\{' scope: meta.block.c++ punctuation.section.block.begin.c++ set: - meta_content_scope: meta.union.c++ meta.block.c++ - match: '\}' scope: meta.union.c++ meta.block.c++ punctuation.section.block.end.c++ pop: true - include: data-structures-body - match: '(?=;)' pop: true data-structures-definition-common-begin: - include: comments - match: '(?=\b(?:{{before_tag}}|{{control_keywords}})\b)' pop: true - include: preprocessor-other - include: modifiers-parens - include: modifiers - include: preprocessor-workaround-eat-macro-before-identifier data-structures-definition-common-end: - include: angle-brackets - match: \bfinal\b scope: storage.modifier.c++ - match: ':' scope: punctuation.separator.c++ push: - include: comments - include: preprocessor-other - include: modifiers-parens - include: modifiers - match: '\b(virtual|{{visibility_modifiers}})\b' scope: storage.modifier.c++ - match: (?={{path_lookahead}}) push: - meta_scope: entity.other.inherited-class.c++ - include: identifiers - match: '' pop: true - include: angle-brackets - match: ',' scope: punctuation.separator.c++ - match: (?=\{|;) pop: true - match: '(?=;)' pop: true data-structures-body: - include: preprocessor-data-structures - match: '(?=\btemplate\b)' push: - include: template - match: (?=\S) set: data-structures-modifier - include: typedef - match: \b({{visibility_modifiers}})\s*(:)(?!:) captures: 1: storage.modifier.c++ 2: punctuation.section.class.c++ - match: '^\s*(?=(?:~?\w+|::))' push: data-structures-modifier - include: expressions-minus-generic-type data-structures-modifier: - match: '\bfriend\b' scope: storage.modifier.c++ push: - match: (?=;) pop: true - match: '\{' scope: punctuation.section.block.begin.c++ set: - meta_scope: meta.block.c++ - match: '\}' scope: punctuation.section.block.end.c++ pop: true - include: statements - match: '\b({{before_tag}})\b' scope: storage.type.c++ - include: expressions-minus-function-call - include: comments - include: modifiers-parens - include: modifiers - match: '\bstatic_assert(?=\s*\()' scope: meta.static-assert.c++ keyword.operator.word.c++ push: - match: '\(' scope: meta.group.c++ punctuation.section.group.begin.c++ set: - meta_content_scope: meta.function-call.c++ meta.group.c++ - match: '\)' scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions # Destructor - match: '(?:{{identifier}}\s*(::)\s*)?~{{identifier}}(?=\s*(\(|$))' scope: meta.method.destructor.c++ entity.name.function.destructor.c++ captures: 1: punctuation.accessor.c++ set: method-definition-params # It's a macro, not a constructor if there is no type in the first param - match: '({{identifier}})\s*(\()(?=\s*(?!void){{identifier}}\s*[),])' captures: 1: variable.function.c++ 2: meta.group.c++ punctuation.section.group.begin.c++ push: - meta_scope: meta.function-call.c++ - meta_content_scope: meta.group.c++ - match: '\)' scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions # Constructor - include: preprocessor-workaround-eat-macro-before-identifier - match: '((?!{{before_tag}}|template){{identifier}})(?=\s*\()' scope: meta.method.constructor.c++ entity.name.function.constructor.c++ set: method-definition-params # Long form constructor - match: '({{identifier}}\s*(::)\s*{{identifier}})(?=\s*\()' captures: 1: meta.method.constructor.c++ entity.name.function.constructor.c++ 2: punctuation.accessor.c++ push: method-definition-params - match: '(?=\S)' set: data-structures-type data-structures-type: - include: comments - match: \*|& scope: keyword.operator.c++ # Cast methods - match: '(operator)\s+({{identifier}})(?=\s*(\(|$))' captures: 1: keyword.control.c++ 2: meta.method.c++ entity.name.function.c++ set: method-definition-params - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}}|operator)\b)' pop: true - match: '(?=\s)' set: data-structures-maybe-method # If a class/struct/enum followed by a name that is not a macro or declspec # then this is likely a return type of a function. This is uncommon. - match: |- (?x: ({{before_tag}}) \s+ (?= (?![[:upper:][:digit:]_]+\b|__declspec|{{before_tag}}) {{path_lookahead}} (\s+{{identifier}}\s*\(|\s*[*&]) ) ) captures: 1: storage.type.c++ set: - include: identifiers - match: '' set: data-structures-maybe-method # The previous match handles return types of struct/enum/etc from a func, # there this one exits the context to allow matching an actual struct/class - match: '(?=\b({{before_tag}})\b)' set: data-structures - match: '(?=\b({{casts}})\b\s*<)' pop: true - match: '{{non_angle_brackets}}' pop: true - include: angle-brackets - include: types - include: variables - include: constants - include: identifiers - match: (?=[&*]) set: data-structures-maybe-method - match: (?=\W) pop: true data-structures-maybe-method: - include: comments # Consume pointer info, macros and any type info that was offset by macros - match: \*|& scope: keyword.operator.c++ - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}})\b)' pop: true - match: '\b({{type_qualifier}})\b' scope: storage.modifier.c++ - match: '{{non_angle_brackets}}' pop: true - include: angle-brackets - include: types - include: modifiers-parens - include: modifiers # Operator overloading - match: '{{operator_method_name}}(?=\s*(\(|$))' scope: meta.method.c++ entity.name.function.c++ set: method-definition-params # Identifier that is not the function name - likely a macro or type - match: '(?={{path_lookahead}}([ \t]+|[*&])(?!\s*(<|::|\()))' push: - include: identifiers - match: '' pop: true # Real function definition - match: '(?={{path_lookahead}}({{generic_lookahead}})\s*(\())' set: [method-definition-params, data-structures-function-identifier-generic] - match: '(?={{path_lookahead}}\s*(\())' set: [method-definition-params, data-structures-function-identifier] - match: '(?={{path_lookahead}}\s*::\s*$)' set: [method-definition-params, data-structures-function-identifier] - match: '(?=\S)' pop: true data-structures-function-identifier-generic: - include: angle-brackets - match: '(?={{identifier}})' push: - meta_content_scope: entity.name.function.c++ - include: identifiers - match: '(?=<)' pop: true - match: '(?=\()' pop: true data-structures-function-identifier: - meta_content_scope: entity.name.function.c++ - include: identifiers - match: '(?=\S)' pop: true method-definition-params: - meta_content_scope: meta.method.c++ - include: comments - match: '(?=\()' set: - match: \( scope: meta.method.parameters.c++ meta.group.c++ punctuation.section.group.begin.c++ set: - meta_content_scope: meta.method.parameters.c++ meta.group.c++ - match : \) scope: punctuation.section.group.end.c++ set: method-definition-continue - match: '\bvoid\b' scope: storage.type.c++ - match: '{{identifier}}(?=\s*(\[|,|\)|=))' scope: variable.parameter.c++ - match: '=' scope: keyword.operator.assignment.c++ push: - match: '(?=,|\))' pop: true - include: expressions-minus-generic-type - include: expressions-minus-generic-type - match: '(?=\S)' pop: true method-definition-continue: - meta_content_scope: meta.method.c++ - include: comments - match: '(?=;)' pop: true - match: '->' scope: punctuation.separator.c++ set: method-definition-trailing-return - include: function-specifiers - match: '=' scope: keyword.operator.assignment.c++ - match: '&' scope: keyword.operator.c++ - match: \b0\b scope: constant.numeric.c++ - match: \b(default|delete)\b scope: storage.modifier.c++ - match: '(?=:)' set: - match: ':' scope: punctuation.separator.initializer-list.c++ set: - meta_scope: meta.method.constructor.initializer-list.c++ - match: '{{identifier}}' scope: variable.other.readwrite.member.c++ push: - match: \( scope: meta.group.c++ punctuation.section.group.begin.c++ set: - meta_content_scope: meta.group.c++ - match: \) scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions - match: \{ scope: meta.group.c++ punctuation.section.group.begin.c++ set: - meta_content_scope: meta.group.c++ - match: \} scope: meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions - include: comments - match: (?=\{|;) set: method-definition-continue - include: expressions - match: '(?=\{)' set: method-definition-body - match: '(?=\S)' pop: true method-definition-trailing-return: - include: comments - match: '(?=;)' pop: true - match: '(?=\{)' set: method-definition-body - include: function-specifiers - include: function-trailing-return-type method-definition-body: - meta_content_scope: meta.method.c++ meta.block.c++ - match: '\{' scope: punctuation.section.block.begin.c++ set: - meta_content_scope: meta.method.c++ meta.block.c++ - match: '\}' scope: meta.method.c++ meta.block.c++ punctuation.section.block.end.c++ pop: true - match: (?=^\s*#\s*(elif|else|endif)\b) pop: true - match: '(?=({{before_tag}})([^(;]+$|.*\{))' push: data-structures - include: statements ## Preprocessor for data-structures preprocessor-data-structures: - include: preprocessor-rule-enabled-data-structures - include: preprocessor-rule-disabled-data-structures - include: preprocessor-practical-workarounds preprocessor-rule-disabled-data-structures: - match: ^\s*((#if)\s+(0))\b captures: 1: meta.preprocessor.c++ 2: keyword.control.import.c++ 3: constant.numeric.preprocessor.c++ push: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: ^\s*(#\s*else)\b captures: 1: meta.preprocessor.c++ keyword.control.import.else.c++ push: - match: (?=^\s*#\s*endif\b) pop: true - include: negated-block - include: data-structures-body - match: "" push: - meta_scope: comment.block.preprocessor.if-branch.c++ - match: (?=^\s*#\s*(else|endif)\b) pop: true - include: scope:source.c#preprocessor-disabled preprocessor-rule-enabled-data-structures: - match: ^\s*((#if)\s+(0*1))\b captures: 1: meta.preprocessor.c++ 2: keyword.control.import.c++ 3: constant.numeric.preprocessor.c++ push: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: ^\s*(#\s*else)\b captures: 1: meta.preprocessor.c++ keyword.control.import.else.c++ push: - meta_content_scope: comment.block.preprocessor.else-branch.c++ - match: (?=^\s*#\s*endif\b) pop: true - include: scope:source.c#preprocessor-disabled - match: "" push: - match: (?=^\s*#\s*(else|endif)\b) pop: true - include: negated-block - include: data-structures-body ## Preprocessor for global preprocessor-global: - include: preprocessor-rule-enabled-global - include: preprocessor-rule-disabled-global - include: preprocessor-rule-other-global preprocessor-statements: - include: preprocessor-rule-enabled-statements - include: preprocessor-rule-disabled-statements - include: preprocessor-rule-other-statements preprocessor-expressions: - include: scope:source.c#incomplete-inc - include: preprocessor-macro-define - include: scope:source.c#pragma-mark - include: preprocessor-other preprocessor-rule-disabled-global: - match: ^\s*((#if)\s+(0))\b captures: 1: meta.preprocessor.c++ 2: keyword.control.import.c++ 3: constant.numeric.preprocessor.c++ push: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: ^\s*(#\s*else)\b captures: 1: meta.preprocessor.c++ keyword.control.import.else.c++ push: - match: (?=^\s*#\s*endif\b) pop: true - include: preprocessor-global - include: negated-block - include: global - match: "" push: - meta_scope: comment.block.preprocessor.if-branch.c++ - match: (?=^\s*#\s*(else|endif)\b) pop: true - include: scope:source.c#preprocessor-disabled preprocessor-rule-enabled-global: - match: ^\s*((#if)\s+(0*1))\b captures: 1: meta.preprocessor.c++ 2: keyword.control.import.c++ 3: constant.numeric.preprocessor.c++ push: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: ^\s*(#\s*else)\b captures: 1: meta.preprocessor.c++ keyword.control.import.else.c++ push: - meta_content_scope: comment.block.preprocessor.else-branch.c++ - match: (?=^\s*#\s*endif\b) pop: true - include: scope:source.c#preprocessor-disabled - match: "" push: - match: (?=^\s*#\s*(else|endif)\b) pop: true - include: preprocessor-global - include: negated-block - include: global preprocessor-rule-other-global: - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b captures: 1: keyword.control.import.c++ push: - meta_scope: meta.preprocessor.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-comments - match: \bdefined\b scope: keyword.control.c++ # Enter a new scope where all elif/else branches have their # contexts popped by a subsequent elif/else/endif. This ensures that # preprocessor branches don't push multiple meta.block scopes on # the stack, thus messing up the "global" context's detection of # functions. - match: $\n set: preprocessor-if-branch-global # These gymnastics here ensure that we are properly handling scope even # when the preprocessor is used to create different scope beginnings, such # as a different if/while condition preprocessor-if-branch-global: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: (?=^\s*#\s*(elif|else)\b) push: preprocessor-elif-else-branch-global - match: \{ scope: punctuation.section.block.begin.c++ set: preprocessor-block-if-branch-global - include: preprocessor-global - include: negated-block - include: global preprocessor-block-if-branch-global: - meta_scope: meta.block.c++ - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ set: preprocessor-block-finish-global - match: (?=^\s*#\s*(elif|else)\b) push: preprocessor-elif-else-branch-global - match: \} scope: punctuation.section.block.end.c++ set: preprocessor-if-branch-global - include: statements preprocessor-block-finish-global: - meta_scope: meta.block.c++ - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ set: preprocessor-block-finish-if-branch-global - match: \} scope: punctuation.section.block.end.c++ pop: true - include: statements preprocessor-block-finish-if-branch-global: - match: ^\s*(#\s*endif)\b captures: 1: keyword.control.import.c++ pop: true - match: \} scope: punctuation.section.block.end.c++ set: preprocessor-if-branch-global - include: statements preprocessor-elif-else-branch-global: - match: (?=^\s*#\s*(endif)\b) pop: true - include: preprocessor-global - include: negated-block - include: global ## Preprocessor for statements preprocessor-rule-disabled-statements: - match: ^\s*((#if)\s+(0))\b captures: 1: meta.preprocessor.c++ 2: keyword.control.import.c++ 3: constant.numeric.preprocessor.c++ push: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: ^\s*(#\s*else)\b captures: 1: meta.preprocessor.c++ keyword.control.import.else.c++ push: - match: (?=^\s*#\s*endif\b) pop: true - include: negated-block - include: statements - match: "" push: - meta_scope: comment.block.preprocessor.if-branch.c++ - match: (?=^\s*#\s*(else|endif)\b) pop: true - include: scope:source.c#preprocessor-disabled preprocessor-rule-enabled-statements: - match: ^\s*((#if)\s+(0*1))\b captures: 1: meta.preprocessor.c++ 2: keyword.control.import.c++ 3: constant.numeric.preprocessor.c++ push: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: ^\s*(#\s*else)\b captures: 1: meta.preprocessor.c++ keyword.control.import.else.c++ push: - meta_content_scope: comment.block.preprocessor.else-branch.c++ - match: (?=^\s*#\s*endif\b) pop: true - include: scope:source.c#preprocessor-disabled - match: "" push: - match: (?=^\s*#\s*(else|endif)\b) pop: true - include: negated-block - include: statements preprocessor-rule-other-statements: - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b captures: 1: keyword.control.import.c++ push: - meta_scope: meta.preprocessor.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-comments - match: \bdefined\b scope: keyword.control.c++ # Enter a new scope where all elif/else branches have their # contexts popped by a subsequent elif/else/endif. This ensures that # preprocessor branches don't push multiple meta.block scopes on # the stack, thus messing up the "global" context's detection of # functions. - match: $\n set: preprocessor-if-branch-statements # These gymnastics here ensure that we are properly handling scope even # when the preprocessor is used to create different scope beginnings, such # as a different if/while condition preprocessor-if-branch-statements: - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ pop: true - match: (?=^\s*#\s*(elif|else)\b) push: preprocessor-elif-else-branch-statements - match: \{ scope: punctuation.section.block.begin.c++ set: preprocessor-block-if-branch-statements - match: (?=(?!{{non_func_keywords}}){{path_lookahead}}\s*\() set: preprocessor-if-branch-function-call - include: negated-block - include: statements preprocessor-if-branch-function-call: - meta_content_scope: meta.function-call.c++ - include: scope:source.c#c99 - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' scope: variable.function.c++ captures: 1: punctuation.accessor.c++ 2: punctuation.accessor.c++ - match: '(?:(::)\s*)?{{identifier}}' scope: variable.function.c++ captures: 1: punctuation.accessor.c++ - match: '\(' scope: meta.group.c++ punctuation.section.group.begin.c++ set: preprocessor-if-branch-function-call-arguments preprocessor-if-branch-function-call-arguments: - meta_content_scope: meta.function-call.c++ meta.group.c++ - match : \) scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ set: preprocessor-if-branch-statements - match: ^\s*(#\s*(?:elif|else))\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ set: preprocessor-if-branch-statements - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ set: preprocessor-if-branch-function-call-arguments-finish - include: expressions preprocessor-if-branch-function-call-arguments-finish: - meta_content_scope: meta.function-call.c++ meta.group.c++ - match: \) scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ pop: true - include: expressions preprocessor-block-if-branch-statements: - meta_scope: meta.block.c++ - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ set: preprocessor-block-finish-statements - match: (?=^\s*#\s*(elif|else)\b) push: preprocessor-elif-else-branch-statements - match: \} scope: punctuation.section.block.end.c++ set: preprocessor-if-branch-statements - include: statements preprocessor-block-finish-statements: - meta_scope: meta.block.c++ - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ set: preprocessor-block-finish-if-branch-statements - match: \} scope: punctuation.section.block.end.c++ pop: true - include: statements preprocessor-block-finish-if-branch-statements: - match: ^\s*(#\s*endif)\b captures: 1: keyword.control.import.c++ pop: true - match: \} scope: meta.block.c++ punctuation.section.block.end.c++ set: preprocessor-if-branch-statements - include: statements preprocessor-elif-else-branch-statements: - match: (?=^\s*#\s*endif\b) pop: true - include: negated-block - include: statements ## Preprocessor other negated-block: - match: '\}' scope: punctuation.section.block.end.c++ push: - match: '\{' scope: punctuation.section.block.begin.c++ pop: true - match: (?=^\s*#\s*(elif|else|endif)\b) pop: true - include: statements preprocessor-macro-define: - match: ^\s*(\#\s*define)\b captures: 1: meta.preprocessor.macro.c++ keyword.control.import.define.c++ push: - meta_content_scope: meta.preprocessor.macro.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-line-ending - include: scope:source.c#preprocessor-comments - match: '({{identifier}})(?=\()' scope: entity.name.function.preprocessor.c++ set: - match: '\(' scope: punctuation.section.group.begin.c++ set: preprocessor-macro-params - match: '{{identifier}}' scope: entity.name.constant.preprocessor.c++ set: preprocessor-macro-definition preprocessor-macro-params: - meta_scope: meta.preprocessor.macro.parameters.c++ meta.group.c++ - match: '{{identifier}}' scope: variable.parameter.c++ - match: \) scope: punctuation.section.group.end.c++ set: preprocessor-macro-definition - match: ',' scope: punctuation.separator.c++ push: - match: '{{identifier}}' scope: variable.parameter.c++ pop: true - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-comments - match: '\.\.\.' scope: keyword.operator.variadic.c++ - match: '(?=\))' pop: true - match: (/\*).*(\*/) scope: comment.block.c++ captures: 1: punctuation.definition.comment.c++ 2: punctuation.definition.comment.c++ - match: '\S+' scope: invalid.illegal.unexpected-character.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-comments - match: '\.\.\.' scope: keyword.operator.variadic.c++ - match: (/\*).*(\*/) scope: comment.block.c++ captures: 1: punctuation.definition.comment.c++ 2: punctuation.definition.comment.c++ - match: $\n scope: invalid.illegal.unexpected-end-of-line.c++ preprocessor-macro-definition: - meta_content_scope: meta.preprocessor.macro.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-line-ending - include: scope:source.c#preprocessor-comments # Don't define blocks in define statements - match: '\{' scope: punctuation.section.block.begin.c++ - match: '\}' scope: punctuation.section.block.end.c++ - include: expressions preprocessor-practical-workarounds: - include: preprocessor-convention-ignore-uppercase-ident-lines - include: scope:source.c#preprocessor-convention-ignore-uppercase-calls-without-semicolon preprocessor-convention-ignore-uppercase-ident-lines: - match: ^(\s*{{macro_identifier}})+\s*$ scope: meta.assumed-macro.c++ push: # It's possible that we are dealing with a function return type on its own line, and the # name of the function is on the subsequent line. - match: '(?={{path_lookahead}}({{generic_lookahead}}({{path_lookahead}})?)\s*\()' set: [function-definition-params, global-function-identifier-generic] - match: '(?={{path_lookahead}}\s*\()' set: [function-definition-params, global-function-identifier] - match: ^ pop: true preprocessor-other: - match: ^\s*(#\s*(?:if|ifdef|ifndef|elif|else|line|pragma|undef))\b captures: 1: keyword.control.import.c++ push: - meta_scope: meta.preprocessor.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-line-ending - include: scope:source.c#preprocessor-comments - match: \bdefined\b scope: keyword.control.c++ - match: ^\s*(#\s*endif)\b captures: 1: meta.preprocessor.c++ keyword.control.import.c++ - match: ^\s*(#\s*(?:error|warning))\b captures: 1: keyword.control.import.error.c++ push: - meta_scope: meta.preprocessor.diagnostic.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-line-ending - include: scope:source.c#preprocessor-comments - include: strings - match: '\S+' scope: string.unquoted.c++ - match: ^\s*(#\s*(?:include|include_next|import))\b captures: 1: keyword.control.import.include.c++ push: - meta_scope: meta.preprocessor.include.c++ - include: scope:source.c#preprocessor-line-continuation - include: scope:source.c#preprocessor-line-ending - include: scope:source.c#preprocessor-comments - match: '"' scope: punctuation.definition.string.begin.c++ push: - meta_scope: string.quoted.double.include.c++ - match: '"' scope: punctuation.definition.string.end.c++ pop: true - match: < scope: punctuation.definition.string.begin.c++ push: - meta_scope: string.quoted.other.lt-gt.include.c++ - match: '>' scope: punctuation.definition.string.end.c++ pop: true - include: preprocessor-practical-workarounds ================================================ FILE: support/README ================================================ This directory contains build support files such as * CMake modules * Build scripts ================================================ FILE: support/Vagrantfile ================================================ # -*- mode: ruby -*- # vi: set ft=ruby : # A vagrant config for testing against gcc-4.8. Vagrant.configure("2") do |config| config.vm.box = "bento/ubuntu-22.04-arm64" config.vm.provider "vmware_desktop" do |vb| vb.memory = "4096" end config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y g++ make wget git wget -q https://github.com/Kitware/CMake/releases/download/v3.26.0/cmake-3.26.0-Linux-x86_64.tar.gz tar xzf cmake-3.26.0-Linux-x86_64.tar.gz ln -s `pwd`/cmake-3.26.0-Linux-x86_64/bin/cmake /usr/local/bin SHELL end ================================================ FILE: support/build.gradle ================================================ import java.nio.file.Paths // General gradle arguments for root project buildscript { repositories { google() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:9.0.0" } } repositories { google() mavenCentral() } // Project's root where CMakeLists.txt exists: rootDir/support/.cxx -> rootDir def rootDir = Paths.get(project.buildDir.getParent()).getParent() println("rootDir: ${rootDir}") // Output: Shared library (.so) for Android apply plugin: "com.android.library" android { namespace = "dev.fmt" compileSdk 36 // Target Android 16 (API 36). /* Target ABI - This option controls target platform of module - The platform might be limited by compiler's support some can work with Clang(default), but some can work only with GCC... if bad, both toolchains might not support it */ ndkVersion = "28.2.13676358" defaultConfig { minSdk 21 // Android 5.0+ targetSdkVersion 36 // Follow Compile SDK versionCode 34 // Follow release count versionName "7.1.2" // Follow Official version ndk{ abiFilters "arm64-v8a", "armeabi-v7a", "x86_64" } externalNativeBuild { cmake { arguments "-DANDROID_STL=c++_shared" // Specify Android STL arguments "-DBUILD_SHARED_LIBS=true" // Build shared object arguments "-DFMT_TEST=false" // Skip test arguments "-DFMT_DOC=false" // Skip documentation cppFlags "-std=c++17" targets "fmt" } } println(externalNativeBuild.cmake.cppFlags) println(externalNativeBuild.cmake.arguments) } // External Native build // - Use existing CMakeList.txt // - Give path to CMake. This gradle file should be // neighbor of the top level cmake externalNativeBuild { cmake { version = "3.22.1" path "${rootDir}/CMakeLists.txt" // buildStagingDirectory "./build" // Custom path for cmake output } } sourceSets{ // Android Manifest for Gradle main { manifest.srcFile "AndroidManifest.xml" } } // https://developer.android.com/studio/build/native-dependencies#build_system_configuration buildFeatures { prefab = true prefabPublishing = true } prefab { fmt { headers = "${rootDir}/include" } } } assemble.doLast { /* *- Instead of `ninja install`, Gradle will deploy the files. *- We are doing this since FMT is dependent to the ANDROID_STL after build */ copy { from "build/intermediates/cmake" into "${rootDir}/libs" } // Copy debug binaries copy { from "${rootDir}/libs/debug/obj" into "${rootDir}/libs/debug" } // Copy Release binaries copy { from "${rootDir}/libs/release/obj" into "${rootDir}/libs/release" } // Remove empty directory delete "${rootDir}/libs/debug/obj" delete "${rootDir}/libs/release/obj" // Copy AAR files. Notice that the aar is named after the folder of this script. copy { from "build/outputs/aar/support-release.aar" into "${rootDir}/libs" rename "support-release.aar", "fmt-release.aar" } copy { from "build/outputs/aar/support-debug.aar" into "${rootDir}/libs" rename "support-debug.aar", "fmt-debug.aar" } } ================================================ FILE: support/check-commits ================================================ #!/usr/bin/env python3 """Compile source on a range of commits Usage: check-commits """ import docopt, os, sys, tempfile from subprocess import check_call, check_output, run args = docopt.docopt(__doc__) start = args.get('') source = args.get('') cwd = os.getcwd() with tempfile.TemporaryDirectory() as work_dir: check_call(['git', 'clone', 'https://github.com/fmtlib/fmt.git'], cwd=work_dir) repo_dir = os.path.join(work_dir, 'fmt') commits = check_output( ['git', 'rev-list', f'{start}..HEAD', '--abbrev-commit', '--', 'include', 'src'], text=True, cwd=repo_dir).rstrip().split('\n') commits.reverse() print('Time\tCommit') for commit in commits: check_call(['git', '-c', 'advice.detachedHead=false', 'checkout', commit], cwd=repo_dir) returncode = run( ['c++', '-std=c++11', '-O3', '-DNDEBUG', '-I', 'include', 'src/format.cc', os.path.join(cwd, source)], cwd=repo_dir).returncode if returncode != 0: continue times = [] for i in range(5): output = check_output([os.path.join(repo_dir, 'a.out')], text=True) times.append(float(output)) message = check_output(['git', 'log', '-1', '--pretty=format:%s', commit], cwd=repo_dir, text=True) print(f'{min(times)}\t{commit} {message[:40]}') sys.stdout.flush() ================================================ FILE: support/cmake/FindSetEnv.cmake ================================================ # A CMake script to find SetEnv.cmd. find_program( WINSDK_SETENV NAMES SetEnv.cmd PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin" ) if (WINSDK_SETENV AND PRINT_PATH) execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}") endif () ================================================ FILE: support/cmake/JoinPaths.cmake ================================================ # This module provides function for joining paths known from from most languages # # Original license: # SPDX-License-Identifier: (MIT OR CC0-1.0) # Explicit permission given to distribute this module under the terms of the # project as described in /LICENSE.md. # Copyright 2020 Jan Tojnar # https://github.com/jtojnar/cmake-snips # # Modelled after Python’s os.path.join # https://docs.python.org/3.7/library/os.path.html#os.path.join # Windows not supported function (join_paths joined_path first_path_segment) set(temp_path "${first_path_segment}") foreach (current_segment IN LISTS ARGN) if (NOT ("${current_segment}" STREQUAL "")) if (IS_ABSOLUTE "${current_segment}") set(temp_path "${current_segment}") else () set(temp_path "${temp_path}/${current_segment}") endif () endif () endforeach () set(${joined_path} "${temp_path}" PARENT_SCOPE) endfunction () ================================================ FILE: support/cmake/fmt-config.cmake.in ================================================ @PACKAGE_INIT@ if (NOT TARGET fmt::fmt) include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) endif () check_required_components(fmt) ================================================ FILE: support/cmake/fmt.pc.in ================================================ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@libdir_for_pc_file@ includedir=@includedir_for_pc_file@ Name: fmt Description: A modern formatting library Version: @FMT_VERSION@ Libs: -L${libdir} -l@FMT_LIB_NAME@ Cflags: -I${includedir} ================================================ FILE: support/docopt.py ================================================ """Pythonic command-line interface parser that will make you smile. * http://docopt.org * Repository and issue-tracker: https://github.com/docopt/docopt * Licensed under terms of MIT license (see LICENSE-MIT) * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com """ import sys import re __all__ = ['docopt'] __version__ = '0.6.1' class DocoptLanguageError(Exception): """Error in construction of usage-message by developer.""" class DocoptExit(SystemExit): """Exit in case user invoked program with incorrect arguments.""" usage = '' def __init__(self, message=''): SystemExit.__init__(self, (message + '\n' + self.usage).strip()) class Pattern(object): def __eq__(self, other): return repr(self) == repr(other) def __hash__(self): return hash(repr(self)) def fix(self): self.fix_identities() self.fix_repeating_arguments() return self def fix_identities(self, uniq=None): """Make pattern-tree tips point to same object if they are equal.""" if not hasattr(self, 'children'): return self uniq = list(set(self.flat())) if uniq is None else uniq for i, child in enumerate(self.children): if not hasattr(child, 'children'): assert child in uniq self.children[i] = uniq[uniq.index(child)] else: child.fix_identities(uniq) def fix_repeating_arguments(self): """Fix elements that should accumulate/increment values.""" either = [list(child.children) for child in transform(self).children] for case in either: for e in [child for child in case if case.count(child) > 1]: if type(e) is Argument or type(e) is Option and e.argcount: if e.value is None: e.value = [] elif type(e.value) is not list: e.value = e.value.split() if type(e) is Command or type(e) is Option and e.argcount == 0: e.value = 0 return self def transform(pattern): """Expand pattern into an (almost) equivalent one, but with single Either. Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) Quirks: [-a] => (-a), (-a...) => (-a -a) """ result = [] groups = [[pattern]] while groups: children = groups.pop(0) parents = [Required, Optional, OptionsShortcut, Either, OneOrMore] if any(t in map(type, children) for t in parents): child = [c for c in children if type(c) in parents][0] children.remove(child) if type(child) is Either: for c in child.children: groups.append([c] + children) elif type(child) is OneOrMore: groups.append(child.children * 2 + children) else: groups.append(child.children + children) else: result.append(children) return Either(*[Required(*e) for e in result]) class LeafPattern(Pattern): """Leaf/terminal node of a pattern tree.""" def __init__(self, name, value=None): self.name, self.value = name, value def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) def flat(self, *types): return [self] if not types or type(self) in types else [] def match(self, left, collected=None): collected = [] if collected is None else collected pos, match = self.single_match(left) if match is None: return False, left, collected left_ = left[:pos] + left[pos + 1:] same_name = [a for a in collected if a.name == self.name] if type(self.value) in (int, list): if type(self.value) is int: increment = 1 else: increment = ([match.value] if type(match.value) is str else match.value) if not same_name: match.value = increment return True, left_, collected + [match] same_name[0].value += increment return True, left_, collected return True, left_, collected + [match] class BranchPattern(Pattern): """Branch/inner node of a pattern tree.""" def __init__(self, *children): self.children = list(children) def __repr__(self): return '%s(%s)' % (self.__class__.__name__, ', '.join(repr(a) for a in self.children)) def flat(self, *types): if type(self) in types: return [self] return sum([child.flat(*types) for child in self.children], []) class Argument(LeafPattern): def single_match(self, left): for n, pattern in enumerate(left): if type(pattern) is Argument: return n, Argument(self.name, pattern.value) return None, None @classmethod def parse(class_, source): name = re.findall('(<\S*?>)', source)[0] value = re.findall('\[default: (.*)\]', source, flags=re.I) return class_(name, value[0] if value else None) class Command(Argument): def __init__(self, name, value=False): self.name, self.value = name, value def single_match(self, left): for n, pattern in enumerate(left): if type(pattern) is Argument: if pattern.value == self.name: return n, Command(self.name, True) else: break return None, None class Option(LeafPattern): def __init__(self, short=None, long=None, argcount=0, value=False): assert argcount in (0, 1) self.short, self.long, self.argcount = short, long, argcount self.value = None if value is False and argcount else value @classmethod def parse(class_, option_description): short, long, argcount, value = None, None, 0, False options, _, description = option_description.strip().partition(' ') options = options.replace(',', ' ').replace('=', ' ') for s in options.split(): if s.startswith('--'): long = s elif s.startswith('-'): short = s else: argcount = 1 if argcount: matched = re.findall('\[default: (.*)\]', description, flags=re.I) value = matched[0] if matched else None return class_(short, long, argcount, value) def single_match(self, left): for n, pattern in enumerate(left): if self.name == pattern.name: return n, pattern return None, None @property def name(self): return self.long or self.short def __repr__(self): return 'Option(%r, %r, %r, %r)' % (self.short, self.long, self.argcount, self.value) class Required(BranchPattern): def match(self, left, collected=None): collected = [] if collected is None else collected l = left c = collected for pattern in self.children: matched, l, c = pattern.match(l, c) if not matched: return False, left, collected return True, l, c class Optional(BranchPattern): def match(self, left, collected=None): collected = [] if collected is None else collected for pattern in self.children: m, left, collected = pattern.match(left, collected) return True, left, collected class OptionsShortcut(Optional): """Marker/placeholder for [options] shortcut.""" class OneOrMore(BranchPattern): def match(self, left, collected=None): assert len(self.children) == 1 collected = [] if collected is None else collected l = left c = collected l_ = None matched = True times = 0 while matched: # could it be that something didn't match but changed l or c? matched, l, c = self.children[0].match(l, c) times += 1 if matched else 0 if l_ == l: break l_ = l if times >= 1: return True, l, c return False, left, collected class Either(BranchPattern): def match(self, left, collected=None): collected = [] if collected is None else collected outcomes = [] for pattern in self.children: matched, _, _ = outcome = pattern.match(left, collected) if matched: outcomes.append(outcome) if outcomes: return min(outcomes, key=lambda outcome: len(outcome[1])) return False, left, collected class Tokens(list): def __init__(self, source, error=DocoptExit): self += source.split() if hasattr(source, 'split') else source self.error = error @staticmethod def from_pattern(source): source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source) source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s] return Tokens(source, error=DocoptLanguageError) def move(self): return self.pop(0) if len(self) else None def current(self): return self[0] if len(self) else None def parse_long(tokens, options): """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" long, eq, value = tokens.move().partition('=') assert long.startswith('--') value = None if eq == value == '' else value similar = [o for o in options if o.long == long] if tokens.error is DocoptExit and similar == []: # if no exact match similar = [o for o in options if o.long and o.long.startswith(long)] if len(similar) > 1: # might be simply specified ambiguously 2+ times? raise tokens.error('%s is not a unique prefix: %s?' % (long, ', '.join(o.long for o in similar))) elif len(similar) < 1: argcount = 1 if eq == '=' else 0 o = Option(None, long, argcount) options.append(o) if tokens.error is DocoptExit: o = Option(None, long, argcount, value if argcount else True) else: o = Option(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value) if o.argcount == 0: if value is not None: raise tokens.error('%s must not have an argument' % o.long) else: if value is None: if tokens.current() in [None, '--']: raise tokens.error('%s requires argument' % o.long) value = tokens.move() if tokens.error is DocoptExit: o.value = value if value is not None else True return [o] def parse_shorts(tokens, options): """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" token = tokens.move() assert token.startswith('-') and not token.startswith('--') left = token.lstrip('-') parsed = [] while left != '': short, left = '-' + left[0], left[1:] similar = [o for o in options if o.short == short] if len(similar) > 1: raise tokens.error('%s is specified ambiguously %d times' % (short, len(similar))) elif len(similar) < 1: o = Option(short, None, 0) options.append(o) if tokens.error is DocoptExit: o = Option(short, None, 0, True) else: # why copying is necessary here? o = Option(short, similar[0].long, similar[0].argcount, similar[0].value) value = None if o.argcount != 0: if left == '': if tokens.current() in [None, '--']: raise tokens.error('%s requires argument' % short) value = tokens.move() else: value = left left = '' if tokens.error is DocoptExit: o.value = value if value is not None else True parsed.append(o) return parsed def parse_pattern(source, options): tokens = Tokens.from_pattern(source) result = parse_expr(tokens, options) if tokens.current() is not None: raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) return Required(*result) def parse_expr(tokens, options): """expr ::= seq ( '|' seq )* ;""" seq = parse_seq(tokens, options) if tokens.current() != '|': return seq result = [Required(*seq)] if len(seq) > 1 else seq while tokens.current() == '|': tokens.move() seq = parse_seq(tokens, options) result += [Required(*seq)] if len(seq) > 1 else seq return [Either(*result)] if len(result) > 1 else result def parse_seq(tokens, options): """seq ::= ( atom [ '...' ] )* ;""" result = [] while tokens.current() not in [None, ']', ')', '|']: atom = parse_atom(tokens, options) if tokens.current() == '...': atom = [OneOrMore(*atom)] tokens.move() result += atom return result def parse_atom(tokens, options): """atom ::= '(' expr ')' | '[' expr ']' | 'options' | long | shorts | argument | command ; """ token = tokens.current() result = [] if token in '([': tokens.move() matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] result = pattern(*parse_expr(tokens, options)) if tokens.move() != matching: raise tokens.error("unmatched '%s'" % token) return [result] elif token == 'options': tokens.move() return [OptionsShortcut()] elif token.startswith('--') and token != '--': return parse_long(tokens, options) elif token.startswith('-') and token not in ('-', '--'): return parse_shorts(tokens, options) elif token.startswith('<') and token.endswith('>') or token.isupper(): return [Argument(tokens.move())] else: return [Command(tokens.move())] def parse_argv(tokens, options, options_first=False): """Parse command-line argument vector. If options_first: argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; else: argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; """ parsed = [] while tokens.current() is not None: if tokens.current() == '--': return parsed + [Argument(None, v) for v in tokens] elif tokens.current().startswith('--'): parsed += parse_long(tokens, options) elif tokens.current().startswith('-') and tokens.current() != '-': parsed += parse_shorts(tokens, options) elif options_first: return parsed + [Argument(None, v) for v in tokens] else: parsed.append(Argument(None, tokens.move())) return parsed def parse_defaults(doc): defaults = [] for s in parse_section('options:', doc): # FIXME corner case "bla: options: --foo" _, _, s = s.partition(':') # get rid of "options:" split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:] split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])] options = [Option.parse(s) for s in split if s.startswith('-')] defaults += options return defaults def parse_section(name, source): pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', re.IGNORECASE | re.MULTILINE) return [s.strip() for s in pattern.findall(source)] def formal_usage(section): _, _, section = section.partition(':') # drop "usage:" pu = section.split() return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' def extras(help, version, options, doc): if help and any((o.name in ('-h', '--help')) and o.value for o in options): print(doc.strip("\n")) sys.exit() if version and any(o.name == '--version' and o.value for o in options): print(version) sys.exit() class Dict(dict): def __repr__(self): return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) def docopt(doc, argv=None, help=True, version=None, options_first=False): """Parse `argv` based on command-line interface described in `doc`. `docopt` creates your command-line interface based on its description that you pass as `doc`. Such description can contain --options, , commands, which could be [optional], (required), (mutually | exclusive) or repeated... Parameters ---------- doc : str Description of your command-line interface. argv : list of str, optional Argument vector to be parsed. sys.argv[1:] is used if not provided. help : bool (default: True) Set to False to disable automatic help on -h or --help options. version : any object If passed, the object will be printed if --version is in `argv`. options_first : bool (default: False) Set to True to require options precede positional arguments, i.e. to forbid options and positional arguments intermix. Returns ------- args : dict A dictionary, where keys are names of command-line elements such as e.g. "--verbose" and "", and values are the parsed values of those elements. Example ------- >>> from docopt import docopt >>> doc = ''' ... Usage: ... my_program tcp [--timeout=] ... my_program serial [--baud=] [--timeout=] ... my_program (-h | --help | --version) ... ... Options: ... -h, --help Show this screen and exit. ... --baud= Baudrate [default: 9600] ... ''' >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30'] >>> docopt(doc, argv) {'--baud': '9600', '--help': False, '--timeout': '30', '--version': False, '': '127.0.0.1', '': '80', 'serial': False, 'tcp': True} See also -------- * For video introduction see http://docopt.org * Full documentation is available in README.rst as well as online at https://github.com/docopt/docopt#readme """ argv = sys.argv[1:] if argv is None else argv usage_sections = parse_section('usage:', doc) if len(usage_sections) == 0: raise DocoptLanguageError('"usage:" (case-insensitive) not found.') if len(usage_sections) > 1: raise DocoptLanguageError('More than one "usage:" (case-insensitive).') DocoptExit.usage = usage_sections[0] options = parse_defaults(doc) pattern = parse_pattern(formal_usage(DocoptExit.usage), options) # [default] syntax for argument is disabled #for a in pattern.flat(Argument): # same_name = [d for d in arguments if d.name == a.name] # if same_name: # a.value = same_name[0].value argv = parse_argv(Tokens(argv), list(options), options_first) pattern_options = set(pattern.flat(Option)) for options_shortcut in pattern.flat(OptionsShortcut): doc_options = parse_defaults(doc) options_shortcut.children = list(set(doc_options) - pattern_options) #if any_options: # options_shortcut.children += [Option(o.short, o.long, o.argcount) # for o in argv if type(o) is Option] extras(help, version, argv, doc) matched, left, collected = pattern.fix().match(argv) if matched and left == []: # better error message if left? return Dict((a.name, a.value) for a in (pattern.flat() + collected)) raise DocoptExit() ================================================ FILE: support/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists # This downloads Gradle 9.3 automatically. distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: support/gradle.properties ================================================ # Required for modern Android builds (AGP 8.0+) android.useAndroidX=true # Improves build performance android.nonTransitiveRClass=true # Memory settings for the build process org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g ================================================ FILE: support/mkdocs ================================================ #!/usr/bin/env python3 # A script to invoke mkdocs with the correct environment. # Additionally supports deploying via mike: # ./mkdocs deploy [mike-deploy-options] # For example: # ./mkdocs deploy # This will checkout the website to fmt/build/fmt.dev and deploy documentation # there. import errno, os, shutil, sys from subprocess import call support_dir = os.path.dirname(os.path.normpath(__file__)) build_dir = os.path.join(os.path.dirname(support_dir), 'build') # Set PYTHONPATH for the mkdocstrings handler. env = os.environ.copy() path = env.get('PYTHONPATH') env['PYTHONPATH'] = \ (path + ':' if path else '') + os.path.join(support_dir, 'python') redirect_page = \ ''' Redirecting Redirecting to api... ''' config_path = os.path.join(support_dir, 'mkdocs.yml') args = sys.argv[1:] if len(args) > 0: command = args[0] if command == 'deploy' or command == 'set-default': git_url = 'https://github.com/' if 'CI' in os.environ else 'git@github.com:' site_repo = git_url + 'fmtlib/fmt.dev.git' site_dir = os.path.join(build_dir, 'fmt.dev') try: shutil.rmtree(site_dir) except OSError as e: if e.errno == errno.ENOENT: pass ret = call(['git', 'clone', '--depth=1', site_repo, site_dir]) if ret != 0: sys.exit(ret) # Copy the config to the build dir because the site is built relative to it. config_build_path = os.path.join(build_dir, 'mkdocs.yml') shutil.copyfile(config_path, config_build_path) version = args[1] ret = call(['mike'] + args + ['--config-file', config_build_path, '--branch', 'master'], cwd=site_dir, env=env) if ret != 0 or version == 'dev': sys.exit(ret) current_doc_path = os.path.join(site_dir, version) # mike stages files added by deploy for deletion for unclear reason, # undo it. ret = call(['git', 'reset', '--hard'], cwd=site_dir) if False: os.makedirs(current_doc_path, exist_ok=True) redirect_page_path = os.path.join(current_doc_path, 'api.html') with open(redirect_page_path, "w") as file: file.write(redirect_page) ret = call(['git', 'add', redirect_page_path], cwd=site_dir) if ret != 0: sys.exit(ret) ret = call(['git', 'commit', '--amend', '--no-edit'], cwd=site_dir) sys.exit(ret) elif not command.startswith('-'): args += ['-f', config_path] sys.exit(call(['mkdocs'] + args, env=env)) ================================================ FILE: support/mkdocs.yml ================================================ site_name: '{fmt}' docs_dir: ../doc repo_url: https://github.com/fmtlib/fmt theme: name: material features: - navigation.tabs - navigation.top - toc.integrate extra_javascript: - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js - fmt.js extra_css: - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css - fmt.css markdown_extensions: - pymdownx.highlight: # Use JavaScript syntax highlighter instead of Pygments because it # automatically applies to code blocks extracted through Doxygen. use_pygments: false anchor_linenums: true line_spans: __span pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets plugins: - search - mkdocstrings: default_handler: cxx nav: - Home: index.md - Get Started: get-started.md - API: api.md - Syntax: syntax.md exclude_docs: ChangeLog-old.md extra: version: provider: mike generator: false ================================================ FILE: support/printable.py ================================================ #!/usr/bin/env python3 # This script is based on # https://github.com/rust-lang/rust/blob/master/library/core/src/unicode/printable.py # distributed under https://github.com/rust-lang/rust/blob/master/LICENSE-MIT. # This script uses the following Unicode tables: # - UnicodeData.txt from collections import namedtuple import csv import os import subprocess NUM_CODEPOINTS=0x110000 def to_ranges(iter): current = None for i in iter: if current is None or i != current[1] or i in (0x10000, 0x20000): if current is not None: yield tuple(current) current = [i, i + 1] else: current[1] += 1 if current is not None: yield tuple(current) def get_escaped(codepoints): for c in codepoints: if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '): yield c.value def get_file(f): try: return open(os.path.basename(f)) except FileNotFoundError: subprocess.run(["curl", "-O", f], check=True) return open(os.path.basename(f)) Codepoint = namedtuple('Codepoint', 'value class_') def get_codepoints(f): r = csv.reader(f, delimiter=";") prev_codepoint = 0 class_first = None for row in r: codepoint = int(row[0], 16) name = row[1] class_ = row[2] if class_first is not None: if not name.endswith("Last>"): raise ValueError("Missing Last after First") for c in range(prev_codepoint + 1, codepoint): yield Codepoint(c, class_first) class_first = None if name.endswith("First>"): class_first = class_ yield Codepoint(codepoint, class_) prev_codepoint = codepoint if class_first is not None: raise ValueError("Missing Last after First") for c in range(prev_codepoint + 1, NUM_CODEPOINTS): yield Codepoint(c, None) def compress_singletons(singletons): uppers = [] # (upper, # items in lowers) lowers = [] for i in singletons: upper = i >> 8 lower = i & 0xff if len(uppers) == 0 or uppers[-1][0] != upper: uppers.append((upper, 1)) else: upper, count = uppers[-1] uppers[-1] = upper, count + 1 lowers.append(lower) return uppers, lowers def compress_normal(normal): # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f # lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] prev_start = 0 for start, count in normal: truelen = start - prev_start falselen = count prev_start = start + count assert truelen < 0x8000 and falselen < 0x8000 entry = [] if truelen > 0x7f: entry.append(0x80 | (truelen >> 8)) entry.append(truelen & 0xff) else: entry.append(truelen & 0x7f) if falselen > 0x7f: entry.append(0x80 | (falselen >> 8)) entry.append(falselen & 0xff) else: entry.append(falselen & 0x7f) compressed.append(entry) return compressed def print_singletons(uppers, lowers, uppersname, lowersname): print(" static constexpr singleton {}[] = {{".format(uppersname)) for u, c in uppers: print(" {{{:#04x}, {}}},".format(u, c)) print(" };") print(" static constexpr unsigned char {}[] = {{".format(lowersname)) for i in range(0, len(lowers), 8): print(" {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8]))) print(" };") def print_normal(normal, normalname): print(" static constexpr unsigned char {}[] = {{".format(normalname)) for v in normal: print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) print(" };") def main(): file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") codepoints = get_codepoints(file) CUTOFF=0x10000 singletons0 = [] singletons1 = [] normal0 = [] normal1 = [] extra = [] for a, b in to_ranges(get_escaped(codepoints)): if a > 2 * CUTOFF: extra.append((a, b - a)) elif a == b - 1: if a & CUTOFF: singletons1.append(a & ~CUTOFF) else: singletons0.append(a) elif a == b - 2: if a & CUTOFF: singletons1.append(a & ~CUTOFF) singletons1.append((a + 1) & ~CUTOFF) else: singletons0.append(a) singletons0.append(a + 1) else: if a >= 2 * CUTOFF: extra.append((a, b - a)) elif a & CUTOFF: normal1.append((a & ~CUTOFF, b - a)) else: normal0.append((a, b - a)) singletons0u, singletons0l = compress_singletons(singletons0) singletons1u, singletons1l = compress_singletons(singletons1) normal0 = compress_normal(normal0) normal1 = compress_normal(normal1) print("""\ FMT_FUNC auto is_printable(uint32_t cp) -> bool {\ """) print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') print_normal(normal0, 'normal0') print_normal(normal1, 'normal1') print("""\ auto lower = static_cast(cp); if (cp < 0x10000) { return is_printable(lower, singletons0, sizeof(singletons0) / sizeof(*singletons0), singletons0_lower, normal0, sizeof(normal0)); } if (cp < 0x20000) { return is_printable(lower, singletons1, sizeof(singletons1) / sizeof(*singletons1), singletons1_lower, normal1, sizeof(normal1)); }\ """) for a, b in extra: print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b)) print("""\ return cp < 0x{:x}; }}\ """.format(NUM_CODEPOINTS)) if __name__ == '__main__': main() ================================================ FILE: support/python/mkdocstrings_handlers/cxx/__init__.py ================================================ # A basic mkdocstrings handler for {fmt}. # Copyright (c) 2012 - present, Victor Zverovich # https://github.com/fmtlib/fmt/blob/master/LICENSE # pyright: strict import os import xml.etree.ElementTree as ET from pathlib import Path from subprocess import PIPE, STDOUT, CalledProcessError, Popen from markupsafe import Markup from mkdocstrings import BaseHandler from typing_extensions import TYPE_CHECKING, Any, ClassVar, final, override if TYPE_CHECKING: from collections.abc import Mapping, MutableMapping from mkdocs.config.defaults import MkDocsConfig from mkdocstrings import CollectorItem, HandlerOptions @final class Definition: """A definition extracted by Doxygen.""" def __init__( self, name: str, kind: "str | None" = None, node: "ET.Element | None" = None, is_member: bool = False, ): self.name = name self.kind: "str | None" = None if kind is not None: self.kind = kind elif node is not None: self.kind = node.get("kind") self.desc: "list[ET.Element[str]] | None" = None self.id: "str | None" = name if not is_member else None self.members: "list[Definition] | None" = None self.params: "list[Definition] | None" = None self.template_params: "list[Definition] | None" = None self.trailing_return_type: "str | None" = None self.type: "str | None" = None # A map from Doxygen to HTML tags. tag_map = { "bold": "b", "emphasis": "em", "computeroutput": "code", "para": "p", "itemizedlist": "ul", "listitem": "li", } # A map from Doxygen tags to text. tag_text_map = {"codeline": "", "highlight": "", "sp": " "} def escape_html(s: str) -> str: return s.replace("<", "<") # Converts a node from doxygen to HTML format. def convert_node( node: ET.Element, tag: str, attrs: "Mapping[str, str] | None" = None ) -> str: if attrs is None: attrs = {} out: str = "<" + tag for key, value in attrs.items(): out += " " + key + '="' + value + '"' out += ">" if node.text: out += escape_html(node.text) out += doxyxml2html(list(node)) out += "" if node.tail: out += node.tail return out def doxyxml2html(nodes: "list[ET.Element]"): out = "" for n in nodes: tag = tag_map.get(n.tag) if tag: out += convert_node(n, tag) continue if n.tag == "programlisting" or n.tag == "verbatim": out += "

"
            out += convert_node(n, "code", {"class": "language-cpp"})
            out += "
" continue if n.tag == "ulink": out += convert_node(n, "a", {"href": n.attrib["url"]}) continue out += tag_text_map[n.tag] return out def convert_template_params(node: ET.Element) -> "list[Definition] | None": template_param_list = node.find("templateparamlist") if template_param_list is None: return None params: "list[Definition]" = [] for param_node in template_param_list.findall("param"): name = param_node.find("declname") if name is not None: name = name.text if name is None: name = "" param = Definition(name, "param") param_type = param_node.find("type") if param_type is not None: param.type = param_type.text params.append(param) return params def get_description(node: ET.Element) -> list[ET.Element]: return node.findall("briefdescription/para") + node.findall( "detaileddescription/para" ) def normalize_type(type_: str) -> str: type_ = type_.replace("< ", "<").replace(" >", ">") return type_.replace(" &", "&").replace(" *", "*") def convert_type(type_: "ET.Element | None") -> "str | None": if type_ is None: return None result = type_.text if type_.text else "" for ref in type_: if ref.text is None: raise ValueError result += ref.text if ref.tail: result += ref.tail if type_.tail is None: raise ValueError result += type_.tail.strip() return normalize_type(result) def convert_params(func: ET.Element) -> list[Definition]: params: "list[Definition]" = [] for p in func.findall("param"): declname = p.find("declname") if declname is None or declname.text is None: raise ValueError d = Definition(declname.text, "param") d.type = convert_type(p.find("type")) params.append(d) return params def convert_return_type(d: Definition, node: ET.Element) -> None: d.trailing_return_type = None if d.type == "auto" or d.type == "constexpr auto": argsstring = node.find("argsstring") if argsstring is None or argsstring.text is None: raise ValueError parts = argsstring.text.split(" -> ") if len(parts) > 1: d.trailing_return_type = normalize_type(parts[1]) def render_param(param: Definition) -> str: if param.type is None: raise ValueError return param.type + (f" {param.name}" if len(param.name) > 0 else "") def render_decl(d: Definition) -> str: text = "" if d.id is not None: text += f'\n' text += '
'

    text += "
" if d.template_params is not None: text += "template <" text += ", ".join([render_param(p) for p in d.template_params]) text += ">\n" text += "
" text += "
" end = ";" if d.kind is None: raise ValueError if d.kind == "function" or d.kind == "variable": if d.type is None: raise ValueError text += d.type + " " if len(d.type) > 0 else "" elif d.kind == "typedef": text += "using " elif d.kind == "define": end = "" else: text += d.kind + " " text += d.name if d.params is not None: params = ", ".join([ (p.type + " " if p.type else "") + p.name for p in d.params ]) text += "(" + escape_html(params) + ")" if d.trailing_return_type: text += " -⁠> " + escape_html(d.trailing_return_type) elif d.kind == "typedef": if d.type is None: raise ValueError text += " = " + escape_html(d.type) text += end text += "
" text += "
\n" if d.id is not None: text += "
\n" return text @final class CxxHandler(BaseHandler): name: ClassVar[str] = "cxx" domain: ClassVar[str] = "cxx" def __init__( self, config: "Mapping[str, Any]", base_dir: Path, **kwargs: Any ) -> None: super().__init__(**kwargs) self.config = config """The handler configuration.""" self.base_dir = base_dir """The base directory of the project.""" headers = [ "args.h", "base.h", "chrono.h", "color.h", "compile.h", "format.h", "os.h", "ostream.h", "printf.h", "ranges.h", "std.h", "xchar.h", ] # Run doxygen. cmd = ["doxygen", "-"] support_dir = Path(__file__).parents[3] top_dir = os.path.dirname(support_dir) include_dir = os.path.join(top_dir, "include", "fmt") self._ns2doxyxml: "dict[str, ET.ElementTree[ET.Element[str]]]" = {} build_dir = os.path.join(top_dir, "build") os.makedirs(build_dir, exist_ok=True) self._doxyxml_dir = os.path.join(build_dir, "doxyxml") p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT) _, _ = p.communicate( input=r""" PROJECT_NAME = fmt GENERATE_XML = YES GENERATE_LATEX = NO GENERATE_HTML = NO INPUT = {0} XML_OUTPUT = {1} QUIET = YES AUTOLINK_SUPPORT = NO MACRO_EXPANSION = YES PREDEFINED = _WIN32=1 \ __linux__=1 \ FMT_ENABLE_IF(...)= \ FMT_USE_USER_LITERALS=1 \ FMT_USE_ALIAS_TEMPLATES=1 \ FMT_USE_NONTYPE_TEMPLATE_ARGS=1 \ FMT_API= \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_END_NAMESPACE=}}" \ "FMT_DOC=1" """.format( " ".join([os.path.join(include_dir, h) for h in headers]), self._doxyxml_dir, ).encode("utf-8") ) if p.returncode != 0: raise CalledProcessError(p.returncode, cmd) # Merge all file-level XMLs into one to simplify search. self._file_doxyxml: "ET.ElementTree[ET.Element[str]] | None" = None for h in headers: filename = h.replace(".h", "_8h.xml") with open(os.path.join(self._doxyxml_dir, filename)) as f: doxyxml = ET.parse(f) if self._file_doxyxml is None: self._file_doxyxml = doxyxml continue root = self._file_doxyxml.getroot() for node in doxyxml.getroot(): root.append(node) def collect_compound(self, identifier: str, cls: "list[ET.Element]") -> Definition: """Collect a compound definition such as a struct.""" refid = cls[0].get("refid") if refid is None: raise ValueError path = os.path.join(self._doxyxml_dir, refid + ".xml") with open(path) as f: xml = ET.parse(f) node = xml.find("compounddef") if node is None: raise ValueError d = Definition(identifier, node=node) d.template_params = convert_template_params(node) d.desc = get_description(node) d.members = [] for m in node.findall( 'sectiondef[@kind="public-attrib"]/memberdef' ) + node.findall('sectiondef[@kind="public-func"]/memberdef'): name = m.find("name") if name is None or name.text is None: raise ValueError name = name.text # Doxygen incorrectly classifies members of private unnamed unions as # public members of the containing class. if name.endswith("_"): continue desc = get_description(m) if len(desc) == 0: continue kind = m.get("kind") member = Definition(name if name else "", kind=kind, is_member=True) type_ = m.find("type") if type_ is None: raise ValueError type_text = type_.text member.type = type_text if type_text else "" if kind == "function": member.params = convert_params(m) convert_return_type(member, m) member.template_params = None member.desc = desc d.members.append(member) return d @override def collect(self, identifier: str, options: "Mapping[str, Any]") -> Definition: qual_name = "fmt::" + identifier param_str = None paren = qual_name.find("(") if paren > 0: qual_name, param_str = qual_name[:paren], qual_name[paren + 1 : -1] colons = qual_name.rfind("::") namespace, name = qual_name[:colons], qual_name[colons + 2 :] # Load XML. doxyxml = self._ns2doxyxml.get(namespace) if doxyxml is None: path = f"namespace{namespace.replace('::', '_1_1')}.xml" with open(os.path.join(self._doxyxml_dir, path)) as f: doxyxml = ET.parse(f) self._ns2doxyxml[namespace] = doxyxml nodes = doxyxml.findall(f"compounddef/sectiondef/memberdef/name[.='{name}']/..") if len(nodes) == 0: if self._file_doxyxml is None: raise ValueError nodes = self._file_doxyxml.findall( f"compounddef/sectiondef/memberdef/name[.='{name}']/.." ) candidates: "list[str]" = [] for node in nodes: # Process a function or a typedef. params: "list[Definition] | None" = None d = Definition(name, node=node) if d.kind == "function": params = convert_params(node) params_type: "list[str]" = [] for p in params: if p.type is None: raise ValueError else: params_type.append(p.type) node_param_str = ", ".join(params_type) if param_str and param_str != node_param_str: candidates.append(f"{name}({node_param_str})") continue elif d.kind == "define": params = [] for p in node.findall("param"): defname = p.find("defname") if defname is None or defname.text is None: raise ValueError param = Definition(defname.text, kind="param") param.type = None params.append(param) d.type = convert_type(node.find("type")) d.template_params = convert_template_params(node) d.params = params convert_return_type(d, node) d.desc = get_description(node) return d cls = doxyxml.findall(f"compounddef/innerclass[.='{qual_name}']") if not cls: raise Exception(f"Cannot find {identifier}. Candidates: {candidates}") return self.collect_compound(identifier, cls) @override def render( self, data: "CollectorItem", options: "HandlerOptions", *, locale: "str | None" = None, ) -> str: d = data if d.id is not None: _ = self.do_heading(Markup(), 0, id=d.id) if d.desc is None: raise ValueError text = '
\n' text += render_decl(d) text += '
\n' text += doxyxml2html(d.desc) if d.members is not None: for m in d.members: text += self.render(m, options, locale=locale) text += "
\n" text += "
\n" return text def get_handler( handler_config: "MutableMapping[str, Any]", tool_config: "MkDocsConfig", **kwargs: Any ) -> CxxHandler: """Return an instance of `CxxHandler`. Arguments: handler_config: The handler configuration. tool_config: The tool (SSG) configuration. """ base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent return CxxHandler(config=handler_config, base_dir=base_dir, **kwargs) ================================================ FILE: support/python/mkdocstrings_handlers/cxx/templates/README ================================================ mkdocsstrings requires a handler to have a templates directory. ================================================ FILE: support/release.py ================================================ #!/usr/bin/env python3 """Make a release. Usage: release.py [] For the release command $FMT_TOKEN should contain a GitHub personal access token obtained from https://github.com/settings/tokens. """ from __future__ import print_function import datetime, docopt, errno, fileinput, json, os import re, shutil, sys from subprocess import check_call import urllib.request class Git: def __init__(self, dir): self.dir = dir def call(self, method, args, **kwargs): return check_call(['git', method] + list(args), **kwargs) def add(self, *args): return self.call('add', args, cwd=self.dir) def checkout(self, *args): return self.call('checkout', args, cwd=self.dir) def clean(self, *args): return self.call('clean', args, cwd=self.dir) def clone(self, *args): return self.call('clone', list(args) + [self.dir]) def commit(self, *args): return self.call('commit', args, cwd=self.dir) def pull(self, *args): return self.call('pull', args, cwd=self.dir) def push(self, *args): return self.call('push', args, cwd=self.dir) def reset(self, *args): return self.call('reset', args, cwd=self.dir) def update(self, *args): clone = not os.path.exists(self.dir) if clone: self.clone(*args) return clone def clean_checkout(repo, branch): repo.clean('-f', '-d') repo.reset('--hard') repo.checkout(branch) class Runner: def __init__(self, cwd): self.cwd = cwd def __call__(self, *args, **kwargs): kwargs['cwd'] = kwargs.get('cwd', self.cwd) check_call(args, **kwargs) def create_build_env(): """Create a build environment.""" class Env: pass env = Env() env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) env.build_dir = 'build' env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt')) return env if __name__ == '__main__': args = docopt.docopt(__doc__) env = create_build_env() fmt_repo = env.fmt_repo branch = args.get('') if branch is None: branch = 'master' if not fmt_repo.update('-b', branch, 'git@github.com:fmtlib/fmt'): clean_checkout(fmt_repo, branch) # Update the date in the changelog and extract the version and the first # section content. changelog = 'ChangeLog.md' changelog_path = os.path.join(fmt_repo.dir, changelog) is_first_section = True first_section = [] for i, line in enumerate(fileinput.input(changelog_path, inplace=True)): if i == 0: version = re.match(r'# (.*) - TBD', line).group(1) line = '# {} - {}\n'.format( version, datetime.date.today().isoformat()) elif not is_first_section: pass elif line.startswith('#'): is_first_section = False else: first_section.append(line) sys.stdout.write(line) if first_section[0] == '\n': first_section.pop(0) ns_version = None base_h_path = os.path.join(fmt_repo.dir, 'include', 'fmt', 'base.h') for line in fileinput.input(base_h_path): m = re.match(r'\s*inline namespace v(.*) .*', line) if m: ns_version = m.group(1) break major_version = version.split('.')[0] if not ns_version or ns_version != major_version: raise Exception(f'Version mismatch {ns_version} != {major_version}') # Workaround GitHub-flavored Markdown treating newlines as
. changes = '' code_block = False stripped = False for line in first_section: if re.match(r'^\s*```', line): code_block = not code_block changes += line stripped = False continue if code_block: changes += line continue if line == '\n' or re.match(r'^\s*\|.*', line): if stripped: changes += '\n' stripped = False changes += line continue if stripped: line = ' ' + line.lstrip() changes += line.rstrip() stripped = True fmt_repo.checkout('-B', 'release') fmt_repo.add(changelog) fmt_repo.commit('-m', 'Update version') # Build the docs and package. run = Runner(fmt_repo.dir) run('cmake', '.') run('make', 'doc', 'package_source') # Create a release on GitHub. fmt_repo.push('origin', 'release') auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')} req = urllib.request.Request( 'https://api.github.com/repos/fmtlib/fmt/releases', data=json.dumps({'tag_name': version, 'target_commitish': 'release', 'body': changes, 'draft': True}).encode('utf-8'), headers=auth_headers, method='POST') with urllib.request.urlopen(req) as response: if response.status != 201: raise Exception(f'Failed to create a release ' + '{response.status} {response.reason}') response_data = json.loads(response.read().decode('utf-8')) id = response_data['id'] # Upload the package. uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases' package = 'fmt-{}.zip'.format(version) req = urllib.request.Request( f'{uploads_url}/{id}/assets?name={package}', headers={'Content-Type': 'application/zip'} | auth_headers, data=open('build/fmt/' + package, 'rb').read(), method='POST') with urllib.request.urlopen(req) as response: if response.status != 201: raise Exception(f'Failed to upload an asset ' '{response.status} {response.reason}') short_version = '.'.join(version.split('.')[:-1]) check_call(['./mkdocs', 'deploy', short_version]) ================================================ FILE: test/CMakeLists.txt ================================================ add_subdirectory(gtest) set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc) add_library(test-main STATIC ${TEST_MAIN_SRC}) target_include_directories( test-main PUBLIC $) target_link_libraries(test-main gtest fmt) # Adds a test. # Usage: add_fmt_test(name srcs...) function (add_fmt_test name) cmake_parse_arguments(ADD_FMT_TEST "HEADER_ONLY;MODULE" "" "" ${ARGN}) set(sources ${name}.cc ${ADD_FMT_TEST_UNPARSED_ARGUMENTS}) if (ADD_FMT_TEST_HEADER_ONLY) set(sources ${sources} ${TEST_MAIN_SRC} ../src/os.cc) set(libs gtest fmt-header-only) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables) endif () elseif (ADD_FMT_TEST_MODULE) set(libs test-main) else () set(libs test-main fmt) endif () add_executable(${name} ${sources}) target_link_libraries(${name} ${libs}) if (ADD_FMT_TEST_HEADER_ONLY AND NOT FMT_UNICODE) target_compile_definitions(${name} PUBLIC FMT_UNICODE=0) endif () # Define if certain C++ features can be used. if (FMT_PEDANTIC) target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () if (FMT_WERROR) target_compile_options(${name} PRIVATE ${WERROR_FLAG}) endif () add_test(NAME ${name} COMMAND ${name}) endfunction () add_fmt_test(args-test) add_fmt_test(base-test) add_fmt_test(assert-test) add_fmt_test(chrono-test) add_fmt_test(color-test) add_fmt_test(gtest-extra-test) add_fmt_test(format-test mock-allocator.h) if (MSVC) target_compile_options(format-test PRIVATE /bigobj) endif () if (NOT (MSVC AND BUILD_SHARED_LIBS)) add_fmt_test(format-impl-test HEADER_ONLY header-only-test.cc) endif () add_fmt_test(ostream-test) add_fmt_test(compile-test) add_fmt_test(printf-test) add_fmt_test(ranges-test ranges-odr-test.cc) add_fmt_test(no-builtin-types-test HEADER_ONLY) add_fmt_test(scan-test HEADER_ONLY) add_fmt_test(std-test) try_compile( compile_result_unused ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_LIST_DIR}/detect-stdfs.cc OUTPUT_VARIABLE RAWOUTPUT) string(REGEX REPLACE ".*libfound \"([^\"]*)\".*" "\\1" STDLIBFS "${RAWOUTPUT}") if (STDLIBFS) target_link_libraries(std-test ${STDLIBFS}) endif () add_fmt_test(unicode-test HEADER_ONLY) if (MSVC) target_compile_options(unicode-test PRIVATE /utf-8) endif () add_fmt_test(xchar-test) add_fmt_test(enforce-checks-test) target_compile_definitions(enforce-checks-test PRIVATE -DFMT_ENFORCE_COMPILE_STRING) add_executable(perf-sanity perf-sanity.cc) target_link_libraries(perf-sanity fmt::fmt) if (FMT_MODULE) # Module tests are currently disabled. # add_fmt_test(module-test MODULE test-main.cc) # target_link_libraries(module-test fmt-module) endif () if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC) foreach (flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if (${flag_var} MATCHES "^(/|-)(MT|MTd)") set(MSVC_STATIC_RUNTIME ON) break() endif () endforeach () endif () if (NOT MSVC_STATIC_RUNTIME) add_executable(posix-mock-test posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC}) target_include_directories(posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include) target_link_libraries(posix-mock-test gtest) if (FMT_PEDANTIC) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () if (MSVC) target_compile_options(posix-mock-test PRIVATE /utf-8) endif () add_test(NAME posix-mock-test COMMAND posix-mock-test) add_fmt_test(os-test) endif () message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}") if (FMT_PEDANTIC) # Test that the library can be compiled with exceptions disabled. # -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822. if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG) endif () if (HAVE_FNO_EXCEPTIONS_FLAG) add_library(noexception-test ../src/format.cc noexception-test.cc) target_include_directories(noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) target_compile_options(noexception-test PRIVATE -fno-exceptions) target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () # Test that the library compiles without locale. add_library(nolocale-test ../src/format.cc) target_include_directories(nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include) target_compile_definitions(nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1) endif () # These tests are disabled on Windows because they take too long. # They are disabled on GCC < 4.9 because it can't parse UDLs without a space # after operator"" but that is an incorrect syntax for any more modern compiler. if (FMT_PEDANTIC AND NOT WIN32 AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) # Test if incorrect API usages produce compilation error. add_test( compile-error-test ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test" "${CMAKE_CURRENT_BINARY_DIR}/compile-error-test" --build-generator ${CMAKE_GENERATOR} --build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}" "-DFMT_DIR=${CMAKE_SOURCE_DIR}") # Test if the targets are found from the build directory. add_test( find-package-test ${CMAKE_CTEST_COMMAND} -C ${CMAKE_BUILD_TYPE} --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/find-package-test" "${CMAKE_CURRENT_BINARY_DIR}/find-package-test" --build-generator ${CMAKE_GENERATOR} --build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DFMT_DIR=${PROJECT_BINARY_DIR}" "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") # Test if the targets are found when add_subdirectory is used. add_test( add-subdirectory-test ${CMAKE_CTEST_COMMAND} -C ${CMAKE_BUILD_TYPE} --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/add-subdirectory-test" "${CMAKE_CURRENT_BINARY_DIR}/add-subdirectory-test" --build-generator ${CMAKE_GENERATOR} --build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") endif () # This test is disabled on Windows because it is POSIX-specific. if (FMT_PEDANTIC AND NOT WIN32) add_test( static-export-test ${CMAKE_CTEST_COMMAND} -C ${CMAKE_BUILD_TYPE} --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/static-export-test" "${CMAKE_CURRENT_BINARY_DIR}/static-export-test" --build-generator ${CMAKE_GENERATOR} --build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") endif () # Activate optional CUDA tests if CUDA is found. For version selection see # https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features if (FMT_CUDA_TEST) if (${CMAKE_VERSION} VERSION_LESS 3.15) find_package(CUDA 9.0) else () include(CheckLanguage) check_language(CUDA) if (CMAKE_CUDA_COMPILER) enable_language(CUDA OPTIONAL) set(CUDA_FOUND TRUE) endif () endif () if (CUDA_FOUND) add_subdirectory(cuda-test) add_test(NAME cuda-test COMMAND fmt-in-cuda-test) endif () endif () enable_language(C) add_executable(c-test c-test.c) target_link_libraries(c-test PRIVATE fmt::fmt-c) add_test(NAME c-test COMMAND c-test) ================================================ FILE: test/add-subdirectory-test/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.8...3.25) project(fmt-test CXX) add_subdirectory(../.. fmt) add_executable(library-test main.cc) target_include_directories(library-test PUBLIC SYSTEM .) target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_link_libraries(library-test fmt::fmt) if (TARGET fmt::fmt-header-only) add_executable(header-only-test main.cc) target_include_directories(header-only-test PUBLIC SYSTEM .) target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_link_libraries(header-only-test fmt::fmt-header-only) endif () ================================================ FILE: test/add-subdirectory-test/main.cc ================================================ #include "fmt/base.h" int main(int argc, char** argv) { for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]); } ================================================ FILE: test/args-test.cc ================================================ // Formatting library for C++ - dynamic argument store tests // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #include "fmt/args.h" #include #include "gtest/gtest.h" TEST(args_test, basic) { fmt::dynamic_format_arg_store store; store.push_back(42); store.push_back("abc1"); store.push_back(1.5f); EXPECT_EQ("42 and abc1 and 1.5", fmt::vformat("{} and {} and {}", store)); } TEST(args_test, strings_and_refs) { // Unfortunately the tests are compiled with old ABI so strings use COW. fmt::dynamic_format_arg_store store; char str[] = "1234567890"; store.push_back(str); store.push_back(std::cref(str)); store.push_back(fmt::string_view{str}); str[0] = 'X'; auto result = fmt::vformat("{} and {} and {}", store); EXPECT_EQ("1234567890 and X234567890 and X234567890", result); } struct custom_type { int i = 0; }; FMT_BEGIN_NAMESPACE template <> struct formatter { auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } template auto format(const custom_type& p, FormatContext& ctx) const -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "cust={}", p.i); } }; FMT_END_NAMESPACE TEST(args_test, custom_format) { fmt::dynamic_format_arg_store store; auto c = custom_type(); store.push_back(c); ++c.i; store.push_back(c); ++c.i; store.push_back(std::cref(c)); ++c.i; auto result = fmt::vformat("{} and {} and {}", store); EXPECT_EQ("cust=0 and cust=1 and cust=3", result); } struct to_stringable { friend auto to_string_view(to_stringable) -> fmt::string_view { return {}; } }; FMT_BEGIN_NAMESPACE template <> struct formatter { auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } auto format(to_stringable, format_context& ctx) const -> decltype(ctx.out()) { return ctx.out(); } }; FMT_END_NAMESPACE TEST(args_test, to_string_and_formatter) { fmt::dynamic_format_arg_store store; auto s = to_stringable(); store.push_back(s); store.push_back(std::cref(s)); fmt::vformat("", store); } TEST(args_test, named_int) { fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("a1", 42)); EXPECT_EQ("42", fmt::vformat("{a1}", store)); } TEST(args_test, named_strings) { fmt::dynamic_format_arg_store store; char str[] = "1234567890"; store.push_back(fmt::arg("a1", str)); store.push_back(fmt::arg("a2", std::cref(str))); str[0] = 'X'; EXPECT_EQ("1234567890 and X234567890", fmt::vformat("{a1} and {a2}", store)); } TEST(args_test, named_arg_by_ref) { fmt::dynamic_format_arg_store store; char band[] = "Rolling Stones"; store.push_back(fmt::arg("band", std::cref(band))); band[9] = 'c'; // Changing band affects the output. EXPECT_EQ(fmt::vformat("{band}", store), "Rolling Scones"); } TEST(args_test, named_custom_format) { fmt::dynamic_format_arg_store store; auto c = custom_type(); store.push_back(fmt::arg("c1", c)); ++c.i; store.push_back(fmt::arg("c2", c)); ++c.i; store.push_back(fmt::arg("c_ref", std::cref(c))); ++c.i; auto result = fmt::vformat("{c1} and {c2} and {c_ref}", store); EXPECT_EQ("cust=0 and cust=1 and cust=3", result); } TEST(args_test, clear) { fmt::dynamic_format_arg_store store; store.push_back(42); auto result = fmt::vformat("{}", store); EXPECT_EQ("42", result); store.push_back(43); result = fmt::vformat("{} and {}", store); EXPECT_EQ("42 and 43", result); store.clear(); store.push_back(44); result = fmt::vformat("{}", store); EXPECT_EQ("44", result); } TEST(args_test, reserve) { fmt::dynamic_format_arg_store store; store.reserve(2, 1); store.push_back(1.5f); store.push_back(fmt::arg("a", 42)); auto result = fmt::vformat("{} and {a}", store); EXPECT_EQ("1.5 and 42", result); } struct copy_throwable { copy_throwable() {} copy_throwable(const copy_throwable&) { throw "deal with it"; } }; FMT_BEGIN_NAMESPACE template <> struct formatter { auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } auto format(copy_throwable, format_context& ctx) const -> decltype(ctx.out()) { return ctx.out(); } }; FMT_END_NAMESPACE TEST(args_test, throw_on_copy) { fmt::dynamic_format_arg_store store; store.push_back(std::string("foo")); try { store.push_back(copy_throwable()); } catch (...) { } EXPECT_EQ(fmt::vformat("{}", store), "foo"); } TEST(args_test, move_constructor) { using store_type = fmt::dynamic_format_arg_store; auto store = std::unique_ptr(new store_type()); store->push_back(42); store->push_back(std::string("foo")); store->push_back(fmt::arg("a1", "foo")); auto moved_store = std::move(*store); store.reset(); EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo"); } TEST(args_test, size) { fmt::dynamic_format_arg_store store; EXPECT_EQ(store.size(), 0); store.push_back(42); EXPECT_EQ(store.size(), 1); store.push_back("Molybdenum"); EXPECT_EQ(store.size(), 2); store.clear(); EXPECT_EQ(store.size(), 0); } ================================================ FILE: test/assert-test.cc ================================================ // Formatting library for C++ - FMT_ASSERT test // // It is a separate test to minimize the number of EXPECT_DEBUG_DEATH checks // which are slow on some platforms. In other tests FMT_ASSERT is made to throw // an exception which is much faster and easier to check. // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #include "fmt/base.h" #include "gtest/gtest.h" TEST(assert_test, fail) { #if GTEST_HAS_DEATH_TEST EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!"); #else fmt::print("warning: death tests are not supported\n"); #endif } TEST(assert_test, dangling_else) { bool test_condition = false; bool executed_else = false; if (test_condition) FMT_ASSERT(true, ""); else executed_else = true; EXPECT_TRUE(executed_else); } ================================================ FILE: test/base-test.cc ================================================ // Formatting library for C++ - core tests // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. // Turn assertion failures into exceptions for testing. // clang-format off #include "test-assert.h" // clang-format on // Suppress warnings for pathological types convertible to detail::value. #pragma GCC diagnostic ignored "-Wconversion" #include "fmt/base.h" #include // INT_MAX #include // strlen #include // std::equal_to #include // std::back_insert_iterator, std::distance #include // std::numeric_limits #include // std::string #include // std::is_same #include "gmock/gmock.h" #ifdef FMT_FORMAT_H_ # error base-test includes format.h #endif using testing::_; using testing::Invoke; using testing::Return; auto copy(fmt::string_view s, fmt::appender out) -> fmt::appender { for (char c : s) *out++ = c; return out; } TEST(string_view_test, value_type) { static_assert(std::is_same::value, ""); } TEST(string_view_test, ctor) { EXPECT_STREQ(fmt::string_view("abc").data(), "abc"); EXPECT_EQ(fmt::string_view("abc").size(), 3u); EXPECT_STREQ(fmt::string_view(std::string("defg")).data(), "defg"); EXPECT_EQ(fmt::string_view(std::string("defg")).size(), 4u); } TEST(string_view_test, length) { // Test that string_view::size() returns string length, not buffer size. char str[100] = "some string"; EXPECT_EQ(fmt::string_view(str).size(), strlen(str)); EXPECT_LT(strlen(str), sizeof(str)); } // Check string_view's comparison operator. template