Compare commits

..

14 Commits
10.x ... 4.x

Author SHA1 Message Date
Janusz Chorko
8f5e07656e Remove an old mingw workaround (#2059) (#3975)
Backports fix from master branch as the code no longer compiles
on mingw64/gcc 14.1.0 without gnu extensions enabled.
2024-05-25 07:49:24 -07:00
escherstair
3e8d2c57f3 Add CE6 support (#1749) 2020-07-07 09:29:16 -07:00
Mark Stapper
8ad1c12fb4 Make FMT_CTOR and FMT_WRAP1 macros work with bcc32 2019-03-31 07:21:28 -07:00
Mark Stapper
d2744bc848 Fix compatibility with bcc32 compiler
Resolve namespace issues
Add workaround for compile error on bool template argument of ArgArray struct
Squelch bcc32 warning on accessing the digits array
Squelch bcc32 warning on unused values
Fix warnings about redefinig macros and conditions always true
Disable "LConv" block for bcc32 compiler
Remove macro test for deprecated macro
Fix appveyor-build for cmake v3.13+
2019-03-27 10:10:18 -07:00
Victor Zverovich
b6d435b9a6 Fix an MSVC warning (#798) 2018-07-04 05:42:59 -07:00
Jean-Michaël Celerier
857b382fc3 Mark the whole class FormatError as FMT_API
Else on windows across DLLs the vtable is not visible which causes linking error
2018-05-09 06:12:31 -07:00
Henry Schreiner
b6ac63faf0 Fix warnings when with master project set to hidden 2018-04-27 06:34:52 -07:00
Gabi Melman
1d6188404c Added optional INILINE_BUFFER_SIZE template param to BasicMemoryWriter (#716) 2018-04-21 17:18:13 -07:00
Victor Zverovich
bdab94baf8 Merge branch '4.x' of github.com:fmtlib/fmt into 4.x 2018-04-12 06:07:55 -07:00
Victor Zverovich
a9c0bb4b16 Fix a warning on msvc/clang (#703) 2018-04-12 06:07:16 -07:00
Henry Schreiner
ea2cf449f7 Stop newer CMake's from warning about OLD policy 2018-04-11 09:48:36 -07:00
Henry Schreiner
5c0d7ee157 Fix return that can't be reached 2018-04-11 09:31:10 -07:00
Victor Zverovich
64440783ba Fix an issue with incorrect [[noreturn]] position in clang-cl (#701) 2018-04-08 11:56:47 -07:00
Victor Zverovich
1ecdc1a3bb Fix compilation on gcc 4.4 (#692) 2018-03-23 08:39:24 -07:00
164 changed files with 50825 additions and 67869 deletions

View File

@@ -1,8 +0,0 @@
# Run manually to reformat a file:
# clang-format -i --style=file <file>
Language: Cpp
BasedOnStyle: Google
IndentPPDirectives: AfterHash
IndentCaseLabels: false
AlwaysBreakTemplateDeclarations: false
DerivePointerAlignment: false

View File

@@ -1,8 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions" # Necessary to update action hashs
directory: "/"
schedule:
interval: "monthly"
# Allow up to 3 opened pull requests for github-actions versions
open-pull-requests-limit: 3

View File

@@ -1,6 +0,0 @@
<!--
Please make sure that the problem reproduces on the current master before
submitting an issue.
If possible please provide a repro on Compiler Explorer:
https://godbolt.org/z/fxccbh53W.
-->

3
.github/pull_request_template vendored Normal file
View File

@@ -0,0 +1,3 @@
<!---
Please make sure you've followed the guidelines outlined in the CONTRIBUTING.rst file.
--->

View File

@@ -1,7 +0,0 @@
<!--
Please read the contribution guidelines before submitting a pull request:
https://github.com/fmtlib/fmt/blob/master/CONTRIBUTING.md.
By submitting this pull request, you agree to license your contribution(s)
under the terms outlined in LICENSE.rst and represent that you have the right
to do so.
-->

View File

@@ -1,30 +0,0 @@
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@061583ebb5a96653e42feb3a97ee513eedc18078 # master
with:
oss-fuzz-project-name: 'fmt'
dry-run: false
language: c++
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@061583ebb5a96653e42feb3a97ee513eedc18078 # master
with:
oss-fuzz-project-name: 'fmt'
fuzz-seconds: 300
dry-run: false
language: c++
- name: Upload Crash
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts

View File

@@ -1,35 +0,0 @@
name: doc
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
# Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken.
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.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 python3-virtualenv
sudo npm install -g less clean-css
cmake -E make_directory ${{runner.workspace}}/build
- name: Build
working-directory: ${{runner.workspace}}/build
env:
KEY: ${{secrets.KEY}}
run: $GITHUB_WORKSPACE/support/build-docs.py

View File

@@ -1,26 +0,0 @@
name: lint
on:
pull_request:
paths:
- '**.h'
- '**.cc'
permissions:
contents: read
jobs:
format_code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install clang-format
uses: aminya/setup-cpp@v1
with:
clangformat: 17.0.5
- name: Run clang-format
run: |
find include src -name '*.h' -o -name '*.cc' | xargs clang-format -i -style=file -fallback-style=none
git diff --exit-code

View File

@@ -1,111 +0,0 @@
name: linux
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-20.04
strategy:
matrix:
cxx: [g++-4.8, g++-10, clang++-9]
build_type: [Debug, Release]
std: [11]
include:
- cxx: g++-4.8
install: sudo apt install g++-4.8
- cxx: g++-8
build_type: Debug
std: 14
install: sudo apt install g++-8
- cxx: g++-8
build_type: Debug
std: 17
install: sudo apt install g++-8
- cxx: g++-9
build_type: Debug
std: 17
- cxx: g++-10
build_type: Debug
std: 17
- cxx: g++-11
build_type: Debug
std: 20
install: sudo apt install g++-11
- cxx: clang++-8
build_type: Debug
std: 17
cxxflags: -stdlib=libc++
install: sudo apt install clang-8 libc++-8-dev libc++abi-8-dev
- cxx: clang++-9
install: sudo apt install clang-9
- cxx: clang++-9
build_type: Debug
fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
std: 17
install: sudo apt install clang-9
- cxx: clang++-11
build_type: Debug
std: 20
- cxx: clang++-11
build_type: Debug
std: 20
cxxflags: -stdlib=libc++
install: sudo apt install libc++-11-dev libc++abi-11-dev
- shared: -DBUILD_SHARED_LIBS=ON
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set timezone
run: sudo timedatectl set-timezone 'Asia/Yekaterinburg'
- name: Add repositories for older GCC
run: |
# Below two repos provide GCC 4.8, 5.5 and 6.4
sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ bionic main'
sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ bionic universe'
# Below two repos additionally update GCC 6 to 6.5
# sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ bionic-updates main'
# sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ bionic-updates universe'
if: ${{ matrix.cxx == 'g++-4.8' }}
- 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
${{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}} ${{matrix.fuzz}} ${{matrix.shared}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} -DFMT_DOC=OFF \
-DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \
-DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE
- 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

View File

@@ -1,55 +0,0 @@
name: macos
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [macos-11, macos-13]
build_type: [Debug, Release]
std: [11, 17, 20]
exclude:
- { os: macos-11, std: 20 }
- { os: macos-13, std: 11 }
- { os: macos-13, std: 17 }
include:
- shared: -DBUILD_SHARED_LIBS=ON
runs-on: '${{ matrix.os }}'
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set timezone
run: sudo systemsetup -settimezone 'Asia/Yekaterinburg'
- 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

View File

@@ -1,65 +0,0 @@
# 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@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
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@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.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@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
with:
sarif_file: results.sarif

View File

@@ -1,100 +0,0 @@
name: windows
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ${{matrix.os}}
strategy:
matrix:
# windows-2019 has MSVC 2019 installed;
# windows-2022 has MSVC 2022 installed:
# https://github.com/actions/virtual-environments.
os: [windows-2019]
platform: [Win32, x64]
toolset: [v140, v141, v142]
standard: [14, 17, 20]
shared: ["", -DBUILD_SHARED_LIBS=ON]
build_type: [Debug, Release]
exclude:
- { toolset: v140, standard: 17 }
- { toolset: v140, standard: 20 }
- { toolset: v141, standard: 14 }
- { toolset: v141, standard: 20 }
- { toolset: v142, standard: 14 }
- { platform: Win32, toolset: v140 }
- { platform: Win32, toolset: v141 }
- { platform: Win32, standard: 14 }
- { platform: Win32, standard: 20 }
- { platform: x64, toolset: v140, shared: -DBUILD_SHARED_LIBS=ON }
- { platform: x64, toolset: v141, shared: -DBUILD_SHARED_LIBS=ON }
- { 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@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set timezone
run: tzutil /s "Ekaterinburg 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 "Ekaterinburg Standard Time"
shell: cmd
- uses: msys2/setup-msys2@7efe20baefed56359985e327d329042cde2434ff # v2
with:
release: false
msystem: ${{matrix.sys}}
pacboy: cc:p cmake:p ninja:p lld:p
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.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

29
.gitignore vendored
View File

@@ -1,24 +1,19 @@
*.a
*.so*
*.xcodeproj
*~
.vscode/
/CMakeScripts
/Testing
bin/
/_CPack_Packages
/doc/doxyxml
/doc/html
/doc/node_modules
virtualenv
/Testing
/install_manifest.txt
*~
*.a
*.so*
*.zip
cmake_install.cmake
CPack*.cmake
fmt-*.cmake
CTestTestfile.cmake
CMakeCache.txt
CMakeFiles
CPack*.cmake
CTestTestfile.cmake
FMT.build
Makefile
bin/
build/
cmake_install.cmake
fmt-*.cmake
fmt.pc
virtualenv
run-msbuild.bat

27
.travis.yml Normal file
View File

@@ -0,0 +1,27 @@
language: cpp
dist: trusty
sudo: required # the doc target uses sudo to install dependencies
os:
- linux
- osx
env:
global:
- secure: |-
Gsnp9ERFnXt+diCfc7Vb72g+7HDn1MCHvw4zfUDdoBh9bxxFlLQRlzZZfwWhzni57lflrt
0QHXafu+oBVOJuNv6WauV3+ZyuWIQRmNGjZFNLvZsXHK/dyad2vGQBPvEkb+8l/aCyTpbr
6pxmyzLHSn1ZR7OX5rfPvwM3tOyZ3H0=
matrix:
- BUILD=Doc
- BUILD=Debug STANDARD=0x
- BUILD=Release STANDARD=98
- BUILD=Release STANDARD=0x
matrix:
exclude:
- os: osx
env: BUILD=Doc
script:
- support/travis-build.py

View File

@@ -4,7 +4,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := fmt_static
LOCAL_MODULE_FILENAME := libfmt
LOCAL_SRC_FILES := ../src/format.cc
LOCAL_SRC_FILES := fmt/format.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)

View File

@@ -1,176 +1,54 @@
cmake_minimum_required(VERSION 3.8...3.26)
message(STATUS "CMake version: ${CMAKE_VERSION}")
# 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})
cmake_minimum_required(VERSION 2.8.12)
if (POLICY CMP0048) # Version variables
cmake_policy(SET CMP0048 NEW)
endif ()
if (POLICY CMP0063) # Visibility
cmake_policy(SET CMP0063 NEW)
endif (POLICY CMP0063)
# 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)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(FMT_MASTER_PROJECT ON)
message(STATUS "CMake version: ${CMAKE_VERSION}")
endif ()
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
endif ()
# Joins arguments and places the results in ${result_var}.
function(join result_var)
set(result "")
set(result )
foreach (arg ${ARGN})
set(result "${result}${arg}")
endforeach ()
set(${result_var} "${result}" PARENT_SCOPE)
endfunction()
# DEPRECATED! Should be merged into add_module_library.
function(enable_module target)
if (MSVC)
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI})
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
endif ()
endfunction()
# Adds a library compiled with C++20 module support.
# `enabled` is a CMake variables that specifies if modules are enabled.
# If modules are disabled `add_module_library` falls back to creating a
# non-modular library.
#
# Usage:
# add_module_library(<name> [sources...] FALLBACK [sources...] [IF enabled])
function(add_module_library name)
cmake_parse_arguments(AML "" "IF" "FALLBACK" ${ARGN})
set(sources ${AML_UNPARSED_ARGUMENTS})
add_library(${name})
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX)
if (NOT ${${AML_IF}})
# Create a non-modular library.
target_sources(${name} PRIVATE ${AML_FALLBACK})
return()
endif ()
# Modules require C++20.
target_compile_features(${name} PUBLIC cxx_std_20)
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$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-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} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
endfunction()
include(CMakeParseArguments)
# Sets a cache variable with a docstring joined from multiple arguments:
# set(<variable> <value>... CACHE <type> <docstring>...)
# This allows splitting a long docstring for readability.
function(set_verbose)
# cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
# list instead.
list(GET ARGN 0 var)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 val)
list(REMOVE_AT ARGN 0)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 type)
list(REMOVE_AT ARGN 0)
join(doc ${ARGN})
set(${var} ${val} 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.")
if (NOT CMAKE_BUILD_TYPE)
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
endif ()
project(FMT CXX)
include(GNUInstallDirs)
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.")
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ON)
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 core requiring OS (Windows/Posix) " ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
option(FMT_USE_CPP11 "Enable the addition of C++11 compiler flags." ON)
if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.")
endif ()
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
endif ()
project(FMT)
# Get version from core.h
file(READ include/fmt/core.h core_h)
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.")
# Starting with cmake 3.0 VERSION is part of the project command.
file(READ fmt/format.h format_h)
if (NOT format_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from format.h.")
endif ()
# Use math to skip leading zeros if any.
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
@@ -182,72 +60,18 @@ message(STATUS "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 ()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(CheckCXXCompilerFlag)
include(JoinPaths)
include(cxx11)
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)
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wshadow -pedantic)
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
-Wnull-dereference -Wduplicated-cond)
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 (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
@@ -257,158 +81,20 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
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")
set(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 ()
function(add_headers VAR)
set(headers ${${VAR}})
foreach (header ${ARGN})
set(headers ${headers} include/fmt/${header})
endforeach()
set(${VAR} ${headers} PARENT_SCOPE)
endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS args.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_SOURCES src/format.cc)
if (FMT_OS)
set(FMT_SOURCES ${FMT_SOURCES} src/os.cc)
endif ()
add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
IF FMT_MODULE)
add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
enable_module(fmt)
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)
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN)
else ()
message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler")
check_symbol_exists(open fcntl.h HAVE_OPEN)
endif ()
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}"
# Workaround for Visual Studio 2017:
# Ensure the .pdb is created with the same name and in the same directory
# as the .lib. Newer VS versions already do this by default, but there is no
# harm in setting it for those too. Ignored by other generators.
COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
COMPILE_PDB_NAME "fmt"
COMPILE_PDB_NAME_DEBUG "fmt${FMT_DEBUG_POSTFIX}")
# 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 ()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
target_include_directories(fmt-header-only
${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
# 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)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} 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})
# 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})
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif ()
add_subdirectory(fmt)
if (FMT_DOC)
add_subdirectory(doc)
@@ -419,23 +105,11 @@ if (FMT_TEST)
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})
if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore.
file (STRINGS ${gitignore} lines)
list(REMOVE_ITEM lines /doc/html)
LIST(REMOVE_ITEM lines /doc/html)
foreach (line ${lines})
string(REPLACE "." "[.]" line "${line}")
string(REPLACE "*" ".*" line "${line}")
@@ -448,6 +122,6 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
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)
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst)
include(CPack)
endif ()

View File

@@ -1,20 +0,0 @@
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!

11
CONTRIBUTING.rst Normal file
View File

@@ -0,0 +1,11 @@
Contributing to fmt
===================
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 names
Thanks for contributing!

File diff suppressed because it is too large Load Diff

1038
ChangeLog.rst Normal file

File diff suppressed because it is too large Load Diff

27
LICENSE
View File

@@ -1,27 +0,0 @@
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.

23
LICENSE.rst Normal file
View File

@@ -0,0 +1,23 @@
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

490
README.md
View File

@@ -1,490 +0,0 @@
<img src="https://user-images.githubusercontent.com/576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png" alt="{fmt}" width="25%"/>
[![image](https://github.com/fmtlib/fmt/workflows/linux/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](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)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/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: <https://www.stopputin.net/>.
[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/Eq5763).
# Features
- Simple [format API](https://fmt.dev/latest/api.html) 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.html) 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.html#printf-formatting)
including the POSIX extension for positional arguments
- Extensibility: [support for user-defined
types](https://fmt.dev/latest/api.html#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, `core.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.rst)
- [Portability](https://fmt.dev/latest/index.html#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 <fmt/core.h>
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 <fmt/chrono.h>
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 <vector>
#include <fmt/ranges.h>
int main() {
std::vector<int> 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 <fmt/os.h>
int main() {
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
}
```
This can be [5 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 <fmt/color.h>
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.91 |
| libc++ | std::ostream | 2.49 |
| {fmt} 9.1 | fmt::print | 0.74 |
| Boost Format 1.80 | boost::format | 6.26 |
| Folly Format | folly::format | 1.87 |
{fmt} is the fastest of the benchmarked methods, \~20% faster than
`printf`.
The above results were generated by building `tinyformat_test.cpp` on
macOS 12.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](https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py)
from [format-benchmark](https://github.com/fmtlib/format-benchmark)
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 LLVM version 8.1.0 (clang-802.0.42), macOS
Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------|
| printf | 2.6 | 29 | 26 |
| printf+string | 16.4 | 29 | 26 |
| iostreams | 31.1 | 59 | 55 |
| {fmt} | 19.0 | 37 | 34 |
| Boost Format | 91.9 | 226 | 203 |
| Folly Format | 115.7 | 101 | 88 |
As you can see, {fmt} has 60% less overhead in terms of resulting binary
code size compared to iostreams and comes pretty close to `printf`.
Boost Format and Folly Format have the largest overheads.
`printf+string` is the same as `printf` but with an extra `<string>`
include to measure the overhead of the latter.
**Non-optimized build**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------|
| printf | 2.2 | 33 | 30 |
| printf+string | 16.0 | 33 | 30 |
| iostreams | 28.3 | 56 | 52 |
| {fmt} | 18.2 | 59 | 50 |
| Boost Format | 54.1 | 365 | 303 |
| Folly Format | 79.9 | 445 | 430 |
`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/usage.html#building-the-library) 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/) v17 (not yet
released) 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
- [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://lyft.github.io/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 executing 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/Function-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, STLSoft, which might be
too restrictive for using it 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.html) 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.

438
README.rst Normal file
View File

@@ -0,0 +1,438 @@
{fmt}
=====
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
:target: https://travis-ci.org/fmtlib/fmt
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://badges.gitter.im/Join%20Chat.svg
:alt: Join the chat at https://gitter.im/fmtlib/fmt
:target: https://gitter.im/fmtlib/fmt
**fmt** is an open-source formatting library for C++.
It can be used as a safe alternative to printf or as a fast
alternative to IOStreams.
`Documentation <http://fmtlib.net/latest/>`_
Features
--------
* Two APIs: faster concatenation-based `write API
<http://fmtlib.net/latest/api.html#write-api>`_ and slower,
but still very fast, replacement-based `format API
<http://fmtlib.net/latest/api.html#format-api>`_ with positional arguments
for localization.
* Write API similar to the one used by IOStreams but stateless allowing
faster implementation.
* Format API with `format string syntax
<http://fmtlib.net/latest/syntax.html>`_
similar to the one used by `str.format
<https://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.
* Safe `printf implementation
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_
including the POSIX extension for positional arguments.
* Support for user-defined types.
* High speed: performance of the format API is close to that of
glibc's `printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_
and better than the performance of IOStreams. See `Speed tests`_ and
`Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
* Small code size both in terms of source code (the core library consists of a single
header file and a single source file) and compiled code.
See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests
<https://github.com/fmtlib/fmt/tree/master/test>`_.
* Safety: the library is fully type safe, errors in format strings are
reported using exceptions, automatic memory management prevents buffer
overflow errors.
* Ease of use: small self-contained code base, no external dependencies,
permissive BSD `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with consistent output
across platforms and support for older compilers.
* Clean warning-free codebase even on high warning levels
(-Wall -Wextra -pedantic).
* Support for wide strings.
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
See the `documentation <http://fmtlib.net/latest/>`_ for more details.
Examples
--------
This prints ``Hello, world!`` to stdout:
.. code:: c++
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // uses printf format string syntax
Arguments can be accessed by position and arguments' indices can be repeated:
.. code:: c++
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
// s == "abracadabra"
fmt can be used as a safe portable replacement for ``itoa``:
.. code:: c++
fmt::MemoryWriter w;
w << 42; // replaces itoa(42, buffer, 10)
w << fmt::hex(42); // replaces itoa(42, buffer, 16)
// access the string using w.str() or w.c_str()
An object of any user-defined type for which there is an overloaded
:code:`std::ostream` insertion operator (``operator<<``) can be formatted:
.. code:: 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"
You can use the `FMT_VARIADIC
<http://fmtlib.net/latest/api.html#utilities>`_
macro to create your own functions similar to `format
<http://fmtlib.net/latest/api.html#format>`_ and
`print <http://fmtlib.net/latest/api.html#print>`_
which take arbitrary arguments:
.. code:: c++
// Prints formatted error message.
void report_error(const char *format, fmt::ArgList args) {
fmt::print("Error: ");
fmt::print(format, args);
}
FMT_VARIADIC(void, report_error, const char *)
report_error("file not found: {}", path);
Note that you only need to define one function that takes ``fmt::ArgList``
argument. ``FMT_VARIADIC`` automatically defines necessary wrappers that
accept variable number of arguments.
Projects using this library
---------------------------
* `0 A.D. <http://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
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus (Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to generate randomized datasets
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization framework
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
An MMO server, compatible with most Ultima Online clients
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance, associative database
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster proxy
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
Small crossplatform 2D graphic engine
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software
* `Scylla <http://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
`More... <https://github.com/search?q=cppformat&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 library and
FastFormat. 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 mostly solved with `__attribute__ ((format (printf, ...))
<http://gcc.gnu.org/onlinedocs/gcc/Function-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:
.. code:: c++
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
which is a lot of typing compared to printf:
.. code:: c++
printf("%.2f\n", 1.23456);
Matthew Wilson, the author of FastFormat, referred to this situation with
IOStreams as "chevron hell". IOStreams doesn't support positional arguments
by design.
The good part is that IOStreams supports user-defined types and is safe
although error reporting is awkward.
Boost Format library
~~~~~~~~~~~~~~~~~~~~
This is a very powerful library which supports both printf-like format
strings and positional arguments. The main its 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`_).
FastFormat
~~~~~~~~~~
This is an interesting library which 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, STLSoft, which might be
too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses printf-like format strings
and is type safe. It doesn't support user-defined types or positional
arguments. It makes unconventional use of ``operator()`` for passing
format arguments.
Tinyformat
~~~~~~~~~~
This library supports printf-like format strings and is very small and
fast. Unfortunately it doesn't support positional arguments and wrapping
it in C++98 is somewhat difficult. Also its performance and code compactness
are limited by IOStreams.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
This is not really 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::Writer`` on Karma's own benchmark,
see `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
Benchmarks
----------
Speed tests
~~~~~~~~~~~
The following speed tests results were generated by building
``tinyformat_test.cpp`` on Ubuntu GNU/Linux 14.04.1 with
``g++-4.8.2 -O3 -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 2000000 times with output sent to ``/dev/null``; for
further details see the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
EGLIBC 2.19 printf 1.30
libstdc++ 4.8.2 std::ostream 1.85
fmt 1.0 fmt::print 1.42
tinyformat 2.0.1 tfm::printf 2.25
Boost Format 1.54 boost::format 9.94
================= ============= ===========
As you can see ``boost::format`` is much slower than the alternative methods; this
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat
cannot be faster than the IOStreams because it uses them internally.
Performance of fmt is close to that of printf, being `faster than printf on integer
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_,
but slower on floating-point formatting which dominates this benchmark.
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
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 (g++-4.8.1, Ubuntu GNU/Linux 13.10,
best of three) is shown in the following tables.
**Optimized build (-O3)**
============ =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============ =============== ==================== ==================
printf 2.6 41 30
IOStreams 19.4 92 70
fmt 46.8 46 34
tinyformat 64.6 418 386
Boost Format 222.8 990 923
============ =============== ==================== ==================
As you can see, fmt has two times less overhead in terms of resulting
code size compared to IOStreams and comes pretty close to ``printf``.
Boost Format has by far the largest overheads.
**Non-optimized build**
============ =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============ =============== ==================== ==================
printf 2.1 41 30
IOStreams 19.7 86 62
fmt 47.9 108 86
tinyformat 27.7 234 190
Boost Format 122.6 884 763
============ =============== ==================== ==================
``libc``, ``libstdc++`` and ``libfmt`` are all linked as shared
libraries to compare formatting function overhead only. Boost Format
and tinyformat are header-only libraries so they don't provide any
linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ http://fmtlib.net/latest/usage.html#building-the-library
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
License
-------
fmt is distributed under the BSD `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax
<http://fmtlib.net/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. 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.
Acknowledgments
---------------
The fmt library is maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`_)
and Jonathan Müller (`foonathan <https://github.com/foonathan>`_) 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.
The benchmark section of this readme file and the performance tests are taken
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
written by Chris Foster. Boost Format library is acknowledged transitively
since it had some influence on tinyformat.
Some ideas used in the implementation are borrowed from `Loki
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
formatting. Thanks `Ruslan Baratov <https://github.com/ruslo>`_ for comprehensive
`comparison of integer formatting algorithms <https://github.com/ruslo/int-dec-format-tests>`_
and useful comments regarding performance, `Boris Kaul <https://github.com/localvoid>`_ for
`C++ counting digits benchmark <https://github.com/localvoid/cxx-benchmark-count-digits>`_.
Thanks to `CarterLi <https://github.com/CarterLi>`_ for contributing various
improvements to the code.

View File

@@ -1,26 +1,12 @@
find_program(DOXYGEN doxygen
PATHS "$ENV{ProgramFiles}/doxygen/bin"
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")
find_program(DOXYGEN doxygen)
if (NOT DOXYGEN)
message(STATUS "Target 'doc' disabled (requires doxygen)")
return ()
endif ()
# Find the Python interpreter and set the PYTHON_EXECUTABLE variable.
if (CMAKE_VERSION VERSION_LESS 3.12)
# This logic is deprecated in CMake after 3.12.
find_package(PythonInterp QUIET REQUIRED)
else ()
find_package(Python QUIET REQUIRED)
set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
endif ()
add_custom_target(doc
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/build.py
${FMT_VERSION}
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
SOURCES build.py conf.py _templates/layout.html)
include(GNUInstallDirs)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL
PATTERN ".doctrees" EXCLUDE)
DESTINATION share/doc/fmt OPTIONAL)

File diff suppressed because one or more lines are too long

View File

@@ -6,13 +6,14 @@
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
{# Google Analytics #}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;
a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-20116650-4', 'fmtlib.net');
ga('send', 'pageview');
</script>
{% endblock %}
@@ -57,7 +58,7 @@
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
{% for v in versions.split(',') %}
<li><a href="https://fmt.dev/{{v}}">{{v}}</a></li>
<li><a href="http://fmtlib.net/{{v}}">{{v}}</a></li>
{% endfor %}
</ul>
</li>
@@ -83,7 +84,7 @@
<div class="jumbotron">
<div class="tb-container">
<h1>{fmt}</h1>
<p class="lead">A modern formatting library</p>
<p class="lead">Small, safe and fast formatting library</p>
<div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success"

View File

@@ -4,675 +4,308 @@
API Reference
*************
The {fmt} library API consists of the following parts:
* :ref:`fmt/core.h <core-api>`: the core API providing main formatting functions
for ``char``/UTF-8 with C++20 compile-time checks and minimal dependencies
* :ref:`fmt/format.h <format-api>`: the full format API providing additional
formatting functions and locale support
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/std.h <std-api>`: formatters for standard library types
* :ref:`fmt/compile.h <compile-api>`: format string compilation
* :ref:`fmt/color.h <color-api>`: terminal color and text style
* :ref:`fmt/os.h <os-api>`: system APIs
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/args.h <args-api>`: dynamic argument lists
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
* :ref:`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_``.
.. _core-api:
Core API
========
``fmt/core.h`` defines the core 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 built-in and string types.
The following functions use :ref:`format string syntax <syntax>`
similar to that of Python's `str.format
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
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` 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`.
*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.
.. _format:
.. doxygenfunction:: format(format_string<T...> fmt, T&&... args) -> std::string
.. doxygenfunction:: vformat(string_view fmt, format_args args) -> std::string
.. doxygenfunction:: format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt
.. doxygenfunction:: format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T&&... args) -> format_to_n_result<OutputIt>
.. doxygenfunction:: formatted_size(format_string<T...> fmt, T&&... args) -> size_t
.. doxygenstruct:: fmt::format_to_n_result
:members:
.. _print:
.. doxygenfunction:: fmt::print(format_string<T...> fmt, T&&... args)
.. doxygenfunction:: fmt::vprint(string_view fmt, format_args args)
.. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args)
Compile-Time Format String Checks
---------------------------------
Compile-time format string checks are enabled by default on compilers
that support C++20 ``consteval``. On older compilers you can use the
:ref:`FMT_STRING <legacy-checks>`: macro defined in ``fmt/format.h`` instead.
Unused arguments are allowed as in Python's `str.format` and ordinary functions.
.. doxygenclass:: fmt::basic_format_string
:members:
.. doxygentypedef:: fmt::format_string
.. doxygenfunction:: fmt::runtime(string_view) -> runtime_format_string<>
.. _udt:
Formatting User-Defined Types
-----------------------------
The {fmt} library provides formatters for many standard C++ types.
See :ref:`fmt/ranges.h <ranges-api>` for ranges and tuples including standard
containers such as ``std::vector``, :ref:`fmt/chrono.h <chrono-api>` for date
and time formatting and :ref:`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.
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 (https://godbolt.org/z/nvME4arz8)::
#include <fmt/format.h>
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); // prints "7"
}
Using 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::
// color.h:
#include <fmt/core.h>
enum class color {red, green, blue};
template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
auto format(color c, format_context& ctx) const;
};
// color.cc:
#include "color.h"
#include <fmt/format.h>
auto fmt::formatter<color>::format(color c, format_context& ctx) const {
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<string_view>::format(name, ctx);
}
Note that ``formatter<string_view>::format`` is defined in ``fmt/format.h`` so
it has to be included in the source file. Since ``parse`` is inherited from
``formatter<string_view>`` it will recognize all string format specifications,
for example
.. code-block:: c++
fmt::format("{:>10}", color::blue)
will return ``" blue"``.
The experimental ``nested_formatter`` provides an easy way of applying a
formatter to one or more subobjects.
For example::
#include <fmt/format.h>
struct point {
double x, y;
};
template <>
struct fmt::formatter<point> : nested_formatter<double> {
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)]
Notice that fill, align and width are applied to the whole object which is the
recommended behavior while the remaining specifiers apply to elements.
In general the formatter has the following form::
template <> struct fmt::formatter<T> {
// 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::
// demo.h:
#include <type_traits>
#include <fmt/core.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return "A"; }
};
struct B : A {
virtual std::string name() const { return "B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> {
auto format(const A& a, format_context& ctx) const {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
// demo.cc:
#include "demo.h"
#include <fmt/format.h>
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}
Providing both a ``formatter`` specialization and a ``format_as`` overload is
disallowed.
Named Arguments
---------------
.. doxygenfunction:: fmt::arg(const S&, const T&)
Named arguments are not supported in compile-time checks at the moment.
Argument Lists
--------------
You can create your own formatting function with compile-time checks and small
binary footprint, for example (https://godbolt.org/z/vajfWEG4b):
.. code:: c++
#include <fmt/core.h>
void vlog(const char* file, int line, fmt::string_view format,
fmt::format_args args) {
fmt::print("{}: {}: ", file, line);
fmt::vprint(format, args);
}
template <typename... T>
void log(const char* file, int line, fmt::format_string<T...> format, T&&... args) {
vlog(file, line, format, fmt::make_format_args(args...));
}
#define MY_LOG(format, ...) log(__FILE__, __LINE__, format, __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.
.. doxygenfunction:: fmt::make_format_args(const Args&...)
.. doxygenclass:: fmt::format_arg_store
:members:
.. doxygenclass:: fmt::basic_format_args
:members:
.. doxygentypedef:: fmt::format_args
.. doxygenclass:: fmt::basic_format_arg
:members:
.. doxygenclass:: fmt::basic_format_parse_context
:members:
.. doxygenclass:: fmt::basic_format_context
:members:
.. doxygentypedef:: fmt::format_context
.. _args-api:
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.
.. doxygenclass:: fmt::dynamic_format_arg_store
:members:
Compatibility
-------------
.. doxygenclass:: fmt::basic_string_view
:members:
.. doxygentypedef:: fmt::string_view
.. _format-api:
All functions and classes provided by the fmt library reside
in namespace ``fmt`` and macros have prefix ``FMT_``. For brevity the
namespace is usually omitted in examples.
Format API
==========
``fmt/format.h`` defines the full format API providing additional formatting
functions and locale support.
The following functions defined in ``fmt/format.h`` use :ref:`format string
syntax <syntax>` similar to the one used by Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#str.format>`_ function.
They take *format_str* and *args* as arguments.
Literal-Based API
-----------------
*format_str* 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.
The following user-defined literals are defined in ``fmt/format.h``.
*args* is an argument list representing arbitrary arguments.
.. doxygenfunction:: operator""_a()
The `performance of the format API
<https://github.com/fmtlib/fmt/blob/master/README.rst#speed-tests>`_ is close
to that of glibc's ``printf`` and better than the performance of IOStreams.
For even better speed use the `write API`_.
Utilities
---------
.. _format:
.. doxygenfunction:: fmt::ptr(T p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T, Deleter> &p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
.. doxygenfunction:: format(CStringRef, ArgList)
.. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type<Enum>::type
.. doxygenfunction:: operator""_format(const char *, std::size_t)
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string
.. _print:
.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
.. doxygenfunction:: print(CStringRef, ArgList)
.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
.. doxygenfunction:: print(std::FILE *, CStringRef, ArgList)
.. doxygenfunction:: fmt::group_digits(T value) -> group_digits_view<T>
.. doxygenclass:: fmt::detail::buffer
.. doxygenclass:: fmt::BasicFormatter
:members:
.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
:members:
Date and time formatting
------------------------
System Errors
-------------
The library supports `strftime
<http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time
formatting::
{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.
#include "fmt/time.h"
.. doxygenfunction:: fmt::system_error
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));
.. doxygenfunction:: fmt::format_system_error
The format string syntax is described in the documentation of
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
Custom Allocators
-----------------
Formatting user-defined types
-----------------------------
The {fmt} library supports custom dynamic memory allocators.
A custom allocator class can be specified as a template argument to
:class:`fmt::basic_memory_buffer`::
A custom ``format_arg`` function may be implemented and used to format any
user-defined type. That is how date and time formatting described in the
previous section is implemented in :file:`fmt/time.h`. The following example
shows how to implement custom formatting for a user-defined structure.
using custom_memory_buffer =
fmt::basic_memory_buffer<char, fmt::inline_buffer_size, custom_allocator>;
::
It is also possible to write a formatting function that uses a custom
allocator::
struct MyStruct { double a, b; };
using custom_string =
std::basic_string<char, std::char_traits<char>, custom_allocator>;
custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
fmt::format_args args) {
auto buf = custom_memory_buffer(alloc);
fmt::vformat_to(std::back_inserter(buf), format_str, args);
return custom_string(buf.data(), buf.size(), alloc);
}
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args& ... args) {
return vformat(alloc, format_str, 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 <fmt/core.h>
#include <locale>
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 ``<locale>`` include.
.. doxygenfunction:: format(const Locale& loc, format_string<T...> fmt, T&&... args) -> std::string
.. doxygenfunction:: format_to(OutputIt out, const Locale& loc, format_string<T...> fmt, T&&... args) -> OutputIt
.. doxygenfunction:: formatted_size(const Locale& loc, format_string<T...> fmt, T&&... args) -> size_t
.. _legacy-checks:
Legacy Compile-Time Format String Checks
----------------------------------------
``FMT_STRING`` enables compile-time checks on older compilers. It requires C++14
or later and is a no-op in C++11.
.. doxygendefine:: 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.
.. _ranges-api:
Range and Tuple Formatting
==========================
The library also supports convenient formatting of ranges and tuples::
#include <fmt/ranges.h>
std::tuple<char, int, float> t{'a', 1, 2.0f};
// Prints "('a', 1, 2.0)"
fmt::print("{}", t);
NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main
``format.h`` header, but expect this to change in the future.
Using ``fmt::join``, you can separate tuple elements with a custom separator::
#include <fmt/ranges.h>
std::tuple<int, char> t = {1, 'a'};
// Prints "1, a"
fmt::print("{}", fmt::join(t, ", "));
.. _chrono-api:
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 :ref:`chrono-specs`.
**Example**::
#include <fmt/chrono.h>
int main() {
std::time_t t = std::time(nullptr);
// Prints "The date is 2020-11-07." (with the current date):
fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
using namespace std::literals::chrono_literals;
// Prints "Default format: 42s 100ms":
fmt::print("Default format: {} {}\n", 42s, 100ms);
// Prints "strftime-like format: 03:15:30":
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
void format_arg(fmt::BasicFormatter<char> &f,
const char *&format_str, const MyStruct &s) {
f.writer().write("[MyStruct: a={:.1f}, b={:.2f}]", s.a, s.b);
}
.. doxygenfunction:: localtime(std::time_t time)
MyStruct m = { 1, 2 };
std::string s = fmt::format("m={}", n);
// s == "m=[MyStruct: a=1.0, b=2.00]"
.. doxygenfunction:: gmtime(std::time_t time)
Note in the example above the ``format_arg`` function ignores the contents of
``format_str`` so the type will always be formatted as specified. See
``format_arg`` in :file:`fmt/time.h` for an advanced example of how to use
the ``format_str`` argument to customize the formatted output.
.. _std-api:
This technique can also be used for formatting class hierarchies::
Standard Library Types Formatting
=================================
``fmt/std.h`` provides formatters for:
* `std::filesystem::path <https://en.cppreference.com/w/cpp/filesystem/path>`_
* `std::thread::id <https://en.cppreference.com/w/cpp/thread/thread/id>`_
* `std::monostate <https://en.cppreference.com/w/cpp/utility/variant/monostate>`_
* `std::variant <https://en.cppreference.com/w/cpp/utility/variant/variant>`_
* `std::optional <https://en.cppreference.com/w/cpp/utility/optional>`_
Formatting Variants
-------------------
A ``std::variant`` is only formattable if every variant alternative is formattable, and requires the
``__cpp_lib_variant`` `library feature <https://en.cppreference.com/w/cpp/feature_test>`_.
**Example**::
#include <fmt/std.h>
std::variant<char, float> v0{'x'};
// Prints "variant('x')"
fmt::print("{}", v0);
std::variant<std::monostate, char> v1;
// Prints "variant(monostate)"
.. _compile-api:
Format String Compilation
=========================
``fmt/compile.h`` provides format string compilation enabled via the
``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. 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`` functions taking
the format context type as a template parameter in their ``formatter``
specializations. For example::
template <> struct fmt::formatter<point> {
constexpr auto parse(format_parse_context& ctx);
template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) const;
namespace local {
struct Parent {
Parent(int p) : p(p) {}
virtual void write(fmt::Writer &w) const {
w.write("Parent : p={}", p);
}
int p;
};
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.
struct Child : Parent {
Child(int c, int p) : Parent(p), c(c) {}
virtual void write(fmt::Writer &w) const {
w.write("Child c={} : ", c);
Parent::write(w);
}
int c;
};
.. doxygendefine:: FMT_COMPILE
void format_arg(fmt::BasicFormatter<char> &f,
const char *&format_str, const Parent &p) {
p.write(f.writer());
}
}
Local::Child c(1,2);
Local::Parent &p = c;
fmt::print("via ref to base: {}\n", p);
fmt::print("direct to child: {}\n", c);
.. doxygenfunction:: operator""_cf()
This section shows how to define a custom format function for a user-defined
type. The next section describes how to get ``fmt`` to use a conventional stream
output ``operator<<`` when one is defined for a user-defined type.
.. _color-api:
``std::ostream`` support
------------------------
Terminal Color and Text Style
=============================
The header ``fmt/ostream.h`` provides ``std::ostream`` support including
formatting of user-defined types that have overloaded ``operator<<``::
``fmt/color.h`` provides support for terminal color and text style output.
#include "fmt/ostream.h"
.. doxygenfunction:: print(const text_style &ts, const S &format_str, const Args&... args)
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day): year_(year), month_(month), day_(day) {}
.. doxygenfunction:: fg(detail::color_type)
.. doxygenfunction:: bg(detail::color_type)
.. doxygenfunction:: styled(const T& value, text_style ts)
.. _os-api:
System APIs
===========
.. doxygenclass:: fmt::ostream
:members:
.. doxygenfunction:: fmt::windows_error
.. _ostream-api:
``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 <fmt/ostream.h>
struct date {
int year, month, day;
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year << '-' << d.month << '-' << d.day;
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
template <> struct fmt::formatter<date> : ostream_formatter {};
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
// s == "The date is 2012-12-9"
.. doxygenfunction:: streamed(const T &)
.. doxygenfunction:: print(std::ostream&, CStringRef, ArgList)
.. doxygenfunction:: print(std::ostream &os, format_string<T...> fmt, T&&... args)
Argument formatters
-------------------
.. _printf-api:
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
``printf`` Formatting
=====================
// A custom argument formatter that formats negative integers as unsigned
// with the ``x`` format specifier.
class CustomArgFormatter :
public fmt::BasicArgFormatter<CustomArgFormatter, char> {
public:
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
fmt::FormatSpec &s, const char *fmt)
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
void visit_int(int value) {
if (spec().type() == 'x')
visit_uint(value); // convert to unsigned and format
else
fmt::BasicArgFormatter<CustomArgFormatter, char>::visit_int(value);
}
};
std::string custom_format(const char *format_str, fmt::ArgList args) {
fmt::MemoryWriter writer;
// Pass custom argument formatter as a template arg to BasicFormatter.
fmt::BasicFormatter<char, CustomArgFormatter> formatter(args, writer);
formatter.format(format_str);
return writer.str();
}
FMT_VARIADIC(std::string, custom_format, const char *)
std::string s = custom_format("{:x}", -42); // s == "ffffffd6"
.. doxygenclass:: fmt::ArgVisitor
:members:
.. doxygenclass:: fmt::BasicArgFormatter
:members:
.. doxygenclass:: fmt::ArgFormatter
:members:
Printf formatting
-----------------
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
<http://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.
.. doxygenfunction:: printf(string_view fmt, const T&... args) -> int
.. doxygenfunction:: printf(CStringRef, ArgList)
.. doxygenfunction:: fprintf(std::FILE *f, const S &fmt, const T&... args) -> int
.. doxygenfunction:: fprintf(std::FILE *, CStringRef, ArgList)
.. doxygenfunction:: sprintf(const S&, const T&...)
.. doxygenfunction:: fprintf(std::ostream&, CStringRef, ArgList)
.. _xchar-api:
.. doxygenfunction:: sprintf(CStringRef, ArgList)
``wchar_t`` Support
===================
.. doxygenclass:: fmt::PrintfFormatter
:members:
The optional header ``fmt/xchar.h`` provides support for ``wchar_t`` and exotic
character types.
.. doxygenclass:: fmt::BasicPrintfArgFormatter
:members:
.. doxygenstruct:: fmt::is_char
.. doxygenclass:: fmt::PrintfArgFormatter
:members:
.. doxygentypedef:: fmt::wstring_view
Write API
=========
.. doxygentypedef:: fmt::wformat_context
The write API provides classes for writing formatted data into character
streams. It is usually faster than the `format API`_ but, as IOStreams,
may result in larger compiled code size. The main writer class is
`~fmt::BasicMemoryWriter` which stores its output in a memory buffer and
provides direct access to it. It is possible to create custom writers that
store output elsewhere by subclassing `~fmt::BasicWriter`.
.. doxygenfunction:: fmt::to_wstring(const T &value)
.. doxygenclass:: fmt::BasicWriter
:members:
Compatibility with C++20 ``std::format``
========================================
.. doxygenclass:: fmt::BasicMemoryWriter
:members:
{fmt} implements nearly all of the `C++20 formatting library
<https://en.cppreference.com/w/cpp/utility/format>`_ with the following
differences:
.. doxygenclass:: fmt::BasicArrayWriter
:members:
* 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.
* Most C++20 chrono types are not supported yet.
.. doxygenclass:: fmt::BasicStringWriter
:members:
.. doxygenclass:: fmt::BasicContainerWriter
:members:
.. doxygenfunction:: bin(int)
.. doxygenfunction:: oct(int)
.. doxygenfunction:: hex(int)
.. doxygenfunction:: hexu(int)
.. doxygenfunction:: pad(int, unsigned, Char)
Utilities
=========
.. doxygenfunction:: fmt::arg(StringRef, const T&)
.. doxygenfunction:: operator""_a(const char *, std::size_t)
.. doxygendefine:: FMT_CAPTURE
.. doxygendefine:: FMT_VARIADIC
.. doxygenclass:: fmt::ArgList
:members:
.. doxygenfunction:: fmt::to_string(const T&)
.. doxygenfunction:: fmt::to_wstring(const T&)
.. doxygenclass:: fmt::BasicStringRef
:members:
.. doxygenclass:: fmt::BasicCStringRef
:members:
.. doxygenclass:: fmt::Buffer
:protected-members:
:members:
System errors
=============
.. doxygenclass:: fmt::SystemError
:members:
.. doxygenfunction:: fmt::format_system_error
.. doxygenclass:: fmt::WindowsError
:members:
.. _formatstrings:
Custom allocators
=================
The fmt library supports custom dynamic memory allocators.
A custom allocator class can be specified as a template argument to
:class:`fmt::BasicMemoryWriter`::
typedef fmt::BasicMemoryWriter<char, CustomAllocator> CustomMemoryWriter;
It is also possible to write a formatting function that uses a custom
allocator::
typedef std::basic_string<char, std::char_traits<char>, CustomAllocator>
CustomString;
CustomString format(CustomAllocator alloc, fmt::CStringRef format_str,
fmt::ArgList args) {
CustomMemoryWriter writer(alloc);
writer.write(format_str, args);
return CustomString(writer.data(), writer.size(), alloc);
}
FMT_VARIADIC(CustomString, format, CustomAllocator, fmt::CStringRef)

View File

@@ -90,14 +90,12 @@
VERSION: '{{ release|e }}',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
LINK_SUFFIX: '{{ link_suffix }}',
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}',
HAS_SOURCE: {{ has_source|lower }},
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
};
</script>
{%- for scriptfile in script_files %}
{{ js_tag(scriptfile) }}
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
{%- endmacro %}

View File

@@ -1,62 +1,81 @@
#!/usr/bin/env python3
#!/usr/bin/env python
# Build the documentation.
import errno, os, re, sys
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT
from __future__ import print_function
import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion
versions = [
'1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0',
'5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0',
'6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2',
'7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0']
versions += ['10.0.0', '10.1.0', '10.1.1', '10.1.1', '10.2.0', '10.2.1']
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0']
class Pip:
def __init__(self, venv_dir):
self.path = os.path.join(venv_dir, 'bin', 'pip')
def pip_install(package, commit=None, **kwargs):
"Install package using pip."
min_version = kwargs.get('min_version')
if min_version:
from pkg_resources import get_distribution, DistributionNotFound
try:
installed_version = get_distribution(os.path.basename(package)).version
if LooseVersion(installed_version) >= min_version:
print('{} {} already installed'.format(package, min_version))
return
except DistributionNotFound:
pass
if commit:
package = 'git+https://github.com/{0}.git@{1}'.format(package, commit)
print('Installing {0}'.format(package))
check_call(['pip', 'install', package])
def install(self, package, commit=None):
"Install package using pip."
if commit:
package = 'git+https://github.com/{0}.git@{1}'.format(package, commit)
print('Installing {0}'.format(package))
check_call([self.path, 'install', package])
def create_build_env(venv_dir='virtualenv'):
def create_build_env(dirname='virtualenv'):
# Create virtualenv.
if not os.path.exists(venv_dir):
check_call(['python3', '-m', 'venv', venv_dir])
# Install Sphinx and Breathe. Require the exact version of Sphinx which is
# compatible with Breathe.
pip = Pip(venv_dir)
pip.install('wheel')
pip.install('six')
# See: https://github.com/sphinx-doc/sphinx/issues/9777
pip.install('docutils==0.17.1')
# Jinja2 >= 3.1 incompatible with sphinx 3.3.0
# See: https://github.com/sphinx-doc/sphinx/issues/10291
pip.install('Jinja2<3.1')
pip.install('sphinx==3.3.0')
pip.install('michaeljones/breathe', 'v4.25.0')
if not os.path.exists(dirname):
check_call(['virtualenv', dirname])
import sysconfig
scripts_dir = os.path.basename(sysconfig.get_path('scripts'))
activate_this_file = os.path.join(dirname, scripts_dir, 'activate_this.py')
with open(activate_this_file) as f:
exec(f.read(), dict(__file__=activate_this_file))
# Import get_distribution after activating virtualenv to get info about
# the correct packages.
from pkg_resources import get_distribution, DistributionNotFound
# Upgrade pip because installation of sphinx with pip 1.1 available on Travis
# is broken (see #207) and it doesn't support the show command.
pip_version = get_distribution('pip').version
if LooseVersion(pip_version) < LooseVersion('1.5.4'):
print("Updating pip")
check_call(['pip', 'install', '--upgrade', 'pip'])
# Upgrade distribute because installation of sphinx with distribute 0.6.24
# available on Travis is broken (see #207).
try:
distribute_version = get_distribution('distribute').version
if LooseVersion(distribute_version) <= LooseVersion('0.6.24'):
print("Updating distribute")
check_call(['pip', 'install', '--upgrade', 'distribute'])
except DistributionNotFound:
pass
# Install Sphinx and Breathe.
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
min_version='1.4.1.dev20160531')
pip_install('michaeljones/breathe',
'6b1c5bb7a1866f15fc328b8716258354b10c1daa',
min_version='4.2.0')
def build_docs(version='dev', **kwargs):
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
work_dir = kwargs.get('work_dir', '.')
include_dir = kwargs.get(
'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt'))
include_dir = kwargs.get('include_dir',
os.path.join(os.path.dirname(doc_dir), 'fmt'))
# Build docs.
cmd = ['doxygen', '-']
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
p = Popen(cmd, stdin=PIPE)
doxyxml_dir = os.path.join(work_dir, 'doxyxml')
out, _ = p.communicate(input=r'''
p.communicate(input=r'''
PROJECT_NAME = fmt
GENERATE_LATEX = NO
GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = {0}/args.h {0}/chrono.h {0}/color.h {0}/core.h \
{0}/compile.h {0}/format.h {0}/os.h {0}/ostream.h \
{0}/printf.h {0}/xchar.h
INPUT = {0}/container.h {0}/format.h {0}/ostream.h \
{0}/printf.h {0}/string.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
AUTOLINK_SUPPORT = NO
@@ -67,49 +86,23 @@ def build_docs(version='dev', **kwargs):
ALIASES += "endrst=\endverbatim"
MACRO_EXPANSION = YES
PREDEFINED = _WIN32=1 \
__linux__=1 \
FMT_ENABLE_IF(...)= \
FMT_USE_VARIADIC_TEMPLATES=1 \
FMT_USE_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=1 \
FMT_USE_NONTYPE_TEMPLATE_ARGS=1 \
FMT_API= \
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}" \
"FMT_STRING_ALIAS=1" \
"FMT_VARIADIC(...)=" \
"FMT_VARIADIC_W(...)=" \
"FMT_DOC=1"
EXCLUDE_SYMBOLS = fmt::formatter fmt::printf_formatter fmt::arg_join \
fmt::basic_format_arg::handle
FMT_API=
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
out = out.decode('utf-8')
internal_symbols = [
'fmt::detail::.*',
'basic_data<>',
'fmt::type_identity'
]
noisy_warnings = [
'warning: (Compound|Member .* of class) (' + '|'.join(internal_symbols) + \
') is not documented.',
'warning: Internal inconsistency: .* does not belong to any container!'
]
for w in noisy_warnings:
out = re.sub('.*' + w + '\n', '', out)
print(out)
if p.returncode != 0:
raise CalledProcessError(p.returncode, cmd)
html_dir = os.path.join(work_dir, 'html')
main_versions = reversed(versions[-3:])
check_call([os.path.join(work_dir, 'virtualenv', 'bin', 'sphinx-build'),
check_call(['sphinx-build',
'-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir),
'-Dversion=' + version, '-Drelease=' + version,
'-Aversion=' + version, '-Aversions=' + ','.join(main_versions),
'-b', 'html', doc_dir, html_dir])
try:
check_call(['lessc', '--verbose', '--clean-css',
check_call(['lessc', '--clean-css',
'--include-path=' + os.path.join(doc_dir, 'bootstrap'),
os.path.join(doc_dir, 'fmt.less'),
os.path.join(html_dir, '_static', 'fmt.css')])

View File

@@ -47,7 +47,7 @@ source_suffix = '.rst'
# General information about the project.
project = u'fmt'
copyright = u'2012-present, Victor Zverovich'
copyright = u'2012-2015, Victor Zverovich'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the

View File

@@ -56,11 +56,6 @@ div.sphinxsidebar {
padding: 0;
}
// Override center alignment in tables.
td {
text-align: left;
}
p.rubric {
margin-top: 10px;
}

View File

@@ -1,68 +1,72 @@
Overview
========
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
**fmt** (formerly cppformat) is an open-source formatting library.
It can be used as a safe alternative to printf or as a fast
alternative to C++ IOStreams.
.. raw:: html
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for
a long time. Ive used both <code>boost::format</code> and
<code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
Thanks for creating this library. Its been a hole in C++ for a long
time. Ive used both boost::format and loki::SPrintf, and neither felt
like the right answer. This does.
</div>
</div>
.. _format-api-intro:
.. _format-api:
Format API
----------
The format API is similar in spirit to the C ``printf`` family of function but
is safer, simpler and several times `faster
<https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
than common standard library implementations.
The replacement-based Format API provides a safe alternative to ``printf``,
``sprintf`` and friends with comparable or `better performance
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
The `format string syntax <syntax.html>`_ is similar to the one used by
`str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ in
Python:
`str.format <http://docs.python.org/2/library/stdtypes.html#str.format>`_
in Python:
.. code:: c++
std::string s = fmt::format("The answer is {}.", 42);
fmt::format("The answer is {}", 42);
The ``fmt::format`` function returns a string "The answer is 42.". You can use
``fmt::memory_buffer`` to avoid constructing ``std::string``:
The ``fmt::format`` function returns a string "The answer is 42". You can use
``fmt::MemoryWriter`` to avoid constructing ``std::string``:
.. code:: c++
auto out = fmt::memory_buffer();
fmt::format_to(std::back_inserter(out),
"For a moment, {} happened.", "nothing");
auto data = out.data(); // pointer to the formatted data
auto size = out.size(); // size of the formatted data
fmt::MemoryWriter w;
w.write("Look, a {} string", 'C');
w.c_str(); // returns a C string (const char*)
The ``fmt::print`` function performs formatting and writes the result to a stream:
The ``fmt::print`` function performs formatting and writes the result to a file:
.. code:: c++
fmt::print(stderr, "System error code = {}\n", errno);
If you omit the file argument the function will print to ``stdout``:
The file argument can be omitted in which case the function prints to
``stdout``:
.. code:: c++
fmt::print("Don't {}\n", "panic");
The format API also supports positional arguments useful for localization:
If your compiler supports C++11, then the formatting functions are implemented
with variadic templates. Otherwise variadic functions are emulated by generating
a set of lightweight wrappers. This ensures compatibility with older compilers
while providing a natural API.
The Format API also supports positional arguments useful for localization:
.. code:: c++
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
You can pass named arguments with ``fmt::arg``:
Named arguments can be created with ``fmt::arg``. This makes it easier to track
what goes where when multiple values are being inserted:
.. code:: c++
@@ -74,125 +78,135 @@ an alternative, slightly terser syntax for named arguments:
.. code:: c++
using namespace fmt::literals;
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
"name"_a="World", "number"_a=42);
The ``_format`` suffix may be used to format string literals similar to Python:
.. code:: c++
std::string message = "{0}{1}{0}"_format("abra", "cad");
Other than the placement of the format string on the left of the operator,
``_format`` is functionally identical to ``fmt::format``. In order to use the
literal operators, they must be made visible with the directive
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
``_format`` but nothing else from the ``fmt`` namespace.
.. _write-api:
Write API
---------
The concatenation-based Write API (experimental) provides a `fast
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
stateless alternative to IOStreams:
.. code:: c++
fmt::MemoryWriter out;
out << "The answer in hexadecimal is " << hex(42);
.. _safety:
Safety
------
The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions or at compile
time. For example, the code
overflow, errors in format strings are reported using exceptions. For example,
the code
.. code:: c++
fmt::format("The answer is {:d}", "forty-two");
throws the ``format_error`` exception because the argument ``"forty-two"`` is a
string while the format code ``d`` only applies to integers.
throws a ``FormatError`` exception with description
"unknown format code 'd' for string", because the argument
``"forty-two"`` is a string while the format code ``d``
only applies to integers.
The code
.. code:: c++
format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error on compilers that support relaxed ``constexpr``.
See `here <api.html#compile-time-format-string-checks>`_ for details.
The following code
Where possible, errors are caught at compile time. For example, the code
.. code:: c++
fmt::format("Cyrillic letter {}", L'\x42e');
produces a compile-time error because wide character ``L'\x42e'`` cannot be
formatted into a narrow string. For comparison, writing a wide character to
``std::ostream`` results in its numeric value being written to the stream
(i.e. 1070 instead of letter 'ю' which is represented by ``L'\x42e'`` if we
use Unicode) which is rarely desirable.
Compact Binary Code
-------------------
The library produces compact per-call compiled code. For example
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
formatted into a narrow string. You can use a wide format string instead:
.. code:: c++
#include <fmt/core.h>
fmt::format(L"Cyrillic letter {}", L'\x42e');
int main() {
fmt::print("The answer is {}.", 42);
}
For comparison, writing a wide character to ``std::ostream`` results in
its numeric value being written to the stream (i.e. 1070 instead of letter 'ю'
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
needed.
compiles to just
.. code:: asm
main: # @main
sub rsp, 24
mov qword ptr [rsp], 42
mov rcx, rsp
mov edi, offset .L.str
mov esi, 17
mov edx, 1
call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
xor eax, eax
add rsp, 24
ret
.L.str:
.asciz "The answer is {}."
Note that fmt does not use the value of the ``errno`` global to communicate
errors to the user, but it may call system functions which set ``errno``. Since
fmt does not attempt to preserve the value of ``errno``, users should not make
any assumptions about it and always set it to ``0`` before making any system
calls that convey error information via ``errno``.
.. _portability:
Portability
-----------
The library is highly portable and relies only on a small set of C++11 features:
The library is highly portable. Here is an incomplete list of operating systems
and compilers where it has been tested and known to work:
* variadic templates
* type traits
* rvalue references
* decltype
* trailing return types
* deleted functions
* alias templates
* 64-bit (amd64) GNU/Linux with GCC 4.4.3,
`4.6.3 <https://travis-ci.org/fmtlib/fmt>`_, 4.7.2, 4.8.1, and Intel C++
Compiler (ICC) 14.0.2
These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent
compiler version. For older compilers use {fmt} `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which is maintained and
only requires C++98.
* 32-bit (i386) GNU/Linux with GCC 4.4.3, 4.6.3
The output of all formatting functions is consistent across platforms.
For example,
* Mac OS X with GCC 4.2.1 and Clang 4.2, 5.1.0
* 64-bit Windows with Visual C++ 2010, 2013 and
`2015 <https://ci.appveyor.com/project/vitaut/fmt>`_
* 32-bit Windows with Visual C++ 2010
Although the library uses C++11 features when available, it also works with
older compilers and standard library implementations. The only thing to keep in
mind for C++98 portability:
* Variadic templates: minimum GCC 4.4, Clang 2.9 or VS2013. This feature allows
the Format API to accept an unlimited number of arguments. With older
compilers the maximum is 15.
* User-defined literals: minimum GCC 4.7, Clang 3.1 or VS2015. The suffixes
``_format`` and ``_a`` are functionally equivalent to the functions
``fmt::format`` and ``fmt::arg``.
The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the
output of ``printf`` is platform-dependent in this case. For example,
.. code::
fmt::print("{}", std::numeric_limits<double>::infinity());
always prints ``inf`` while the output of ``printf`` is platform-dependent.
always prints ``inf``.
.. _ease-of-use:
Ease of Use
-----------
{fmt} has a small self-contained code base with the core library consisting of
just three header files and no external dependencies.
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
fmt has a small self-contained code base with the core library consisting of
a single header file and a single source file and no external dependencies.
A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects.
`Learn more... <contents.html>`_
.. raw:: html
<a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a>
<div class="section footer">
<iframe src="https://ghbtns.com/github-btn.html?user=fmtlib&amp;repo=fmt&amp;type=watch&amp;count=true"
<iframe src="http://ghbtns.com/github-btn.html?user=fmtlib&amp;repo=fmt&amp;type=watch&amp;count=true"
class="github-btn" width="100" height="20"></iframe>
</div>

View File

@@ -4,9 +4,8 @@
Format String Syntax
********************
Formatting functions such as :ref:`fmt::format() <format>` and
:ref:`fmt::print() <print>` use the same format string syntax described in this
section.
Formatting functions such as :ref:`fmt::format() <format>` and :ref:`fmt::print() <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
@@ -16,7 +15,7 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``.
The grammar for a replacement field is as follows:
.. productionlist:: sf
replacement_field: "{" [`arg_id`] [":" (`format_spec` | `chrono_format_spec`)] "}"
replacement_field: "{" [`arg_id`] [":" `format_spec`] "}"
arg_id: `integer` | `identifier`
integer: `digit`+
digit: "0"..."9"
@@ -27,8 +26,8 @@ The grammar for a replacement field is as follows:
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.
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 :ref:`formatspec` section.
@@ -54,8 +53,8 @@ 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.
argument id; format specifications are not allowed. This allows the
formatting of a value to be dynamically specified.
See the :ref:`formatexamples` section for some examples.
@@ -75,20 +74,20 @@ although some of the formatting options are only supported by the numeric types.
The general form of a *standard format specifier* is:
.. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`]["L"][`type`]
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
fill: <a character other than '{' or '}'>
align: "<" | ">" | "^"
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" | "?"
width: `integer` | "{" `arg_id` "}"
precision: `integer` | "{" `arg_id` "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "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 *fill* character can be any character 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:
@@ -101,6 +100,11 @@ The meaning of the various alignment options is as follows:
| ``'>'`` | Forces the field to be right-aligned within the |
| | available space (this is the default for numbers). |
+---------+----------------------------------------------------------+
| ``'='`` | Forces the padding to be placed after the sign (if any) |
| | but before the digits. This is used for printing fields |
| | in the form '+000000120'. This alignment option is only |
| | valid for numeric types. |
+---------+----------------------------------------------------------+
| ``'^'`` | Forces the field to be centered within the available |
| | space. |
+---------+----------------------------------------------------------+
@@ -109,21 +113,21 @@ 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:
The *sign* option is only valid for number 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. |
+---------+------------------------------------------------------------+
+---------+----------------------------------------------------------+
| Option | Meaning |
+=========+==========================================================+
| ``'+'`` | indicates that a sign should be used for both |
| | positive 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 |
| | positive 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
@@ -143,17 +147,15 @@ conversions, trailing zeros are not removed from the result.
.. ifconfig:: False
The ``','`` option signals the use of a comma for a thousands separator.
For a locale aware separator, use the ``'L'`` integer presentation type
For a locale aware separator, use the ``'n'`` integer presentation type
instead.
*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.
Preceding the *width* field by a zero (``'0'``) character enables
sign-aware zero-padding for numeric types. This is equivalent to a *fill*
character of ``'0'`` with an *alignment* type of ``'='``.
The *precision* is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted with
@@ -161,11 +163,7 @@ displayed after the decimal point for a floating-point value formatted with
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.
character, Boolean, and pointer values.
Finally, the *type* determines how the data should be presented.
@@ -177,9 +175,6 @@ The available string presentation types are:
| ``'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'``. |
+---------+----------------------------------------------------------+
@@ -191,9 +186,6 @@ The available character presentation types are:
| ``'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'``. |
+---------+----------------------------------------------------------+
@@ -210,8 +202,6 @@ The available integer presentation types are:
| | ``'#'`` 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. |
@@ -226,13 +216,16 @@ The available integer presentation types are:
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
+---------+----------------------------------------------------------+
| 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.
Integer presentation types can also be used with character and Boolean values.
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:
@@ -250,7 +243,7 @@ The available presentation types for floating-point values are:
| | 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. |
| | upper-case 'E' as the separator character. |
+---------+----------------------------------------------------------+
| ``'f'`` | Fixed point. Displays the number as a fixed-point |
| | number. |
@@ -270,10 +263,11 @@ The available presentation types for floating-point values are:
| | ``'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. |
| none | The same as ``'g'``. |
+---------+----------------------------------------------------------+
Floating-point formatting is locale-dependent.
.. ifconfig:: False
+---------+----------------------------------------------------------+
@@ -306,215 +300,9 @@ The available presentation types for pointers are:
| none | The same as ``'p'``. |
+---------+----------------------------------------------------------+
.. _chrono-specs:
Chrono Format Specifications
============================
Format specifications for chrono duration and time point types as well as
``std::tm`` have the following syntax:
.. productionlist:: sf
chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`]
chrono_specs: [`chrono_specs`] `conversion_spec` | `chrono_specs` `literal_char`
conversion_spec: "%" [`modifier`] `chrono_type`
literal_char: <a character other than '{', '}' or '%'>
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. "55". 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 character for the |
| | decimal point is localized according to the locale. 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.
.. range-specs:
Range Format Specifications
===========================
Format specifications for range types have the following syntax:
.. productionlist:: sf
range_format_spec: [":" [`underlying_spec`]]
The `underlying_spec` is parsed based on the formatter of the range's
reference type.
By default, a range of characters or strings is printed escaped and quoted. But
if any `underlying_spec` is provided (even if it is empty), then the characters
or strings are printed according to the provided specification.
Examples::
fmt::format("{}", std::vector{10, 20, 30});
// Result: [10, 20, 30]
fmt::format("{::#x}", std::vector{10, 20, 30});
// Result: [0xa, 0x14, 0x1e]
fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: ['h', 'e', 'l', 'l', 'o']
fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: [h, e, l, l, o]
fmt::format("{::d}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: [104, 101, 108, 108, 111]
.. _formatexamples:
Format Examples
Format examples
===============
This section contains examples of the format syntax and comparison with
@@ -529,94 +317,67 @@ following examples.
Accessing arguments by position::
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c"
fmt::format("{}, {}, {}", 'a', 'b', 'c');
format("{}, {}, {}", 'a', 'b', 'c');
// Result: "a, b, c"
fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
format("{2}, {1}, {0}", 'a', 'b', 'c');
// Result: "c, b, a"
fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
// Result: "abracadabra"
Aligning the text and specifying a width::
fmt::format("{:<30}", "left aligned");
format("{:<30}", "left aligned");
// Result: "left aligned "
fmt::format("{:>30}", "right aligned");
format("{:>30}", "right aligned");
// Result: " right aligned"
fmt::format("{:^30}", "centered");
format("{:^30}", "centered");
// Result: " centered "
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********"
Dynamic width::
fmt::format("{:<{}}", "left aligned", 30);
format("{:<{}}", "left aligned", 30);
// Result: "left aligned "
Dynamic precision::
fmt::format("{:.{}f}", 3.14, 1);
format("{:.{}f}", 3.14, 1);
// Result: "3.1"
Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::
fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
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
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}'
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::
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
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::
fmt::format("{:#04x}", 0);
// Result: "0x00"
Box drawing using Unicode fill::
fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
prints::
┌────────────────────┐
│ Hello, world! │
└────────────────────┘
Using type-specific formatting::
#include <fmt/chrono.h>
auto t = tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 7;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58
Using the comma as a thousands separator::
#include <fmt/format.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
.. ifconfig:: False
Using the comma as a thousands separator::
format("{:,}", 1234567890);
'1,234,567,890'
Using type-specific formatting::
>>> import datetime
>>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)
Format("{:%Y-%m-%d %H:%M:%S}") << d)
'2010-07-04 12:15:58'
Nesting arguments and more complex examples::
>>> for align, text in zip('<^>', ['left', 'center', 'right']):
@@ -645,3 +406,4 @@ Using the comma as a thousands separator::
9 9 11 1001
10 A 12 1010
11 B 13 1011

View File

@@ -2,20 +2,26 @@
Usage
*****
To use the {fmt} library, add :file:`fmt/core.h`, :file:`fmt/format.h`,
:file:`fmt/format-inl.h`, :file:`src/format.cc` and optionally other headers
from a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_ or
the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
To use the fmt library, add :file:`format.h` and :file:`format.cc` from
a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_
or the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
Alternatively, you can :ref:`build the library with CMake <building>`.
If you are using Visual C++ with precompiled headers, you might need to add
the line ::
#include "stdafx.h"
before other includes in :file:`format.cc`.
.. _building:
Building the Library
Building the library
====================
The included `CMake build script`__ can be used to build the fmt
library on a wide range of platforms. CMake is freely available for
download from https://www.cmake.org/download/.
download from http://www.cmake.org/download/.
__ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt
@@ -25,7 +31,7 @@ workflow starts with::
mkdir build # Create a directory to hold the build output.
cd build
cmake .. # Generate native build scripts.
cmake <path/to/fmt> # Generate native build scripts.
where :file:`{<path/to/fmt>}` is a path to the ``fmt`` repository.
@@ -39,7 +45,7 @@ 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.
If you use Windows and have Visual Studio installed, a :file:`FMT.sln`
If you use Windows and have Visual Studio installed, a :file:`FORMAT.sln`
file and several :file:`.vcproj` files will be created. You can then build them
using Visual Studio or msbuild.
@@ -50,129 +56,26 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
cmake -DBUILD_SHARED_LIBS=TRUE ...
__ https://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
Header-only usage with CMake
============================
To build a `static library` with position independent code (required if the main
consumer of the fmt library is a shared library i.e. a Python extension) set the
``CMAKE_POSITION_INDEPENDENT_CODE`` CMake variable to ``TRUE``::
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ...
Installing the Library
======================
After building the library you can install it on a Unix-like system by running
:command:`sudo make install`.
Usage with CMake
================
You can add the ``fmt`` library directory into your project and include it in
your ``CMakeLists.txt`` file::
In order to add ``fmtlib`` into an existing ``CMakeLists.txt`` file, you can add the ``fmt`` library directory into your main project, which will enable the ``fmt`` library::
add_subdirectory(fmt)
If you have a project called ``foo`` that you would like to link against the fmt library in a header-only fashion, you can enable with with::
or
::
target_link_libraries(foo PRIVATE fmt::fmt-header-only)
And then to ensure that the ``fmt`` library does not always get built, you can modify the call to ``add_subdirectory`` to read ::
add_subdirectory(fmt EXCLUDE_FROM_ALL)
This will ensure that the ``fmt`` library is exluded from calls to ``make``, ``make all``, or ``cmake --build .``.
to exclude it from ``make``, ``make all``, or ``cmake --build .``.
You can detect and use an installed version of {fmt} as follows::
find_package(fmt)
target_link_libraries(<your-target> fmt::fmt)
Setting up your target to use a header-only version of ``fmt`` is equally easy::
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
Usage with build2
=================
You can use `build2 <https://build2.org>`_, a dependency manager and a
build-system combined, to use ``fmt``.
Currently this package is available in these package repositories:
- **https://cppget.org/fmt/** for released and published versions.
- `The git repository with the sources of the build2 package of fmt <https://github.com/build2-packaging/fmt.git>`_
for unreleased or custom revisions of ``fmt``.
**Usage:**
- ``build2`` package name: ``fmt``
- Library target name : ``lib{fmt}``
For example, 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 ``v7.0.x``)::
depends: fmt ~7.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`.
For ``build2`` newcomers or to get more details and use cases, you can read the
``build2``
`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_.
Usage with Meson
================
`Meson's WrapDB <https://mesonbuild.com/Wrapdb-projects.html>` includes a ``fmt``
package, which repackages fmt to be built by Meson as a subproject.
**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`` may 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')
fmt_dep = fmt.get_variable('fmt_header_only_dep')
Building the Documentation
Building the documentation
==========================
To build the documentation you need the following software installed on your
@@ -192,45 +95,7 @@ the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documentation in ``doc/html``.
Conda
=====
fmt can 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>`__, as follows::
conda install -c conda-forge fmt
Vcpkg
=====
You can download and install fmt using the `vcpkg
<https://github.com/Microsoft/vcpkg>`__ dependency manager::
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install fmt
The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please `create an issue or pull
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
LHelper
=======
You can download and install fmt using
`lhelper <https://github.com/franko/lhelper>`__ dependency manager::
lhelper activate <some-environment>
lhelper install fmt
All the recipes for lhelper are kept in the
`lhelper's recipe <https://github.com/franko/lhelper-recipes>`__ repository.
Android NDK
===========
@@ -240,11 +105,11 @@ For an example of using fmt with Android NDK, see the
`android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_
repository.
__ https://github.com/fmtlib/fmt/blob/master/support/Android.mk
__ https://github.com/fmtlib/fmt/blob/master/Android.mk
Homebrew
========
fmt can be installed on OS X using `Homebrew <https://brew.sh/>`_::
fmt can be installed on OS X using `Homebrew <http://brew.sh/>`_::
brew install fmt

95
fmt/CMakeLists.txt Normal file
View File

@@ -0,0 +1,95 @@
# Define the fmt library, its includes and the needed defines.
# *.cc are added to FMT_HEADERS for the header-only configuration.
set(FMT_HEADERS container.h format.h format.cc ostream.h ostream.cc printf.h
printf.cc string.h time.h)
if (HAVE_OPEN)
set(FMT_HEADERS ${FMT_HEADERS} posix.h)
set(FMT_SOURCES ${FMT_SOURCES} posix.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} ../README.rst ../ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
# Starting with cmake 3.1 the CXX_STANDARD property can be used instead.
# Note: Don't make -std=c++11 public or interface, since it breaks projects
# that use C++14.
target_compile_options(fmt PRIVATE ${CPP11_FLAG})
if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
set_target_properties(fmt PROPERTIES DEBUG_POSTFIX d)
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE)
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed)
endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif ()
#------------------------------------------------------------------------------
# additionally define a header only library when cmake is new enough
if (CMAKE_VERSION VERSION_GREATER 3.1.0 OR CMAKE_VERSION VERSION_EQUAL 3.1.0)
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
endif ()
# Install targets.
if (FMT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(targets_export_name fmt-targets)
set (INSTALL_TARGETS fmt)
if (TARGET fmt-header-only)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
${version_config}
VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
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})
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
endif ()

82
fmt/container.h Normal file
View File

@@ -0,0 +1,82 @@
/*
Formatting library for C++ - standard container utilities
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_CONTAINER_H_
#define FMT_CONTAINER_H_
#include "format.h"
namespace fmt {
namespace internal {
/**
\rst
A "buffer" that appends data to a standard container (e.g. typically a
``std::vector`` or ``std::basic_string``).
\endrst
*/
template <typename Container>
class ContainerBuffer : public Buffer<typename Container::value_type> {
private:
Container& container_;
protected:
virtual void grow(std::size_t size) FMT_OVERRIDE {
container_.resize(size);
this->ptr_ = &container_[0];
this->capacity_ = size;
}
public:
explicit ContainerBuffer(Container& container) : container_(container) {
this->size_ = container_.size();
if (this->size_ > 0) {
this->ptr_ = &container_[0];
this->capacity_ = this->size_;
}
}
};
} // namespace internal
/**
\rst
This class template provides operations for formatting and appending data
to a standard *container* like ``std::vector`` or ``std::basic_string``.
**Example**::
void vecformat(std::vector<char>& dest, fmt::BasicCStringRef<char> format,
fmt::ArgList args) {
fmt::BasicContainerWriter<std::vector<char> > appender(dest);
appender.write(format, args);
}
FMT_VARIADIC(void, vecformat, std::vector<char>&,
fmt::BasicCStringRef<char>);
\endrst
*/
template <class Container>
class BasicContainerWriter
: public BasicWriter<typename Container::value_type> {
private:
internal::ContainerBuffer<Container> buffer_;
public:
/**
\rst
Constructs a :class:`fmt::BasicContainerWriter` object.
\endrst
*/
explicit BasicContainerWriter(Container& dest)
: BasicWriter<typename Container::value_type>(buffer_), buffer_(dest) {}
};
} // namespace fmt
#endif // FMT_CONTAINER_H_

517
fmt/format.cc Normal file
View File

@@ -0,0 +1,517 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include <string.h>
#include <cctype>
#if !defined(UNDER_CE)
# include <cerrno>
#endif
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
#if FMT_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 _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
FMT_MAYBE_UNUSED
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
FMT_MAYBE_UNUSED
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
namespace internal {
FMT_FUNC RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
} // namespace internal
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
# if !defined(UNDER_CE)
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
# else
int result = _vsnprintf_s(buffer, size, _TRUNCATE, format, args);
# endif
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# if defined(UNDER_CE)
# define FMT_SWPRINTF swprintf_s
# else
# define FMT_SWPRINTF swprintf
#endif
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
#if !defined(UNDER_CE)
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
#endif
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
#if !defined(UNDER_CE)
errno = 0;
buffer_ = strerror(error_code_);
return errno;
#else
return 0;
#endif
}
#ifdef __clang__
# pragma clang diagnostic pop
#endif
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT {
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
namespace internal {
template <typename T>
int CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
} // namespace internal
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
namespace internal {
FMT_FUNC void format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
} // namespace internal
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
} // namespace fmt
template <typename Char>
void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC fmt::internal::Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
fmt::internal::Arg arg = args_[arg_index];
switch (arg.type) {
case fmt::internal::Arg::NONE:
error = "argument index out of range";
break;
case fmt::internal::Arg::NAMED_ARG:
arg = *static_cast<const fmt::internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
namespace fmt {
FMT_FUNC void report_system_error(
int error_code, StringRef message) FMT_NOEXCEPT {
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, StringRef message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
namespace internal {
template void FixedBuffer<char>::grow(std::size_t);
template FMT_API int CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template FMT_API int CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void FixedBuffer<wchar_t>::grow(std::size_t);
template FMT_API int CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template FMT_API int CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
} //namespace internal
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

4254
fmt/format.h Normal file

File diff suppressed because it is too large Load Diff

35
fmt/ostream.cc Normal file
View File

@@ -0,0 +1,35 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
} // namespace fmt

109
fmt/ostream.h Normal file
View File

@@ -0,0 +1,109 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include "format.h"
#include <ostream>
namespace fmt {
namespace internal {
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
protected:
// The put-area is actually 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. The obvious
// 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.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
template <typename T>
typename EnableIf<sizeof(T) == 0>::type operator<<(const T &);
};
No &operator<<(std::ostream &, int);
template <typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
#pragma warning(suppress: 4244)
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
FMT_API void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

244
fmt/posix.cc Normal file
View File

@@ -0,0 +1,244 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
// Disable bogus MSVC warnings.
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "posix.h"
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <io.h>
#include <algorithm>
# ifndef O_CREAT
# define O_CREAT _O_CREAT
# endif
# ifndef O_TRUNC
# define O_TRUNC _O_TRUNC
# endif
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return static_cast<unsigned>(std::min<std::size_t>(count, UINT_MAX));
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
}
fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
fmt::BufferedFile::BufferedFile(
fmt::CStringRef filename, fmt::CStringRef mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
if (!file_)
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
}
void fmt::BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = FMT_NULL;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int fmt::BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
return fd;
}
fmt::File::File(fmt::CStringRef path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__BORLANDC__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(SystemError(errno, "cannot open file {}", path));
}
fmt::File::~File() FMT_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)
fmt::report_system_error(errno, "cannot close file");
}
void fmt::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(SystemError(errno, "cannot close file"));
}
fmt::LongLong fmt::File::size() const {
#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<HANDLE>(_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(WindowsError(GetLastError(), "cannot get file size"));
}
fmt::ULongLong long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(SystemError(errno, "cannot get file attributes"));
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
"return type of File::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t fmt::File::read(void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
fmt::File fmt::File::dup(int fd) {
// 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(SystemError(errno, "cannot duplicate file descriptor {}", fd));
return File(new_fd);
}
void fmt::File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(SystemError(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
}
}
void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
void fmt::File::pipe(File &read_end, File &write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
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(SystemError(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
}
fmt::BufferedFile fmt::File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
BufferedFile file(f);
fd_ = -1;
return file;
}
long fmt::getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
FMT_THROW(SystemError(errno, "cannot get memory page size"));
return size;
#endif
}

366
fmt/posix.h Normal file
View File

@@ -0,0 +1,366 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#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_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
# if defined(__BORLANDC__) && !defined(_dup2)
// for some reason the borland headers do define _dup but not _dup2
# define _dup2 dup2
# endif
# 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)
namespace fmt {
// An error code.
class ErrorCode {
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class BufferedFile {
private:
FILE *file_;
friend class File;
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL;
}
BufferedFile& operator=(BufferedFile &&other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
#endif
// Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int (fileno)() const;
void print(CStringRef format_str, const ArgList &args) {
fmt::print(file_, format_str, args);
}
FMT_VARIADIC(void, print, CStringRef)
};
// A file. Closed file is represented by a File object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::SystemError 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 File {
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
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.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a File object representing this file.
FMT_API File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
int fd;
};
public:
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
// A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
File& operator=(File &&other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
FMT_API BufferedFile fdopen(const char *mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) {
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
Type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const {
char *end = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; }
inline fmt::File &move(fmt::File &f) { return f; }
}
#endif
#endif // FMT_POSIX_H_

32
fmt/printf.cc Normal file
View File

@@ -0,0 +1,32 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "format.h"
#include "printf.h"
namespace fmt {
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template void PrintfFormatter<char>::format(CStringRef format);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
#endif // FMT_HEADER_ONLY
} // namespace fmt

603
fmt/printf.h Normal file
View File

@@ -0,0 +1,603 @@
/*
Formatting library for C++
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_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
namespace fmt {
namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker {
template <typename T>
static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <>
struct IntChecker<true> {
template <typename T>
static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int) { return true; }
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
public:
void report_unhandled_arg() {
FMT_THROW(FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) { return value == 0; }
};
// returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char> {
public:
char visit_char(int) { return 'c'; }
char visit_bool(bool) { return 's'; }
char visit_pointer(const void *) { return 'p'; }
template <typename T>
char visit_any_int(T) { return 'd'; }
template <typename T>
char visit_any_double(T) { return 'g'; }
char visit_unhandled_arg() { return 's'; }
};
template <typename T, typename U>
struct is_same {
enum { value = 0 };
};
template <typename T>
struct is_same<T, T> {
enum { value = 1 };
};
// An argument visitor that 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 <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
if (type_ != 's')
visit_any_int(value);
}
void visit_char(int value) {
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's') {
is_signed = std::numeric_limits<U>::is_signed;
}
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (const_check(sizeof(TargetType) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
} else {
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
} else {
if (is_signed) {
arg_.type = Arg::LONG_LONG;
// 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.
arg_.long_long_value = static_cast<LongLong>(value);
} else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> {
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value) {
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value)) {
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template <typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter :
public internal::ArgFormatterBase<Impl, Char, Spec> {
private:
void write_null_pointer() {
this->spec().type_ = 0;
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value) {
Spec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value) {
const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1) {
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT) {
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
} else {
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
} else {
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value) {
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
/** The default printf argument formatter. */
template <typename Char>
class PrintfArgFormatter :
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> {
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase {
private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
internal::Arg get_arg(
const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: internal::FormatterBase(al), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
for (;;) {
switch (*s++) {
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index) {
(void)s;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : internal::FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index
++s;
arg_index = value;
} else {
if (c == '0')
spec.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.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9') {
spec.width_ = internal::parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str();
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
} else if (*s == '*') {
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
} else {
spec.precision_ = 0;
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's') {
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
}
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type.
switch (spec.type_) {
case 'i': case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
}
inline void printf(Writer &w, CStringRef format, ArgList args) {
PrintfFormatter<char>(args, w).format(format);
}
FMT_VARIADIC(void, printf, Writer &, CStringRef)
inline void printf(WWriter &w, WCStringRef format, ArgList args) {
PrintfFormatter<wchar_t>(args, w).format(format);
}
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args) {
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args) {
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

148
fmt/string.h Normal file
View File

@@ -0,0 +1,148 @@
/*
Formatting library for C++ - string utilities
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifdef FMT_INCLUDE
# error "Add the fmt's parent directory and not fmt itself to includes."
#endif
#ifndef FMT_STRING_H_
#define FMT_STRING_H_
#include "format.h"
namespace fmt {
namespace internal {
// A buffer that stores data in ``std::basic_string``.
template <typename Char, typename Allocator = std::allocator<Char> >
class StringBuffer : public Buffer<Char> {
public:
typedef std::basic_string<Char, std::char_traits<Char>, Allocator> StringType;
private:
StringType data_;
protected:
virtual void grow(std::size_t size) FMT_OVERRIDE {
data_.resize(size);
this->ptr_ = &data_[0];
this->capacity_ = size;
}
public:
explicit StringBuffer(const Allocator &allocator = Allocator())
: data_(allocator) {}
// Moves the data to ``str`` clearing the buffer.
void move_to(StringType &str) {
data_.resize(this->size_);
str.swap(data_);
this->capacity_ = this->size_ = 0;
this->ptr_ = FMT_NULL;
}
};
} // namespace internal
/**
\rst
This class template provides operations for formatting and writing data
into a character stream. The output is stored in a ``std::basic_string``
that grows dynamically.
You can use one of the following typedefs for common character types
and the standard allocator:
+---------------+----------------------------+
| Type | Definition |
+===============+============================+
| StringWriter | BasicStringWriter<char> |
+---------------+----------------------------+
| WStringWriter | BasicStringWriter<wchar_t> |
+---------------+----------------------------+
**Example**::
StringWriter out;
out << "The answer is " << 42 << "\n";
This will write the following output to the ``out`` object:
.. code-block:: none
The answer is 42
The output can be moved to a ``std::basic_string`` with ``out.move_to()``.
\endrst
*/
template <typename Char, typename Allocator = std::allocator<Char> >
class BasicStringWriter : public BasicWriter<Char> {
private:
internal::StringBuffer<Char, Allocator> buffer_;
public:
/**
\rst
Constructs a :class:`fmt::BasicStringWriter` object.
\endrst
*/
explicit BasicStringWriter(const Allocator &allocator = Allocator())
: BasicWriter<Char>(buffer_), buffer_(allocator) {}
/**
\rst
Moves the buffer content to *str* clearing the buffer.
\endrst
*/
void move_to(std::basic_string<Char, std::char_traits<Char>, Allocator> &str) {
buffer_.move_to(str);
}
};
typedef BasicStringWriter<char> StringWriter;
typedef BasicStringWriter<wchar_t> WStringWriter;
/**
\rst
Converts *value* to ``std::string`` using the default format for type *T*.
**Example**::
#include "fmt/string.h"
std::string answer = fmt::to_string(42);
\endrst
*/
template <typename T>
std::string to_string(const T &value) {
fmt::MemoryWriter w;
w << value;
return w.str();
}
/**
\rst
Converts *value* to ``std::wstring`` using the default format for type *T*.
**Example**::
#include "fmt/string.h"
std::wstring answer = fmt::to_wstring(42);
\endrst
*/
template <typename T>
std::wstring to_wstring(const T &value) {
fmt::WMemoryWriter w;
w << value;
return w.str();
}
}
#endif // FMT_STRING_H_

143
fmt/time.h Normal file
View File

@@ -0,0 +1,143 @@
/*
Formatting library for C++ - time formatting
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // "deprecated" functions
#endif
namespace fmt {
template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm) {
if (*format_str == ':')
++format_str;
const char *end = format_str;
while (*end && *end != '}')
++end;
if (*end != '}')
FMT_THROW(FormatError("missing '}' in format string"));
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format.append(format_str, end + 1);
format[format.size() - 1] = '\0';
Buffer<char> &buffer = f.writer().buffer();
std::size_t start = buffer.size();
for (;;) {
std::size_t size = buffer.capacity() - start;
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
if (count != 0) {
buffer.resize(start + count);
break;
}
if (size >= format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
format_str = end + 1;
}
namespace internal{
inline Null<> localtime_r(...) { return Null<>(); }
inline Null<> localtime_s(...) { return Null<>(); }
inline Null<> gmtime_r(...) { return Null<>(); }
inline Null<> gmtime_s(...) { return Null<>(); }
}
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct LocalTime {
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::Null<>) {
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct GMTime {
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::Null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
} //namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif // FMT_TIME_H_

View File

@@ -1,235 +0,0 @@
// 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_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#include "core.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// 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 storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};
template <typename T>
using stored_type = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> 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<Context>;
auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg));
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) {
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
data_.insert(data_.begin(), {zero_ptr, 0});
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
constexpr dynamic_format_arg_store() = default;
/**
\rst
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<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> 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"
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::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 <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(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_ = detail::dynamic_arg_list();
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
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);
}
};
FMT_END_NAMESPACE
#endif // FMT_ARGS_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,643 +0,0 @@
// 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 {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_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 {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
: is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
} // namespace detail
/** A text style consisting of foreground and background colors and emphasis. */
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {}
FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
-> text_style {
return lhs |= rhs;
}
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return set_foreground_color;
}
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color;
}
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) noexcept
: set_foreground_color(), set_background_color(), ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style;
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
/** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background);
}
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
-> text_style {
return text_style(lhs) | rhs;
}
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::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_rgb) {
bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
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;
size_t index = 0;
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* {
return buffer + std::char_traits<Char>::length(buffer);
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
-> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg : detail::view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
detail::vformat_to(buf, format_str, args, {});
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace detail
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
format_args args) {
// Legacy wide streams are not supported.
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
if (detail::is_utf8()) {
detail::print(f, string_view(buf.begin(), buf.size()));
return;
}
buf.push_back('\0');
int result = std::fputs(buf.data(), f);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
/**
\rst
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);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
}
/**
\rst
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);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S>>
inline auto vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline auto format(const text_style& ts, const S& format_str,
const Args&... args) -> std::basic_string<Char> {
return fmt::vformat(ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
auto vformat_to(OutputIt out, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf, out);
}
/**
\rst
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<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <
typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value &&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
}
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
const auto& value = arg.value;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = std::copy(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = std::copy(background.begin(), background.end(), out);
}
out = formatter<T, Char>::format(value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
out = std::copy(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
/**
\rst
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)));
\endrst
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-> detail::styled_arg<remove_cvref_t<T>> {
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View File

@@ -1,535 +0,0 @@
// 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_
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end,
counting_iterator it) -> counting_iterator {
return it + (end - begin);
}
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
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);
\endrst
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
explicit constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1};
}
};
#endif
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return detail::get<N - 1>(rest...);
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type =
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
*out++ = value;
return out;
}
};
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) {
auto s = basic_string_view<Char>(arg);
return copy_str<Char>(s.begin(), s.end(), out);
}
return write<Char>(out, arg);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument with name.
template <typename Char> struct runtime_named_field {
using char_type = Char;
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
return true;
}
}
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
}
return out;
}
};
template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>();
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 <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
};
template <typename Char> struct parse_arg_id_result {
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
constexpr auto result = parse_specs<typename field_type<T>::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<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
}
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
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<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} 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<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
} else {
constexpr auto arg_id_result =
parse_arg_id<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.arg_id.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.val.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
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<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} 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<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
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 <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(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<decltype(first)>>::value) {
return fmt::to_string(first.value);
} else {
return fmt::to_string(first);
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), format_str,
std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args)
-> size_t {
return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>;
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
Str>();
}
} // namespace literals
#endif
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,455 +0,0 @@
// 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 <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#include "format.h"
#if defined __APPLE__ || defined(__FreeBSD__)
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
# endif
#endif
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
# endif
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // 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
/**
\rst
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<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> 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) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& 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<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept;
namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
}
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args);
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* 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 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
return vwindows_error(error_code, message, fmt::make_format_args(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
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
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.
buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept;
public:
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
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.
auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
}
};
#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) {}
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.
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;
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
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.
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 pipe setting up read_end and write_end file objects for reading
// and writing respectively.
// DEPRECATED! Taking files as out parameters is deprecated.
static void pipe(file& read_end, file& write_end);
// 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
};
// Returns the memory page size.
auto getpagesize() -> long;
namespace detail {
struct buffer_size {
buffer_size() = default;
size_t value = 0;
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;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
oflag = new_oflag;
}
template <typename... T>
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
};
class file_buffer final : public buffer<char> {
file file_;
FMT_API void grow(size_t) override;
public:
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other);
FMT_API ~file_buffer();
void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
void close() {
flush();
file_.close();
}
};
} // namespace detail
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
/**
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(std::back_inserter(buffer_), fmt,
fmt::make_format_args(args...));
}
};
/**
\rst
Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
*/
template <typename... T>
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_

View File

@@ -1,245 +0,0 @@
// 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_
#include <fstream> // std::filebuf
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& 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<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
#if FMT_MSC_VERSION
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
#else
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
void set_debug_format() = delete;
template <typename T, typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
};
using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
template <typename OutputIt>
auto format(detail::streamed_view<T> view,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
/**
\rst
Returns a view that formats `value` via an ostream ``operator<<``.
**Example**::
fmt::print("Current thread id: {}\n",
fmt::streamed(std::this_thread::get_id()));
\endrst
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
} // namespace detail
FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args);
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::is_utf8())
vprint(os, fmt, vargs);
else
detail::vprint_directly(os, fmt, vargs);
}
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View File

@@ -1,675 +0,0 @@
// 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_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
detail::buffer_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(detail::buffer_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
auto out() -> detail::buffer_appender<Char> { return out_; }
void advance_to(detail::buffer_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>();
return value <= max;
}
static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static auto fits_in_int(int) -> bool { return true; }
};
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int {
throw_format_error("precision is not integer");
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
struct is_zero_int {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> bool {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> bool {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
auto n = static_cast<int>(static_cast<target_type>(value));
arg_ = detail::make_arg<Context>(n);
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
arg_ = detail::make_arg<Context>(n);
}
} else {
if (is_signed) {
// 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.
auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else {
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
arg_ = detail::make_arg<Context>(n);
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::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 <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
auto c = static_cast<typename Context::char_type>(value);
arg_ = detail::make_arg<Context>(c);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::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 <typename Char> struct get_cstring {
template <typename T> 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.
template <typename Char> class printf_width_handler {
private:
format_specs<Char>& specs_;
public:
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) throw_format_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned {
throw_format_error("width is not integer");
return 0;
}
};
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The ``printf`` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<Char>;
context_type& context_;
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = presentation_type::none;
write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::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<T, Char>::value) {
base::operator()(value);
return;
}
format_specs<Char> fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
write<Char>(this->out, static_cast<Char>(value), fmt_specs);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) {
base::operator()(value);
}
/** Formats a null-terminated C string. */
void operator()(const char* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
/** Formats a null-terminated wide C string. */
void operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
void operator()(basic_string_view<Char> value) { base::operator()(value); }
/** Formats a pointer. */
void operator()(const void* value) {
if (value)
base::operator()(value);
else
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({});
handle.format(parse_ctx, context_);
}
};
template <typename Char>
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ':
if (specs.sign != sign::plus) specs.sign = sign::space;
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs<Char>& 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<int>();
} else {
if (c == '0') specs.fill[0] = '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) throw_format_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) throw_format_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<Char>(specs), get_arg(-1)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t)
-> 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':
return in(t, integral_set) ? pt::hex_lower : pt::none;
case 'X':
return in(t, integral_set) ? pt::hex_upper : pt::none;
case 'a':
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
case 'A':
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
case 'e':
return in(t, float_set) ? pt::exp_lower : pt::none;
case 'E':
return in(t, float_set) ? pt::exp_upper : pt::none;
case 'f':
return in(t, float_set) ? pt::fixed_lower : pt::none;
case 'F':
return in(t, float_set) ? pt::fixed_upper : pt::none;
case 'g':
return in(t, float_set) ? pt::general_lower : pt::none;
case 'G':
return in(t, float_set) ? pt::general_upper : 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 <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using iterator = buffer_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(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);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!find<false, Char>(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<Char>(start, to_unsigned(it - start)));
start = ++it;
continue;
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs<Char>();
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) throw_format_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;
specs.precision = static_cast<int>(
visit_format_arg(printf_precision_handler(), get_arg(-1)));
} 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 && arg.is_integral()) {
// Ignore '0' for non-numeric types or if '-' present.
specs.fill[0] = ' ';
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv);
}
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// 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<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(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<void>(arg, c);
}
// Parse type.
if (it == end) throw_format_error("invalid format string");
char type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (type) {
case 'i':
case 'u':
type = 'd';
break;
case 'c':
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
break;
}
}
specs.type = parse_printf_presentation_type(type, arg.type());
if (specs.type == presentation_type::none)
throw_format_error("invalid format specifier");
start = it;
// Format argument.
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
}
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
} // namespace detail
using printf_context = basic_printf_context<char>;
using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
}
// DEPRECATED!
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename Char>
inline auto vsprintf(
basic_string_view<Char> fmt,
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename Char>
inline auto vfprintf(
std::FILE* f, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(
basic_string_view<Char> fmt,
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int {
return vfprintf(stdout, fmt, args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_wprintf_args(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View File

@@ -1,738 +0,0 @@
// 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_
#include <initializer_list>
#include <tuple>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Range, typename OutputIt>
auto copy(const Range& range, OutputIt out) -> OutputIt {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIt>
auto copy(const char* str, OutputIt out) -> OutputIt {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
// Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static constexpr const bool value =
is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
#endif
};
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif
};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overload
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
// ADL overload. Only participates in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T,
void_t<
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())),
decltype(detail::range_end(std::declval<T>())),
// the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
# undef FMT_DECLTYPE_RETURN
#endif
// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is>
static auto check2(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>) -> std::true_type;
static auto check2(...) -> std::false_type;
template <std::size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(check2(
index_sequence<Is...>{},
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
public:
static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
template <typename Tuple, typename F, size_t... Is>
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get;
// Using a free function get<Is>(Tuple) now.
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
ignore_unused(unused);
}
template <typename Tuple, typename F>
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
std::forward<Tuple>(t), std::forward<F>(f));
}
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
}
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range>
using range_reference_type =
decltype(*detail::range_begin(std::declval<Range&>()));
#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 <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Formatter>
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
-> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
}
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
// These are not generic lambdas for compatibility with C++11.
template <typename ParseContext> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
ParseContext& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0)
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx));
++i;
}
int i;
FormatContext& ctx;
basic_string_view<char_type> separator;
};
} // namespace detail
template <typename T> struct is_tuple_like {
static constexpr const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<Tuple, Char>::value>> {
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '('>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ')'>{};
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it != '}')
FMT_THROW(format_error("invalid format specifier"));
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it;
}
template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
detail::for_each2(
formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy_str<Char>(closing_bracket_, ctx.out());
}
};
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_convertible<T, detail::std_string_view<Char>>::value;
};
namespace detail {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;
template <typename T,
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value) -> T&& {
return static_cast<T&&>(value);
}
template <typename T,
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value)
-> decltype(mapper().map(static_cast<T&&>(value))) {
return mapper().map(static_cast<T&&>(value));
}
};
template <typename Char, typename Element>
using range_formatter_type =
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>,
Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
#endif
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
template <typename T, typename Char>
struct range_formatter<
T, Char,
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
is_formattable<T, Char>>::value>> {
private:
detail::range_formatter_type<Char, T> underlying_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
public:
FMT_CONSTEXPR range_formatter() {}
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
return underlying_;
}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && *it == 'n') {
set_brackets({}, {});
++it;
}
if (it != end && *it != '}') {
if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
}
ctx.advance_to(it);
return underlying_.parse(ctx);
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out();
out = detail::copy_str<Char>(opening_bracket_, out);
int i = 0;
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it;
out = underlying_.format(mapper.map(item), ctx);
++i;
}
out = detail::copy_str<Char>(closing_bracket_, out);
return out;
}
};
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail {
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K, typename R, typename Char, typename Enable = void>
struct range_default_formatter;
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
template <range_format K, typename R, typename Char>
struct range_default_formatter<
K, R, Char,
enable_if_t<(K == range_format::sequence || K == range_format::map ||
K == range_format::set)>> {
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
underlying_.underlying().set_brackets({}, {});
underlying_.underlying().set_separator(
detail::string_literal<Char, ':', ' '>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return underlying_.format(range, ctx);
}
};
} // namespace detail
template <typename T, typename Char, typename Enable = void>
struct range_format_kind
: conditional_t<
is_range<T, Char>::value, detail::range_format_kind_<T>,
std::integral_constant<range_format, range_format::disabled>> {};
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
range_format::disabled>
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
,
detail::is_formattable_delayed<R, Char>
#endif
>::value>>
: detail::range_default_formatter<range_format_kind<R, Char>::value, R,
Char> {
};
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> 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 <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
}
private:
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
template <typename ParseContext>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, 0>)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename ParseContext, size_t N>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, N>)
-> decltype(ctx.begin()) {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
FMT_THROW(format_error("incompatible format specs for tuple elements"));
}
#endif
return end;
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_)
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
if (N > 1) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
return out;
}
};
namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> 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
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& t) -> all {
return {t.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(t), ctx);
}
};
FMT_BEGIN_EXPORT
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@@ -1,537 +0,0 @@
// 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 <atomic>
#include <bitset>
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "format.h"
#include "ostream.h"
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
#if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
#endif
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// 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
// Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& 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;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = *it++;
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
# ifdef _WIN32
auto path_string = !path_type_ ? p.native() : p.generic_wstring();
# else
auto path_string = !path_type_ ? p.native() : p.generic_string();
# endif
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
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<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
template <typename Char, typename OutputIt, typename T>
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (is_string<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v);
else
return write<Char>(out, v);
}
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_TYPEID != 0;
}
return it;
}
template <typename OutputIt>
auto format(const std::exception& ex,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
format_specs<Char> spec;
auto out = ctx.out();
if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec);
#if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex);
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
out = detail::write_bytes(out, demangled_name_view, spec);
# elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec);
# else
out = detail::write_bytes(out, string_view(ti.name()), spec);
# endif
*out++ = ':';
*out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec);
#endif
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@@ -1,259 +0,0 @@
// 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 <cwchar>
#include "format.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
loc_value value, const format_specs<wchar_t>& specs,
locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... T>
constexpr auto make_wformat_args(const T&... args)
-> format_arg_store<wformat_context, T...> {
return {args...};
}
inline namespace literals {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
constexpr auto operator""_a(const wchar_t* s, size_t)
-> detail::udl_arg<wchar_t> {
return {s};
}
#endif
} // namespace literals
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range>
auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args);
return to_string(buf);
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... T, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename Locale, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(
const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::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<buffer_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename Locale, typename S, typename... T,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename S, typename... T, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(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 <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename T> 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_

View File

@@ -1,108 +0,0 @@
module;
// Put all implementation-provided headers into the global module fragment
// to prevent attachment to this module.
#include <algorithm>
#include <cerrno>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <exception>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <variant>
#include <vector>
#include <version>
#if __has_include(<cxxabi.h>)
# include <cxxabi.h>
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <intrin.h>
#endif
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h>
#endif
#if __has_include(<winapifamily.h>)
# include <winapifamily.h>
#endif
#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# include <io.h>
# endif
#endif
#ifdef _WIN32
# if defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
export module fmt;
#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
// 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"
#include "fmt/os.h"
#include "fmt/printf.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
#include "format.cc"
#include "os.cc"

View File

@@ -1,43 +0,0 @@
// 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
namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept
-> dragonbox::decimal_fp<float>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
// Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void buffer<char>::append(const char*, const char*);
template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref);
// Explicit instantiations for wchar_t.
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail
FMT_END_NAMESPACE

402
src/os.cc
View File

@@ -1,402 +0,0 @@
// 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"
#include <climits>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize
# endif
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# 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 // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
#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(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
namespace detail {
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<wchar_t*>(&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 basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(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 = to_utf8<wchar_t>();
if (utf8_message.convert(msg)) {
return utf8_message.str();
}
}
return "unknown error";
}
};
} // namespace detail
FMT_API const std::error_category& system_category() noexcept {
static const detail::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<char>& out, int error_code,
const char* message) noexcept {
FMT_TRY {
auto&& msg = system_message(error_code);
if (msg) {
auto utf8_message = to_utf8<wchar_t>();
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 {
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")));
}
int buffered_file::descriptor() const {
#if !defined(fileno)
int fd = FMT_POSIX_CALL(fileno(file_));
#elif defined(FMT_HAS_SYSTEM)
// fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
# define FMT_DISABLE_MACRO
int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(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")));
}
long long file::size() const {
# 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<HANDLE>(_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(GetLastError(), "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
}
std::size_t file::read(void* buffer, std::size_t count) {
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);
}
std::size_t file::write(const void* buffer, std::size_t count) {
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);
}
file file::dup(int fd) {
// 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());
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
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 because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// 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<wchar_t>(path.c_str()).c_str()));
}
return file(fd);
}
# endif
# if !defined(__MSDOS__)
long getpagesize() {
# 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
namespace detail {
void file_buffer::grow(size_t) {
if (this->size() == this->capacity()) flush();
}
file_buffer::file_buffer(cstring_view path,
const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
file_buffer::file_buffer(file_buffer&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
file_buffer::~file_buffer() {
flush();
delete[] data();
}
} // namespace detail
ostream::~ostream() = default;
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@@ -1 +0,0 @@
<manifest package="dev.fmt" />

File diff suppressed because it is too large Load Diff

View File

@@ -2,3 +2,5 @@ This directory contains build support files such as
* CMake modules
* Build scripts
* qmake (static build with dynamic libc only)

20
support/Vagrantfile vendored
View File

@@ -1,20 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.disksize.size = '15GB'
config.vm.provider "virtualbox" 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

33
support/appveyor-build.py Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env python
# Build the project on AppVeyor.
import os
from subprocess import check_call
build = os.environ['BUILD']
config = os.environ['CONFIGURATION']
platform = os.environ.get('PLATFORM')
path = os.environ['PATH']
cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '.']
if build == 'mingw':
cmake_command.append('-GMinGW Makefiles')
build_command = ['mingw32-make', '-j4']
test_command = ['mingw32-make', 'test']
# Remove the path to Git bin directory from $PATH because it breaks
# MinGW config.
path = path.replace(r'C:\Program Files (x86)\Git\bin', '')
os.environ['PATH'] = r'C:\MinGW\bin;' + path
else:
# Add MSBuild 14.0 to PATH as described in
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\14.0\Bin;' + path
generator = 'Visual Studio 14 2015'
if platform == 'x64':
generator += ' Win64'
cmake_command.append('-G' + generator)
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
test_command = ['ctest', '-C', config]
check_call(cmake_command)
check_call(build_command)
check_call(test_command)

26
support/appveyor.yml Normal file
View File

@@ -0,0 +1,26 @@
configuration:
- Debug
- Release
environment:
CTEST_OUTPUT_ON_FAILURE: 1
matrix:
- BUILD: msvc
- BUILD: msvc
PLATFORM: x64
- BUILD: mingw
before_build:
# Workaround for CMake not wanting sh.exe on PATH for MinGW.
- set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
build_script:
- python support/appveyor-build.py
on_failure:
- appveyor PushArtifact Testing/Temporary/LastTest.log
- appveyor AddTest test
# Uncomment this to debug AppVeyor failures.
#on_finish:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))

View File

@@ -1 +0,0 @@
6.1.2

View File

@@ -1,28 +0,0 @@
cc_library(
name = "fmt",
srcs = [
#"src/fmt.cc", # No C++ module support
"src/format.cc",
"src/os.cc",
],
hdrs = [
"include/fmt/args.h",
"include/fmt/chrono.h",
"include/fmt/color.h",
"include/fmt/compile.h",
"include/fmt/core.h",
"include/fmt/format.h",
"include/fmt/format-inl.h",
"include/fmt/os.h",
"include/fmt/ostream.h",
"include/fmt/printf.h",
"include/fmt/ranges.h",
"include/fmt/std.h",
"include/fmt/xchar.h",
],
includes = [
"include",
],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)

View File

@@ -1,74 +0,0 @@
# Bazel support
To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`, `WORKSPACE.bazel`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project. This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}).
## Using {fmt} as a dependency
The following minimal example shows how to use {fmt} as a dependency within a Bazel project.
The following file structure is assumed:
```
example
├── BUILD.bazel
├── main.cpp
└── WORKSPACE.bazel
```
*main.cpp*:
```c++
#include "fmt/core.h"
int main() {
fmt::print("The answer is {}\n", 42);
}
```
The expected output of this example is `The answer is 42`.
*WORKSPACE.bazel*:
```python
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt",
patch_cmds = [
"mv support/bazel/.bazelversion .bazelversion",
"mv support/bazel/BUILD.bazel BUILD.bazel",
"mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel",
],
# Windows-related patch commands are only needed in the case MSYS2 is not installed.
# More details about the installation process of MSYS2 on Windows systems can be found here:
# https://docs.bazel.build/versions/main/install-windows.html#installing-compilers-and-language-runtimes
# Even if MSYS2 is installed the Windows related patch commands can still be used.
patch_cmds_win = [
"Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion",
"Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel",
"Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel",
],
)
```
In the *WORKSPACE* file, the {fmt} GitHub repository is fetched. Using the attribute `patch_cmds` the files `BUILD.bazel`, `WORKSPACE.bazel`, and `.bazelversion` are moved to the root of the {fmt} repository. This way the {fmt} repository is recognized as a bazelized workspace.
*BUILD.bazel*:
```python
cc_binary(
name = "Demo",
srcs = ["main.cpp"],
deps = ["@fmt"],
)
```
The *BUILD* file defines a binary named `Demo` that has a dependency to {fmt}.
To execute the binary you can run `bazel run //:Demo`.
# Using Bzlmod
The [Bazel Central Registry](https://github.com/bazelbuild/bazel-central-registry/tree/main/modules/fmt) also provides support for {fmt}.

View File

@@ -1 +0,0 @@
workspace(name = "fmt")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python
# Build the documentation in CI.
from __future__ import print_function
import errno, os, shutil, subprocess, sys, urllib
from subprocess import call, check_call, Popen, PIPE, STDOUT
def rmtree_if_exists(dir):
try:
shutil.rmtree(dir)
except OSError as e:
if e.errno == errno.ENOENT:
pass
# Build the docs.
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
import build
build.create_build_env()
html_dir = build.build_docs()
repo = 'fmtlib.github.io'
branch = os.environ['GITHUB_REF']
is_ci = 'CI' in os.environ
if is_ci and branch != 'refs/heads/master':
print('Branch: ' + branch)
exit(0) # Ignore non-master branches
if is_ci and 'KEY' not in os.environ:
# Don't update the repo if building in CI from an account that doesn't have
# push access.
print('Skipping update of ' + repo)
exit(0)
# Clone the fmtlib.github.io repo.
rmtree_if_exists(repo)
git_url = 'https://github.com/' if is_ci else 'git@github.com:'
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
# Copy docs to the repo.
target_dir = os.path.join(repo, 'dev')
rmtree_if_exists(target_dir)
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
if is_ci:
check_call(['git', 'config', '--global', 'user.name', 'fmtbot'])
check_call(['git', 'config', '--global', 'user.email', 'viz@fmt.dev'])
# Push docs to GitHub pages.
check_call(['git', 'add', '--all'], cwd=repo)
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
cmd = 'git push'
if is_ci:
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
# Print the output without the key.
print(p.communicate()[0].decode('utf-8').replace(os.environ['KEY'], '$KEY'))
if p.returncode != 0:
raise subprocess.CalledProcessError(p.returncode, cmd)

View File

@@ -1,132 +0,0 @@
import java.nio.file.Paths
// General gradle arguments for root project
buildscript {
repositories {
google()
jcenter()
}
dependencies {
//
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
//
// Notice that 4.0.0 here is the version of [Android Gradle Plugin]
// According to URL above you will need Gradle 6.1 or higher
//
classpath "com.android.tools.build:gradle:4.1.1"
}
}
repositories {
google()
jcenter()
}
// 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 {
compileSdkVersion 25 // Android 7.0
// 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
splits {
abi {
enable true
// Specify platforms for Application
reset()
include "arm64-v8a", "armeabi-v7a", "x86_64"
}
}
ndkVersion "21.3.6528147" // ANDROID_NDK_HOME is deprecated. Be explicit
defaultConfig {
minSdkVersion 21 // Android 5.0+
targetSdkVersion 25 // Follow Compile SDK
versionCode 34 // Follow release count
versionName "7.1.2" // Follow Official version
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 document
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.10.0+"
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"
}
}

View File

@@ -1,26 +0,0 @@
# 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.rst.
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons 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()

85
support/cmake/cxx11.cmake Normal file
View File

@@ -0,0 +1,85 @@
# C++11 feature support detection
if (NOT FMT_USE_CPP11)
return()
endif ()
include(CheckCXXCompilerFlag)
if (FMT_USE_CPP11)
check_cxx_compiler_flag(-std=c++11 HAVE_STD_CPP11_FLAG)
if (HAVE_STD_CPP11_FLAG)
# Check if including cmath works with -std=c++11 and -O3.
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -O3")
check_cxx_source_compiles("
#include <cmath>
int main() {}" FMT_CPP11_CMATH)
# Check if including <unistd.h> works with -std=c++11.
# It may not in MinGW due to bug http://sourceforge.net/p/mingw/bugs/2024/.
check_cxx_source_compiles("
#include <unistd.h>
int main() {}" FMT_CPP11_UNISTD_H)
# Check if snprintf works with -std=c++11. It may not in MinGW.
check_cxx_source_compiles("
#include <stdio.h>
int main() {
char buffer[10];
snprintf(buffer, 10, \"foo\");
}" FMT_CPP11_SNPRINTF)
if (FMT_CPP11_CMATH AND FMT_CPP11_UNISTD_H AND FMT_CPP11_SNPRINTF)
set(CPP11_FLAG -std=c++11)
else ()
check_cxx_compiler_flag(-std=gnu++11 HAVE_STD_GNUPP11_FLAG)
if (HAVE_STD_CPP11_FLAG)
set(CPP11_FLAG -std=gnu++11)
endif ()
endif ()
set(CMAKE_REQUIRED_FLAGS )
else ()
check_cxx_compiler_flag(-std=c++0x HAVE_STD_CPP0X_FLAG)
if (HAVE_STD_CPP0X_FLAG)
set(CPP11_FLAG -std=c++0x)
endif ()
endif ()
endif ()
if (CMAKE_CXX_STANDARD)
# Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified.
set(CPP11_FLAG )
endif ()
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
# Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
check_cxx_source_compiles("
template <class T, class ...Types>
struct S { typedef typename S<Types...>::type type; };
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
# Check if initializer lists are supported.
check_cxx_source_compiles("
#include <initializer_list>
int main() {}" SUPPORTS_INITIALIZER_LIST)
# Check if enum bases are available
check_cxx_source_compiles("
enum C : char {A};
int main() {}"
SUPPORTS_ENUM_BASE)
# Check if type traits are available
check_cxx_source_compiles("
#include <type_traits>
class C { void operator=(const C&); };
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
SUPPORTS_TYPE_TRAITS)
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
set(CMAKE_REQUIRED_FLAGS )

View File

@@ -1,7 +1,4 @@
@PACKAGE_INIT@
if (NOT TARGET fmt::fmt)
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
endif ()
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
check_required_components(fmt)

View File

@@ -1,11 +0,0 @@
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}

View File

@@ -0,0 +1,11 @@
@echo on
rem This scripts configures build environment and runs CMake.
rem Use it instead of running CMake directly when building with
rem the Microsoft SDK toolchain rather than Visual Studio.
rem It is used in the same way as cmake, for example:
rem
rem run-cmake -G "Visual Studio 10 Win64" .
for /F "delims=" %%i IN ('cmake "-DPRINT_PATH=1" -P %~dp0/FindSetEnv.cmake') DO set setenv=%%i
if NOT "%setenv%" == "" call "%setenv%"
cmake %*

View File

@@ -1,53 +0,0 @@
#!/usr/bin/env python
# Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print
# normalized (with most-significant bit equal to 1) significands in hexadecimal.
from __future__ import print_function
min_exponent = -348
max_exponent = 340
step = 8
significand_size = 64
exp_offset = 2000
class fp:
pass
powers = []
for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)):
result = fp()
n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp
k = significand_size + 1
# Convert to binary and round.
binary = '{:b}'.format(n)
result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2
result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size
powers.append(result)
# Sanity check.
exp_offset10 = 400
actual = result.f * 10 ** exp_offset10
if result.e > 0:
actual *= 2 ** result.e
else:
for j in range(-result.e):
actual /= 2
expected = 10 ** (exp_offset10 + exp)
precision = len('{}'.format(expected)) - len('{}'.format(actual - expected))
if precision < 19:
print('low precision:', precision)
exit(1)
print('Significands:', end='')
for i, fp in enumerate(powers):
if i % 3 == 0:
print(end='\n ')
print(' {:0<#16x}'.format(fp.f, ), end=',')
print('\n\nExponents:', end='')
for i, fp in enumerate(powers):
if i % 11 == 0:
print(end='\n ')
print(' {:5}'.format(fp.e), end=',')
print('\n\nMax exponent difference:',
max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:]))

29
support/fmt.pro Normal file
View File

@@ -0,0 +1,29 @@
# Staticlib configuration for qmake builds
# For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc
# from compilation so it _MUST_ be called as qmake -nodepend
# A workaround is implemented below: a custom compiler is defined which does not track dependencies
TEMPLATE = lib
TARGET = fmt
QMAKE_EXT_CPP = .cc
CONFIG = staticlib warn_on c++11
FMT_SOURCES = \
../fmt/format.cc \
../fmt/ostream.cc \
../fmt/posix.cc \
../fmt/printf.cc
fmt.name = libfmt
fmt.input = FMT_SOURCES
fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
fmt.depends = ${QMAKE_FILE_IN}
# QMAKE_RUN_CXX will not be expanded
fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN}
fmt.variable_out = OBJECTS
fmt.CONFIG = no_dependencies no_link
QMAKE_EXTRA_COMPILERS += fmt

View File

@@ -1,18 +1,15 @@
#!/usr/bin/env python3
#!/usr/bin/env python
"""Manage site and releases.
Usage:
manage.py release [<branch>]
manage.py site
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, requests, shutil, sys
import re, requests, shutil, sys, tempfile
from contextlib import contextmanager
from distutils.version import LooseVersion
from subprocess import check_call
@@ -137,66 +134,25 @@ def update_site(env):
if not os.path.exists(contents):
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
# Fix issues in reference.rst/api.rst.
for filename in ['reference.rst', 'api.rst', 'index.rst']:
for filename in ['reference.rst', 'api.rst']:
pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
with rewrite(os.path.join(target_doc_dir, filename)) as b:
b.data = b.data.replace('std::ostream &', 'std::ostream&')
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned')
#b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(
'format_to_n(OutputIt, size_t, string_view, Args&&',
'format_to_n(OutputIt, size_t, const S&, const Args&')
b.data = b.data.replace(
'format_to_n(OutputIt, std::size_t, string_view, Args&&',
'format_to_n(OutputIt, std::size_t, const S&, const Args&')
if version == ('3.0.2'):
b.data = b.data.replace(
'fprintf(std::ostream&', 'fprintf(std::ostream &')
if version == ('5.3.0'):
b.data = b.data.replace(
'format_to(OutputIt, const S&, const Args&...)',
'format_to(OutputIt, const S &, const Args &...)')
if version.startswith('5.') or version.startswith('6.'):
b.data = b.data.replace(', size_t', ', std::size_t')
if version.startswith('7.'):
b.data = b.data.replace(', std::size_t', ', size_t')
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
if version.startswith('7.1.'):
b.data = b.data.replace(', std::size_t', ', size_t')
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
b.data = b.data.replace(
'fmt::format_to(OutputIt, const S&, Args&&...)',
'fmt::format_to(OutputIt, const S&, Args&&...) -> ' +
'typename std::enable_if<enable, OutputIt>::type')
b.data = b.data.replace('aa long', 'a long')
b.data = b.data.replace('serveral', 'several')
if version.startswith('6.2.'):
b.data = b.data.replace(
'vformat(const S&, basic_format_args<' +
'buffer_context<Char>>)',
'vformat(const S&, basic_format_args<' +
'buffer_context<type_identity_t<Char>>>)')
b.data = b.data.replace('operator""_', 'operator"" _')
# Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b:
b.data = b.data.replace(
'doc/latest/index.html#format-string-syntax', 'syntax.html')
# Fix issues in syntax.rst.
index = os.path.join(target_doc_dir, 'syntax.rst')
with rewrite(index) as b:
b.data = b.data.replace(
'..productionlist:: sf\n', '.. productionlist:: sf\n ')
b.data = b.data.replace('Examples:\n', 'Examples::\n')
# Build the docs.
html_dir = os.path.join(env.build_dir, 'html')
if os.path.exists(html_dir):
shutil.rmtree(html_dir)
include_dir = env.fmt_repo.dir
if LooseVersion(version) >= LooseVersion('5.0.0'):
include_dir = os.path.join(include_dir, 'include', 'fmt')
elif LooseVersion(version) >= LooseVersion('3.0.0'):
if LooseVersion(version) >= LooseVersion('3.0.0'):
include_dir = os.path.join(include_dir, 'fmt')
import build
build.build_docs(version, doc_dir=target_doc_dir,
@@ -229,50 +185,12 @@ def release(args):
if not fmt_repo.update('-b', branch, fmt_repo_url):
clean_checkout(fmt_repo, branch)
# Update the date in the changelog and extract the version and the first
# section content.
changelog = 'ChangeLog.md'
# Convert changelog from RST to GitHub-flavored Markdown and get the
# version.
changelog = 'ChangeLog.rst'
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)
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':
changes += line
if stripped:
changes += line
stripped = False
continue
if stripped:
line = ' ' + line.lstrip()
changes += line.rstrip()
stripped = True
import rst2md
changes, version = rst2md.convert(changelog_path)
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists),
inplace=True):
@@ -281,11 +199,23 @@ def release(args):
line = prefix + version + ')\n'
sys.stdout.write(line)
# Update the version in the changelog.
title_len = 0
for line in fileinput.input(changelog_path, inplace=True):
if line.decode('utf-8').startswith(version + ' - TBD'):
line = version + ' - ' + datetime.date.today().isoformat()
title_len = len(line)
line += '\n'
elif title_len:
line = '-' * title_len + '\n'
title_len = 0
sys.stdout.write(line)
# Add the version to the build script.
script = os.path.join('doc', 'build.py')
script_path = os.path.join(fmt_repo.dir, script)
for line in fileinput.input(script_path, inplace=True):
m = re.match(r'( *versions \+= )\[(.+)\]', line)
m = re.match(r'( *versions = )\[(.+)\]', line)
if m:
line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version)
sys.stdout.write(line)
@@ -302,9 +232,9 @@ def release(args):
# Create a release on GitHub.
fmt_repo.push('origin', 'release')
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
params = {'access_token': os.getenv('FMT_TOKEN')}
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
headers=auth_headers,
params=params,
data=json.dumps({'tag_name': version,
'target_commitish': 'release',
'body': changes, 'draft': True}))
@@ -313,12 +243,12 @@ def release(args):
id = r.json()['id']
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
package = 'fmt-{}.zip'.format(version)
r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package),
headers={'Content-Type': 'application/zip'} | auth_headers,
data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))
with open('build/fmt/' + package, 'rb') as f:
r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package),
params=params, files={package: f})
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))
if __name__ == '__main__':

View File

@@ -1,201 +0,0 @@
#!/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<uint16_t>(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()

127
support/rst2md.py Normal file
View File

@@ -0,0 +1,127 @@
# reStructuredText (RST) to GitHub-flavored Markdown converter
import re
from docutils import core, nodes, writers
def is_github_ref(node):
return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri'])
class Translator(nodes.NodeVisitor):
def __init__(self, document):
nodes.NodeVisitor.__init__(self, document)
self.output = ''
self.indent = 0
self.preserve_newlines = False
def write(self, text):
self.output += text.replace('\n', '\n' + ' ' * self.indent)
def visit_document(self, node):
pass
def depart_document(self, node):
pass
def visit_section(self, node):
pass
def depart_section(self, node):
# Skip all sections except the first one.
raise nodes.StopTraversal
def visit_title(self, node):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren
def depart_title(self, node):
pass
def visit_Text(self, node):
if not self.preserve_newlines:
node = node.replace('\n', ' ')
self.write(node)
def depart_Text(self, node):
pass
def visit_bullet_list(self, node):
pass
def depart_bullet_list(self, node):
pass
def visit_list_item(self, node):
self.write('* ')
self.indent += 2
def depart_list_item(self, node):
self.indent -= 2
self.write('\n\n')
def visit_paragraph(self, node):
pass
def depart_paragraph(self, node):
pass
def visit_reference(self, node):
if not is_github_ref(node):
self.write('[')
def depart_reference(self, node):
if not is_github_ref(node):
self.write('](' + node['refuri'] + ')')
def visit_target(self, node):
pass
def depart_target(self, node):
pass
def visit_literal(self, node):
self.write('`')
def depart_literal(self, node):
self.write('`')
def visit_literal_block(self, node):
self.write('\n\n```')
if 'c++' in node['classes']:
self.write('c++')
self.write('\n')
self.preserve_newlines = True
def depart_literal_block(self, node):
self.write('\n```\n')
self.preserve_newlines = False
def visit_inline(self, node):
pass
def depart_inline(self, node):
pass
def visit_image(self, node):
self.write('![](' + node['uri'] + ')')
def depart_image(self, node):
pass
class MDWriter(writers.Writer):
"""GitHub-flavored markdown writer"""
supported = ('md',)
"""Formats this writer supports."""
def translate(self):
translator = Translator(self.document)
self.document.walkabout(translator)
self.output = (translator.output, translator.version)
def convert(rst_path):
"""Converts RST file to Markdown."""
return core.publish_file(source_path=rst_path, writer=MDWriter())

View File

@@ -1,2 +1,2 @@
If you are not redirected automatically, follow the
`link to the fmt documentation <https://fmt.dev/latest/>`_.
`link to the fmt documentation <http://fmtlib.net/latest/>`_.

View File

@@ -2,15 +2,15 @@
{% block extrahead %}
<meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/">
<meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/">
<script type="text/javascript">
window.location.href = "https://fmt.dev/latest/"
window.location.href = "http://fmtlib.net/latest/"
</script>
<title>Page Redirection</title>
{% endblock %}
{% block document %}
If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>.
If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>.
{% endblock %}
{% block footer %}

118
support/travis-build.py Executable file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env python
# Build the project on Travis CI.
from __future__ import print_function
import errno, os, re, shutil, sys, tempfile, urllib
from subprocess import call, check_call, check_output, Popen, PIPE, STDOUT
def rmtree_if_exists(dir):
try:
shutil.rmtree(dir)
except OSError as e:
if e.errno == errno.ENOENT:
pass
def makedirs_if_not_exist(dir):
try:
os.makedirs(dir)
except OSError as e:
if e.errno != errno.EEXIST:
raise
def install_dependencies():
branch = os.environ['TRAVIS_BRANCH']
if branch != 'master':
print('Branch: ' + branch)
exit(0) # Ignore non-master branches
check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' +
'| sudo apt-key add -', shell=True)
check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' +
'| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True)
check_call(['sudo', 'apt-get', 'update'])
check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs'])
check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css'])
deb_file = 'doxygen_1.8.6-2_amd64.deb'
urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' +
deb_file, deb_file)
check_call(['sudo', 'dpkg', '-i', deb_file])
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
build = os.environ['BUILD']
if build == 'Doc':
travis = 'TRAVIS' in os.environ
if travis:
install_dependencies()
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
import build
build.create_build_env()
html_dir = build.build_docs()
repo = 'fmtlib.github.io'
if travis and 'KEY' not in os.environ:
# Don't update the repo if building on Travis from an account that
# doesn't have push access.
print('Skipping update of ' + repo)
exit(0)
# Clone the fmtlib.github.io repo.
rmtree_if_exists(repo)
git_url = 'https://github.com/' if travis else 'git@github.com:'
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
# Copy docs to the repo.
target_dir = os.path.join(repo, 'dev')
rmtree_if_exists(target_dir)
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
if travis:
check_call(['git', 'config', '--global', 'user.name', 'amplbot'])
check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com'])
# Push docs to GitHub pages.
check_call(['git', 'add', '--all'], cwd=repo)
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
cmd = 'git push'
if travis:
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
# Print the output without the key.
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
if p.returncode != 0:
raise CalledProcessError(p.returncode, cmd)
exit(0)
standard = os.environ['STANDARD']
install_dir = os.path.join(fmt_dir, "_install")
build_dir = os.path.join(fmt_dir, "_build")
test_build_dir = os.path.join(fmt_dir, "_build_test")
# Configure library.
makedirs_if_not_exist(build_dir)
common_cmake_flags = [
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build
]
extra_cmake_flags = []
if standard != '0x':
extra_cmake_flags = [
'-DCMAKE_CXX_FLAGS=-std=c++' + standard, '-DFMT_USE_CPP11=OFF'
]
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', fmt_dir] +
common_cmake_flags + extra_cmake_flags, cwd=build_dir)
# Build library.
check_call(['make', '-j4'], cwd=build_dir)
# Test library.
env = os.environ.copy()
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
if call(['make', 'test'], env=env, cwd=build_dir):
with open('Testing/Temporary/LastTest.log', 'r') as f:
print(f.read())
sys.exit(-1)
# Install library.
check_call(['make', 'install'], cwd=build_dir)
# Test installation.
makedirs_if_not_exist(test_build_dir)
check_call(['cmake', '-DCMAKE_CXX_FLAGS=-std=c++' + standard,
os.path.join(fmt_dir, "test", "find-package-test")] +
common_cmake_flags, cwd=test_build_dir)
check_call(['make', '-j4'], cwd=test_build_dir)

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python
# Update the coverity branch from the master branch.
# It is not done automatically because Coverity Scan limits
# the number of submissions per day.
from __future__ import print_function
import shutil, tempfile
from subprocess import check_output, STDOUT
class Git:
def __init__(self, dir):
self.dir = dir
def __call__(self, *args):
output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT)
print(output)
return output
dir = tempfile.mkdtemp()
try:
git = Git(dir)
git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir)
output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master')
if 'Fast-forward' not in output:
git('reset', 'HEAD', '.travis.yml')
git('checkout', '--', '.travis.yml')
git('commit', '-m', 'Update coverity branch')
git('push')
finally:
shutil.rmtree(dir)

View File

@@ -1,201 +1,157 @@
add_subdirectory(gtest)
#------------------------------------------------------------------------------
# Build the google test library
include(CheckSymbolExists)
# We compile Google Test ourselves instead of using pre-compiled libraries.
# See the Google Test FAQ "Why is it not recommended to install a
# pre-compiled copy of Google Test (for example, into /usr/local)?"
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
add_library(gmock STATIC
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
target_compile_options(gmock PUBLIC ${CPP11_FLAG})
target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
target_include_directories(gmock PUBLIC .)
# Workaround for Cygwin to make google-tests compile and run because the macro
# _POSIX_C_SOURCE must be defined to allow fileno(), strdup(), fdopen() calls.
if (CYGWIN)
target_compile_definitions(gmock PUBLIC _POSIX_C_SOURCE=200809)
endif ()
find_package(Threads)
if (Threads_FOUND)
target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT})
else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
if (NOT SUPPORTS_VARIADIC_TEMPLATES OR NOT SUPPORTS_INITIALIZER_LIST)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
# Workaround a bug in implementation of variadic templates in MSVC11.
if (MSVC)
target_compile_definitions(gmock PUBLIC _VARIADIC_MAX=10)
endif ()
# GTest doesn't detect <tuple> with clang.
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1)
endif ()
#------------------------------------------------------------------------------
# Build the actual library tests
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
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(test-main gtest fmt)
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_link_libraries(test-main gmock fmt)
include(CheckCXXCompilerFlag)
# Workaround GTest bug https://github.com/google/googletest/issues/705.
check_cxx_compiler_flag(
-fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
target_compile_options(test-main PUBLIC -fno-delete-null-pointer-checks)
endif ()
# Use less strict pedantic flags for the tests because GMock doesn't compile
# cleanly with -pedantic and -std=c++98.
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
endif ()
function(add_fmt_executable name)
add_executable(${name} ${ARGN})
# (Wstringop-overflow) - [meta-bug] bogus/missing -Wstringop-overflow warnings
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443
# Bogus -Wstringop-overflow warning
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100395
# [10 Regression] spurious -Wstringop-overflow writing to a trailing array plus offset
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95353
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND
NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
target_compile_options(${name} PRIVATE -Wno-stringop-overflow)
# The linker flag is needed for LTO.
target_link_libraries(${name} -Wno-stringop-overflow)
if (MINGW)
target_link_libraries(${name} -static-libgcc -static-libstdc++)
endif ()
endfunction()
# Adds a test.
# Usage: add_fmt_test(name srcs...)
function(add_fmt_test name)
cmake_parse_arguments(ADD_FMT_TEST "HEADER_ONLY;MODULE" "" "" ${ARGN})
add_fmt_executable(${name} ${name}.cc ${ARGN})
target_link_libraries(${name} test-main)
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 test-module)
set_source_files_properties(${name}.cc PROPERTIES OBJECT_DEPENDS test-module)
else ()
set(libs test-main fmt)
endif ()
add_fmt_executable(${name} ${sources})
target_link_libraries(${name} ${libs})
# Define if certain C++ features can be used.
# define if certain c++ features can be used
target_compile_definitions(${name} PRIVATE
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
FMT_USE_ENUM_BASE=$<BOOL:${SUPPORTS_ENUM_BASE}>)
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()
if (FMT_MODULE)
return ()
endif ()
add_fmt_test(args-test)
add_fmt_test(assert-test)
add_fmt_test(chrono-test)
add_fmt_test(color-test)
add_fmt_test(core-test)
add_fmt_test(container-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(format-test)
add_fmt_test(format-impl-test)
add_fmt_test(ostream-test)
add_fmt_test(compile-test)
add_fmt_test(compile-fp-test HEADER_ONLY)
if (MSVC)
# Without this option, MSVC returns 199711L for the __cplusplus macro.
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
endif()
add_fmt_test(printf-test)
add_fmt_test(ranges-test ranges-odr-test.cc)
add_fmt_test(string-test)
add_fmt_test(time-test)
add_fmt_test(util-test mock-allocator.h)
add_fmt_test(macro-test)
add_fmt_test(custom-formatter-test)
add_fmt_test(scan-test)
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
if (HAVE_STRPTIME)
target_compile_definitions(scan-test PRIVATE FMT_HAVE_STRPTIME)
# Enable stricter options for one test to make sure that the header is free of
# warnings.
if (FMT_PEDANTIC AND MSVC)
target_compile_options(format-test PRIVATE /W4)
endif ()
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)
if (FMT_MODULE)
# The tests need {fmt} to be compiled as traditional library
# because of visibility of implementation details.
# If module support is present the module tests require a
# test-only module to be built from {fmt}
add_library(test-module OBJECT ${CMAKE_SOURCE_DIR}/src/fmt.cc)
target_compile_features(test-module PUBLIC cxx_std_11)
target_include_directories(test-module PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
enable_module(test-module)
add_fmt_test(module-test MODULE test-main.cc)
if (MSVC)
target_compile_options(test-module PRIVATE /utf-8 /Zc:__cplusplus
/Zc:externConstexpr /Zc:inline)
target_compile_options(module-test PRIVATE /utf-8 /Zc:__cplusplus
/Zc:externConstexpr /Zc:inline)
endif ()
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)
if (HAVE_OPEN)
add_fmt_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 ()
posix-mock-test.cc ../fmt/format.cc ../fmt/printf.cc ${TEST_MAIN_SRC})
target_include_directories(posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock)
add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(os-test)
add_fmt_test(posix-test)
endif ()
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
add_fmt_executable(header-only-test
header-only-test.cc header-only-test2.cc test-main.cc)
target_link_libraries(header-only-test gmock)
if (TARGET fmt-header-only)
target_link_libraries(header-only-test fmt-header-only)
else ()
target_include_directories(header-only-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
endif ()
# Test that the library can be compiled with exceptions disabled.
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../fmt/format.cc)
target_include_directories(noexception-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_options(noexception-test PRIVATE -fno-exceptions)
endif ()
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})
# Test that the library compiles without windows.h.
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_library(no-windows-h-test ../fmt/format.cc)
target_include_directories(no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
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 not 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}
add_test(compile-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
"${CMAKE_CURRENT_BINARY_DIR}/compile-error-test"
"${CMAKE_CURRENT_SOURCE_DIR}/compile-test"
"${CMAKE_CURRENT_BINARY_DIR}/compile-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}")
"-DCPP11_FLAG=${CPP11_FLAG}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
# Test if the targets are found from the build directory.
# test if the targets are findable from the build directory
add_test(find-package-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}
--build-and-test
@@ -205,13 +161,10 @@ if (FMT_PEDANTIC AND NOT WIN32 AND NOT (
--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.
# test if the targets are findable when add_subdirectory is used
add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}
--build-and-test
@@ -221,42 +174,5 @@ if (FMT_PEDANTIC AND NOT WIN32 AND NOT (
--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 are disabled on Windows because it is only *NIX issue.
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 ()

View File

@@ -1,17 +1,13 @@
cmake_minimum_required(VERSION 3.8...3.25)
cmake_minimum_required(VERSION 2.8.12)
project(fmt-test CXX)
project(fmt-test)
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})
add_executable(library-test "main.cc")
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})
add_executable(header-only-test "main.cc")
target_link_libraries(header-only-test fmt::fmt-header-only)
endif ()

View File

@@ -1,5 +1,6 @@
#include "fmt/core.h"
#include "fmt/format.h"
int main(int argc, char** argv) {
for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]);
for(int i = 0; i < argc; ++i)
fmt::print("{}: {}\n", i, argv[i]);
}

View File

@@ -1,186 +0,0 @@
// 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 <memory>
#include "gtest/gtest.h"
TEST(args_test, basic) {
fmt::dynamic_format_arg_store<fmt::format_context> 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<fmt::format_context> 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<custom_type> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const custom_type& p, FormatContext& ctx) -> 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<fmt::format_context> 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 fmt::string_view to_string_view(to_stringable) { return {}; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<to_stringable> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(to_stringable, format_context& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(args_test, to_string_and_formatter) {
fmt::dynamic_format_arg_store<fmt::format_context> 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<fmt::format_context> 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<fmt::format_context> 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<fmt::format_context> 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<fmt::format_context> 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<fmt::format_context> 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<fmt::format_context> store;
store.reserve(2, 1);
store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42));
auto result = fmt::vformat("{a1} and {}", store);
EXPECT_EQ("42 and 1.5", result);
}
struct copy_throwable {
copy_throwable() {}
copy_throwable(const copy_throwable&) { throw "deal with it"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<copy_throwable> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(copy_throwable, format_context& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(args_test, throw_on_copy) {
fmt::dynamic_format_arg_store<fmt::format_context> 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<fmt::format_context>;
auto store = std::unique_ptr<store_type>(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");
}

View File

@@ -1,31 +1,41 @@
// 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.
/*
Assertion tests
#include "fmt/core.h"
Copyright (c) 2015, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmt/format.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!");
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEBUG_DEATH(statement, regex)
#else
fmt::print("warning: death tests are not supported\n");
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
#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);
TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"), "don't panic!");
}

View File

@@ -1,999 +0,0 @@
// Formatting library for C++ - time formatting tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/chrono.h"
#include <algorithm>
#include <ctime>
#include <vector>
#include "gtest-extra.h" // EXPECT_THROW_MSG
#include "util.h" // get_locale
using fmt::runtime;
using testing::Contains;
template <typename Duration>
using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
#if defined(__MINGW32__) && !defined(_UCRT)
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
# define FMT_HAS_C99_STRFTIME 0
#else
# define FMT_HAS_C99_STRFTIME 1
#endif
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L
using days = std::chrono::days;
#else
using days = std::chrono::duration<std::chrono::hours::rep, std::ratio<86400>>;
#endif
auto make_tm() -> std::tm {
auto time = std::tm();
time.tm_mday = 1;
return time;
}
auto make_hour(int h) -> std::tm {
auto time = make_tm();
time.tm_hour = h;
return time;
}
auto make_minute(int m) -> std::tm {
auto time = make_tm();
time.tm_min = m;
return time;
}
auto make_second(int s) -> std::tm {
auto time = make_tm();
time.tm_sec = s;
return time;
}
std::string system_strftime(const std::string& format, const std::tm* timeptr,
std::locale* locptr = nullptr) {
auto loc = locptr ? *locptr : std::locale::classic();
auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os;
os.imbue(loc);
facet.put(os, os, ' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == "-0000") str = "+0000";
return str;
#else
return os.str();
#endif
}
FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min,
int sec) {
auto tm = std::tm();
tm.tm_sec = sec;
tm.tm_min = min;
tm.tm_hour = hour;
tm.tm_mday = mday;
tm.tm_mon = mon - 1;
tm.tm_year = year - 1900;
return tm;
}
TEST(chrono_test, format_tm) {
auto tm = std::tm();
tm.tm_year = 116;
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 2016-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "2016");
EXPECT_EQ(fmt::format("{:%C}", tm), "20");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
EXPECT_EQ(fmt::format("{:%e}", tm), "25");
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/16");
EXPECT_EQ(fmt::format("{:%F}", tm), "2016-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
// Short year
tm.tm_year = 999 - 1900;
tm.tm_mon = 0; // for %G
tm.tm_mday = 2; // for %G
tm.tm_wday = 3; // for %G
tm.tm_yday = 1; // for %G
EXPECT_EQ(fmt::format("{:%Y}", tm), "0999");
EXPECT_EQ(fmt::format("{:%C%y}", tm), "0999");
EXPECT_EQ(fmt::format("{:%G}", tm), "0999");
tm.tm_year = 27 - 1900;
EXPECT_EQ(fmt::format("{:%Y}", tm), "0027");
EXPECT_EQ(fmt::format("{:%C%y}", tm), "0027");
// Overflow year
tm.tm_year = 2147483647;
EXPECT_EQ(fmt::format("{:%Y}", tm), "2147485547");
tm.tm_year = -2147483648;
EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748");
// for week on the year
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
std::vector<std::tm> tm_list = {
make_tm(1975, 12, 29, 12, 14, 16), // W01
make_tm(1977, 1, 2, 12, 14, 16), // W53
make_tm(1999, 12, 27, 12, 14, 16), // W52
make_tm(1999, 12, 31, 12, 14, 16), // W52
make_tm(2000, 1, 1, 12, 14, 16), // W52
make_tm(2000, 1, 2, 12, 14, 16), // W52
make_tm(2000, 1, 3, 12, 14, 16) // W1
};
#if !FMT_HAS_C99_STRFTIME
GTEST_SKIP() << "Skip the rest of this test because it relies on strftime() "
"conforming to C99, but on this platform, MINGW + MSVCRT, "
"the function conforms only to C89.";
#endif
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
for (auto ctm : tm_list) {
// Calculate tm_yday, tm_wday, etc.
std::time_t t = std::mktime(&ctm);
tm = *std::localtime(&t);
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
fmt::format(fmt::runtime(fmt_spec), tm));
}
// Every day from 1970-01-01
std::time_t time_now = std::time(nullptr);
for (std::time_t t = 6 * 3600; t < time_now; t += 86400) {
tm = *std::localtime(&t);
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
fmt::format(fmt::runtime(fmt_spec), tm));
}
}
// MSVC:
// minkernel\crts\ucrt\src\appcrt\time\wcsftime.cpp(971) : Assertion failed:
// timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099
#ifndef _WIN32
TEST(chrono_test, format_tm_future) {
auto tm = std::tm();
tm.tm_year = 10445; // 10000+ years
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 12345-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "12345");
EXPECT_EQ(fmt::format("{:%C}", tm), "123");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/45");
EXPECT_EQ(fmt::format("{:%F}", tm), "12345-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
}
TEST(chrono_test, format_tm_past) {
auto tm = std::tm();
tm.tm_year = -2001;
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is -101-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "-101");
// macOS %C - "-1"
// Linux %C - "-2"
// fmt %C - "-1"
EXPECT_EQ(fmt::format("{:%C}", tm), "-1");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
// macOS %D - "04/25/01" (%y)
// Linux %D - "04/25/99" (%y)
// fmt %D - "04/25/01" (%y)
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/01");
EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
tm.tm_year = -1901; // -1
EXPECT_EQ(fmt::format("{:%Y}", tm), "-001");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
tm.tm_year = -1911; // -11
EXPECT_EQ(fmt::format("{:%Y}", tm), "-011");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
}
#endif
TEST(chrono_test, grow_buffer) {
auto s = std::string("{:");
for (int i = 0; i < 30; ++i) s += "%c";
s += "}\n";
auto t = std::time(nullptr);
(void)fmt::format(fmt::runtime(s), *std::localtime(&t));
}
TEST(chrono_test, format_to_empty_container) {
auto time = std::tm();
time.tm_sec = 42;
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{:%S}", time);
EXPECT_EQ(s, "42");
}
TEST(chrono_test, empty_result) { EXPECT_EQ(fmt::format("{}", std::tm()), ""); }
auto equal(const std::tm& lhs, const std::tm& rhs) -> bool {
return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min &&
lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday &&
lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year &&
lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday &&
lhs.tm_isdst == rhs.tm_isdst;
}
TEST(chrono_test, gmtime) {
auto t = std::time(nullptr);
auto tm = *std::gmtime(&t);
EXPECT_TRUE(equal(tm, fmt::gmtime(t)));
}
template <typename TimePoint>
auto strftime_full_utc(TimePoint tp) -> std::string {
auto t = std::chrono::system_clock::to_time_t(tp);
auto tm = *std::gmtime(&t);
return system_strftime("%Y-%m-%d %H:%M:%S", &tm);
}
TEST(chrono_test, system_clock_time_point) {
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::system_clock::now());
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{}", t1));
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:}", t1));
auto t2 = sys_time<std::chrono::seconds>(std::chrono::seconds(42));
EXPECT_EQ(strftime_full_utc(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
std::vector<std::string> spec_list = {
"%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C",
"%EC", "%G", "%g", "%b", "%h", "%B", "%m", "%Om", "%U",
"%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e",
"%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH",
"%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X",
"%EX", "%D", "%F", "%R", "%T", "%p"};
#ifndef _WIN32
// Disabled on Windows because these formats are not consistent among
// platforms.
spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"});
#elif !FMT_HAS_C99_STRFTIME
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d",
"%a", "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p"};
#endif
spec_list.push_back("%Y-%m-%d %H:%M:%S");
for (const auto& spec : spec_list) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::gmtime(&t);
auto sys_output = system_strftime(spec, &tm);
auto fmt_spec = fmt::format("{{:{}}}", spec);
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
}
// Timezone formatters tests makes sense for localtime.
#if FMT_HAS_C99_STRFTIME
spec_list = {"%z", "%Z"};
#else
spec_list = {"%Z"};
#endif
for (const auto& spec : spec_list) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::localtime(&t);
auto sys_output = system_strftime(spec, &tm);
auto fmt_spec = fmt::format("{{:{}}}", spec);
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
if (spec == "%z") {
sys_output.insert(sys_output.end() - 2, 1, ':');
EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm));
EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm));
}
}
// Separate tests for UTC, since std::time_put can use local time and ignoring
// the timezone in std::tm (if it presents on platform).
if (fmt::detail::has_member_data_tm_zone<std::tm>::value) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::gmtime(&t);
std::vector<std::string> tz_names = {"GMT", "UTC"};
EXPECT_THAT(tz_names, Contains(fmt::format("{:%Z}", t1)));
EXPECT_THAT(tz_names, Contains(fmt::format("{:%Z}", tm)));
}
if (fmt::detail::has_member_data_tm_gmtoff<std::tm>::value) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::gmtime(&t);
EXPECT_EQ(fmt::format("{:%z}", t1), "+0000");
EXPECT_EQ(fmt::format("{:%z}", tm), "+0000");
EXPECT_EQ(fmt::format("{:%Ez}", t1), "+00:00");
EXPECT_EQ(fmt::format("{:%Ez}", tm), "+00:00");
EXPECT_EQ(fmt::format("{:%Oz}", t1), "+00:00");
EXPECT_EQ(fmt::format("{:%Oz}", tm), "+00:00");
}
}
#if FMT_USE_LOCAL_TIME
TEST(chrono_test, localtime) {
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
EXPECT_TRUE(equal(tm, fmt::localtime(t)));
}
template <typename Duration>
auto strftime_full_local(std::chrono::local_time<Duration> tp) -> std::string {
auto t = std::chrono::system_clock::to_time_t(
std::chrono::current_zone()->to_sys(tp));
auto tm = *std::localtime(&t);
return system_strftime("%Y-%m-%d %H:%M:%S", &tm);
}
TEST(chrono_test, local_system_clock_time_point) {
# ifdef _WIN32
return; // Not supported on Windows.
# endif
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::current_zone()->to_local(std::chrono::system_clock::now()));
EXPECT_EQ(strftime_full_local(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
EXPECT_EQ(strftime_full_local(t1), fmt::format("{}", t1));
EXPECT_EQ(strftime_full_local(t1), fmt::format("{:}", t1));
using time_point = std::chrono::local_time<std::chrono::seconds>;
auto t2 = time_point(std::chrono::seconds(86400 + 42));
EXPECT_EQ(strftime_full_local(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
std::vector<std::string> spec_list = {
"%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C",
"%EC", "%G", "%g", "%b", "%h", "%B", "%m", "%Om", "%U",
"%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e",
"%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH",
"%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X",
"%EX", "%D", "%F", "%R", "%T", "%p", "%z", "%Z"};
# ifndef _WIN32
// Disabled on Windows because these formats are not consistent among
// platforms.
spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"});
# elif !FMT_HAS_C99_STRFTIME
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a",
"%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p", "%Z"};
# endif
spec_list.push_back("%Y-%m-%d %H:%M:%S");
for (const auto& spec : spec_list) {
auto t = std::chrono::system_clock::to_time_t(
std::chrono::current_zone()->to_sys(t1));
auto tm = *std::localtime(&t);
auto sys_output = system_strftime(spec, &tm);
auto fmt_spec = fmt::format("{{:{}}}", spec);
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
}
if (std::find(spec_list.cbegin(), spec_list.cend(), "%z") !=
spec_list.cend()) {
auto t = std::chrono::system_clock::to_time_t(
std::chrono::current_zone()->to_sys(t1));
auto tm = *std::localtime(&t);
auto sys_output = system_strftime("%z", &tm);
sys_output.insert(sys_output.end() - 2, 1, ':');
EXPECT_EQ(sys_output, fmt::format("{:%Ez}", t1));
EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm));
EXPECT_EQ(sys_output, fmt::format("{:%Oz}", t1));
EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm));
}
}
#endif // FMT_USE_LOCAL_TIME
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
TEST(chrono_test, format_default) {
EXPECT_EQ(fmt::format("{}", std::chrono::seconds(42)), "42s");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::atto>(42)),
"42as");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::femto>(42)),
"42fs");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::pico>(42)),
"42ps");
EXPECT_EQ(fmt::format("{}", std::chrono::nanoseconds(42)), "42ns");
EXPECT_EQ(fmt::format("{}", std::chrono::microseconds(42)), "42µs");
EXPECT_EQ(fmt::format("{}", std::chrono::milliseconds(42)), "42ms");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::centi>(42)),
"42cs");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::deci>(42)),
"42ds");
EXPECT_EQ(fmt::format("{}", std::chrono::seconds(42)), "42s");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::deca>(42)),
"42das");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::hecto>(42)),
"42hs");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::kilo>(42)),
"42ks");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::mega>(42)),
"42Ms");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::giga>(42)),
"42Gs");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::tera>(42)),
"42Ts");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::peta>(42)),
"42Ps");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::exa>(42)),
"42Es");
EXPECT_EQ(fmt::format("{}", std::chrono::minutes(42)), "42min");
EXPECT_EQ(fmt::format("{}", std::chrono::hours(42)), "42h");
EXPECT_EQ(fmt::format("{}", days(42)), "42d");
EXPECT_EQ(
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)),
"42[15]s");
EXPECT_EQ(
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)),
"42[15/4]s");
}
TEST(chrono_test, duration_align) {
auto s = std::chrono::seconds(42);
EXPECT_EQ(fmt::format("{:5}", s), "42s ");
EXPECT_EQ(fmt::format("{:{}}", s, 5), "42s ");
EXPECT_EQ(fmt::format("{:>5}", s), " 42s");
EXPECT_EQ(fmt::format("{:*^7}", s), "**42s**");
EXPECT_EQ(fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)),
"03:25:45 ");
EXPECT_EQ(fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)),
" 03:25:45");
EXPECT_EQ(fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)),
"~~03:25:45~~");
EXPECT_EQ(fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12),
"03:25:45 ");
}
TEST(chrono_test, tm_align) {
auto t = make_tm(1975, 12, 29, 12, 14, 16);
EXPECT_EQ(fmt::format("{:%F %T}", t), "1975-12-29 12:14:16");
EXPECT_EQ(fmt::format("{:30%F %T}", t), "1975-12-29 12:14:16 ");
EXPECT_EQ(fmt::format("{:{}%F %T}", t, 30), "1975-12-29 12:14:16 ");
EXPECT_EQ(fmt::format("{:<30%F %T}", t), "1975-12-29 12:14:16 ");
EXPECT_EQ(fmt::format("{:^30%F %T}", t), " 1975-12-29 12:14:16 ");
EXPECT_EQ(fmt::format("{:>30%F %T}", t), " 1975-12-29 12:14:16");
EXPECT_EQ(fmt::format("{:*<30%F %T}", t), "1975-12-29 12:14:16***********");
EXPECT_EQ(fmt::format("{:*^30%F %T}", t), "*****1975-12-29 12:14:16******");
EXPECT_EQ(fmt::format("{:*>30%F %T}", t), "***********1975-12-29 12:14:16");
}
TEST(chrono_test, tp_align) {
auto tp = std::chrono::time_point_cast<std::chrono::microseconds>(
std::chrono::system_clock::from_time_t(0));
EXPECT_EQ(fmt::format("{:%M:%S}", tp), "00:00.000000");
EXPECT_EQ(fmt::format("{:15%M:%S}", tp), "00:00.000000 ");
EXPECT_EQ(fmt::format("{:{}%M:%S}", tp, 15), "00:00.000000 ");
EXPECT_EQ(fmt::format("{:<15%M:%S}", tp), "00:00.000000 ");
EXPECT_EQ(fmt::format("{:^15%M:%S}", tp), " 00:00.000000 ");
EXPECT_EQ(fmt::format("{:>15%M:%S}", tp), " 00:00.000000");
EXPECT_EQ(fmt::format("{:*<15%M:%S}", tp), "00:00.000000***");
EXPECT_EQ(fmt::format("{:*^15%M:%S}", tp), "*00:00.000000**");
EXPECT_EQ(fmt::format("{:*>15%M:%S}", tp), "***00:00.000000");
}
TEST(chrono_test, format_specs) {
EXPECT_EQ(fmt::format("{:%%}", std::chrono::seconds(0)), "%");
EXPECT_EQ(fmt::format("{:%n}", std::chrono::seconds(0)), "\n");
EXPECT_EQ(fmt::format("{:%t}", std::chrono::seconds(0)), "\t");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(0)), "00");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(60)), "00");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(42)), "42");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds(1234)), "01.234");
EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(0)), "00");
EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(60)), "00");
EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(42)), "42");
EXPECT_EQ(fmt::format("{:%M}", std::chrono::seconds(61)), "01");
EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(0)), "00");
EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(24)), "00");
EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(14)), "14");
EXPECT_EQ(fmt::format("{:%H}", std::chrono::minutes(61)), "01");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(0)), "12");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(12)), "12");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(24)), "12");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(4)), "04");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(14)), "02");
EXPECT_EQ(fmt::format("{:%j}", days(12345)), "12345");
EXPECT_EQ(fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12)), "12345");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)),
"03:25:45");
EXPECT_EQ(fmt::format("{:%R}", std::chrono::seconds(12345)), "03:25");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::seconds(12345)), "03:25:45");
EXPECT_EQ(fmt::format("{:%Q}", std::chrono::seconds(12345)), "12345");
EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(12345)), "s");
}
TEST(chrono_test, invalid_specs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%a}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%A}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%c}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%x}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ex}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%X}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%EX}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%D}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%F}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ec}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%w}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%u}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%b}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%B}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%z}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Z}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Eq}"), sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Oq}"), sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:abc}"), sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.2f}"), sec), fmt::format_error,
"invalid format");
}
auto format_tm(const std::tm& time, fmt::string_view spec,
const std::locale& loc) -> std::string {
auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os;
os.imbue(loc);
facet.put(os, os, ' ', &time, spec.begin(), spec.end());
return os.str();
}
TEST(chrono_test, locale) {
auto loc = get_locale("ja_JP.utf8");
if (loc == std::locale::classic()) return;
# define EXPECT_TIME(spec, time, duration) \
{ \
auto jp_loc = std::locale("ja_JP.utf8"); \
EXPECT_EQ(format_tm(time, spec, jp_loc), \
fmt::format(jp_loc, "{:L" spec "}", duration)); \
}
EXPECT_TIME("%OH", make_hour(14), std::chrono::hours(14));
EXPECT_TIME("%OI", make_hour(14), std::chrono::hours(14));
EXPECT_TIME("%OM", make_minute(42), std::chrono::minutes(42));
EXPECT_TIME("%OS", make_second(42), std::chrono::seconds(42));
auto time = make_tm();
time.tm_hour = 3;
time.tm_min = 25;
time.tm_sec = 45;
auto sec = std::chrono::seconds(12345);
EXPECT_TIME("%r", time, sec);
EXPECT_TIME("%p", time, sec);
}
using dms = std::chrono::duration<double, std::milli>;
TEST(chrono_test, format_default_fp) {
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float>(1.234)), "1.234s");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::milli>(1.234)),
"1.234ms");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<double>(1.234)), "1.234s");
EXPECT_EQ(fmt::format("{}", dms(1.234)), "1.234ms");
}
TEST(chrono_test, format_precision) {
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{:.2%Q}"), std::chrono::seconds(42)),
fmt::format_error, "precision not allowed for this argument type");
EXPECT_EQ(fmt::format("{:.0}", dms(1.234)), "1ms");
EXPECT_EQ(fmt::format("{:.1}", dms(1.234)), "1.2ms");
EXPECT_EQ(fmt::format("{:.{}}", dms(1.234), 2), "1.23ms");
EXPECT_EQ(fmt::format("{:.0}", dms(12.56)), "13ms");
EXPECT_EQ(fmt::format("{:.1}", dms(12.56)), "12.6ms");
EXPECT_EQ(fmt::format("{:.2}", dms(12.56)), "12.56ms");
}
TEST(chrono_test, format_full_specs) {
EXPECT_EQ(fmt::format("{:6.0}", dms(1.234)), "1ms ");
EXPECT_EQ(fmt::format("{:6.1}", dms(1.234)), "1.2ms ");
EXPECT_EQ(fmt::format("{:>8.{}}", dms(1.234), 2), " 1.23ms");
EXPECT_EQ(fmt::format("{:^{}.{}}", dms(1.234), 7, 1), " 1.2ms ");
EXPECT_EQ(fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8), " 1.23ms ");
EXPECT_EQ(fmt::format("{:=^{}.{}}", dms(1.234), 9, 3), "=1.234ms=");
EXPECT_EQ(fmt::format("{:*^10.4}", dms(1.234)), "*1.2340ms*");
EXPECT_EQ(fmt::format("{:6.0}", dms(12.56)), "13ms ");
EXPECT_EQ(fmt::format("{:>8.{}}", dms(12.56), 0), " 13ms");
EXPECT_EQ(fmt::format("{:^{}.{}}", dms(12.56), 6, 0), " 13ms ");
EXPECT_EQ(fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8), " 13ms ");
EXPECT_EQ(fmt::format("{:=^{}.{}}", dms(12.56), 9, 0), "==13ms===");
EXPECT_EQ(fmt::format("{:*^10.0}", dms(12.56)), "***13ms***");
}
TEST(chrono_test, format_simple_q) {
EXPECT_EQ(fmt::format("{:%Q %q}", std::chrono::duration<float>(1.234)),
"1.234 s");
EXPECT_EQ(
fmt::format("{:%Q %q}", std::chrono::duration<float, std::milli>(1.234)),
"1.234 ms");
EXPECT_EQ(fmt::format("{:%Q %q}", std::chrono::duration<double>(1.234)),
"1.234 s");
EXPECT_EQ(fmt::format("{:%Q %q}", dms(1.234)), "1.234 ms");
}
TEST(chrono_test, format_precision_q) {
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
fmt::format_error, "precision not allowed for this argument type");
EXPECT_EQ(fmt::format("{:.1%Q %q}", dms(1.234)), "1.2 ms");
EXPECT_EQ(fmt::format("{:.{}%Q %q}", dms(1.234), 2), "1.23 ms");
}
TEST(chrono_test, format_full_specs_q) {
EXPECT_EQ(fmt::format("{:7.0%Q %q}", dms(1.234)), "1 ms ");
EXPECT_EQ(fmt::format("{:7.1%Q %q}", dms(1.234)), "1.2 ms ");
EXPECT_EQ(fmt::format("{:>8.{}%Q %q}", dms(1.234), 2), " 1.23 ms");
EXPECT_EQ(fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1), " 1.2 ms ");
EXPECT_EQ(fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9), " 1.23 ms ");
EXPECT_EQ(fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3), "=1.234 ms=");
EXPECT_EQ(fmt::format("{:*^11.4%Q %q}", dms(1.234)), "*1.2340 ms*");
EXPECT_EQ(fmt::format("{:7.0%Q %q}", dms(12.56)), "13 ms ");
EXPECT_EQ(fmt::format("{:>8.{}%Q %q}", dms(12.56), 0), " 13 ms");
EXPECT_EQ(fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0), " 13 ms ");
EXPECT_EQ(fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9), " 13 ms ");
EXPECT_EQ(fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0), "==13 ms==");
EXPECT_EQ(fmt::format("{:*^11.0%Q %q}", dms(12.56)), "***13 ms***");
}
TEST(chrono_test, invalid_width_id) {
EXPECT_THROW((void)fmt::format(runtime("{:{o}"), std::chrono::seconds(0)),
fmt::format_error);
}
TEST(chrono_test, invalid_colons) {
EXPECT_THROW((void)fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)),
fmt::format_error);
}
TEST(chrono_test, negative_durations) {
EXPECT_EQ(fmt::format("{:%Q}", std::chrono::seconds(-12345)), "-12345");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)),
"-03:25:45");
EXPECT_EQ(fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)),
"-00:01");
EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(-12345)), "s");
EXPECT_EQ(fmt::format("{:%S}",
std::chrono::duration<signed char, std::milli>(-127)),
"-00.127");
auto min = std::numeric_limits<int>::min();
EXPECT_EQ(fmt::format("{}", min),
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
}
TEST(chrono_test, special_durations) {
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<double>(1e20)), "40");
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)),
"nan nan nan nan nan:nan nan");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)),
"1Es");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
"1as");
EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration<char, std::mega>{2}),
"03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20");
EXPECT_EQ(
fmt::format("{:.3%S}", std::chrono::duration<float, std::pico>(1.234e12)),
"01.234");
}
TEST(chrono_test, unsigned_duration) {
EXPECT_EQ(fmt::format("{}", std::chrono::duration<unsigned>(42)), "42s");
}
TEST(chrono_test, weekday) {
auto loc = get_locale("es_ES.UTF-8");
std::locale::global(loc);
auto sat = fmt::weekday(6);
auto tm = std::tm();
tm.tm_wday = static_cast<int>(sat.c_encoding());
EXPECT_EQ(fmt::format("{}", sat), "Sat");
EXPECT_EQ(fmt::format("{:%a}", tm), "Sat");
if (loc != std::locale::classic()) {
auto saturdays = std::vector<std::string>{"sáb", "sá."};
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:L}", sat)));
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:%a}", tm)));
}
}
TEST(chrono_test, cpp20_duration_subsecond_support) {
using attoseconds = std::chrono::duration<long long, std::atto>;
// Check that 18 digits of subsecond precision are supported.
EXPECT_EQ(fmt::format("{:%S}", attoseconds{999999999999999999}),
"00.999999999999999999");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}),
"00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}),
"-00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13420148734}),
"13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}),
"-13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234");
// Check subsecond presision modifier.
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::nanoseconds{1234}),
"00.000001");
EXPECT_EQ(fmt::format("{:.18%S}", std::chrono::nanoseconds{1234}),
"00.000001234000000000");
EXPECT_EQ(fmt::format("{:.{}%S}", std::chrono::nanoseconds{1234}, 6),
"00.000001");
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::milliseconds{1234}),
"01.234000");
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::milliseconds{-1234}),
"-01.234000");
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::seconds{1234}), "34.000");
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::hours{1234}), "00.000");
EXPECT_EQ(fmt::format("{:.5%S}", dms(1.234)), "00.00123");
EXPECT_EQ(fmt::format("{:.8%S}", dms(1.234)), "00.00123400");
{
// Check that {:%H:%M:%S} is equivalent to {:%T}.
auto dur = std::chrono::milliseconds{3601234};
auto formatted_dur = fmt::format("{:%T}", dur);
EXPECT_EQ(formatted_dur, "01:00:01.234");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur);
EXPECT_EQ(fmt::format("{:.6%H:%M:%S}", dur), "01:00:01.234000");
}
using nanoseconds_dbl = std::chrono::duration<double, std::nano>;
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789");
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789}), "09.123456789");
// Verify that only the seconds part is extracted and printed.
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123456789}), "39.123456789");
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123000000}), "39.123000000");
{
// Now the hour is printed, and we also test if negative doubles work.
auto dur = nanoseconds_dbl{-99123456789};
auto formatted_dur = fmt::format("{:%T}", dur);
EXPECT_EQ(formatted_dur, "-00:01:39.123456789");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur);
EXPECT_EQ(fmt::format("{:.3%H:%M:%S}", dur), "-00:01:39.123");
}
// Check that durations with precision greater than std::chrono::seconds have
// fixed precision, and print zeros even if there is no fractional part.
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
"07.000000");
EXPECT_EQ(fmt::format("{:%S}",
std::chrono::duration<long long, std::ratio<1, 3>>(1)),
"00.333333");
EXPECT_EQ(fmt::format("{:%S}",
std::chrono::duration<long long, std::ratio<1, 7>>(1)),
"00.142857");
EXPECT_EQ(
fmt::format("{:%S}",
std::chrono::duration<signed char, std::ratio<1, 100>>(0x80)),
"-01.28");
EXPECT_EQ(
fmt::format("{:%M:%S}",
std::chrono::duration<short, std::ratio<1, 100>>(0x8000)),
"-05:27.68");
// Check that floating point seconds with ratio<1,1> are printed.
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<double>{1.5}),
"01.500000");
EXPECT_EQ(fmt::format("{:%M:%S}", std::chrono::duration<double>{-61.25}),
"-01:01.250000");
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
// Disable the utc_clock test for windows, as the icu.dll used for tzdb
// (time zone database) is not shipped with many windows versions.
#if FMT_USE_UTC_TIME && !defined(_WIN32)
TEST(chrono_test, utc_clock) {
auto t1 = std::chrono::system_clock::now();
auto t1_utc = std::chrono::utc_clock::from_sys(t1);
EXPECT_EQ(fmt::format("{:%Y-%m-%d %H:%M:%S}", t1),
fmt::format("{:%Y-%m-%d %H:%M:%S}", t1_utc));
}
#endif
TEST(chrono_test, timestamp_ratios) {
auto t1 =
sys_time<std::chrono::milliseconds>(std::chrono::milliseconds(67890));
EXPECT_EQ(fmt::format("{:%M:%S}", t1), "01:07.890");
auto t2 = sys_time<std::chrono::minutes>(std::chrono::minutes(7));
EXPECT_EQ(fmt::format("{:%M:%S}", t2), "07:00");
auto t3 = sys_time<std::chrono::duration<int, std::ratio<9>>>(
std::chrono::duration<int, std::ratio<9>>(7));
EXPECT_EQ(fmt::format("{:%M:%S}", t3), "01:03");
auto t4 = sys_time<std::chrono::duration<int, std::ratio<63>>>(
std::chrono::duration<int, std::ratio<63>>(1));
EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03");
if (sizeof(time_t) > 4) {
auto tp =
sys_time<std::chrono::milliseconds>(std::chrono::seconds(32503680000));
EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tp), "3000-01-01");
}
if (FMT_SAFE_DURATION_CAST) {
using years = std::chrono::duration<std::int64_t, std::ratio<31556952>>;
auto tp = sys_time<years>(years(std::numeric_limits<std::int64_t>::max()));
EXPECT_THROW_MSG((void)fmt::format("{:%Y-%m-%d}", tp), fmt::format_error,
"cannot format duration");
}
}
TEST(chrono_test, timestamp_sub_seconds) {
auto t1 = sys_time<std::chrono::duration<long long, std::ratio<1, 3>>>(
std::chrono::duration<long long, std::ratio<1, 3>>(4));
EXPECT_EQ(fmt::format("{:%S}", t1), "01.333333");
auto t2 = sys_time<std::chrono::duration<double, std::ratio<1, 3>>>(
std::chrono::duration<double, std::ratio<1, 3>>(4));
EXPECT_EQ(fmt::format("{:%S}", t2), "01.333333");
auto t3 = sys_time<std::chrono::seconds>(std::chrono::seconds(2));
EXPECT_EQ(fmt::format("{:%S}", t3), "02");
auto t4 = sys_time<std::chrono::duration<double>>(
std::chrono::duration<double, std::ratio<1, 1>>(9.5));
EXPECT_EQ(fmt::format("{:%S}", t4), "09.500000");
auto t5 = sys_time<std::chrono::duration<double>>(
std::chrono::duration<double, std::ratio<1, 1>>(9));
EXPECT_EQ(fmt::format("{:%S}", t5), "09");
auto t6 = sys_time<std::chrono::milliseconds>(std::chrono::seconds(1) +
std::chrono::milliseconds(120));
EXPECT_EQ(fmt::format("{:%S}", t6), "01.120");
auto t7 =
sys_time<std::chrono::microseconds>(std::chrono::microseconds(1234567));
EXPECT_EQ(fmt::format("{:%S}", t7), "01.234567");
auto t8 =
sys_time<std::chrono::nanoseconds>(std::chrono::nanoseconds(123456789));
EXPECT_EQ(fmt::format("{:%S}", t8), "00.123456789");
auto t9 = std::chrono::time_point_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now());
auto t9_sec = std::chrono::time_point_cast<std::chrono::seconds>(t9);
auto t9_sub_sec_part = fmt::format("{0:09}", (t9 - t9_sec).count());
EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part),
fmt::format("{:%Y-%m-%d %H:%M:%S}", t9));
EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part),
fmt::format("{:%Y-%m-%d %T}", t9));
auto t10 =
sys_time<std::chrono::milliseconds>(std::chrono::milliseconds(2000));
EXPECT_EQ(fmt::format("{:%S}", t10), "02.000");
auto epoch = sys_time<std::chrono::milliseconds>();
auto d = std::chrono::milliseconds(250);
EXPECT_EQ(fmt::format("{:%S}", epoch - d), "59.750");
EXPECT_EQ(fmt::format("{:%S}", epoch), "00.000");
EXPECT_EQ(fmt::format("{:%S}", epoch + d), "00.250");
}
TEST(chrono_test, glibc_extensions) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%0}"), std::chrono::seconds()),
fmt::format_error, "invalid format");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%_}"), std::chrono::seconds()),
fmt::format_error, "invalid format");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%-}"), std::chrono::seconds()),
fmt::format_error, "invalid format");
{
const auto d = std::chrono::hours(1) + std::chrono::minutes(2) +
std::chrono::seconds(3);
EXPECT_EQ(fmt::format("{:%I,%H,%M,%S}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0I,%0H,%0M,%0S}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_I,%_H,%_M,%_S}", d), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-I,%-H,%-M,%-S}", d), "1,1,2,3");
EXPECT_EQ(fmt::format("{:%OI,%OH,%OM,%OS}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0OI,%0OH,%0OM,%0OS}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_OI,%_OH,%_OM,%_OS}", d), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-OI,%-OH,%-OM,%-OS}", d), "1,1,2,3");
}
{
const auto tm = make_tm(1970, 1, 1, 1, 2, 3);
EXPECT_EQ(fmt::format("{:%I,%H,%M,%S}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0I,%0H,%0M,%0S}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_I,%_H,%_M,%_S}", tm), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-I,%-H,%-M,%-S}", tm), "1,1,2,3");
EXPECT_EQ(fmt::format("{:%OI,%OH,%OM,%OS}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0OI,%0OH,%0OM,%0OS}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_OI,%_OH,%_OM,%_OS}", tm), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-OI,%-OH,%-OM,%-OS}", tm), "1,1,2,3");
}
{
const auto d = std::chrono::seconds(3) + std::chrono::milliseconds(140);
EXPECT_EQ(fmt::format("{:%S}", d), "03.140");
EXPECT_EQ(fmt::format("{:%0S}", d), "03.140");
EXPECT_EQ(fmt::format("{:%_S}", d), " 3.140");
EXPECT_EQ(fmt::format("{:%-S}", d), "3.140");
}
{
const auto d = std::chrono::duration<double>(3.14);
EXPECT_EQ(fmt::format("{:%S}", d), "03.140000");
EXPECT_EQ(fmt::format("{:%0S}", d), "03.140000");
EXPECT_EQ(fmt::format("{:%_S}", d), " 3.140000");
EXPECT_EQ(fmt::format("{:%-S}", d), "3.140000");
}
}
TEST(chrono_test, out_of_range) {
auto d = std::chrono::duration<unsigned long, std::giga>(538976288);
EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error);
}

View File

@@ -1,72 +0,0 @@
// Formatting library for C++ - color tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/color.h"
#include <iterator> // std::back_inserter
#include "gtest-extra.h" // EXPECT_WRITE
TEST(color_test, format) {
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_EQ(
fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::faint, "faint"), "\x1b[2mfaint\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"),
"\x1b[3mitalic\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::blink, "blink"), "\x1b[5mblink\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::reverse, "reverse"),
"\x1b[7mreverse\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::conceal, "conceal"),
"\x1b[8mconceal\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"),
"\x1b[9mstrikethrough\x1b[0m");
EXPECT_EQ(
fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"),
"\x1b[38;2;000;000;255mblue log\x1b[0m");
EXPECT_EQ(fmt::format(fmt::text_style(), "hi"), "hi");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "tred"),
"\x1b[31mtred\x1b[0m");
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::cyan), "tcyan"),
"\x1b[46mtcyan\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::bright_green), "tbgreen"),
"\x1b[92mtbgreen\x1b[0m");
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
"\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m");
EXPECT_EQ(fmt::format("{}{}", fmt::styled("red", fg(fmt::color::red)),
fmt::styled("bold", fmt::emphasis::bold)),
"\x1b[38;2;255;000;000mred\x1b[0m\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) |
fmt::emphasis::underline)),
"\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m");
}
TEST(color_test, format_to) {
auto out = std::string();
fmt::format_to(std::back_inserter(out), fg(fmt::rgb(255, 20, 30)),
"rgb(255,20,30){}{}{}", 1, 2, 3);
EXPECT_EQ(fmt::to_string(out),
"\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
}
TEST(color_test, print) {
EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
}

View File

@@ -1,241 +0,0 @@
# Test if compile errors are produced where necessary.
cmake_minimum_required(VERSION 3.8...3.25)
project(compile-error-test CXX)
set(fmt_headers "
#include <fmt/format.h>
#include <fmt/xchar.h>
#include <fmt/ostream.h>
#include <iostream>
")
set(error_test_names "")
set(non_error_test_content "")
# For error tests (we expect them to produce compilation error):
# * adds a name of test into `error_test_names` list
# * generates a single source file (with the same name) for each test
# For non-error tests (we expect them to compile successfully):
# * adds a code segment as separate function to `non_error_test_content`
function (expect_compile name code_fragment)
cmake_parse_arguments(EXPECT_COMPILE "ERROR" "" "" ${ARGN})
string(MAKE_C_IDENTIFIER "${name}" test_name)
if (EXPECT_COMPILE_ERROR)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/${test_name}.cc" "
${fmt_headers}
void ${test_name}() {
${code_fragment}
}
")
set(error_test_names_copy "${error_test_names}")
list(APPEND error_test_names_copy "${test_name}")
set(error_test_names "${error_test_names_copy}" PARENT_SCOPE)
else()
set(non_error_test_content "
${non_error_test_content}
void ${test_name}() {
${code_fragment}
}" PARENT_SCOPE)
endif()
endfunction ()
# Generates a source file for non-error test with `non_error_test_content` and
# CMake project file with all error and single non-error test targets.
function (run_tests)
set(cmake_targets "")
foreach(test_name IN LISTS error_test_names)
set(cmake_targets "
${cmake_targets}
add_library(test-${test_name} ${test_name}.cc)
target_link_libraries(test-${test_name} PRIVATE fmt::fmt)
")
endforeach()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/non_error_test.cc" "
${fmt_headers}
${non_error_test_content}
")
set(cmake_targets "
${cmake_targets}
add_library(non-error-test non_error_test.cc)
target_link_libraries(non-error-test PRIVATE fmt::fmt)
")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/CMakeLists.txt" "
cmake_minimum_required(VERSION 3.8...3.25)
project(tests CXX)
add_subdirectory(${FMT_DIR} fmt)
${cmake_targets}
")
set(build_directory "${CMAKE_CURRENT_BINARY_DIR}/test/build")
file(MAKE_DIRECTORY "${build_directory}")
execute_process(
COMMAND
"${CMAKE_COMMAND}"
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
"-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}"
"-DFMT_DIR=${FMT_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/test"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE output_var)
if (NOT result_var EQUAL 0)
message(FATAL_ERROR "Unable to configure:\n${output_var}")
endif()
foreach(test_name IN LISTS error_test_names)
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build "${build_directory}" --target "test-${test_name}"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_QUIET)
if (result_var EQUAL 0)
message(SEND_ERROR "No compile error for \"${test_name}\":\n${output_var}")
endif ()
endforeach()
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build "${build_directory}" --target "non-error-test"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE output_var)
if (NOT result_var EQUAL 0)
message(SEND_ERROR "Compile error for combined non-error test:\n${output_var}")
endif ()
endfunction ()
# check if the source file skeleton compiles
expect_compile(check "")
expect_compile(check-error "compilation_error" ERROR)
# Formatting a wide character with a narrow format string is forbidden.
expect_compile(wide-character-narrow-format-string "fmt::format(L\"{}\", L'a');")
expect_compile(wide-character-narrow-format-string-error "fmt::format(\"{}\", L'a');" ERROR)
# Formatting a wide string with a narrow format string is forbidden.
expect_compile(wide-string-narrow-format-string "fmt::format(L\"{}\", L\"foo\");")
expect_compile(wide-string-narrow-format-string-error "fmt::format(\"{}\", L\"foo\");" ERROR)
# Formatting a narrow string with a wide format string is forbidden because
# mixing UTF-8 with UTF-16/32 can result in an invalid output.
expect_compile(narrow-string-wide-format-string "fmt::format(L\"{}\", L\"foo\");")
expect_compile(narrow-string-wide-format-string-error "fmt::format(L\"{}\", \"foo\");" ERROR)
expect_compile(cast-to-string "
struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", std::string(S()));
")
expect_compile(cast-to-string-error "
struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", S());
" ERROR)
# Formatting a function
expect_compile(format-function "
void (*f)();
fmt::format(\"{}\", fmt::ptr(f));
")
expect_compile(format-function-error "
void (*f)();
fmt::format(\"{}\", f);
" ERROR)
# Formatting an unformattable argument should always be a compile time error
expect_compile(format-lots-of-arguments-with-unformattable "
struct E {};
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E());
" ERROR)
expect_compile(format-lots-of-arguments-with-function "
void (*f)();
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f);
" ERROR)
# Check if user-defined literals are available
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
set(CMAKE_REQUIRED_FLAGS )
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif ()
# Make sure that compiler features detected in the header
# match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS)
set(supports_udl 1)
else ()
set(supports_udl 0)
endif ()
expect_compile(udl-check "
#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
# error
#endif
")
if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
# Compile-time argument type check
expect_compile(format-string-number-spec "
#ifdef FMT_HAS_CONSTEVAL
fmt::format(\"{:d}\", 42);
#endif
")
expect_compile(format-string-number-spec-error "
#ifdef FMT_HAS_CONSTEVAL
fmt::format(\"{:d}\", \"I am not a number\");
#else
#error
#endif
" ERROR)
expect_compile(print-string-number-spec-error "
#ifdef FMT_HAS_CONSTEVAL
fmt::print(\"{:d}\", \"I am not a number\");
#else
#error
#endif
" ERROR)
expect_compile(print-stream-string-number-spec-error "
#ifdef FMT_HAS_CONSTEVAL
fmt::print(std::cout, \"{:d}\", \"I am not a number\");
#else
#error
#endif
" ERROR)
# Compile-time argument name check
expect_compile(format-string-name "
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
using namespace fmt::literals;
fmt::print(\"{foo}\", \"foo\"_a=42);
#endif
")
expect_compile(format-string-name-error "
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
using namespace fmt::literals;
fmt::print(\"{foo}\", \"bar\"_a=42);
#else
#error
#endif
" ERROR)
endif ()
# Run all tests
run_tests()

View File

@@ -1,63 +0,0 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/compile.h"
#include "gmock/gmock.h"
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \
defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \
defined(__cpp_constexpr_dynamic_alloc) && \
__cpp_constexpr_dynamic_alloc >= 201907 && FMT_CPLUSPLUS >= 202002L
template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
}
Char buffer[max_string_length]{};
};
template <size_t max_string_length, typename Char = char, typename... Args>
consteval auto test_format(auto format, const Args&... args) {
test_string<max_string_length, Char> string{};
fmt::format_to(string.buffer, format, args...);
return string;
}
TEST(compile_time_formatting_test, floating_point) {
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
EXPECT_EQ("9223372036854775808.000000",
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
if (std::signbit(-nan))
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
else
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
constexpr double inf = std::numeric_limits<double>::infinity();
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
}
#endif

View File

@@ -1,377 +0,0 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/compile.h"
#include <type_traits>
#include "fmt/chrono.h"
#include "gmock/gmock.h"
#include "gtest-extra.h"
TEST(iterator_test, counting_iterator) {
auto it = fmt::detail::counting_iterator();
auto prev = it++;
EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1);
EXPECT_EQ((it + 41).count(), 42);
}
TEST(compile_test, compile_fallback) {
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
// not available.
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
}
struct type_with_get {
template <int> friend void get(type_with_get);
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<type_with_get> : formatter<int> {
template <typename FormatContext>
auto format(type_with_get, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<int>::format(42, ctx);
}
};
FMT_END_NAMESPACE
TEST(compile_test, compile_type_with_get) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), type_with_get()));
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
struct test_formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<test_formattable> : formatter<const char*> {
char word_spec = 'f';
constexpr auto parse(format_parse_context& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
if (it != end && (*it == 'f' || *it == 'b')) word_spec = *it++;
if (it != end && *it != '}') throw format_error("invalid format");
return it;
}
template <typename FormatContext>
constexpr auto format(test_formattable, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<const char*>::format(word_spec == 'f' ? "foo" : "bar",
ctx);
}
};
FMT_END_NAMESPACE
TEST(compile_test, format_default) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ull));
EXPECT_EQ("true", fmt::format(FMT_COMPILE("{}"), true));
EXPECT_EQ("x", fmt::format(FMT_COMPILE("{}"), 'x'));
EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{}"), 4.2));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), "foo"));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo")));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), test_formattable()));
auto t = std::chrono::system_clock::now();
EXPECT_EQ(fmt::format("{}", t), fmt::format(FMT_COMPILE("{}"), t));
# ifdef __cpp_lib_byte
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), std::byte{42}));
# endif
}
TEST(compile_test, format_wide_string) {
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42));
}
TEST(compile_test, format_specs) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
EXPECT_EQ("1.2 ms ",
fmt::format(FMT_COMPILE("{:7.1%Q %q}"),
std::chrono::duration<double, std::milli>(1.234)));
}
TEST(compile_test, dynamic_format_specs) {
EXPECT_EQ("foo ", fmt::format(FMT_COMPILE("{:{}}"), "foo", 5));
EXPECT_EQ(" 3.14", fmt::format(FMT_COMPILE("{:{}.{}f}"), 3.141592, 6, 2));
EXPECT_EQ(
"=1.234ms=",
fmt::format(FMT_COMPILE("{:=^{}.{}}"),
std::chrono::duration<double, std::milli>(1.234), 9, 3));
}
TEST(compile_test, manual_ordering) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{0}"), 42));
EXPECT_EQ(" -42", fmt::format(FMT_COMPILE("{0:4}"), -42));
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {1}"), 41, 43));
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{1} {0}"), 43, 41));
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {2}"), 41, 42, 43));
EXPECT_EQ(" 41 43", fmt::format(FMT_COMPILE("{1:{2}} {0:4}"), 43, 41, 4));
EXPECT_EQ("42 1.2 ms ",
fmt::format(FMT_COMPILE("{0} {1:7.1%Q %q}"), 42,
std::chrono::duration<double, std::milli>(1.234)));
EXPECT_EQ(
"true 42 42 foo 0x1234 foo",
fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,
"foo", reinterpret_cast<void*>(0x1234), test_formattable()));
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42));
}
TEST(compile_test, named) {
auto runtime_named_field_compiled =
fmt::detail::compile<decltype(fmt::arg("arg", 42))>(FMT_COMPILE("{arg}"));
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
fmt::detail::runtime_named_field<char>>);
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), fmt::arg("arg", 42)));
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{} {}"), fmt::arg("arg", 41),
fmt::arg("arg", 43)));
EXPECT_EQ("foobar",
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"),
fmt::arg("a1", "bar")));
EXPECT_EQ("foobar", fmt::format(FMT_COMPILE("{}{a1}"), fmt::arg("a0", "foo"),
fmt::arg("a1", "bar")));
EXPECT_EQ("foofoo", fmt::format(FMT_COMPILE("{a0}{}"), fmt::arg("a0", "foo"),
fmt::arg("a1", "bar")));
EXPECT_EQ("foobar", fmt::format(FMT_COMPILE("{0}{a1}"), fmt::arg("a0", "foo"),
fmt::arg("a1", "bar")));
EXPECT_EQ("foobar", fmt::format(FMT_COMPILE("{a0}{1}"), fmt::arg("a0", "foo"),
fmt::arg("a1", "bar")));
EXPECT_EQ("foobar",
fmt::format(FMT_COMPILE("{}{a1}"), "foo", fmt::arg("a1", "bar")));
EXPECT_EQ("foobar",
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a1", "bar"),
fmt::arg("a2", "baz"), fmt::arg("a0", "foo")));
EXPECT_EQ(" bar foo ",
fmt::format(FMT_COMPILE(" {foo} {bar} "), fmt::arg("foo", "bar"),
fmt::arg("bar", "foo")));
EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)),
fmt::format_error);
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
using namespace fmt::literals;
auto statically_named_field_compiled =
fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}"));
static_assert(std::is_same_v<decltype(statically_named_field_compiled),
fmt::detail::field<char, int, 0>>);
EXPECT_EQ("41 43",
fmt::format(FMT_COMPILE("{a0} {a1}"), "a0"_a = 41, "a1"_a = 43));
EXPECT_EQ("41 43",
fmt::format(FMT_COMPILE("{a1} {a0}"), "a0"_a = 43, "a1"_a = 41));
# endif
}
TEST(compile_test, join) {
unsigned char data[] = {0x1, 0x2, 0xaf};
EXPECT_EQ("0102af", fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, "")));
}
TEST(compile_test, format_to) {
char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
*end = '\0';
EXPECT_STREQ("42", buf);
end = fmt::format_to(buf, FMT_COMPILE("{:x}"), 42);
*end = '\0';
EXPECT_STREQ("2a", buf);
}
TEST(compile_test, format_to_n) {
constexpr auto buffer_size = 8;
char buffer[buffer_size];
auto res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{}"), 42);
*res.out = '\0';
EXPECT_STREQ("42", buffer);
res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{:x}"), 42);
*res.out = '\0';
EXPECT_STREQ("2a", buffer);
}
# ifdef __cpp_lib_bit_cast
TEST(compile_test, constexpr_formatted_size) {
FMT_CONSTEXPR20 size_t size = fmt::formatted_size(FMT_COMPILE("{}"), 42);
EXPECT_EQ(size, 2);
FMT_CONSTEXPR20 size_t hex_size =
fmt::formatted_size(FMT_COMPILE("{:x}"), 15);
EXPECT_EQ(hex_size, 1);
FMT_CONSTEXPR20 size_t binary_size =
fmt::formatted_size(FMT_COMPILE("{:b}"), 15);
EXPECT_EQ(binary_size, 4);
FMT_CONSTEXPR20 size_t padded_size =
fmt::formatted_size(FMT_COMPILE("{:*^6}"), 42);
EXPECT_EQ(padded_size, 6);
FMT_CONSTEXPR20 size_t float_size =
fmt::formatted_size(FMT_COMPILE("{:.3}"), 12.345);
EXPECT_EQ(float_size, 4);
FMT_CONSTEXPR20 size_t str_size =
fmt::formatted_size(FMT_COMPILE("{:s}"), "abc");
EXPECT_EQ(str_size, 3);
}
# endif
TEST(compile_test, text_and_arg) {
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42));
}
TEST(compile_test, unknown_format_fallback) {
EXPECT_EQ(" 42 ",
fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42)));
std::vector<char> v;
fmt::format_to(std::back_inserter(v), FMT_COMPILE("{name:^4}"),
fmt::arg("name", 42));
EXPECT_EQ(" 42 ", fmt::string_view(v.data(), v.size()));
char buffer[4];
auto result = fmt::format_to_n(buffer, 4, FMT_COMPILE("{name:^5}"),
fmt::arg("name", 42));
EXPECT_EQ(5u, result.size);
EXPECT_EQ(buffer + 4, result.out);
EXPECT_EQ(" 42 ", fmt::string_view(buffer, 4));
}
TEST(compile_test, empty) { EXPECT_EQ("", fmt::format(FMT_COMPILE(""))); }
struct to_stringable {
friend fmt::string_view to_string_view(to_stringable) { return {}; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<to_stringable> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const to_stringable&, FormatContext& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(compile_test, to_string_and_formatter) {
fmt::format(FMT_COMPILE("{}"), to_stringable());
}
TEST(compile_test, print) {
EXPECT_WRITE(stdout, fmt::print(FMT_COMPILE("Don't {}!"), "panic"),
"Don't panic!");
EXPECT_WRITE(stderr, fmt::print(stderr, FMT_COMPILE("Don't {}!"), "panic"),
"Don't panic!");
}
#endif
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
TEST(compile_test, compile_format_string_literal) {
using namespace fmt::literals;
EXPECT_EQ("", fmt::format(""_cf));
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
EXPECT_EQ(L"42", fmt::format(L"{}"_cf, 42));
}
#endif
// MSVS 2019 19.29.30145.0 - Support C++20 and OK.
// MSVS 2022 19.32.31332.0, 19.37.32826.1 - compile-test.cc(362,3): fatal error
// C1001: Internal compiler error.
// (compiler file
// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp',
// line 8635)
#if (FMT_CPLUSPLUS >= 202002L || \
(FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \
((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \
(!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \
(!FMT_MSC_VERSION || \
(FMT_MSC_VERSION >= 1928 && FMT_MSC_VERSION < 1930))) && \
defined(__cpp_lib_is_constant_evaluated)
template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
}
Char buffer[max_string_length]{};
};
template <size_t max_string_length, typename Char = char, typename... Args>
consteval auto test_format(auto format, const Args&... args) {
test_string<max_string_length, Char> string{};
fmt::format_to(string.buffer, format, args...);
return string;
}
TEST(compile_time_formatting_test, bool) {
EXPECT_EQ("true", test_format<5>(FMT_COMPILE("{}"), true));
EXPECT_EQ("false", test_format<6>(FMT_COMPILE("{}"), false));
EXPECT_EQ("true ", test_format<6>(FMT_COMPILE("{:5}"), true));
EXPECT_EQ("1", test_format<2>(FMT_COMPILE("{:d}"), true));
}
TEST(compile_time_formatting_test, integer) {
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), 42));
EXPECT_EQ("420", test_format<4>(FMT_COMPILE("{}"), 420));
EXPECT_EQ("42 42", test_format<6>(FMT_COMPILE("{} {}"), 42, 42));
EXPECT_EQ("42 42",
test_format<6>(FMT_COMPILE("{} {}"), uint32_t{42}, uint64_t{42}));
EXPECT_EQ("+42", test_format<4>(FMT_COMPILE("{:+}"), 42));
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{:-}"), 42));
EXPECT_EQ(" 42", test_format<4>(FMT_COMPILE("{: }"), 42));
EXPECT_EQ("-0042", test_format<6>(FMT_COMPILE("{:05}"), -42));
EXPECT_EQ("101010", test_format<7>(FMT_COMPILE("{:b}"), 42));
EXPECT_EQ("0b101010", test_format<9>(FMT_COMPILE("{:#b}"), 42));
EXPECT_EQ("0B101010", test_format<9>(FMT_COMPILE("{:#B}"), 42));
EXPECT_EQ("042", test_format<4>(FMT_COMPILE("{:#o}"), 042));
EXPECT_EQ("0x4a", test_format<5>(FMT_COMPILE("{:#x}"), 0x4a));
EXPECT_EQ("0X4A", test_format<5>(FMT_COMPILE("{:#X}"), 0x4a));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ll));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ull));
EXPECT_EQ("42 ", test_format<5>(FMT_COMPILE("{:<4}"), 42));
EXPECT_EQ(" 42", test_format<5>(FMT_COMPILE("{:>4}"), 42));
EXPECT_EQ(" 42 ", test_format<5>(FMT_COMPILE("{:^4}"), 42));
EXPECT_EQ("**-42", test_format<6>(FMT_COMPILE("{:*>5}"), -42));
}
TEST(compile_time_formatting_test, char) {
EXPECT_EQ("c", test_format<2>(FMT_COMPILE("{}"), 'c'));
EXPECT_EQ("c ", test_format<4>(FMT_COMPILE("{:3}"), 'c'));
EXPECT_EQ("99", test_format<3>(FMT_COMPILE("{:d}"), 'c'));
}
TEST(compile_time_formatting_test, string) {
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), "42"));
EXPECT_EQ("The answer is 42",
test_format<17>(FMT_COMPILE("{} is {}"), "The answer", "42"));
EXPECT_EQ("abc**", test_format<6>(FMT_COMPILE("{:*<5}"), "abc"));
EXPECT_EQ("**🤡**", test_format<9>(FMT_COMPILE("{:*^6}"), "🤡"));
}
TEST(compile_time_formatting_test, combination) {
EXPECT_EQ("420, true, answer",
test_format<18>(FMT_COMPILE("{}, {}, {}"), 420, true, "answer"));
EXPECT_EQ(" -42", test_format<5>(FMT_COMPILE("{:{}}"), -42, 4));
}
TEST(compile_time_formatting_test, custom_type) {
EXPECT_EQ("foo", test_format<4>(FMT_COMPILE("{}"), test_formattable()));
EXPECT_EQ("bar", test_format<4>(FMT_COMPILE("{:b}"), test_formattable()));
}
TEST(compile_time_formatting_test, multibyte_fill) {
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
}
#endif

View File

@@ -0,0 +1,78 @@
# Test if compile errors are produced where necessary.
cmake_minimum_required(VERSION 2.8)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../..)
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
function (generate_source result fragment)
set(${result} "
#define FMT_HEADER_ONLY 1
#include \"fmt/posix.h\"
int main() {
${fragment}
}
" PARENT_SCOPE)
endfunction ()
function (expect_compile code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (NOT compiles)
set(error_msg "Compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (error_msg)
message(FATAL_ERROR ${error_msg})
endif ()
endfunction ()
function (expect_compile_error code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (compiles)
set(error_msg "No compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (error_msg)
message(FATAL_ERROR ${error_msg})
endif ()
endfunction ()
# check if the source file skeleton compiles
expect_compile("")
# MakeArg doesn't accept [const] volatile char *.
expect_compile_error("volatile char s[] = \"test\"; (fmt::internal::MakeArg<char>)(s);")
expect_compile_error("const volatile char s[] = \"test\"; (fmt::internal::MakeArg<char>)(s);")
# MakeArg<char> doesn't accept wchar_t.
expect_compile_error("fmt::internal::MakeValue<char>(L'a');")
expect_compile_error("fmt::internal::MakeValue<char>(L\"test\");")
# Writing a wide character to a character stream Writer is forbidden.
expect_compile_error("fmt::MemoryWriter() << L'a';")
expect_compile_error("fmt::MemoryWriter() << fmt::pad(\"abc\", 5, L' ');")
expect_compile_error("fmt::MemoryWriter() << fmt::pad(42, 5, L' ');")
# Formatting a wide character with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L'a';")
expect_compile("FMT_STATIC_ASSERT(true, \"this should never happen\");")
expect_compile_error("FMT_STATIC_ASSERT(0 > 1, \"oops\");")
# Make sure that compiler features detected in the header
# match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS)
set(supports_udl 1)
else ()
set(supports_udl 0)
endif ()
expect_compile("#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
# error
#endif")

Some files were not shown because too many files have changed in this diff Show More