Compare commits

..

82 Commits
8.0.1 ... 3.0.2

Author SHA1 Message Date
Jonathan Müller
5708bf8fcc Update changelog 2017-06-14 18:05:24 +02:00
Rich E
fbd871e433 added FMT_API declarations where needed when using FMT_EXPORT from a separate dll
(cherry picked from commit ad6d78f2a8)
2017-06-09 13:33:59 +02:00
Andreas Schönle
5bce2c0cef Allow compiling and using as DLL in windows (#502)
* fix warnings and errors in test compiles with BUILD_SHARED_LIBS

* did requested changes and added one change to allow all tests to succeed
in windows DLL

(cherry picked from commit 79f11dbaa7)
2017-06-09 12:42:32 +02:00
Tomek Rozen
c75f6dcd30 Changing ArgArray template argument from unsigned to size_t
Each instantiation of ArgArray template uses sizeof operator, which
returns a std::size_t value. GCC 7.1 warns about invalid conversion
(error: conversion to ‘unsigned int’ from ‘long unsigned
int’ may alter its value [-Werror=conversion]).

(cherry picked from commit e14bac62a0)
2017-06-08 12:08:50 +02:00
Victor Zverovich
ecd0994c15 Use Ubuntu Trusty on Travis for a new CMake
(cherry picked from commit 0284a2893a)
2017-06-08 12:08:50 +02:00
Victor Zverovich
7859c3493f Fix handling of implicit conversion to integral types larger than int (#507)
(cherry picked from commit 07ed421521)
2017-06-08 12:08:50 +02:00
Victor Zverovich
4fca31c497 Don't define WIN32_LEAN_AND_MEAN in header-only mode
(cherry picked from commit c56d345985)
2017-06-08 12:08:50 +02:00
Jonathan Müller
e6df023916 Workaround MSVC lookup issue in ArgFormatterBase
Fixes #505.

(cherry picked from commit 52aabbe7ef)
2017-06-08 12:08:50 +02:00
Quentin Buathier
1934d9e5d9 Don't include the world with WIN32_LEAN_AND_MEAN (#503)
(cherry picked from commit 4423490d0b)
2017-06-08 12:08:50 +02:00
Victor Zverovich
9270e66700 Remove redundant include (#479)
(cherry picked from commit cc736e7611)
2017-06-08 11:33:01 +02:00
Victor Zverovich
9589bf05de Fix error on MinGW
(cherry picked from commit db0d54f855)
2017-06-08 11:32:49 +02:00
Victor Zverovich
aad76bf4c1 Document which header defines formatting functions
(cherry picked from commit e208fbff52)
2017-06-08 11:32:39 +02:00
Magnus Bjerke Vik
0bed09caaf Fix Android not being detected with latest NDK toolchain (#458)
When using the NDK 13b toolchain standalone or with CMake, ANDROID is not defined,
but __ANDROID__ is defined instead.

(cherry picked from commit bc6af7548b)
2017-06-08 11:29:36 +02:00
Victor Zverovich
0e5a4422c2 Workaround an nvcc bug
(cherry picked from commit 9e9ad57f58)
2017-06-08 11:29:32 +02:00
Calum Robinson
72484cc191 Fix missing intrinsic when included from C++/CLI
Managed C++ code doesn't have the _BitScanReverse* intrinsics, we must use the fallback code for count_digits etc.

(cherry picked from commit a2596d685d)
2017-06-08 11:29:27 +02:00
Eric Fiselier
80bfa855a2 Don't export the -std=c++11 flag from the fmt target
(cherry picked from commit b9aaa507fc)
2017-06-08 11:29:16 +02:00
Victor Zverovich
850405f0b0 Fix test compilation for FreeBSD (#433)
(cherry picked from commit fac5546321)
2017-06-08 11:28:48 +02:00
Victor Zverovich
a55df13598 Fix collision with global convert function (#425)
(cherry picked from commit 796beaaddb)
2017-06-08 11:28:43 +02:00
Victor Zverovich
baedba07ca Fix signbit detection (#423)
(cherry picked from commit def687462c)
2017-06-08 11:28:39 +02:00
Victor Zverovich
b9a20d761a Add version macro FMT_VERSION (#411)
(cherry picked from commit f5b1c16e2c)

# Conflicts:
#	CMakeLists.txt
2017-06-08 11:27:51 +02:00
Victor Zverovich
ba0c3b957f Fix warnings on C++98
(cherry picked from commit cfd00af37a)
2017-06-08 11:27:11 +02:00
Victor Zverovich
4a24a38861 Fix warnings
(cherry picked from commit 6274401919)

# Conflicts:
#	fmt/printf.h
#	test/string-test.cc
2017-06-08 11:26:18 +02:00
Jonathan Müller
5c69ecdd5e Create pull_request_template
(cherry picked from commit 8cef95f7c3)
2017-06-08 11:23:13 +02:00
Victor Zverovich
29732d44bb Add CONTRIBUTING file
(cherry picked from commit 922ae4896b)
2017-06-08 11:23:09 +02:00
Chen Hayat
abee8f95ec Fix Klockwork compilation warning
(cherry picked from commit 05ba3e7888)
2017-06-08 11:23:04 +02:00
Victor Zverovich
48ba0f2aa2 FMT_NULLPTR -> FMT_NULL and improve formatting
(cherry picked from commit 1daddec151)

# Conflicts:
#	fmt/printf.h
#	fmt/string.h
#	fmt/time.h
2017-06-08 11:22:50 +02:00
Jonathan Müller
d3a9d03138 Fix missing '>'
(cherry picked from commit d8867a2b07)
2017-06-08 11:21:55 +02:00
Jonathan Müller
7aaa693fb3 Add extern templates for format_float
Fixes #413.

(cherry picked from commit 6c0125785b)
2017-06-08 11:21:49 +02:00
alabuzhev
267bed868f Using FMT_NULLPTR instead of literal 0
(cherry picked from commit 49ccb2e449)
2017-06-08 11:21:41 +02:00
Victor Zverovich
f0a7da0219 argument index -> argument id
(cherry picked from commit 7ce7def515)
2017-06-08 11:18:34 +02:00
Lectem
5a5c3a3c25 fix newer clang warnings
(cherry picked from commit 8f455c10b0)
2017-06-08 11:17:45 +02:00
Jonathan Müller
7fa8f8fa48 Update version 2016-11-01 09:24:49 +01:00
Jonathan Müller
1ef8b4a5de Update version to 3.0.1 2016-10-31 22:02:46 +01:00
Jonathan Müller
4a66897fb6 Update ChangeLog 2016-10-31 22:02:43 +01:00
Jan Hellwig
63639cba98 Fix Windows compilation with -fno-exceptions (#405)
(cherry picked from commit 15d0f32773)
2016-10-31 22:02:42 +01:00
Jan Hellwig
2baf2a5e2c Fix Linux compilation with -fno-exceptions (#402)
(cherry picked from commit 8c63ea432c)
2016-10-31 22:02:42 +01:00
Victor Zverovich
fb9015b3a1 CHAR_WIDTH -> CHAR_SIZE to avoid collision with ISO/IEC TS 18661-1:2014 macro
(cherry picked from commit abbefd7166)
2016-10-28 19:42:17 +02:00
chronoxor
0a0c2c9cdd Remove unnecessary "fmt/" prefix which should be maintained with additional include_directories() in each project. (#397)
(cherry picked from commit 1500f00525)
2016-10-23 12:17:31 +02:00
Victor Zverovich
1c9a674d24 Fix compilation on Cygwin (#388)
(cherry picked from commit cee50b7572)
2016-10-23 12:17:30 +02:00
Jean-Michaël Celerier
6e6adf431f Add FMT_OVERRIDE macro to allow specifying overriding functions in c++11 compilers
(cherry picked from commit 9ec3bea2d6)
2016-10-23 12:17:30 +02:00
Hugh Wang
f8289ba1da Fix compilation on Android.
(cherry picked from commit 5f26b5da28)
2016-10-23 12:17:30 +02:00
Victor Zverovich
79100a21bc Correct syntax
(cherry picked from commit 04335aeadb)
2016-10-23 12:17:30 +02:00
Victor Zverovich
f4a417f8fb Fix handling of unsigned char strings (#373)
(cherry picked from commit a79c7b4e8f)
2016-10-23 12:17:30 +02:00
Victor Zverovich
5047091ad8 Add 'a' and 'A' format specifiers
(cherry picked from commit 53f9e7f65c)
2016-10-23 12:17:30 +02:00
Victor Zverovich
f880a6cedf Add CUAUV, Seastar and ScyllaDB to the list of projects that use fmt
(cherry picked from commit 2bf59a97c6)
2016-10-23 12:01:53 +02:00
Victor Zverovich
7459ce15d3 Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified (#366)
(cherry picked from commit 12a6027b40)
2016-10-23 12:01:42 +02:00
Victor Zverovich
f0a0674040 Use a heuristic to detect empty strftime result (#367)
(cherry picked from commit a5d0adf395)
2016-10-23 12:01:33 +02:00
Victor Zverovich
c79174e1c0 Correct buffer growth when formatting time (#367)
(cherry picked from commit 1a23f9c274)
2016-10-23 12:01:17 +02:00
Victor Zverovich
f116a28134 Add "n" to integer formatting types
(cherry picked from commit ed30108918)
2016-10-23 11:59:32 +02:00
Taro Matsuzawa aka. btm
6d91a801a4 Update usage.rst: change Homebrew package name
(cherry picked from commit 1dbc6bd1fc)
2016-10-23 11:59:05 +02:00
Nils Moehrle
e3de93298b Remove unreachable code below FMT_THROW
(cherry picked from commit c8c9973669)
2016-10-23 11:58:44 +02:00
Victor Zverovich
6f27f98103 Update readme
(cherry picked from commit c110c6eca7)
2016-10-23 11:55:51 +02:00
Victor Zverovich
b8540e613e Workaround broken MinGW installation on AppVeyor (#355)
(cherry picked from commit 1acfd07f1e)
2016-10-23 11:54:55 +02:00
Victor Zverovich
a20c81a061 Break a long line
(cherry picked from commit 903357c853)
2016-10-23 11:53:12 +02:00
Tilman Keskinöz
5d306cf11c Fix compile on freebsd
(cherry picked from commit d7c4e1cb1f)
2016-10-23 11:53:00 +02:00
Victor Zverovich
3f778b2099 check -> const_check to avoid a conflict with AssertMacros.h (#350)
(cherry picked from commit 8631694021)
2016-10-23 11:52:49 +02:00
Victor Zverovich
27f604a153 Fix handling of thousands separator (#353)
(cherry picked from commit 0e6df7e511)
2016-10-23 11:52:39 +02:00
Sean LK
1127e7a953 Fix building with clang in header-only mode
Building under a recent clang compiler causes the use of an extern
template for the BasicData struct. However, the only instantiation of
that structure in format.cc is only done if FMT_HEADER_ONLY is not
defined. This causes the build to fail in C++11 or C++14 mode.
Therefore, only declare the BasicData template as extern if
FMT_HEADER_ONLY is not defined.

(cherry picked from commit 75f862bf57)
2016-10-23 11:52:28 +02:00
Victor Zverovich
b838eb0c2e Reduce noise
(cherry picked from commit 5c876088d3)
2016-10-23 11:52:07 +02:00
Victor Zverovich
59f9936009 Fix a link (#347)
(cherry picked from commit 2e69e454d0)
2016-10-23 11:51:40 +02:00
Victor Zverovich
b26e239c05 Fix MSVC warnings
(cherry picked from commit e0d6f630f8)
2016-10-23 11:51:09 +02:00
Victor Zverovich
300d4f3c89 Suppress -Wpadded warnings
(cherry picked from commit a201c61977)
2016-10-23 11:50:15 +02:00
Victor Zverovich
8cfff4da4c Fix -Wpessimizing-move
(cherry picked from commit 4569b4dbd6)
2016-10-23 11:50:02 +02:00
Victor Zverovich
92d3f31df5 Fix clang warning
(cherry picked from commit c9bb5468b6)
2016-10-23 11:49:54 +02:00
Victor Zverovich
df05947b8a Fix a -Wweak-vtables warning in clang
(cherry picked from commit b26e76efe9)
2016-10-23 11:49:44 +02:00
Victor Zverovich
679fed9187 Don't use strtod_l on Android (#345)
(cherry picked from commit c7739536e8)
2016-10-23 11:49:31 +02:00
Victor Zverovich
14b4a2b073 Suppress clang's documentation warnings
(cherry picked from commit 18e3f16576)
2016-10-23 11:49:19 +02:00
Victor Zverovich
8bb431fef3 Break long lines
(cherry picked from commit 231c16df25)
2016-10-23 11:47:59 +02:00
Victor Zverovich
01e31f3154 Don't use --upgrade because it causes pip install older version of sphinx
(cherry picked from commit e68836c123)
2016-10-23 11:47:45 +02:00
Victor Zverovich
713a061bc5 Make documentation installation optional (#333)
(cherry picked from commit dc1e36fa0e)
2016-10-23 11:47:36 +02:00
Victor Zverovich
87f5f867f6 Update sphinx
(cherry picked from commit 7bb1b50835)
2016-10-23 11:47:32 +02:00
Victor Zverovich
ae85c91e80 Detect if lconv contains thousands_sep
(cherry picked from commit 96c28f748d)
2016-10-23 11:47:15 +02:00
Victor Zverovich
584fa85d18 Add fmt prefix to includes (#332)
Technically it is not necessary, but prevents errors when people add the
fmt directory itself rather than its parent to includes.

(cherry picked from commit e160c2b79a)
2016-10-23 11:46:38 +02:00
Victor Zverovich
d5dd1cd2a0 Use a mock to test locale support
(cherry picked from commit e2a332e5df)
2016-10-23 11:46:05 +02:00
Victor Zverovich
078c44e31c Improve formatting consistency
(cherry picked from commit ebff26f8f1)
2016-10-23 11:44:52 +02:00
Dmitry Panteleev
2fd6c0b245 Workaround for missing lconv support in android (#327)
(cherry picked from commit 45a1509455)
2016-10-23 11:44:42 +02:00
Victor Zverovich
affac0bea0 Check if setlocale succeeded
(cherry picked from commit 5b106083e7)
2016-10-23 11:44:32 +02:00
Victor Zverovich
ae8ae747bd Workaround an issue with "delete this" in GMock and gcc 6.1.1
(cherry picked from commit d00b43c592)
2016-10-23 11:44:19 +02:00
Victor Zverovich
c1c963e8b9 Correct docs
(cherry picked from commit d67eb8af2f)
2016-10-23 11:44:10 +02:00
Victor Zverovich
5b26ada01c Fix clang -Weverything warnings (#250)
(cherry picked from commit 3400e0d609)
2016-10-23 11:44:02 +02:00
Victor Zverovich
b6481599e6 Clarify performance tradeoffs (#320)
(cherry picked from commit f76583276a)
2016-10-23 11:43:49 +02:00
Victor Zverovich
3f1cd52adc Fix a clang warning about an undefined template (#318)
(cherry picked from commit 744c2824c5)
2016-10-23 11:42:09 +02:00
140 changed files with 48255 additions and 61370 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

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,6 +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 that your contributions are licensed
under the {fmt} license, and agree to future changes to the licensing.
-->

View File

@@ -1,24 +0,0 @@
name: doc
on: [push, pull_request]
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@v2
- 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,78 +0,0 @@
name: linux
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
cxx: [g++-4.8, g++-10, clang++-9]
build_type: [Debug, Release]
std: [11]
os: [ubuntu-18.04]
include:
- cxx: g++-4.8
install: sudo apt install g++-4.8
os: ubuntu-18.04
- cxx: g++-8
build_type: Debug
std: 14
install: sudo apt install g++-8
os: ubuntu-18.04
- cxx: g++-10
build_type: Debug
std: 17
os: ubuntu-18.04
- cxx: g++-10
build_type: Debug
std: 20
os: ubuntu-20.04
- cxx: clang++-9
build_type: Debug
fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
std: 17
os: ubuntu-18.04
- cxx: clang++-11
build_type: Debug
std: 20
os: ubuntu-20.04
- cxx: clang++-11
build_type: Debug
std: 20
cxxflags: -stdlib=libc++
os: ubuntu-20.04
install: sudo apt install libc++-11-dev libc++abi-11-dev
- shared: -DBUILD_SHARED_LIBS=ON
steps:
- uses: actions/checkout@v2
- name: Create Build Environment
run: |
${{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,37 +0,0 @@
name: macos
on: [push, pull_request]
jobs:
build:
runs-on: macos-10.15
strategy:
matrix:
build_type: [Debug, Release]
include:
- shared: -DBUILD_SHARED_LIBS=ON
steps:
- uses: actions/checkout@v2
- 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_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,60 +0,0 @@
name: windows
on: [push, pull_request]
jobs:
build:
runs-on: ${{matrix.os}}
strategy:
matrix:
# windows-2016 and windows-2019 have MSVC 2017 and 2019 installed
# respectively: https://github.com/actions/virtual-environments.
os: [windows-2016, windows-2019]
platform: [Win32, x64]
build_type: [Debug, Release]
standard: [11, 17, 20]
include:
- os: windows-2016
platform: Win32
build_type: Debug
shared: -DBUILD_SHARED_LIBS=ON
exclude:
- os: windows-2016
platform: Win32
- os: windows-2016
standard: 17
- os: windows-2016
standard: 20
- os: windows-2019
standard: 11
- os: windows-2019
standard: 20
platform: Win32
steps:
- uses: actions/checkout@v2
- 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 -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.shared}} \
-A ${{matrix.platform}} \
-DCMAKE_CXX_STANDARD=${{matrix.standard}} \
$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

23
.gitignore vendored
View File

@@ -1,37 +1,18 @@
.vscode/
.vs/
*.iml
.idea/
.externalNativeBuild/
.gradle/
gradle/
gradlew*
local.properties
build/
support/.cxx
bin/
/_CPack_Packages
/CMakeScripts
/doc/doxyxml
/doc/html
/doc/node_modules
virtualenv
/doc/virtualenv
/Testing
/install_manifest.txt
*~
*.a
*.so*
*.xcodeproj
*.zip
/*.cmake
cmake_install.cmake
CPack*.cmake
fmt-*.cmake
CTestTestfile.cmake
CMakeCache.txt
CMakeFiles
FMT.build
Makefile
run-msbuild.bat
fmt.pc

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,106 +1,46 @@
cmake_minimum_required(VERSION 3.1...3.18)
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})
endif()
cmake_minimum_required(VERSION 2.8.12)
# 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()
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})
endif ()
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
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." ${FMT_MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
option(FMT_MODULE "Build a module instead of a traditional library." 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)
set(FMT_CAN_MODULE OFF)
if (CMAKE_CXX_STANDARD GREATER 17 AND
# msvc 16.10-pre4
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
set(FMT_CAN_MODULE ON)
endif ()
if (NOT FMT_CAN_MODULE)
set(FMT_MODULE OFF)
message(STATUS "Module support is disabled.")
endif ()
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 ()
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})
@@ -112,81 +52,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_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(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths)
include(cxx11)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
# compatibility with older CMake versions.
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
endif ()
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
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 -Wswitch-enum -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
@@ -196,176 +73,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 ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
check_symbol_exists(open io.h HAVE_OPEN)
else ()
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
check_symbol_exists(open fcntl.h HAVE_OPEN)
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 locale.h os.h ostream.h printf.h ranges.h
xchar.h)
if (FMT_MODULE)
set(FMT_SOURCES src/fmt.cc)
elseif (FMT_OS)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (MINGW)
check_cxx_compiler_flag("Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
if (${FMT_HAS_MBIG_OBJ})
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif()
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 (FMT_MODULE)
enable_module(fmt)
endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt 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}
DEBUG_POSTFIX "${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)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND
NOT EMSCRIPTEN)
# 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 ()
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 ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only 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 PATH
"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}
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 $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif ()
add_subdirectory(fmt)
if (FMT_DOC)
add_subdirectory(doc)
@@ -376,29 +97,17 @@ 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}")
set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach ()
set(ignored_files ${ignored_files}
set(ignored_files ${ignored_files} ${PROJECT_BINARY_DIR}
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
set(CPACK_SOURCE_GENERATOR ZIP)

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

View File

@@ -1,27 +1,23 @@
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
All rights reserved.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
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.
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.
--- 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.
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.

View File

@@ -1,186 +1,285 @@
{fmt}
=====
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
.. 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
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
:alt: fmt is continuously fuzzed at oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
Summary&q=proj%3Dfmt&can=1
**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.
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: https://stackoverflow.com/questions/tagged/fmt
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to the BYSOL
Foundation that helps victims of political repressions in Belarus:
https://bysol.org/en/bs/general/.
`Documentation <https://fmt.dev>`__
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>`_.
`Documentation <http://fmtlib.net/latest/>`_
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>`__
* `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
* 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
<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`_
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`_
* 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%20
Component%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
<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 (format 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 MIT `license
permissive BSD `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
* `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 <https://fmt.dev>`_ for more details.
See the `documentation <http://fmtlib.net/latest/>`_ for more details.
Examples
--------
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
This prints ``Hello, world!`` to stdout:
.. code:: c++
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
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 *)
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
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
* `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
* `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
* `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::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
which is a lot of typing compared to printf:
.. code:: c++
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."
printf("%.2f\n", 1.23456);
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
Matthew Wilson, the author of FastFormat, referred to this situation with
IOStreams as "chevron hell". IOStreams doesn't support positional arguments
by design.
.. code:: c++
The good part is that IOStreams supports user-defined types and is safe
although error reporting is awkward.
#include <fmt/chrono.h>
Boost Format library
~~~~~~~~~~~~~~~~~~~~
int main() {
using namespace std::literals::chrono_literals;
fmt::print("Default format: {} {}\n", 42s, 100ms);
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}
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`_).
Output::
FastFormat
~~~~~~~~~~
Default format: 42s 100ms
strftime-like format: 03:15:30
This is an interesting library which is fast, safe and has positional
arguments. However it has significant limitations, citing its author:
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_)
Three features that have no hope of being accommodated within the
current design are:
.. code:: c++
* Leading zeros (or any other non-space padding)
* Octal/hexadecimal encoding
* Runtime width/alignment specification
#include <vector>
#include <fmt/ranges.h>
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
int main() {
std::vector<int> v = {1, 2, 3};
fmt::print("{}\n", v);
}
Loki SafeFormat
~~~~~~~~~~~~~~~
Output::
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.
[1, 2, 3]
Tinyformat
~~~~~~~~~~
**Check a format string at compile time**
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.
.. code:: c++
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
std::string s = fmt::format(FMT_STRING("{:d}"), "I am not a number");
This gives a compile-time error because ``d`` is an invalid format specifier for
a string.
**Write a file from a single thread**
.. code:: 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**
.. code:: 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, "Hello, {}!\n", "мир");
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
"Hello, {}!\n", "世界");
}
Output on a modern terminal:
.. image:: https://user-images.githubusercontent.com/
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
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
----------
@@ -188,33 +287,31 @@ 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
================= ============= ===========
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
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
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 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/tinyformat_test.cpp>`_.
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
floating-point 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
:target: https://fmt.dev/unknown_mac64_clang12.0.html
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -225,45 +322,41 @@ 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.
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 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
============= =============== ==================== ==================
============ =============== ==================== ==================
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 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 extra ``<string>``
include to measure the overhead of the latter.
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.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
============= =============== ==================== ==================
============ =============== ==================== ==================
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``, ``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.
``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
~~~~~~~~~~~~~~~~~
@@ -271,7 +364,7 @@ Running the tests
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ https://fmt.dev/latest/usage.html#building-the-library
__ http://fmtlib.net/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
@@ -289,240 +382,42 @@ Then you can run the speed test::
or the bloat test::
$ make bloat-test
Migrating code
--------------
`clang-tidy-fmt <https://github.com/mikecrowe/clang-tidy-fmt>`_ provides clang
tidy checks for converting occurrences of ``printf`` and ``fprintf`` to
``fmt::print``.
Projects using this library
---------------------------
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
real-time strategy game
* `2GIS <https://2gis.ru/>`_: free business listings with a city map
* `AMPL/MP <https://github.com/ampl/mp>`_:
an open-source library for mathematical programming
* `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>`_: analytical database
management system
* `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
* `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
* `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
* `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:
.. 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, 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 which 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`_).
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.
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::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
fmt is distributed under the BSD `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
Documentation License
---------------------
The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
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>`_.
For this reason the documentation is distributed under the Python Software
Foundation license available in `doc/python-license.txt
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}.
It only applies if you distribute the documentation of fmt.
Maintainers
-----------
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.

2
cppformat/format.h Normal file
View File

@@ -0,0 +1,2 @@
#include "../fmt/format.h"
#warning Including cppformat/format.h is deprecated. Include fmt/format.h instead.

2
cppformat/posix.h Normal file
View File

@@ -0,0 +1,2 @@
#include "../fmt/posix.h"
#warning Including cppformat/posix.h is deprecated. Include fmt/posix.h instead.

View File

@@ -4,14 +4,8 @@ if (NOT DOXYGEN)
return ()
endif ()
find_package(PythonInterp QUIET REQUIRED)
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})
include(GNUInstallDirs)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL
PATTERN ".doctrees" EXCLUDE)
DESTINATION share/doc/fmt OPTIONAL)

View File

@@ -6,22 +6,20 @@
<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 %}
{%- macro searchform(classes, button) %}
<form class="{{classes}}" role="search" action="{{ pathto('search') }}"
method="get">
<form class="{{classes}}" role="search" action="{{ pathto('search') }}" method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
{{ 'placeholder="Search"' if not button }} >
<input type="text" name="q" class="form-control" {{ 'placeholder="Search"' if not button }} >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
@@ -38,8 +36,7 @@
<div class="navbar-content">
{# Brand and toggle get grouped for better mobile display #}
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
@@ -52,19 +49,18 @@
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">{{ version }}
<span class="caret"></span></a>
{# TODO: update versions automatically #}
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-expanded="false">{{ version }} <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>
{% endfor %}
<li><a href="http://fmtlib.net/2.0.0/">2.0.0</a></li>
<li><a href="http://fmtlib.net/1.1.0/">1.1.0</a></li>
<li><a href="http://fmtlib.net/1.0.0/">1.0.0</a></li>
</ul>
</li>
{% for name in ['Contents', 'Usage', 'API', 'Syntax'] %}
{% if pagename == name.lower() %}
<li class="active"><a href="{{name.lower()}}.html">{{name}}
<span class="sr-only">(current)</span></a></li>
<li class="active"><a href="{{name.lower()}}.html">{{name}} <span class="sr-only">(current)</span></a></li>
{%else%}
<li><a href="{{name.lower()}}.html">{{name}}</a></li>
{%endif%}
@@ -79,25 +75,20 @@
</div> {# /.tb-container #}
</nav>
{% if pagename == "index" %}
{% set download_url = 'https://github.com/fmtlib/fmt/releases/download' %}
<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"
href="{{download_url}}/{{version}}/{{name}}-{{version}}.zip">
<span class="glyphicon glyphicon-download"></span> Download
href="https://github.com/fmtlib/fmt/releases/download/2.0.0/cppformat-2.0.0.zip">
<span class="glyphicon glyphicon-download"></span> Download
</a>
<button type="button" class="btn btn-success dropdown-toggle"
data-toggle="dropdown"><span class="caret"></span></button>
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu">
{% for v in versions.split(',') %}
{% set name = 'fmt' if v.split('.')[0]|int >= 3 else 'cppformat' %}
<li><a href="{{download_url}}/{{v}}/{{name}}-{{v}}.zip">Version {{v}}
</a></li>
{% endfor %}
<li><a href="https://github.com/fmtlib/fmt/releases/download/2.0.0/cppformat-2.0.0.zip">Version 2.0.0</a></li>
<li><a href="https://github.com/fmtlib/fmt/releases/download/1.1.0/cppformat-1.1.0.zip">Version 1.1.0</a></li>
<li><a href="https://github.com/fmtlib/fmt/releases/download/1.0.0/cppformat-1.0.0.zip">Version 1.0.0</a></li>
</ul>
</div>
</div>
@@ -114,15 +105,14 @@
{% block content %}
<div class="tb-container">
<div class="row">
{# Sidebar is currently disabled.
{# TODO: integrate sidebar
<div class="bs-sidebar">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
{%- block sidebarlogo %}
{%- if logo %}
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}"
alt="Logo"/>
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
</a></p>
{%- endif %}
{%- endblock %}

View File

@@ -4,541 +4,222 @@
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 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/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/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 compile-time checks. It has minimal include dependencies for
better compile times. This header is only beneficial when using {fmt} as a
library and not in the header-only mode.
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. A function taking *fmt* doesn't
participate in an overload resolution if the latter is not a string.
*args* is an argument list representing objects to be formatted.
.. _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, const 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:: 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 checks are enabled when using ``FMT_STRING``. They support built-in
and string types as well as user-defined types with ``constexpr`` ``parse``
functions in their ``formatter`` specializations.
.. doxygendefine:: FMT_STRING
To force the use of compile-time checks, define the preprocessor variable
``FMT_ENFORCE_COMPILE_STRING``. When set, functions accepting ``FMT_STRING``
will fail to compile with regular strings. Runtime-checked
formatting is still possible using ``fmt::vformat``, ``fmt::vprint``, etc.
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/oba4Mc):
.. code:: c++
#include <fmt/format.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 S, typename... Args>
void log(const char* file, int line, const S& format, Args&&... args) {
vlog(file, line, format,
fmt::make_args_checked<Args...>(format, args...));
}
#define MY_LOG(format, ...) \
log(__FILE__, __LINE__, FMT_STRING(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_args_checked(const S&, const remove_reference_t<Args>&...)
.. doxygenfunction:: fmt::make_format_args(const Args&...)
.. doxygenclass:: fmt::format_arg_store
:members:
.. doxygenclass:: fmt::dynamic_format_arg_store
:members:
.. doxygenclass:: fmt::basic_format_args
:members:
.. doxygentypedef:: fmt::format_args
.. doxygenclass:: fmt::basic_format_arg
:members:
.. doxygenclass:: fmt::basic_format_context
:members:
.. doxygentypedef:: fmt::format_context
Compatibility
-------------
.. doxygenclass:: fmt::basic_string_view
:members:
.. doxygentypedef:: fmt::string_view
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"
.. _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.
.. _udt:
*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.
Formatting User-defined Types
-----------------------------
*args* is an argument list representing arbitrary arguments.
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
template and implement ``parse`` and ``format`` methods::
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`_.
#include <fmt/format.h>
.. _format:
struct point { double x, y; };
.. doxygenfunction:: format(CStringRef, ArgList)
template <> struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
.. doxygenfunction:: operator""_format(const char *, std::size_t)
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". 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 '}'.
.. _print:
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
.. doxygenfunction:: print(CStringRef, ArgList)
// Check if reached the end of the range:
if (it != end && *it != '}')
throw format_error("invalid format");
.. doxygenfunction:: print(std::FILE *, CStringRef, ArgList)
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) {
// ctx.out() is an output iterator to write to.
return format_to(
ctx.out(),
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
p.x, p.y);
}
};
Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2};
std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)"
You can also reuse existing formatters via inheritance or composition, for
example::
enum class color {red, green, blue};
template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext& ctx) {
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);
}
};
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"``.
You can also write a formatter for a hierarchy of classes::
#include <type_traits>
#include <fmt/format.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> {
template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}
If a type provides both a ``formatter`` specialization and an implicit
conversion to a formattable type, the specialization takes precedence over the
conversion.
.. doxygenclass:: fmt::basic_format_parse_context
.. doxygenclass:: fmt::BasicFormatter
:members:
Literal-based API
-----------------
Date and time formatting
------------------------
The following user-defined literals are defined in ``fmt/format.h``.
The library supports `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like
date and time formatting::
.. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter<char>
#include "fmt/time.h"
.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg<char>
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));
Utilities
---------
The format string syntax is described in the documentation of
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
.. doxygenfunction:: fmt::ptr(T p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
``std::ostream`` support
------------------------
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string
The header ``fmt/ostream.h`` provides ``std::ostream`` support including
formatting of user-defined types that have overloaded ``operator<<``::
.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char>
#include "fmt/ostream.h"
.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
.. doxygenclass:: fmt::detail::buffer
:members:
.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
:members:
System Errors
-------------
{fmt} does not use ``errno`` to communicate errors to the user, but it may call
system functions which set ``errno``. Users should not make any assumptions
about the value of ``errno`` being preserved by library functions.
.. doxygenfunction:: fmt::system_error
.. doxygenfunction:: fmt::format_system_error
Custom Allocators
-----------------
The {fmt} library supports custom dynamic memory allocators.
A custom allocator class can be specified as a template argument to
:class:`fmt::basic_memory_buffer`::
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::
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) {
custom_memory_buffer buf(alloc);
fmt::vformat_to(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``.
.. _ranges-api:
Ranges 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);
}
.. doxygenfunction:: localtime(std::time_t time)
.. doxygenfunction:: gmtime(std::time_t time)
.. _compile-api:
Format string compilation
=========================
``fmt/compile.h`` provides format string compilation support when using
``FMT_COMPILE``. Format strings 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 ``constexpr`` ``parse`` functions in
their ``formatter`` specializations. 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.
.. doxygendefine:: FMT_COMPILE
.. _color-api:
Terminal color and text style
=============================
``fmt/color.h`` provides support for terminal color and text style output.
.. doxygenfunction:: print(const text_style &ts, const S &format_str, const Args&... args)
.. doxygenfunction:: fg(detail::color_type)
.. doxygenfunction:: bg(detail::color_type)
.. _os-api:
System APIs
===========
.. doxygenclass:: fmt::ostream
:members:
.. doxygenfunction:: fmt::windows_error
:members:
.. _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<<``)::
#include <fmt/ostream.h>
class date {
class Date {
int year_, month_, day_;
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
friend std::ostream& operator<<(std::ostream& os, const date& d) {
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));
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
// s == "The date is 2012-12-9"
{fmt} only supports insertion operators that are defined in the same namespaces
as the types they format and can be found with the argument-dependent lookup.
.. doxygenfunction:: print(std::ostream&, CStringRef, ArgList)
.. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args)
.. doxygenfunction:: fprintf(std::ostream&, CStringRef, ArgList)
.. _printf-api:
Argument formatters
-------------------
``printf`` Formatting
=====================
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
// 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 functions
---------------------------
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
The following functions use `printf format string syntax
<https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
the POSIX extension for positional arguments. Unlike their standard
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
argument type doesn't match its format specification.
<http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
a POSIX extension for positional arguments.
.. doxygenfunction:: printf(const S &format_str, const T&... args)
.. 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:: sprintf(CStringRef, ArgList)
.. _xchar-api:
Write API
=========
``wchar_t`` Support
===================
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`.
The optional header ``fmt/wchar_t.h`` provides support for ``wchar_t`` and
exotic character types.
.. doxygenclass:: fmt::BasicWriter
:members:
.. doxygenstruct:: fmt::is_char
.. doxygenclass:: fmt::BasicMemoryWriter
:members:
.. doxygentypedef:: fmt::wstring_view
.. doxygenclass:: fmt::BasicArrayWriter
:members:
.. doxygentypedef:: fmt::wformat_context
.. doxygenfunction:: bin(int)
.. doxygenfunction:: fmt::to_wstring(const T &value)
.. doxygenfunction:: oct(int)
Compatibility with C++20 ``std::format``
========================================
.. doxygenfunction:: hex(int)
{fmt} implements nearly all of the `C++20 formatting library
<https://en.cppreference.com/w/cpp/utility/format>`_ with the following
differences:
.. doxygenfunction:: hexu(int)
* 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.
.. 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:
.. doxygenclass:: fmt::BasicStringRef
:members:
.. doxygenclass:: fmt::BasicCStringRef
:members:
.. doxygenclass:: fmt::Buffer
:protected-members:
:members:
System errors
=============
.. doxygenclass:: fmt::SystemError
:members:
.. 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,8 +90,7 @@
VERSION: '{{ release|e }}',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
HAS_SOURCE: {{ has_source|lower }},
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
HAS_SOURCE: {{ has_source|lower }}
};
</script>
{%- for scriptfile in script_files %}

View File

@@ -1,115 +1,99 @@
#!/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']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."
if commit:
check_version = kwargs.get('check_version', '')
#output = check_output(['pip', 'show', package.split('/')[1]])
#if check_version in output:
# print('{} already installed'.format(package))
# return
package = 'git+git://github.com/{0}.git@{1}'.format(package, commit)
print('Installing {}'.format(package))
check_call(['pip', 'install', package])
class Pip:
def __init__(self, venv_dir):
self.path = os.path.join(venv_dir, 'bin', 'pip')
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 build_docs(version='dev'):
# 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')
pip.install('sphinx-doc/sphinx', 'v3.3.0')
pip.install('michaeljones/breathe', 'v4.25.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'))
doc_dir = os.path.dirname(os.path.realpath(__file__))
virtualenv_dir = 'virtualenv'
check_call(['virtualenv', virtualenv_dir])
import sysconfig
scripts_dir = os.path.basename(sysconfig.get_path('scripts'))
activate_this_file = os.path.join(virtualenv_dir, scripts_dir,
'activate_this.py')
with open(activate_this_file) as f:
exec(f.read(), dict(__file__=activate_this_file))
# 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.
from pkg_resources import get_distribution, DistributionNotFound
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',
check_version='1.4.1.dev20160525')
pip_install('michaeljones/breathe',
'1c9d7f80378a92cffa755084823a78bb38ee4acc')
# Build docs.
cmd = ['doxygen', '-']
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
doxyxml_dir = os.path.join(work_dir, 'doxyxml')
out, _ = p.communicate(input=r'''
p = Popen(cmd, stdin=PIPE)
p.communicate(input=r'''
PROJECT_NAME = fmt
GENERATE_LATEX = NO
GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = {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}/format.h {0}/ostream.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
AUTOLINK_SUPPORT = NO
GENERATE_HTML = NO
GENERATE_XML = YES
XML_OUTPUT = {1}
XML_OUTPUT = doxyxml
ALIASES = "rst=\verbatim embed:rst"
ALIASES += "endrst=\endverbatim"
MACRO_EXPANSION = YES
PREDEFINED = _WIN32=1 \
__linux__=1 \
FMT_USE_VARIADIC_TEMPLATES=1 \
FMT_USE_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=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
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
out = out.decode('utf-8')
internal_symbols = [
'fmt::detail::.*',
'basic_data<>',
'fmt::type_identity',
'fmt::dynamic_formatter'
]
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)
FMT_API=
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
'''.format(os.path.join(os.path.dirname(doc_dir), 'fmt')).encode('UTF-8'))
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'),
'-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir),
'-Dversion=' + version, '-Drelease=' + version,
'-Aversion=' + version, '-Aversions=' + ','.join(main_versions),
'-b', 'html', doc_dir, html_dir])
check_call(['sphinx-build',
'-Dbreathe_projects.format=' + os.path.join(os.getcwd(), 'doxyxml'),
'-Dversion=' + version, '-Drelease=' + version, '-Aversion=' + version,
'-b', 'html', doc_dir, 'html'])
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')])
'html/_static/fmt.css'])
except OSError as e:
if e.errno != errno.ENOENT:
raise
print('lessc not found; make sure that Less (http://lesscss.org/) ' +
'is installed')
print('lessc not found; make sure that Less (http://lesscss.org/) is installed')
sys.exit(1)
return html_dir
return 'html'
if __name__ == '__main__':
create_build_env()
build_docs(sys.argv[1])

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
@@ -228,7 +228,8 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'fmt', u'fmt documentation', [u'Victor Zverovich'], 1)
('index', 'format', u'format Documentation',
[u'Victor Zverovich'], 1)
]
# If true, show URL addresses after external links.
@@ -241,8 +242,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'fmt', u'fmt documentation',
u'Victor Zverovich', 'fmt', 'One line description of project.',
('index', 'format', u'format Documentation',
u'Victor Zverovich', 'format', 'One line description of project.',
'Miscellaneous'),
]

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();
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,126 @@ 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
The library is fully type safe, automatic memory management prevents buffer 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#c.fmt>`_ 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);
}
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 {}."
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.
.. _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
using the library both in open-source and commercial projects.
`Learn more... <contents.html>`_
fmt has a small self-contained code base 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.
.. 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.
@@ -36,8 +35,6 @@ If the numerical arg_ids in a format string are 0, 1, 2, ... in sequence,
they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be
automatically inserted in that order.
Named arguments can be referred to by their names or indices.
Some simple format string examples::
"First, thou shalt count to {0}" // References the first argument
@@ -54,8 +51,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 +72,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 +98,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. |
+---------+----------------------------------------------------------+
@@ -143,17 +145,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
@@ -163,9 +163,6 @@ 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.
The ``'L'`` option uses the current locale setting to insert the appropriate
number separator characters. This option is only valid for numeric types.
Finally, the *type* determines how the data should be presented.
The available string presentation types are:
@@ -203,8 +200,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. |
@@ -219,6 +214,10 @@ 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'``. |
+---------+----------------------------------------------------------+
@@ -242,7 +241,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. |
@@ -262,10 +261,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
+---------+----------------------------------------------------------+
@@ -298,62 +298,9 @@ The available presentation types for pointers are:
| none | The same as ``'p'``. |
+---------+----------------------------------------------------------+
.. _chrono-specs:
Chrono Format Specifications
============================
Format specifications for chrono types 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*) for chrono durations and time
points are:
+---------+--------------------------------------------------------------------+
| Type | Meaning |
+=========+====================================================================+
| ``'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. |
+---------+--------------------------------------------------------------------+
| ``'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. |
+---------+--------------------------------------------------------------------+
| ``'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. |
+---------+--------------------------------------------------------------------+
Specifiers that have a calendaric component such as `'d'` (the day of month)
are valid only for ``std::tm`` and not durations or time points.
``std::tm`` uses the system's `strftime
<https://en.cppreference.com/w/cpp/chrono/c/strftime>`_ so refer to its
documentation for details on supported conversion specifiers.
.. _formatexamples:
Format Examples
Format examples
===============
This section contains examples of the format syntax and comparison with
@@ -368,94 +315,64 @@ 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);
// Result: "left aligned "
Dynamic precision::
fmt::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);
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/locale.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"
.. ifconfig:: False
Using the comma as a thousands separator::
format("{:,}", 1234567890);
'1,234,567,890'
Expressing a percentage::
>>> points = 19
>>> total = 22
Format("Correct answers: {:.2%}") << points/total)
'Correct answers: 86.36%'
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']):
@@ -484,3 +401,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.
@@ -35,11 +41,7 @@ current directory. Now you can build the library by running :command:`make`.
Once the library has been built you can invoke :command:`make test` to run
the tests.
You can control generation of the make ``test`` target with the ``FMT_TEST``
CMake option. This can be useful if you include fmt as a subdirectory in
your project but don't want to add fmt's tests to your ``test`` target.
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,91 +52,9 @@ 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
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::
add_subdirectory(fmt)
or
::
add_subdirectory(fmt EXCLUDE_FROM_ALL)
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>`_.
Building the Documentation
Building the documentation
==========================
To build the documentation you need the following software installed on your
@@ -142,11 +62,7 @@ system:
* `Python <https://www.python.org/>`_ with pip and virtualenv
* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_
* `Less <http://lesscss.org/>`_ with ``less-plugin-clean-css``.
Ubuntu doesn't package the ``clean-css`` plugin so you should use ``npm``
instead of ``apt`` to install both ``less`` and the plugin::
sudo npm install -g less less-plugin-clean-css.
* `Less <http://lesscss.org/>`_ with less-plugin-clean-css
First generate makefiles or project files using CMake as described in
the previous section. Then compile the ``doc`` target/project, for example::
@@ -154,45 +70,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
===========
@@ -202,11 +80,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.
# format.cc is added to FMT_HEADERS for the header-only configuration.
set(FMT_HEADERS format.h format.cc ostream.h ostream.cc 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} ../ChangeLog.rst)
option(FMT_CPPFORMAT "Build cppformat library for backward compatibility." OFF)
if (FMT_CPPFORMAT)
message(WARNING "The cppformat library is deprecated, use fmt instead.")
add_library(cppformat ${FMT_SOURCES} ${FMT_HEADERS})
endif ()
# 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})
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)
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(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR lib/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 lib CACHE STRING
"Installation directory for libraries, 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}
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})
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
install(FILES ${FMT_HEADERS} DESTINATION include/fmt)
if (FMT_CPPFORMAT)
install(TARGETS cppformat DESTINATION ${FMT_LIB_DIR})
endif ()
endif ()

946
fmt/format.cc Normal file
View File

@@ -0,0 +1,946 @@
/*
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>
#include <cerrno>
#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
using fmt::internal::Arg;
#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.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
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);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
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
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
// 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 = 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 >= INT_MIN && value <= INT_MAX;
}
static bool fits_in_int(int) { return true; }
};
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 != 0 && 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 &) {}
// 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;
}
// 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;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
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);
}
// 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; }
};
// 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;
}
if (width > INT_MAX)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
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);
}
};
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);
}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (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);
}
};
} // namespace
namespace internal {
template <typename Char>
class PrintfArgFormatter :
public ArgFormatterBase<PrintfArgFormatter<Char>, Char> {
void write_null_pointer() {
this->spec().type_ = 0;
this->write("(nil)");
}
typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s) {}
void visit_bool(bool value) {
FormatSpec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
void visit_char(int value) {
const FormatSpec &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);
}
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)");
}
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
void visit_custom(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);
}
};
} // namespace internal
} // namespace fmt
FMT_FUNC void fmt::SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int fmt::internal::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 fmt::internal::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 fmt::internal::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 fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
};
FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::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 fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::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 fmt::internal::UTF16ToUTF8::convert(fmt::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;
}
FMT_FUNC void fmt::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());
}
FMT_FUNC void fmt::internal::format_windows_error(
fmt::Writer &out, int error_code,
fmt::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
FMT_FUNC void fmt::internal::format_system_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(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.
}
template <typename Char>
void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
switch (arg.type) {
case Arg::NONE:
error = "argument index out of range";
break;
case Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::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>
Arg fmt::internal::PrintfFormatter<Char>::get_arg(
const Char *s, unsigned arg_index) {
(void)s;
const char *error = 0;
Arg arg = arg_index == UINT_MAX ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char>
unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = UINT_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 = 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_ = parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, 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>(parse_nonnegative_int(s));
} else if (*s == '*') {
++s;
spec.precision_ = PrecisionHandler().visit(get_arg(s));
}
}
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
spec.flags_ &= ~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.
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 (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
CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
internal::PrintfArgFormatter<Char>(writer, spec).visit(arg);
}
write(writer, start, s);
}
FMT_FUNC void fmt::report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
fmt::report_error(internal::format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void fmt::report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
fmt::report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void fmt::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 fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void fmt::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);
}
FMT_FUNC int fmt::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 struct fmt::internal::BasicData<void>;
// Explicit instantiations for char.
template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
template FMT_API void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, CStringRef format);
template FMT_API int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template FMT_API int fmt::internal::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 fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
template FMT_API void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, WCStringRef format);
template FMT_API int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int FMT_API fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
#ifdef _MSC_VER
# pragma warning(pop)
#endif

3937
fmt/format.h Normal file

File diff suppressed because it is too large Load Diff

43
fmt/ostream.cc Normal file
View File

@@ -0,0 +1,43 @@
/*
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 {
// Write the content of w to os.
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);
write(os, w);
}
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
write(os, w);
return static_cast<int>(w.size());
}
} // namespace fmt

115
fmt/ostream.h Normal file
View File

@@ -0,0 +1,115 @@
/*
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_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) {
this->setp(start_, start_ + buffer_.capacity());
}
int_type overflow(int_type ch = traits_type::eof()) {
if (!traits_type::eq_int_type(ch, traits_type::eof())) {
size_t buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
size_t size() const {
return to_unsigned(this->pptr() - start_);
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
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 {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format(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 << value;
BasicStringRef<Char> str(&buffer[0], format_buf.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)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

241
fmt/posix.cc Normal file
View File

@@ -0,0 +1,241 @@
/*
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>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# 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 count <= UINT_MAX ? static_cast<unsigned>(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__)
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
}

386
fmt/posix.h Normal file
View File

@@ -0,0 +1,386 @@
/*
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_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#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
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
#if FMT_GCC_VERSION >= 407
# define FMT_UNUSED __attribute__((unused))
#else
# define FMT_UNUSED
#endif
#ifndef FMT_USE_STATIC_ASSERT
# define FMT_USE_STATIC_ASSERT 0
#endif
#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
(FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
#else
# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
# define FMT_STATIC_ASSERT(cond, message) \
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
#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_

53
fmt/time.h Normal file
View File

@@ -0,0 +1,53 @@
/*
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>
namespace fmt {
template <typename ArgFormatter>
void format(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;
}
}
#endif // FMT_TIME_H_

View File

@@ -1,232 +0,0 @@
// Formatting library for C++ - dynamic format arguments
//
// 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> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
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> const T& push(const Arg& arg) {
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<detail::is_string<T>::value &&
!has_formatter<T, Context>::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>;
unsigned long long get_types() const {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
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:
/**
\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,627 +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"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
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,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
};
// 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;
};
FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_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) FMT_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;
};
FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
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 text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
const text_style& rhs) {
return and_assign(rhs);
}
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
operator&(text_style lhs, const text_style& rhs) {
return lhs.and_assign(rhs);
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
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) FMT_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;
}
}
// DEPRECATED!
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
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 AND 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 AND 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_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
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 text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(false, background);
}
FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_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) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
size_t index = 0;
for (int i = 0; i < 4; ++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 FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_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);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(L"\x1b[0m", stream);
}
template <typename Char>
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
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);
}
FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
/**
\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_args_checked<Args...>(format_str, 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 std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, 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 std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, 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)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\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, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View File

@@ -1,639 +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 {
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type =
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// 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
*/
#ifdef __cpp_if_constexpr
# 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_PARAMETERS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1};
}
};
#endif
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
#ifdef __cpp_if_constexpr
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 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(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 {
return write<Char>(out, value);
}
};
// 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 = 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 {
return write<Char>(out, get_arg_checked<T, N>(args...));
}
};
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) {
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;
};
constexpr int 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 = basic_format_parse_context<Char>(str, {}, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
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 operator()() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) { throw format_error(message); }
};
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 == ':') {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, 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())
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 != invalid_arg_index) {
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())
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 // __cpp_if_constexpr
} // namespace detail
FMT_MODULE_EXPORT_BEGIN
#ifdef __cpp_if_constexpr
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 format(static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return 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 format_to(out,
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return 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)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
std::forward<Args>(args)...);
return {it.base(), it.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) {
return 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;
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_PARAMETERS
inline namespace literals {
template <detail_exported::fixed_string Str>
constexpr detail::udl_compiled_string<
remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
operator""_cf() {
return {};
}
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
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,2 +0,0 @@
#include "xchar.h"
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead

View File

@@ -1,515 +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 <clocale> // locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// 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
#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
# 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_MODULE_EXPORT_BEGIN
/**
\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. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
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(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT;
FMT_END_DETAIL_NAMESPACE
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) FMT_NOEXCEPT;
#else
inline const std::error_category& system_category() FMT_NOEXCEPT {
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() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
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.
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 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 FMT_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 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() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// 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 long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, 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, std::error_code& 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 buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
FMT_BEGIN_DETAIL_NAMESPACE
struct buffer_size {
buffer_size() = default;
size_t value = 0;
buffer_size operator=(size_t val) const {
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;
}
};
FMT_END_DETAIL_NAMESPACE
constexpr detail::buffer_size buffer_size;
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
private:
file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.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(detail::buffer_appender<char>(*this), 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`` 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 ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(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 = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@@ -1,181 +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 <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
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;
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Write the content of buf to os.
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,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace detail
FMT_MODULE_EXPORT
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_MODULE_EXPORT
template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View File

@@ -1,652 +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 <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_format_args<basic_printf_context> args_;
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_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(OutputIt out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
// 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 bool fits_in_int(T value) {
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_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)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
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) {
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} 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.
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
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) {
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
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> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { 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:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
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) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
// The ``printf`` argument formatter.
template <typename OutputIt, typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_;
OutputIt write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = 0;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt 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) {
format_specs fmt_specs = this->specs;
if (fmt_specs.type && fmt_specs.type != 'c')
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;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
}
return base::operator()(value);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) {
return base::operator()(value);
}
/** Formats a null-terminated C string. */
OutputIt operator()(const char* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != 'p');
}
/** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != 'p');
}
OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value);
}
/** Formats a pointer. */
OutputIt operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx =
basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_);
return this->out;
}
};
template <typename Char>
void parse_flags(basic_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>
int parse_header(const Char*& it, const Char* end,
basic_format_specs<Char>& specs, GetArg get_arg) {
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) FMT_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) FMT_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;
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using OutputIt = buffer_appender<Char>;
auto out = OutputIt(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args);
auto parse_ctx = basic_printf_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 (!detail::find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
break;
}
Char c = *it++;
if (it != end && *it == c) {
out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it;
continue;
}
out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs;
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_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(detail::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())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
}
if (specs.alt && visit_format_arg(detail::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;
using detail::convert_arg;
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) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (specs.type) {
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
break;
}
}
start = it;
// Format argument.
out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
}
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
FMT_END_DETAIL_NAMESPACE
template <typename Char>
using basic_printf_context_t =
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<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...};
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline auto vsprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer);
}
/**
\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> {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vfprintf(
std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size();
return std::fwrite(buffer.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 {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(fmt),
fmt::make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
return vfprintf(stdout, to_string_view(fmt), args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
inline auto printf(const S& fmt, const T&... args) -> int {
return vprintf(
to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
}
template <typename S, typename Char = char_t<S>>
FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
template <typename S, typename... T, typename Char = char_t<S>>
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
const T&... args) -> int {
return vfprintf(os, to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View File

@@ -1,468 +0,0 @@
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
template <typename Char, typename Enable = void> struct formatting_range {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = '[';
Char postfix = ']';
#endif
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void> struct formatting_tuple {
Char prefix = '(';
Char postfix = ')';
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
template <typename OutputIterator>
OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string 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 FMT_CONSTEXPR_DECL const bool value =
is_string<T>::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... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 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_begin(
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_begin(std::declval<T>())),
enable_if_t<std::is_copy_constructible<T>::value>>>
: 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)> {};
template <typename T, typename Enable = void> struct range_to_view;
template <typename T>
struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
struct view_t {
const T* m_range_ptr;
auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
};
static auto view(const T& range) -> view_t { return {&range}; }
};
template <typename T>
struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
has_mutable_begin_end<T>::value>> {
struct view_t {
T m_range_copy;
auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
};
static auto view(const T& range) -> view_t { return {range}; }
};
# 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 FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 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 size_t size() { 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 <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ',';
*out++ = ' ';
return out;
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
*out++ = '"';
out = write<Char>(out, v);
*out++ = '"';
return out;
}
template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\'';
*out++ = v;
*out++ = '\'';
return out;
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
!std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
}
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, v);
++i;
}
formatting_tuple<Char>& formatting;
size_t& i;
typename std::add_lvalue_reference<
decltype(std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
size_t i = 0;
detail::copy(formatting.prefix, out);
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL 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_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<
fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&& (has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto view = detail::range_to_view<T>::view(values);
auto it = view.begin();
auto end = view.end();
for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it);
++i;
}
return detail::copy(formatting.postfix, out);
}
};
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} {}
};
template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>;
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 ctx.begin();
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
typename FormatContext::iterator {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
detail::index_sequence<N...>) ->
typename FormatContext::iterator {
using std::get;
return format_args(value, ctx, get<N>(value.tuple)...);
}
template <typename FormatContext>
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
typename FormatContext::iterator {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) ->
typename FormatContext::iterator {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = base().format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
FMT_MODULE_EXPORT_BEGIN
/**
\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_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@@ -1,236 +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_WCHAR_H_
#define FMT_WCHAR_H_
#include <cwchar>
#include <tuple>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
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;
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#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... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
}
inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_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> {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
// 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... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat(to_string_view(format_str), vargs);
}
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, to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... Args,
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, Args&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, 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, to_string_view(format_str), args);
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
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, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to(out, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
return detail::buffer_appender<Char>(buf);
}
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, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <
typename OutputIt, typename Locale, typename S, typename... Args,
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,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
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> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... Args,
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,
const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to_n(out, n, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
detail::vformat_to(buf, to_string_view(fmt), vargs);
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.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), make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), make_wformat_args(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_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_WCHAR_H_

View File

@@ -1,100 +0,0 @@
module;
#ifndef __cpp_modules
# error Module not supported.
#endif
// put all implementation-provided headers into the global module fragment
// to prevent attachment to this module
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
#endif
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <chrono>
#include <climits>
#include <clocale>
#include <cmath>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwchar>
#include <exception>
#include <functional>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <type_traits>
#include <utility>
#include <vector>
#if _MSC_VER
# 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
# include <windows.h>
#endif
export module fmt;
#define FMT_MODULE_EXPORT export
#define FMT_MODULE_EXPORT_BEGIN export {
#define FMT_MODULE_EXPORT_END }
#define FMT_BEGIN_DETAIL_NAMESPACE \
} \
namespace detail {
#define FMT_END_DETAIL_NAMESPACE \
} \
export {
// 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/xchar.h"
// gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION
module : private;
#endif
#include "format.cc"
#include "os.cc"

View File

@@ -1,78 +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 <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT;
} // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API auto detail::thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API auto detail::thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template struct detail::basic_data<void>;
FMT_END_NAMESPACE

360
src/os.cc
View File

@@ -1,360 +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>
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# 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
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
#endif
#ifdef fileno
# undef fileno
#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
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
}
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) FMT_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 FMT_NOEXCEPT { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
return basic_string_view<wchar_t>(message_, result_);
}
};
class utf8_system_category final : public std::error_category {
public:
const char* name() const FMT_NOEXCEPT override { return "system"; }
std::string message(int error_code) const override {
system_message msg(error_code);
if (msg) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
return utf8_message.str();
}
}
return "unknown error";
}
};
} // namespace detail
FMT_API const std::error_category& system_category() FMT_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) FMT_NOEXCEPT {
FMT_TRY {
system_message msg(error_code);
if (msg) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
return;
}
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() FMT_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, "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, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
# if defined(_WIN32) && !defined(__MINGW32__)
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(system_error(errno, "cannot open file {}", path.c_str()));
}
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)
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, "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, "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, "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, "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, "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, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, std::error_code& ec) FMT_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, "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, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long 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(system_error(errno, "cannot get memory page size"));
return size;
# endif
}
FMT_API void ostream::grow(size_t) {
if (this->size() == this->capacity()) flush();
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@@ -1 +0,0 @@
<manifest package="net.fmtlib" />

File diff suppressed because it is too large Load Diff

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.14.4/cmake-3.14.4-Linux-x86_64.tar.gz
tar xzf cmake-3.14.4-Linux-x86_64.tar.gz
ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin
SHELL
end

45
support/appveyor-build.py Normal file → Executable file
View File

@@ -6,37 +6,26 @@ from subprocess import check_call
build = os.environ['BUILD']
config = os.environ['CONFIGURATION']
platform = os.environ['PLATFORM']
platform = os.environ.get('PLATFORM')
path = os.environ['PATH']
image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE']
jobid = os.environ['APPVEYOR_JOB_ID']
cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..']
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
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\15.0\Bin;' + path
if image == 'Visual Studio 2019':
generator = 'Visual Studio 16 2019'
if platform == 'x64':
cmake_command.extend(['-A', 'x64'])
else:
if image == 'Visual Studio 2015':
generator = 'Visual Studio 14 2015'
elif image == 'Visual Studio 2017':
generator = 'Visual Studio 15 2017'
if platform == 'x64':
generator += ' Win64'
cmake_command.append('-G' + generator)
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
test_command = ['ctest', '-C', config]
# 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)

View File

@@ -2,25 +2,20 @@ configuration:
- Debug
- Release
clone_depth: 1
image:
- Visual Studio 2015
platform:
- x64
environment:
CTEST_OUTPUT_ON_FAILURE: 1
MSVC_DEFAULT_OPTIONS: ON
BUILD: msvc
matrix:
- BUILD: msvc
- BUILD: msvc
PLATFORM: x64
- BUILD: mingw
before_build:
- mkdir build
- cd 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
- python support/appveyor-build.py
on_failure:
- appveyor PushArtifact Testing/Temporary/LastTest.log

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]
// Accroding 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,70 +0,0 @@
# C++14 feature support detection
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
if (CMAKE_CXX_STANDARD EQUAL 20)
check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
if (has_std_20_flag)
set(CXX_STANDARD_FLAG -std=c++20)
elseif (has_std_2a_flag)
set(CXX_STANDARD_FLAG -std=c++2a)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 17)
check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
if (has_std_17_flag)
set(CXX_STANDARD_FLAG -std=c++17)
elseif (has_std_1z_flag)
set(CXX_STANDARD_FLAG -std=c++1z)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 14)
check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
if (has_std_14_flag)
set(CXX_STANDARD_FLAG -std=c++14)
elseif (has_std_1y_flag)
set(CXX_STANDARD_FLAG -std=c++1y)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 11)
check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
if (has_std_11_flag)
set(CXX_STANDARD_FLAG -std=c++11)
elseif (has_std_0x_flag)
set(CXX_STANDARD_FLAG -std=c++0x)
endif ()
endif ()
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif ()
# Check if <variant> is available
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
check_cxx_source_compiles("
#include <variant>
int main() {}"
FMT_HAS_VARIANT)
if (NOT FMT_HAS_VARIANT)
set (FMT_HAS_VARIANT OFF)
endif ()
set(CMAKE_REQUIRED_FLAGS )

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:]))

46
support/download.py Normal file
View File

@@ -0,0 +1,46 @@
# A file downloader.
import contextlib, os, tempfile, timer, urllib2, urlparse
class Downloader:
def __init__(self, dir=None):
self.dir = dir
# Downloads a file and removes it when exiting a block.
# Usage:
# d = Downloader()
# with d.download(url) as f:
# use_file(f)
def download(self, url, cookie=None):
suffix = os.path.splitext(urlparse.urlsplit(url)[2])[1]
fd, filename = tempfile.mkstemp(suffix=suffix, dir=self.dir)
os.close(fd)
with timer.print_time('Downloading', url, 'to', filename):
opener = urllib2.build_opener()
if cookie:
opener.addheaders.append(('Cookie', cookie))
num_tries = 2
for i in range(num_tries):
try:
f = opener.open(url)
except urllib2.URLError, e:
print('Failed to open url', url)
continue
length = f.headers.get('content-length')
if not length:
print('Failed to get content-length')
continue
length = int(length)
with open(filename, 'wb') as out:
count = 0
while count < length:
data = f.read(1024 * 1024)
count += len(data)
out.write(data)
@contextlib.contextmanager
def remove(filename):
try:
yield filename
finally:
os.remove(filename)
return remove(filename)

View File

@@ -1,297 +0,0 @@
#!/usr/bin/env python3
"""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, tempfile
from contextlib import contextmanager
from distutils.version import LooseVersion
from subprocess import check_call
class Git:
def __init__(self, dir):
self.dir = dir
def call(self, method, args, **kwargs):
return check_call(['git', method] + list(args), **kwargs)
def add(self, *args):
return self.call('add', args, cwd=self.dir)
def checkout(self, *args):
return self.call('checkout', args, cwd=self.dir)
def clean(self, *args):
return self.call('clean', args, cwd=self.dir)
def clone(self, *args):
return self.call('clone', list(args) + [self.dir])
def commit(self, *args):
return self.call('commit', args, cwd=self.dir)
def pull(self, *args):
return self.call('pull', args, cwd=self.dir)
def push(self, *args):
return self.call('push', args, cwd=self.dir)
def reset(self, *args):
return self.call('reset', args, cwd=self.dir)
def update(self, *args):
clone = not os.path.exists(self.dir)
if clone:
self.clone(*args)
return clone
def clean_checkout(repo, branch):
repo.clean('-f', '-d')
repo.reset('--hard')
repo.checkout(branch)
class Runner:
def __init__(self, cwd):
self.cwd = cwd
def __call__(self, *args, **kwargs):
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
check_call(args, **kwargs)
def create_build_env():
"""Create a build environment."""
class Env:
pass
env = Env()
# Import the documentation build module.
env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(env.fmt_dir, 'doc'))
import build
env.build_dir = 'build'
env.versions = build.versions
# Virtualenv and repos are cached to speed up builds.
build.create_build_env(os.path.join(env.build_dir, 'virtualenv'))
env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt'))
return env
@contextmanager
def rewrite(filename):
class Buffer:
pass
buffer = Buffer()
if not os.path.exists(filename):
buffer.data = ''
yield buffer
return
with open(filename) as f:
buffer.data = f.read()
yield buffer
with open(filename, 'w') as f:
f.write(buffer.data)
fmt_repo_url = 'git@github.com:fmtlib/fmt'
def update_site(env):
env.fmt_repo.update(fmt_repo_url)
doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io'))
doc_repo.update('git@github.com:fmtlib/fmtlib.github.io')
for version in env.versions:
clean_checkout(env.fmt_repo, version)
target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc')
# Remove the old theme.
for entry in os.listdir(target_doc_dir):
path = os.path.join(target_doc_dir, entry)
if os.path.isdir(path):
shutil.rmtree(path)
# Copy the new theme.
for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap',
'conf.py', 'fmt.less']:
src = os.path.join(env.fmt_dir, 'doc', entry)
dst = os.path.join(target_doc_dir, entry)
copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile
copy(src, dst)
# Rename index to contents.
contents = os.path.join(target_doc_dir, 'contents.rst')
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']:
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>>>)')
# 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')
# 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'):
include_dir = os.path.join(include_dir, 'fmt')
import build
build.build_docs(version, doc_dir=target_doc_dir,
include_dir=include_dir, work_dir=env.build_dir)
shutil.rmtree(os.path.join(html_dir, '.doctrees'))
# Create symlinks for older versions.
for link, target in {'index': 'contents', 'api': 'reference'}.items():
link = os.path.join(html_dir, link) + '.html'
target += '.html'
if os.path.exists(os.path.join(html_dir, target)) and \
not os.path.exists(link):
os.symlink(target, link)
# Copy docs to the website.
version_doc_dir = os.path.join(doc_repo.dir, version)
try:
shutil.rmtree(version_doc_dir)
except OSError as e:
if e.errno != errno.ENOENT:
raise
shutil.move(html_dir, version_doc_dir)
def release(args):
env = create_build_env()
fmt_repo = env.fmt_repo
branch = args.get('<branch>')
if branch is None:
branch = 'master'
if not fmt_repo.update('-b', branch, fmt_repo_url):
clean_checkout(fmt_repo, branch)
# Convert changelog from RST to GitHub-flavored Markdown and get the
# version.
changelog = 'ChangeLog.rst'
changelog_path = os.path.join(fmt_repo.dir, changelog)
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):
prefix = 'set(FMT_VERSION '
if line.startswith(prefix):
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)
if m:
line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version)
sys.stdout.write(line)
fmt_repo.checkout('-B', 'release')
fmt_repo.add(changelog, cmakelists, script)
fmt_repo.commit('-m', 'Update version')
# Build the docs and package.
run = Runner(fmt_repo.dir)
run('cmake', '.')
run('make', 'doc', 'package_source')
update_site(env)
# Create a release on GitHub.
fmt_repo.push('origin', 'release')
params = {'access_token': os.getenv('FMT_TOKEN')}
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
params=params,
data=json.dumps({'tag_name': version,
'target_commitish': 'release',
'body': changes, 'draft': True}))
if r.status_code != 201:
raise Exception('Failed to create a release ' + str(r))
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'},
params=params, data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))
if __name__ == '__main__':
args = docopt.docopt(__doc__)
if args.get('release'):
release(args)
elif args.get('site'):
update_site(create_build_env())

193
support/release.py Executable file
View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python
"""Create a release.
Usage:
release.py [<branch>]
"""
from __future__ import print_function
import datetime, docopt, fileinput, json, os, re, requests, shutil, sys, tempfile
from docutils import nodes, writers, core
from subprocess import check_call
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 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
class Runner:
def __init__(self):
self.cwd = '.'
def __call__(self, *args, **kwargs):
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
check_call(args, **kwargs)
if __name__ == '__main__':
args = docopt.docopt(__doc__)
workdir = tempfile.mkdtemp()
try:
run = Runner()
fmt_dir = os.path.join(workdir, 'fmt')
branch = args.get('<branch>')
if branch is None:
branch = 'master'
run('git', 'clone', '-b', branch, 'git@github.com:fmtlib/fmt.git', fmt_dir)
# Convert changelog from RST to GitHub-flavored Markdown and get the version.
changelog = 'ChangeLog.rst'
changelog_path = os.path.join(fmt_dir, changelog)
changes, version = core.publish_file(source_path=changelog_path, writer=MDWriter())
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(fmt_dir, cmakelists), inplace=True):
prefix = 'set(FMT_VERSION '
if line.startswith(prefix):
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)
run.cwd = fmt_dir
run('git', 'checkout', '-b', 'release')
run('git', 'add', changelog, cmakelists)
run('git', 'commit', '-m', 'Update version')
# Build the docs and package.
run('cmake', '.')
run('make', 'doc', 'package_source')
site_dir = os.path.join(workdir, 'fmtlib.github.io')
run('git', 'clone', 'git@github.com:fmtlib/fmtlib.github.io.git', site_dir)
doc_dir = os.path.join(site_dir, version)
shutil.copytree(os.path.join(fmt_dir, 'doc', 'html'), doc_dir,
ignore=shutil.ignore_patterns('.doctrees', '.buildinfo'))
run.cwd = site_dir
run('git', 'add', doc_dir)
run('git', 'commit', '-m', 'Update docs')
# Create a release on GitHub.
run('git', 'push', 'origin', 'release', cwd=fmt_dir)
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
params={'access_token': os.getenv('FMT_TOKEN')},
data=json.dumps({'tag_name': version, 'target_commitish': 'release',
'body': changes, 'draft': True}))
if r.status_code != 201:
raise Exception('Failed to create a release ' + str(r))
finally:
shutil.rmtree(workdir)

View File

@@ -1,159 +0,0 @@
#!/usr/bin/env python
# reStructuredText (RST) to GitHub-flavored Markdown converter
import re, sys
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 visit_title_reference(self, node):
raise Exception(node)
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):
self.write('\n\n')
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
def write_row(self, row, widths):
for i, entry in enumerate(row):
text = entry[0][0] if len(entry) > 0 else ''
if i != 0:
self.write('|')
self.write('{:{}}'.format(text, widths[i]))
self.write('\n')
def visit_table(self, node):
table = node.children[0]
colspecs = table[:-2]
thead = table[-2]
tbody = table[-1]
widths = [int(cs['colwidth']) for cs in colspecs]
sep = '|'.join(['-' * w for w in widths]) + '\n'
self.write('\n\n')
self.write_row(thead[0], widths)
self.write(sep)
for row in tbody:
self.write_row(row, widths)
raise nodes.SkipChildren
def depart_table(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())
if __name__ == '__main__':
convert(sys.argv[1])

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 %}

35
support/timer.py Normal file
View File

@@ -0,0 +1,35 @@
# A with statement based timer.
from __future__ import print_function
from contextlib import contextmanager
import timeit
class Timer:
"""
A with statement based timer.
Usage:
t = Timer()
with t:
do_something()
time = t.time
"""
def __enter__(self):
self.start = timeit.default_timer()
def __exit__(self, type, value, traceback):
finish = timeit.default_timer()
self.time = finish - self.start
@contextmanager
def print_time(*args):
"""
Measures and prints the time taken to execute nested code.
args: Additional arguments to print.
"""
t = Timer()
print(*args)
with t:
yield
print(*args, end=' ')
print('finished in {0:.2f} second(s)'.format(t.time))

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

@@ -0,0 +1,113 @@
#!/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
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
build = os.environ['BUILD']
if build == 'Doc':
travis = 'TRAVIS' in os.environ
# Install dependencies.
if travis:
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(['npm', 'install', '-g', 'less', '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])
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
import build
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,10 +1,45 @@
add_subdirectory(gtest)
#------------------------------------------------------------------------------
# Build the google test library
# 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 .)
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)
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_link_libraries(test-main gmock fmt)
include(CheckCXXCompilerFlag)
@@ -18,7 +53,7 @@ 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)
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
endif ()
function(add_fmt_executable name)
@@ -31,156 +66,84 @@ 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()
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(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(printf-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
add_fmt_test(unicode-test HEADER_ONLY)
if (MSVC)
target_compile_options(unicode-test PRIVATE /utf-8)
endif ()
add_fmt_test(xchar-test)
add_fmt_test(enforce-checks-test)
target_compile_definitions(enforce-checks-test PRIVATE
-DFMT_ENFORCE_COMPILE_STRING)
add_fmt_test(time-test)
add_fmt_test(util-test mock-allocator.h)
add_fmt_test(macro-test)
if (FMT_CAN_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 ${FMT_REQUIRED_FEATURES})
target_include_directories(test-module PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
enable_module(test-module)
add_fmt_test(module-test MODULE)
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 ()
# 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 ()
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 ()
if (HAVE_STRTOD_L)
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
endif ()
posix-mock-test.cc ../fmt/format.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 ()
if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
# MSVC fails to compile GMock when C++17 is enabled.
if (FMT_HAS_VARIANT AND NOT MSVC)
add_fmt_test(std-format-test)
set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
# 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 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 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)
target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions)
if (FMT_PEDANTIC)
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
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)
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
--build-options
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DCPP11_FLAG=${CPP11_FLAG}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
endif ()
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# 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
@@ -190,13 +153,10 @@ if (FMT_PEDANTIC AND NOT WIN32)
--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
@@ -206,42 +166,5 @@ if (FMT_PEDANTIC AND NOT WIN32)
--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.1...3.18)
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})
target_link_libraries(library-test fmt::fmt)
add_executable(library-test "main.cc")
target_link_libraries(library-test fmt)
if (TARGET fmt::fmt-header-only)
add_executable(header-only-test main.cc)
target_include_directories(header-only-test PUBLIC SYSTEM .)
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_link_libraries(header-only-test fmt::fmt-header-only)
if (TARGET fmt-header-only)
add_executable(header-only-test "main.cc")
target_link_libraries(header-only-test 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,173 +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 "gtest/gtest.h"
TEST(args_test, basic) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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.
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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 format_to(ctx.out(), "cust={}", p.i);
}
};
FMT_END_NAMESPACE
TEST(args_test, custom_format) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
auto s = to_stringable();
store.push_back(s);
store.push_back(std::cref(s));
fmt::vformat("", store);
}
TEST(args_test, named_int) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(fmt::arg("a1", 42));
EXPECT_EQ("42", fmt::vformat("{a1}", store));
}
TEST(args_test, named_strings) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
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) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(std::string("foo"));
try {
store.push_back(copy_throwable());
} catch (...) {
}
EXPECT_EQ(fmt::vformat("{}", store), "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,385 +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 "gtest-extra.h" // EXPECT_THROW_MSG
#include "util.h" // get_locale
using fmt::runtime;
using testing::Contains;
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;
}
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.");
}
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);
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, localtime) {
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
EXPECT_TRUE(equal(tm, fmt::localtime(t)));
}
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(TimePoint tp) -> std::string {
auto t = std::chrono::system_clock::to_time_t(tp);
auto tm = *std::localtime(&t);
char output[256] = {};
std::strftime(output, sizeof(output), "%Y-%m-%d %H:%M:%S", &tm);
return output;
}
TEST(chrono_test, time_point) {
auto t1 = std::chrono::system_clock::now();
EXPECT_EQ(strftime(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
EXPECT_EQ(strftime(t1), fmt::format("{}", t1));
using time_point =
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
auto t2 = time_point(std::chrono::seconds(42));
EXPECT_EQ(strftime(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
}
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
TEST(chrono_test, format_default) {
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42as",
fmt::format("{}", std::chrono::duration<int, std::atto>(42)));
EXPECT_EQ("42fs",
fmt::format("{}", std::chrono::duration<int, std::femto>(42)));
EXPECT_EQ("42ps",
fmt::format("{}", std::chrono::duration<int, std::pico>(42)));
EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42)));
EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42)));
EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42)));
EXPECT_EQ("42cs",
fmt::format("{}", std::chrono::duration<int, std::centi>(42)));
EXPECT_EQ("42ds",
fmt::format("{}", std::chrono::duration<int, std::deci>(42)));
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42das",
fmt::format("{}", std::chrono::duration<int, std::deca>(42)));
EXPECT_EQ("42hs",
fmt::format("{}", std::chrono::duration<int, std::hecto>(42)));
EXPECT_EQ("42ks",
fmt::format("{}", std::chrono::duration<int, std::kilo>(42)));
EXPECT_EQ("42Ms",
fmt::format("{}", std::chrono::duration<int, std::mega>(42)));
EXPECT_EQ("42Gs",
fmt::format("{}", std::chrono::duration<int, std::giga>(42)));
EXPECT_EQ("42Ts",
fmt::format("{}", std::chrono::duration<int, std::tera>(42)));
EXPECT_EQ("42Ps",
fmt::format("{}", std::chrono::duration<int, std::peta>(42)));
EXPECT_EQ("42Es",
fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
EXPECT_EQ(
"42[15]s",
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ(
"42[15/4]s",
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
}
TEST(chrono_test, align) {
auto s = std::chrono::seconds(42);
EXPECT_EQ("42s ", fmt::format("{:5}", s));
EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
EXPECT_EQ(" 42s", fmt::format("{:>5}", s));
EXPECT_EQ("**42s**", fmt::format("{:*^7}", s));
EXPECT_EQ("03:25:45 ",
fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ(" 03:25:45",
fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("~~03:25:45~~",
fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25:45 ",
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
}
TEST(chrono_test, format_specs) {
EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0)));
EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0)));
EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0)));
EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60)));
EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42)));
EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234)));
EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0)));
EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60)));
EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42)));
EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61)));
EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0)));
EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24)));
EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14)));
EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24)));
EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4)));
EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14)));
EXPECT_EQ("03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345)));
EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345)));
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345)));
}
TEST(chrono_test, invalid_specs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG(fmt::format(runtime("{:%a}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%A}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%c}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%x}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ex}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%X}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%EX}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%D}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%F}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ec}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%w}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%u}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%b}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%B}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%z}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Z}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Eq}"), sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Oq}"), 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) {
typedef std::chrono::duration<float> fs;
EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234)));
typedef std::chrono::duration<float, std::milli> fms;
EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234)));
typedef std::chrono::duration<double> ds;
EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234)));
EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234)));
}
TEST(chrono_test, format_precision) {
EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
}
TEST(chrono_test, format_full_specs) {
EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8));
EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3));
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
}
TEST(chrono_test, format_simple_q) {
typedef std::chrono::duration<float> fs;
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234)));
typedef std::chrono::duration<float, std::milli> fms;
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234)));
typedef std::chrono::duration<double> ds;
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234)));
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234)));
}
TEST(chrono_test, format_precision_q) {
EXPECT_THROW_MSG(fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
}
TEST(chrono_test, format_full_specs_q) {
EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2));
EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9));
EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3));
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
}
TEST(chrono_test, invalid_width_id) {
EXPECT_THROW(fmt::format(runtime("{:{o}"), std::chrono::seconds(0)),
fmt::format_error);
}
TEST(chrono_test, invalid_colons) {
EXPECT_THROW(fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)),
fmt::format_error);
}
TEST(chrono_test, negative_durations) {
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
EXPECT_EQ("-00:01",
fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-00.127",
fmt::format("{:%S}",
std::chrono::duration<signed char, std::milli>{-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(
"40.",
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
"nan nan nan nan nan:nan nan",
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
fmt::format("{:%S}",
std::chrono::duration<float, std::atto>(1.79400457e+31f));
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");
}
TEST(chrono_test, unsigned_duration) {
EXPECT_EQ("42s", fmt::format("{}", std::chrono::duration<unsigned>(42)));
}
TEST(chrono_test, weekday) {
auto loc = get_locale("ru_RU.UTF-8");
std::locale::global(loc);
auto mon = fmt::weekday(1);
EXPECT_EQ(fmt::format("{}", mon), "Mon");
if (loc != std::locale::classic()) {
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:L}", mon)));
}
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@@ -1,60 +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::italic, "italic"),
"\x1b[3mitalic\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\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");
}
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,359 +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(iterator_test, truncating_iterator) {
char* p = nullptr;
auto it = fmt::detail::truncating_iterator<char*>(p, 3);
auto prev = it++;
EXPECT_EQ(prev.base(), p);
EXPECT_EQ(it.base(), p + 1);
}
TEST(iterator_test, truncating_iterator_default_construct) {
auto it = fmt::detail::truncating_iterator<char*>();
EXPECT_EQ(nullptr, it.base());
EXPECT_EQ(std::size_t{0}, it.count());
}
#ifdef __cpp_lib_ranges
TEST(iterator_test, truncating_iterator_is_output_iterator) {
static_assert(
std::output_iterator<fmt::detail::truncating_iterator<char*>, char>);
}
#endif
TEST(iterator_test, truncating_back_inserter) {
auto buffer = std::string();
auto bi = std::back_inserter(buffer);
auto it = fmt::detail::truncating_iterator<decltype(bi)>(bi, 2);
*it++ = '4';
*it++ = '2';
*it++ = '1';
EXPECT_EQ(buffer.size(), 2);
EXPECT_EQ(buffer, "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));
}
#ifdef __cpp_if_constexpr
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_PARAMETERS
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, 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);
}
TEST(compile_test, formatted_size) {
EXPECT_EQ(2, fmt::formatted_size(FMT_COMPILE("{0}"), 42));
EXPECT_EQ(5, fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0));
}
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_PARAMETERS
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
#if __cplusplus >= 202002L || \
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
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

@@ -1,17 +1,15 @@
# Test if compile errors are produced where necessary.
cmake_minimum_required(VERSION 3.1...3.18)
cmake_minimum_required(VERSION 2.8)
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG} ${PEDANTIC_COMPILE_FLAGS})
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/format.h\"
#include \"fmt/posix.h\"
int main() {
${fragment}
}
@@ -49,23 +47,24 @@ 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_error("fmt::format(\"{}\", L'a';")
# Formatting a wide string with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L\"foo\");")
# 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_error("fmt::format(L\"{}\", \"foo\");")
# Formatting a wide string with a narrow format string is forbidden.
expect_compile_error("
struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", S());
")
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.

View File

@@ -1,847 +0,0 @@
// Formatting library for C++ - core tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// clang-format off
#include "test-assert.h"
// clang-format on
#include "fmt/core.h"
#include <algorithm> // std::copy_n
#include <climits> // INT_MAX
#include <cstring> // std::strlen
#include <functional> // std::equal_to
#include <iterator> // std::back_insert_iterator
#include <limits> // std::numeric_limits
#include <string> // std::string
#include <type_traits> // std::is_same
#include "gmock/gmock.h"
using fmt::string_view;
using fmt::detail::buffer;
using testing::_;
using testing::Invoke;
using testing::Return;
#ifdef FMT_FORMAT_H_
# error core-test includes format.h
#endif
TEST(string_view_test, value_type) {
static_assert(std::is_same<string_view::value_type, char>::value, "");
}
TEST(string_view_test, ctor) {
EXPECT_STREQ("abc", fmt::string_view("abc").data());
EXPECT_EQ(3u, fmt::string_view("abc").size());
EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data());
EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size());
}
TEST(string_view_test, length) {
// Test that string_view::size() returns string length, not buffer size.
char str[100] = "some string";
EXPECT_EQ(std::strlen(str), string_view(str).size());
EXPECT_LT(std::strlen(str), sizeof(str));
}
// Check string_view's comparison operator.
template <template <typename> class Op> void check_op() {
const char* inputs[] = {"foo", "fop", "fo"};
size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (size_t i = 0; i < num_inputs; ++i) {
for (size_t j = 0; j < num_inputs; ++j) {
string_view lhs(inputs[i]), rhs(inputs[j]);
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
}
}
}
TEST(string_view_test, compare) {
EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
check_op<std::equal_to>();
check_op<std::not_equal_to>();
check_op<std::less>();
check_op<std::less_equal>();
check_op<std::greater>();
check_op<std::greater_equal>();
}
namespace test_ns {
template <typename Char> class test_string {
private:
std::basic_string<Char> s_;
public:
test_string(const Char* s) : s_(s) {}
const Char* data() const { return s_.data(); }
size_t length() const { return s_.size(); }
operator const Char*() const { return s_.c_str(); }
};
template <typename Char>
fmt::basic_string_view<Char> to_string_view(const test_string<Char>& s) {
return {s.data(), s.length()};
}
} // namespace test_ns
TEST(core_test, is_output_iterator) {
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value));
EXPECT_TRUE(
(fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
char>::value));
EXPECT_TRUE(
(fmt::detail::is_output_iterator<std::string::iterator, char>::value));
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,
char>::value));
}
TEST(core_test, buffer_appender) {
// back_insert_iterator is not default-constructible before C++20, so
// buffer_appender can only be default-constructible when back_insert_iterator
// is.
static_assert(
std::is_default_constructible<
std::back_insert_iterator<fmt::detail::buffer<char>>>::value ==
std::is_default_constructible<
fmt::detail::buffer_appender<char>>::value,
"");
#ifdef __cpp_lib_ranges
static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>);
#endif
}
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(buffer_test, noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
# if !FMT_MSC_VER
// std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
# endif
}
TEST(buffer_test, nonmoveable) {
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
# if !FMT_MSC_VER
// std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
# endif
}
#endif
TEST(buffer_test, indestructible) {
static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
"buffer's destructor is protected");
}
template <typename T> struct mock_buffer final : buffer<T> {
MOCK_METHOD1(do_grow, size_t(size_t capacity));
void grow(size_t capacity) { this->set(this->data(), do_grow(capacity)); }
mock_buffer(T* data = nullptr, size_t buf_capacity = 0) {
this->set(data, buf_capacity);
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
return capacity;
}));
}
};
TEST(buffer_test, ctor) {
{
mock_buffer<int> buffer;
EXPECT_EQ(nullptr, buffer.data());
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
{
int dummy;
mock_buffer<int> buffer(&dummy);
EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
{
int dummy;
size_t capacity = std::numeric_limits<size_t>::max();
mock_buffer<int> buffer(&dummy, capacity);
EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(capacity, buffer.capacity());
}
}
TEST(buffer_test, access) {
char data[10];
mock_buffer<char> buffer(data, sizeof(data));
buffer[0] = 11;
EXPECT_EQ(11, buffer[0]);
buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3));
const fmt::detail::buffer<char>& const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]);
}
TEST(buffer_test, try_resize) {
char data[123];
mock_buffer<char> buffer(data, sizeof(data));
buffer[10] = 42;
EXPECT_EQ(42, buffer[10]);
buffer.try_resize(20);
EXPECT_EQ(20u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
EXPECT_EQ(42, buffer[10]);
buffer.try_resize(5);
EXPECT_EQ(5u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
EXPECT_EQ(42, buffer[10]);
// Check if try_resize calls grow.
EXPECT_CALL(buffer, do_grow(124));
buffer.try_resize(124);
EXPECT_CALL(buffer, do_grow(200));
buffer.try_resize(200);
}
TEST(buffer_test, try_resize_partial) {
char data[10];
mock_buffer<char> buffer(data, sizeof(data));
EXPECT_CALL(buffer, do_grow(20)).WillOnce(Return(15));
buffer.try_resize(20);
EXPECT_EQ(buffer.capacity(), 15);
EXPECT_EQ(buffer.size(), 15);
}
TEST(buffer_test, clear) {
mock_buffer<char> buffer;
EXPECT_CALL(buffer, do_grow(20));
buffer.try_resize(20);
buffer.try_resize(0);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(20u, buffer.capacity());
}
TEST(buffer_test, append) {
char data[15];
mock_buffer<char> buffer(data, 10);
auto test = "test";
buffer.append(test, test + 5);
EXPECT_STREQ(test, &buffer[0]);
EXPECT_EQ(5u, buffer.size());
buffer.try_resize(10);
EXPECT_CALL(buffer, do_grow(12));
buffer.append(test, test + 2);
EXPECT_EQ('t', buffer[10]);
EXPECT_EQ('e', buffer[11]);
EXPECT_EQ(12u, buffer.size());
}
TEST(buffer_test, append_partial) {
char data[10];
mock_buffer<char> buffer(data, sizeof(data));
testing::InSequence seq;
EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10));
EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) {
EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789");
buffer.clear();
return 10;
}));
auto test = "0123456789abcde";
buffer.append(test, test + 15);
}
TEST(buffer_test, append_allocates_enough_storage) {
char data[19];
mock_buffer<char> buffer(data, 10);
auto test = "abcdefgh";
buffer.try_resize(10);
EXPECT_CALL(buffer, do_grow(19));
buffer.append(test, test + 9);
}
struct custom_context {
using char_type = char;
using parse_context_type = fmt::format_parse_context;
bool called = false;
template <typename T> struct formatter_type {
auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
const char* format(const T&, custom_context& ctx) {
ctx.called = true;
return nullptr;
}
};
void advance_to(const char*) {}
};
struct test_struct {};
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatter<test_struct, Char> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
TEST(arg_test, format_args) {
auto args = fmt::format_args();
EXPECT_FALSE(args.get(1));
}
TEST(arg_test, make_value_with_custom_context) {
auto t = test_struct();
fmt::detail::value<custom_context> arg(
fmt::detail::arg_mapper<custom_context>().map(t));
auto ctx = custom_context();
auto parse_ctx = fmt::format_parse_context("");
arg.custom.format(&t, parse_ctx, ctx);
EXPECT_TRUE(ctx.called);
}
// Use a unique result type to make sure that there are no undesirable
// conversions.
struct test_result {};
template <typename T> struct mock_visitor {
template <typename U> struct result { using type = test_result; };
mock_visitor() {
ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
}
MOCK_METHOD1_T(visit, test_result(T value));
MOCK_METHOD0_T(unexpected, void());
test_result operator()(T value) { return visit(value); }
template <typename U> test_result operator()(U) {
unexpected();
return test_result();
}
};
template <typename T> struct visit_type { using type = T; };
#define VISIT_TYPE(type_, visit_type_) \
template <> struct visit_type<type_> { using type = visit_type_; }
VISIT_TYPE(signed char, int);
VISIT_TYPE(unsigned char, unsigned);
VISIT_TYPE(short, int);
VISIT_TYPE(unsigned short, unsigned);
#if LONG_MAX == INT_MAX
VISIT_TYPE(long, int);
VISIT_TYPE(unsigned long, unsigned);
#else
VISIT_TYPE(long, long long);
VISIT_TYPE(unsigned long, unsigned long long);
#endif
#define CHECK_ARG(Char, expected, value) \
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
EXPECT_CALL(visitor, visit(expected)); \
using iterator = std::back_insert_iterator<buffer<Char>>; \
fmt::visit_format_arg( \
visitor, \
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \
value)); \
}
#define CHECK_ARG_SIMPLE(value) \
{ \
using value_type = decltype(value); \
typename visit_type<value_type>::type expected = value; \
CHECK_ARG(char, expected, value) \
CHECK_ARG(wchar_t, expected, value) \
}
template <typename T> class numeric_arg_test : public testing::Test {};
using types =
testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
unsigned, long, unsigned long, long long, unsigned long long,
float, double, long double>;
TYPED_TEST_SUITE(numeric_arg_test, types);
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
T test_value() {
return static_cast<T>(42);
}
template <typename T,
fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
T test_value() {
return static_cast<T>(4.2);
}
TYPED_TEST(numeric_arg_test, make_and_visit) {
CHECK_ARG_SIMPLE(test_value<TypeParam>());
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min());
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
}
namespace fmt {
template <> struct is_char<wchar_t> : std::true_type {};
} // namespace fmt
TEST(arg_test, char_arg) {
CHECK_ARG(char, 'a', 'a');
CHECK_ARG(wchar_t, L'a', 'a');
CHECK_ARG(wchar_t, L'a', L'a');
}
TEST(arg_test, string_arg) {
char str_data[] = "test";
char* str = str_data;
const char* cstr = str;
CHECK_ARG(char, cstr, str);
auto sv = fmt::string_view(str);
CHECK_ARG(char, sv, std::string(str));
}
TEST(arg_test, wstring_arg) {
wchar_t str_data[] = L"test";
wchar_t* str = str_data;
const wchar_t* cstr = str;
auto sv = fmt::basic_string_view<wchar_t>(str);
CHECK_ARG(wchar_t, cstr, str);
CHECK_ARG(wchar_t, cstr, cstr);
CHECK_ARG(wchar_t, sv, std::wstring(str));
CHECK_ARG(wchar_t, sv, fmt::basic_string_view<wchar_t>(str));
}
TEST(arg_test, pointer_arg) {
void* p = nullptr;
const void* cp = nullptr;
CHECK_ARG(char, cp, p);
CHECK_ARG(wchar_t, cp, p);
CHECK_ARG_SIMPLE(cp);
}
struct check_custom {
test_result operator()(
fmt::basic_format_arg<fmt::format_context>::handle h) const {
struct test_buffer final : fmt::detail::buffer<char> {
char data[10];
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
void grow(size_t) {}
} buffer;
auto parse_ctx = fmt::format_parse_context("");
auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer),
fmt::format_args());
h.format(parse_ctx, ctx);
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
return test_result();
}
};
TEST(arg_test, custom_arg) {
auto test = test_struct();
using visitor =
mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
testing::StrictMock<visitor> v;
EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
fmt::visit_format_arg(v, fmt::detail::make_arg<fmt::format_context>(test));
}
TEST(arg_test, visit_invalid_arg) {
testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
EXPECT_CALL(visitor, visit(_));
auto arg = fmt::basic_format_arg<fmt::format_context>();
fmt::visit_format_arg(visitor, arg);
}
#if FMT_USE_CONSTEXPR
enum class arg_id_result { none, empty, index, name, error };
struct test_arg_id_handler {
arg_id_result res = arg_id_result::none;
int index = 0;
string_view name;
constexpr void operator()() { res = arg_id_result::empty; }
constexpr void operator()(int i) {
res = arg_id_result::index;
index = i;
}
constexpr void operator()(string_view n) {
res = arg_id_result::name;
name = n;
}
constexpr void on_error(const char*) { res = arg_id_result::error; }
};
template <size_t N>
constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
test_arg_id_handler h;
fmt::detail::parse_arg_id(s, s + N, h);
return h;
}
TEST(format_test, constexpr_parse_arg_id) {
static_assert(parse_arg_id(":").res == arg_id_result::empty, "");
static_assert(parse_arg_id("}").res == arg_id_result::empty, "");
static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
static_assert(parse_arg_id("42:").index == 42, "");
static_assert(parse_arg_id("foo:").res == arg_id_result::name, "");
static_assert(parse_arg_id("foo:").name.size() == 3, "");
static_assert(parse_arg_id("!").res == arg_id_result::error, "");
}
struct test_format_specs_handler {
enum result { none, hash, zero, loc, error };
result res = none;
fmt::align_t alignment = fmt::align::none;
fmt::sign_t sign = fmt::sign::none;
char fill = 0;
int width = 0;
fmt::detail::arg_ref<char> width_ref;
int precision = 0;
fmt::detail::arg_ref<char> precision_ref;
char type = 0;
// Workaround for MSVC2017 bug that results in "expression did not evaluate
// to a constant" with compiler-generated copy ctor.
constexpr test_format_specs_handler() {}
constexpr test_format_specs_handler(const test_format_specs_handler& other) =
default;
constexpr void on_align(fmt::align_t a) { alignment = a; }
constexpr void on_fill(fmt::string_view f) { fill = f[0]; }
constexpr void on_sign(fmt::sign_t s) { sign = s; }
constexpr void on_hash() { res = hash; }
constexpr void on_zero() { res = zero; }
constexpr void on_localized() { res = loc; }
constexpr void on_width(int w) { width = w; }
constexpr void on_dynamic_width(fmt::detail::auto_id) {}
constexpr void on_dynamic_width(int index) { width_ref = index; }
constexpr void on_dynamic_width(string_view) {}
constexpr void on_precision(int p) { precision = p; }
constexpr void on_dynamic_precision(fmt::detail::auto_id) {}
constexpr void on_dynamic_precision(int index) { precision_ref = index; }
constexpr void on_dynamic_precision(string_view) {}
constexpr void end_precision() {}
constexpr void on_type(char t) { type = t; }
constexpr void on_error(const char*) { res = error; }
};
template <size_t N>
constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) {
auto h = test_format_specs_handler();
fmt::detail::parse_format_specs(s, s + N, h);
return h;
}
TEST(core_test, constexpr_parse_format_specs) {
using handler = test_format_specs_handler;
static_assert(parse_test_specs("<").alignment == fmt::align::left, "");
static_assert(parse_test_specs("*^").fill == '*', "");
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
static_assert(parse_test_specs("#").res == handler::hash, "");
static_assert(parse_test_specs("0").res == handler::zero, "");
static_assert(parse_test_specs("L").res == handler::loc, "");
static_assert(parse_test_specs("42").width == 42, "");
static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
static_assert(parse_test_specs(".42").precision == 42, "");
static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(parse_test_specs("d").type == 'd', "");
static_assert(parse_test_specs("{<").res == handler::error, "");
}
struct test_parse_context {
using char_type = char;
constexpr int next_arg_id() { return 11; }
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
constexpr const char* begin() { return nullptr; }
constexpr const char* end() { return nullptr; }
void on_error(const char*) {}
};
template <size_t N>
constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
const char (&s)[N]) {
auto specs = fmt::detail::dynamic_format_specs<char>();
auto ctx = test_parse_context();
auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx);
parse_format_specs(s, s + N, h);
return specs;
}
TEST(format_test, constexpr_dynamic_specs_handler) {
static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
static_assert(parse_dynamic_specs("#").alt, "");
static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
static_assert(parse_dynamic_specs("42").width == 42, "");
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
static_assert(parse_dynamic_specs(".42").precision == 42, "");
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(parse_dynamic_specs("d").type == 'd', "");
}
template <size_t N>
constexpr test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::detail::specs_checker<test_format_specs_handler> checker(
test_format_specs_handler(), fmt::detail::type::double_type);
parse_format_specs(s, s + N, checker);
return checker;
}
TEST(format_test, constexpr_specs_checker) {
using handler = test_format_specs_handler;
static_assert(check_specs("<").alignment == fmt::align::left, "");
static_assert(check_specs("*^").fill == '*', "");
static_assert(check_specs("+").sign == fmt::sign::plus, "");
static_assert(check_specs("-").sign == fmt::sign::minus, "");
static_assert(check_specs(" ").sign == fmt::sign::space, "");
static_assert(check_specs("#").res == handler::hash, "");
static_assert(check_specs("0").res == handler::zero, "");
static_assert(check_specs("42").width == 42, "");
static_assert(check_specs("{42}").width_ref.val.index == 42, "");
static_assert(check_specs(".42").precision == 42, "");
static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(check_specs("d").type == 'd', "");
static_assert(check_specs("{<").res == handler::error, "");
}
struct test_format_string_handler {
constexpr void on_text(const char*, const char*) {}
constexpr int on_arg_id() { return 0; }
template <typename T> constexpr int on_arg_id(T) { return 0; }
constexpr void on_replacement_field(int, const char*) {}
constexpr const char* on_format_specs(int, const char* begin, const char*) {
return begin;
}
constexpr void on_error(const char*) { error = true; }
bool error = false;
};
template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
auto h = test_format_string_handler();
fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
return !h.error;
}
TEST(format_test, constexpr_parse_format_string) {
static_assert(parse_string("foo"), "");
static_assert(!parse_string("}"), "");
static_assert(parse_string("{}"), "");
static_assert(parse_string("{42}"), "");
static_assert(parse_string("{foo}"), "");
static_assert(parse_string("{:}"), "");
}
#endif // FMT_USE_CONSTEXPR
struct enabled_formatter {};
struct disabled_formatter {};
struct disabled_formatter_convertible {
operator int() const { return 42; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<enabled_formatter> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(core_test, has_formatter) {
using fmt::has_formatter;
using context = fmt::format_context;
static_assert(has_formatter<enabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter_convertible, context>::value,
"");
}
TEST(core_test, is_formattable) {
static_assert(fmt::is_formattable<enabled_formatter>::value, "");
static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
}
TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
TEST(core_test, format_to) {
std::string s;
fmt::format_to(std::back_inserter(s), "{}", 42);
EXPECT_EQ(s, "42");
}
struct convertible_to_int {
operator int() const { return 42; }
};
struct convertible_to_c_string {
operator const char*() const { return "foo"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_int> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) {
return std::copy_n("foo", 3, ctx.out());
}
};
template <> struct formatter<convertible_to_c_string> {
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_c_string, format_context& ctx)
-> decltype(ctx.out()) {
return std::copy_n("bar", 3, ctx.out());
}
};
FMT_END_NAMESPACE
TEST(core_test, formatter_overrides_implicit_conversion) {
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar");
}
// Test that check is not found by ADL.
template <typename T> void check(T);
TEST(core_test, adl_check) {
EXPECT_EQ(fmt::format("{}", test_struct()), "test");
}
TEST(core_test, to_string_view_foreign_strings) {
using namespace test_ns;
EXPECT_EQ(to_string_view(test_string<char>("42")), "42");
fmt::detail::type type =
fmt::detail::mapped_type_constant<test_string<char>,
fmt::format_context>::value;
EXPECT_EQ(type, fmt::detail::type::string_type);
}
struct implicitly_convertible_to_string {
operator std::string() const { return "foo"; }
};
struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
TEST(core_test, format_implicitly_convertible_to_string_view) {
EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
}
// std::is_constructible is broken in MSVC until version 2015.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; }
};
TEST(core_test, format_explicitly_convertible_to_string_view) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
# ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
TEST(core_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
# endif
#endif
struct convertible_to_long_long {
operator long long() const { return 1LL << 32; }
};
TEST(format_test, format_convertible_to_long_long) {
EXPECT_EQ("100000000", fmt::format("{:x}", convertible_to_long_long()));
}
struct disabled_rvalue_conversion {
operator const char*() const& { return "foo"; }
operator const char*() & { return "foo"; }
operator const char*() const&& = delete;
operator const char*() && = delete;
};
TEST(core_test, disabled_rvalue_conversion) {
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
}
namespace adl_test {
template <typename... T> void make_format_args(const T&...) = delete;
struct string : std::string {};
} // namespace adl_test
// Test that formatting functions compile when make_format_args is found by ADL.
TEST(core_test, adl) {
// Only check compilation and don't run the code to avoid polluting the output
// and since the output is tested elsewhere.
if (fmt::detail::const_check(true)) return;
auto s = adl_test::string();
char buf[10];
fmt::format("{}", s);
fmt::format_to(buf, "{}", s);
fmt::format_to_n(buf, 10, "{}", s);
fmt::formatted_size("{}", s);
fmt::print("{}", s);
fmt::print(stdout, "{}", s);
}

View File

@@ -1,73 +0,0 @@
# We can find some usecases which follow the guide of CMake which uses
# `enable_language(CUDA)` instead of `find_package(CUDA)` and let the CMake
# built-in functions use NVCC.
# See: https://cmake.org/cmake/help/latest/module/FindCUDA.html#replacement
#
# However, this requires CMake version 3.10 or higher and we can't be sure most
# of the CUDA projects are using those.
#
# This test relies on `find_package(CUDA)` in the parent CMake config.
# These can be updated when NVCC becomes ready for C++ 17 features
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
set(CMAKE_CUDA_STANDARD 14)
set(CMAKE_CUDA_STANDARD_REQUIRED 14)
# In this test, we assume that the user is going to compile CUDA source code
# with some libraries (fmt in this case).
#
# In addition to that, this test invokes both the C++ host compiler and NVCC
# by providing another (non-CUDA) C++ source code.
if (${CMAKE_VERSION} VERSION_LESS 3.15)
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html
list(APPEND CUDA_NVCC_FLAGS "-std=c++14")
if (MSVC)
# This is the solution of pytorch:
# https://github.com/pytorch/pytorch/pull/7118
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/std:c++14")
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/Zc:__cplusplus")
# for the reason of this -Xcompiler options, see below.
endif ()
cuda_add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
if (MSVC)
# This part is for (non-CUDA) C++ code. MSVC can define incorrect
# `__cplusplus` macro. Fix for the issue is to use additional compiler flag.
#
# See Also:
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
# https://github.com/Microsoft/vscode-cpptools/issues/2595
target_compile_options(fmt-in-cuda-test PRIVATE /Zc:__cplusplus /permissive-)
endif ()
else()
# now using a "new" way of handling CUDA
add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
set_target_properties(fmt-in-cuda-test PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
if (MSVC)
# with MSVC, 'cxx_std_14' will only propagate to the host code (MSVC), but will
# not set __cplusplus correctly anyway, while nvcc will ignore it.
# If specified for nvcc on the command line as '-std=c++14' nvcc will emit this
# message instead:
# nvcc warning : The -std=c++14 flag is not supported with the configured host
# compiler. Flag will be ignored.
set_property(SOURCE cuda-cpp14.cu APPEND PROPERTY
COMPILE_OPTIONS -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus)
set_property(SOURCE cpp14.cc APPEND PROPERTY
COMPILE_OPTIONS /std:c++14 /Zc:__cplusplus)
endif()
endif()
get_target_property(IN_USE_CUDA_STANDARD fmt-in-cuda-test CUDA_STANDARD)
message(STATUS "cuda_standard: ${IN_USE_CUDA_STANDARD}")
get_target_property(IN_USE_CUDA_STANDARD_REQUIRED
fmt-in-cuda-test CUDA_STANDARD_REQUIRED)
message(STATUS "cuda_standard_required: ${IN_USE_CUDA_STANDARD_REQUIRED}")
# We don't use PUBLIC or other keyword for reasons explained in the
# CUDA_LINK_LIBRARIES_KEYWORD section in
# https://cmake.org/cmake/help/latest/module/FindCUDA.html
target_link_libraries(fmt-in-cuda-test fmt::fmt)

View File

@@ -1,11 +0,0 @@
#include <fmt/core.h>
// The purpose of this part is to ensure NVCC's host compiler also supports
// the standard version. See 'cuda-cpp14.cu'.
//
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for host compiler");
auto make_message_cpp() -> std::string {
return fmt::format("host compiler \t: __cplusplus == {}", __cplusplus);
}

View File

@@ -1,28 +0,0 @@
// Direct NVCC command line example:
//
// nvcc ./cuda-cpp14.cu -x cu -I"../include" -l"fmtd" -L"../build/Debug" \
// -std=c++14 -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus
// Ensure that we are using the latest C++ standard for NVCC
// The version is C++14
//
// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-cplusplus-language-support
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for nvcc");
#include <fmt/core.h>
#include <cuda.h>
#include <iostream>
extern auto make_message_cpp() -> std::string;
extern auto make_message_cuda() -> std::string;
int main() {
std::cout << make_message_cuda() << std::endl;
std::cout << make_message_cpp() << std::endl;
}
auto make_message_cuda() -> std::string {
return fmt::format("nvcc compiler \t: __cplusplus == {}", __cplusplus);
}

View File

@@ -1,62 +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 <iterator>
#include <vector>
#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/format.h"
#include "fmt/ostream.h"
#include "fmt/ranges.h"
#include "fmt/xchar.h"
// Exercise the API to verify that everything we expect to can compile.
void test_format_api() {
fmt::format(FMT_STRING("{}"), 42);
fmt::format(FMT_STRING(L"{}"), 42);
fmt::format(FMT_STRING("noop"));
fmt::to_string(42);
fmt::to_wstring(42);
std::vector<char> out;
fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42);
char buffer[4];
fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345);
wchar_t wbuffer[4];
fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345);
}
void test_chrono() {
fmt::format(FMT_STRING("{}"), std::chrono::seconds(42));
fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42));
}
void test_text_style() {
fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)");
fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)");
fmt::text_style ts = fg(fmt::rgb(255, 20, 30));
std::string out;
fmt::format_to(std::back_inserter(out), ts,
FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3);
}
void test_range() {
std::vector<char> hello = {'h', 'e', 'l', 'l', 'o'};
fmt::format(FMT_STRING("{}"), hello);
}
int main() {
test_format_api();
test_chrono();
test_text_style();
test_range();
}

View File

@@ -1,17 +1,13 @@
cmake_minimum_required(VERSION 3.1...3.18)
cmake_minimum_required(VERSION 2.8.12)
project(fmt-test)
find_package(FMT REQUIRED)
add_executable(library-test main.cc)
target_link_libraries(library-test fmt::fmt)
target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_include_directories(library-test PUBLIC SYSTEM .)
target_link_libraries(library-test fmt)
if (TARGET fmt::fmt-header-only)
if (TARGET fmt-header-only)
add_executable(header-only-test main.cc)
target_link_libraries(header-only-test fmt::fmt-header-only)
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_include_directories(header-only-test PUBLIC SYSTEM .)
target_link_libraries(header-only-test fmt-header-only)
endif ()

View File

@@ -1,856 +0,0 @@
// Formatting library for C++ - the standard API implementation
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <algorithm>
#include <cassert>
#include <variant>
#include "fmt/format.h"
// This implementation verifies the correctness of the standard API proposed in
// P0645 Text Formatting and is optimized for copy-pasting from the paper, not
// for efficiency or readability. An efficient implementation should not use
// std::variant and should store packed argument type tags separately from
// values in basic_format_args for small number of arguments.
namespace std {
template<class T>
constexpr bool Integral = is_integral_v<T>;
template <class O>
using iter_difference_t = ptrdiff_t;
}
// https://fmt.dev/Text%20Formatting.html#format.syn
namespace std {
// [format.error], class format_error
class format_error;
// [format.formatter], formatter
template<class charT> class basic_format_parse_context;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
template<class Out, class charT> class basic_format_context;
using format_context = basic_format_context<
/* unspecified */ fmt::detail::buffer_appender<char>, char>;
using wformat_context = basic_format_context<
/* unspecified */ fmt::detail::buffer_appender<wchar_t>, wchar_t>;
template<class T, class charT = char> struct formatter {
formatter() = delete;
};
// [format.arguments], arguments
template<class Context> class basic_format_arg;
template<class Visitor, class Context>
/* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
template<class Context, class... Args> struct format_arg_store; // exposition only
template<class Context> class basic_format_args;
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
template<class Out, class charT>
using format_args_t = basic_format_args<basic_format_context<Out, charT>>;
template<class Context = format_context, class... Args>
format_arg_store<Context, Args...>
make_format_args(const Args&... args);
template<class... Args>
format_arg_store<wformat_context, Args...>
make_wformat_args(const Args&... args);
// [format.functions], formatting functions
template<class... Args>
string format(string_view fmt, const Args&... args);
template<class... Args>
wstring format(wstring_view fmt, const Args&... args);
string vformat(string_view fmt, format_args args);
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args);
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args);
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out>
struct format_to_n_result {
Out out;
iter_difference_t<Out> size;
};
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
}
// https://fmt.dev/Text%20Formatting.html#format.error
namespace std {
class format_error : public runtime_error {
public:
explicit format_error(const string& what_arg) : runtime_error(what_arg) {}
explicit format_error(const char* what_arg) : runtime_error(what_arg) {}
};
}
namespace std {
namespace detail {
struct error_handler {
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char* message) {
throw std::format_error(message);
}
};
}
}
// https://fmt.dev/Text%20Formatting.html#format.parse_context
namespace std {
template<class charT>
class basic_format_parse_context {
public:
using char_type = charT;
using const_iterator = typename basic_string_view<charT>::const_iterator;
using iterator = const_iterator;
private:
iterator begin_; // exposition only
iterator end_; // exposition only
enum indexing { unknown, manual, automatic }; // exposition only
indexing indexing_; // exposition only
size_t next_arg_id_; // exposition only
size_t num_args_; // exposition only
public:
explicit constexpr basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args = 0) noexcept;
basic_format_parse_context(const basic_format_parse_context&) = delete;
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr void advance_to(const_iterator it);
constexpr size_t next_arg_id();
constexpr void check_arg_id(size_t id);
// Implementation detail:
constexpr void check_arg_id(fmt::string_view) {}
detail::error_handler error_handler() const { return {}; }
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class charT>
/* explicit */ constexpr basic_format_parse_context<charT>::
basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args) noexcept
: begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {}
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::begin() const noexcept { return begin_; }
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::end() const noexcept { return end_; }
template<class charT>
constexpr void basic_format_parse_context<charT>::advance_to(typename basic_format_parse_context<charT>::iterator it) { begin_ = it; }
template<class charT>
constexpr size_t basic_format_parse_context<charT>::next_arg_id() {
if (indexing_ == manual)
throw format_error("manual to automatic indexing");
if (indexing_ == unknown)
indexing_ = automatic;
return next_arg_id_++;
}
template<class charT>
constexpr void basic_format_parse_context<charT>::check_arg_id(size_t id) {
// clang doesn't support __builtin_is_constant_evaluated yet
//if (!(!__builtin_is_constant_evaluated() || id < num_args_))
// throw format_error(invalid index is out of range");
if (indexing_ == automatic)
throw format_error("automatic to manual indexing");
if (indexing_ == unknown)
indexing_ = manual;
}
}
// https://fmt.dev/Text%20Formatting.html#format.context
namespace std {
template<class Out, class charT>
class basic_format_context {
basic_format_args<basic_format_context> args_; // exposition only
Out out_; // exposition only
public:
using iterator = Out;
using char_type = charT;
template<class T> using formatter_type = formatter<T, charT>;
basic_format_arg<basic_format_context> arg(size_t id) const;
iterator out();
void advance_to(iterator it);
// Implementation details:
using format_arg = basic_format_arg<basic_format_context>;
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::detail::locale_ref)
: args_(args), out_(out) {}
detail::error_handler error_handler() const { return {}; }
basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const {
return {}; // unused: named arguments are not supported yet
}
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class O, class charT>
basic_format_arg<basic_format_context<O, charT>> basic_format_context<O, charT>::arg(size_t id) const { return args_.get(id); }
template<class O, class charT>
typename basic_format_context<O, charT>::iterator basic_format_context<O, charT>::out() { return out_; }
template<class O, class charT>
void basic_format_context<O, charT>::advance_to(typename basic_format_context<O, charT>::iterator it) { out_ = it; }
}
namespace std {
namespace detail {
template <typename T>
constexpr bool is_standard_integer_v =
std::is_same_v<T, signed char> ||
std::is_same_v<T, short int> ||
std::is_same_v<T, int> ||
std::is_same_v<T, long int> ||
std::is_same_v<T, long long int>;
template <typename T>
constexpr bool is_standard_unsigned_integer_v =
std::is_same_v<T, unsigned char> ||
std::is_same_v<T, unsigned short int> ||
std::is_same_v<T, unsigned int> ||
std::is_same_v<T, unsigned long int> ||
std::is_same_v<T, unsigned long long int>;
template <typename T, typename Char> struct formatter;
}
}
// https://fmt.dev/Text%20Formatting.html#format.arg
namespace std {
template<class Context>
class basic_format_arg {
public:
class handle;
private:
using char_type = typename Context::char_type; // exposition only
variant<monostate, bool, char_type,
int, unsigned int, long long int, unsigned long long int,
double, long double,
const char_type*, basic_string_view<char_type>,
const void*, handle> value; // exposition only
template<typename T,
typename = enable_if_t<
std::is_same_v<T, bool> ||
std::is_same_v<T, char_type> ||
(std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) ||
detail::is_standard_integer_v<T> ||
detail::is_standard_unsigned_integer_v<T> ||
sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0
>> explicit basic_format_arg(const T& v) noexcept; // exposition only
explicit basic_format_arg(float n) noexcept; // exposition only
explicit basic_format_arg(double n) noexcept; // exposition only
explicit basic_format_arg(long double n) noexcept; // exposition only
explicit basic_format_arg(const char_type* s); // exposition only
template<class traits>
explicit basic_format_arg(
basic_string_view<char_type, traits> s) noexcept; // exposition only
template<class traits, class Allocator>
explicit basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
explicit basic_format_arg(nullptr_t) noexcept; // exposition only
template<class T, typename = enable_if_t<is_void_v<T>>>
explicit basic_format_arg(const T* p) noexcept; // exposition only
// Fails due to a bug in clang
//template<class Visitor, class Ctx>
// friend auto visit_format_arg(Visitor&& vis,
// basic_format_arg<Ctx> arg); // exposition only
friend auto get_value(basic_format_arg arg) {
return arg.value;
}
template <typename T, typename Char> friend struct detail::formatter;
template<class Ctx, class... Args>
friend format_arg_store<Ctx, Args...>
make_format_args(const Args&... args); // exposition only
public:
basic_format_arg() noexcept;
explicit operator bool() const noexcept;
};
}
namespace std {
template<class Context>
basic_format_arg<Context>::basic_format_arg() noexcept {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T& v) noexcept {
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char_type>)
value = v;
else if constexpr (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>)
value = static_cast<wchar_t>(v);
else if constexpr (detail::is_standard_integer_v<T> && sizeof(T) <= sizeof(int))
value = static_cast<int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T> && sizeof(T) <= sizeof(unsigned))
value = static_cast<unsigned>(v);
else if constexpr (detail::is_standard_integer_v<T>)
value = static_cast<long long int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T>)
value = static_cast<unsigned long long int>(v);
else if constexpr (sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0)
value = handle(v);
}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(float n) noexcept
: value(static_cast<double>(n)) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(long double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(const typename basic_format_arg<Context>::char_type* s)
: value(s) {
assert(s != nullptr);
}
template<class Context>
template<class traits>
/* explicit */ basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type, traits> s) noexcept
: value(s) {}
template<class Context>
template<class traits, class Allocator>
/* explicit */ basic_format_arg<Context>::basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept
: value(basic_string_view<char_type>(s.data(), s.size())) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(nullptr_t) noexcept
: value(static_cast<const void*>(nullptr)) {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T* p) noexcept
: value(p) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::operator bool() const noexcept {
return !holds_alternative<monostate>(value);
}
}
namespace std {
template<class Context>
class basic_format_arg<Context>::handle {
const void* ptr_; // exposition only
void (*format_)(basic_format_parse_context<char_type>&,
Context&, const void*); // exposition only
template<class T> explicit handle(const T& val) noexcept; // exposition only
friend class basic_format_arg<Context>; // exposition only
public:
void format(basic_format_parse_context<char_type>&, Context& ctx) const;
};
}
namespace std {
template<class Context>
template<class T> /* explicit */ basic_format_arg<Context>::handle::handle(const T& val) noexcept
: ptr_(&val), format_([](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) {
typename Context::template formatter_type<T> f;
parse_ctx.advance_to(f.parse(parse_ctx));
format_ctx.advance_to(f.format(*static_cast<const T*>(ptr), format_ctx));
}) {}
template<class Context>
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const {
format_(parse_ctx, format_ctx, ptr_);
}
// https://fmt.dev/Text%20Formatting.html#format.visit
template<class Visitor, class Context>
auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg) {
return visit(vis, get_value(arg));
}
}
// https://fmt.dev/Text%20Formatting.html#format.store
namespace std {
template<class Context, class... Args>
struct format_arg_store { // exposition only
array<basic_format_arg<Context>, sizeof...(Args)> args;
};
}
// https://fmt.dev/Text%20Formatting.html#format.basic_args
namespace std {
template<class Context>
class basic_format_args {
size_t size_; // exposition only
const basic_format_arg<Context>* data_; // exposition only
public:
basic_format_args() noexcept;
template<class... Args>
basic_format_args(const format_arg_store<Context, Args...>& store) noexcept;
basic_format_arg<Context> get(size_t i) const noexcept;
};
}
namespace std {
template<class Context>
basic_format_args<Context>::basic_format_args() noexcept : size_(0) {}
template<class Context>
template<class... Args>
basic_format_args<Context>::basic_format_args(const format_arg_store<Context, Args...>& store) noexcept
: size_(sizeof...(Args)), data_(store.args.data()) {}
template<class Context>
basic_format_arg<Context> basic_format_args<Context>::get(size_t i) const noexcept {
return i < size_ ? data_[i] : basic_format_arg<Context>();
}
}
namespace std {
// https://fmt.dev/Text%20Formatting.html#format.make_args
template<class Context /*= format_context*/, class... Args>
format_arg_store<Context, Args...> make_format_args(const Args&... args) {
return {basic_format_arg<Context>(args)...};
}
// https://fmt.dev/Text%20Formatting.html#format.make_wargs
template<class... Args>
format_arg_store<wformat_context, Args...> make_wformat_args(const Args&... args) {
return make_format_args<wformat_context>(args...);
}
}
namespace std {
namespace detail {
template <typename OutputIt, typename Char>
class arg_formatter
: public fmt::detail::arg_formatter_base<OutputIt, Char, error_handler> {
private:
using char_type = Char;
using base = fmt::detail::arg_formatter_base<OutputIt, Char, error_handler>;
using format_context = std::basic_format_context<OutputIt, Char>;
using parse_context = basic_format_parse_context<Char>;
parse_context* parse_ctx_;
format_context& ctx_;
public:
using iterator = OutputIt;
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
: base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator();
/** Formats an argument of a user-defined type. */
iterator operator()(typename std::basic_format_arg<format_context>::handle handle) {
handle.format(*parse_ctx_, ctx_);
return this->out();
}
iterator operator()(monostate) {
throw format_error("");
}
};
template <typename Context>
inline fmt::detail::type get_type(basic_format_arg<Context> arg) {
return visit_format_arg([&] (auto val) {
using char_type = typename Context::char_type;
using T = decltype(val);
if (std::is_same_v<T, monostate>)
return fmt::detail::type::none_type;
if (std::is_same_v<T, bool>)
return fmt::detail::type::bool_type;
if (std::is_same_v<T, char_type>)
return fmt::detail::type::char_type;
if (std::is_same_v<T, int>)
return fmt::detail::type::int_type;
if (std::is_same_v<T, unsigned int>)
return fmt::detail::type::uint_type;
if (std::is_same_v<T, long long int>)
return fmt::detail::type::long_long_type;
if (std::is_same_v<T, unsigned long long int>)
return fmt::detail::type::ulong_long_type;
if (std::is_same_v<T, double>)
return fmt::detail::type::double_type;
if (std::is_same_v<T, long double>)
return fmt::detail::type::long_double_type;
if (std::is_same_v<T, const char_type*>)
return fmt::detail::type::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::detail::type::string_type;
if (std::is_same_v<T, const void*>)
return fmt::detail::type::pointer_type;
assert(get_value(arg).index() == 12);
return fmt::detail::type::custom_type;
}, arg);
}
template <typename Context>
class custom_formatter {
private:
using parse_context = basic_format_parse_context<typename Context::char_type>;
parse_context& parse_ctx_;
Context& format_ctx_;
public:
custom_formatter(parse_context& parse_ctx, Context& ctx) : parse_ctx_(parse_ctx), format_ctx_(ctx) {}
bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(parse_ctx_, format_ctx_);
return true;
}
template <typename T> bool operator()(T) const { return false; }
};
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler {
using iterator = typename ArgFormatter::iterator;
format_handler(iterator out, basic_string_view<Char> str,
basic_format_args<Context> format_args,
fmt::detail::locale_ref loc)
: parse_ctx(str), context(out, format_args, loc) {}
void on_text(const Char* begin, const Char* end) {
auto size = fmt::detail::to_unsigned(end - begin);
auto out = context.out();
auto&& it = fmt::detail::reserve(out, size);
it = std::copy_n(begin, size, it);
context.advance_to(out);
}
int on_arg_id() { return parse_ctx.next_arg_id(); }
int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; }
int on_arg_id(fmt::basic_string_view<Char>) { return 0; }
void on_replacement_field(int id, const Char* p) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
}
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
fmt::basic_format_specs<Char> specs;
using fmt::detail::specs_handler;
using parse_context = basic_format_parse_context<Char>;
fmt::detail::specs_checker<specs_handler<parse_context, Context>> handler(
specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg));
begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string");
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx, &specs), arg));
return begin;
}
basic_format_parse_context<Char> parse_ctx;
Context context;
};
template <typename T, typename Char>
struct formatter {
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
namespace detail = fmt::detail;
typedef detail::dynamic_specs_handler<ParseContext> handler_type;
auto type = detail::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto type_spec = specs_.type;
auto eh = ctx.error_handler();
switch (type) {
case detail::type::none_type:
FMT_ASSERT(false, "invalid argument type");
break;
case detail::type::int_type:
case detail::type::uint_type:
case detail::type::long_long_type:
case detail::type::ulong_long_type:
case detail::type::bool_type:
handle_int_type_spec(type_spec,
detail::int_type_checker<decltype(eh)>(eh));
break;
case detail::type::char_type:
handle_char_specs(
&specs_, detail::char_specs_checker<decltype(eh)>(type_spec, eh));
break;
case detail::type::double_type:
case detail::type::long_double_type:
detail::parse_float_type_spec(specs_, eh);
break;
case detail::type::cstring_type:
detail::handle_cstring_type_spec(
type_spec, detail::cstring_type_checker<decltype(eh)>(eh));
break;
case detail::type::string_type:
detail::check_string_type_spec(type_spec, eh);
break;
case detail::type::pointer_type:
detail::check_pointer_type_spec(type_spec, eh);
break;
case detail::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
}
return it;
}
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>(
specs_.width, specs_.width_ref, ctx);
fmt::detail::handle_dynamic_spec<fmt::detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
using af = arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(af(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val));
}
private:
fmt::detail::dynamic_format_specs<Char> specs_;
};
} // namespace detail
// https://fmt.dev/Text%20Formatting.html#format.functions
template<class... Args>
string format(string_view fmt, const Args&... args) {
return vformat(fmt, make_format_args(args...));
}
template<class... Args>
wstring format(wstring_view fmt, const Args&... args) {
return vformat(fmt, make_wformat_args(args...));
}
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::detail::buffer<char>& buf = mbuf;
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
detail::format_handler<af, char, format_context>
h(fmt::detail::buffer_appender<char>(buf), fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf);
}
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, make_format_args<context>(args...));
}
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, make_format_args<context>(args...));
}
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using af = detail::arg_formatter<Out, char>;
detail::format_handler<af, char, basic_format_context<Out, char>>
h(out, fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return h.context.out();
}
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
#define charT char
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t>;
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
#define charT wchar_t
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t> : detail::formatter<charT, charT> {};
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<std::basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<std::basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
template<> struct formatter<const wchar_t, char> {
formatter() = delete;
};
}
#endif // FMT_FORMAT_

View File

@@ -1,380 +1,126 @@
// Formatting library for C++ - formatting library implementation tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
/*
Formatting library implementation tests.
Copyright (c) 2012-2014, 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.
*/
#define FMT_NOEXCEPT
#undef FMT_SHARED
#include "test-assert.h"
// Include format.cc instead of format.h to test implementation-specific stuff.
#include "fmt/format.cc"
#include <algorithm>
#include <cstring>
// clang-format off
#include "test-assert.h"
// clang-format on
#include "fmt/format.h"
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
using fmt::detail::bigint;
using fmt::detail::fp;
using fmt::detail::max_value;
#undef min
#undef max
static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
TEST(bigint_test, construct) {
EXPECT_EQ("", fmt::format("{}", bigint()));
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
TEST(FormatTest, ArgConverter) {
using fmt::internal::Arg;
Arg arg = Arg();
arg.type = Arg::LONG_LONG;
arg.long_long_value = std::numeric_limits<fmt::LongLong>::max();
fmt::ArgConverter<fmt::LongLong>(arg, 'd').visit(arg);
EXPECT_EQ(Arg::LONG_LONG, arg.type);
}
TEST(bigint_test, compare) {
bigint n1(42);
bigint n2(42);
EXPECT_EQ(compare(n1, n2), 0);
n2 <<= 32;
EXPECT_LT(compare(n1, n2), 0);
bigint n3(43);
EXPECT_LT(compare(n1, n3), 0);
EXPECT_GT(compare(n3, n1), 0);
bigint n4(42 * 0x100000001);
EXPECT_LT(compare(n2, n4), 0);
EXPECT_GT(compare(n4, n2), 0);
TEST(FormatTest, FormatNegativeNaN) {
double nan = std::numeric_limits<double>::quiet_NaN();
if (fmt::internal::FPUtil::isnegative(-nan))
EXPECT_EQ("-nan", fmt::format("{}", -nan));
else
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
}
TEST(bigint_test, add_compare) {
EXPECT_LT(
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
bigint(0x300000010)),
0);
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000000)),
0);
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000001)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000002)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000003)),
0);
TEST(FormatTest, StrError) {
char *message = 0;
char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = 0, 0), "invalid buffer");
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer");
buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
// Use invalid error code to make sure that safe_strerror returns an error
// message in the buffer rather than a pointer to a static string.
int error_code = -1;
#else
int error_code = EDOM;
#endif
int result = fmt::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(0, result);
std::size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(get_system_error(error_code), message);
// safe_strerror never uses buffer on MinGW.
#ifndef __MINGW32__
result = fmt::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result);
result = fmt::safe_strerror(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message);
#endif
}
TEST(bigint_test, shift_left) {
bigint n(0x42);
n <<= 0;
EXPECT_EQ("42", fmt::format("{}", n));
n <<= 1;
EXPECT_EQ("84", fmt::format("{}", n));
n <<= 25;
EXPECT_EQ("108000000", fmt::format("{}", n));
}
TEST(bigint_test, multiply) {
bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
TEST(bigint_test, accumulator) {
fmt::detail::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(bigint_test, square) {
bigint n0(0);
n0.square();
EXPECT_EQ("0", fmt::format("{}", n0));
bigint n1(0x100);
n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1));
bigint n2(0xfffffffff);
n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
bigint n3(max_value<uint64_t>());
n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
bigint n4;
n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4));
}
TEST(bigint_test, divmod_assign_zero_divisor) {
bigint zero(0);
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
}
TEST(bigint_test, divmod_assign_self) {
bigint n(100);
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
}
TEST(bigint_test, divmod_assign_unaligned) {
// (42 << 340) / pow(10, 100):
bigint n1(42);
n1 <<= 340;
bigint n2;
n2.assign_pow10(100);
int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
fmt::format("{}", n1));
}
TEST(bigint_test, divmod_assign) {
// 100 / 10:
bigint n1(100);
int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1));
// pow(10, 100) / (42 << 320):
n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
fmt::format("{}", n1));
// 42 / 100:
bigint n2(42);
n1.assign_pow10(2);
result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0);
EXPECT_EQ("2a", fmt::format("{}", n2));
}
template <bool is_iec559> void run_double_tests() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <> void run_double_tests<true>() {
// Construct from double.
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
}
TEST(fp_test, double_tests) {
run_double_tests<std::numeric_limits<double>::is_iec559>();
}
TEST(fp_test, normalize) {
const auto v = fp(0xbeef, 42);
auto normalized = normalize(v);
EXPECT_EQ(0xbeef000000000000, normalized.f);
EXPECT_EQ(-6, normalized.e);
}
TEST(fp_test, multiply) {
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
EXPECT_EQ(v.f, 123u * 56u);
EXPECT_EQ(v.e, 4 + 7 + 64);
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
EXPECT_EQ(v.e, 4 + 8 + 64);
}
TEST(fp_test, get_cached_power) {
using limits = std::numeric_limits<double>;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
bigint exact, cache(fp.f);
if (dec_exp >= 0) {
exact.assign_pow10(dec_exp);
if (fp.e <= 0)
exact <<= -fp.e;
else
cache <<= fp.e;
exact.align(cache);
cache.align(exact);
auto exact_str = fmt::format("{}", exact);
auto cache_str = fmt::format("{}", cache);
EXPECT_EQ(exact_str.size(), cache_str.size());
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
int diff = cache_str[15] - exact_str[15];
if (diff == 1)
EXPECT_GT(exact_str[16], '8');
else
EXPECT_EQ(diff, 0);
} else {
cache.assign_pow10(-dec_exp);
cache *= fp.f + 1; // Inexact check.
exact.assign(1);
exact <<= -fp.e;
exact.align(cache);
auto exact_str = fmt::format("{}", exact);
auto cache_str = fmt::format("{}", cache);
EXPECT_EQ(exact_str.size(), cache_str.size());
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16));
}
}
}
TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>;
EXPECT_EQ(fmt::detail::const_check(float_info::max_k),
float_info::kappa - floor_log10_pow2(float_info::min_exponent -
float_info::significand_bits));
using double_info = fmt::detail::dragonbox::float_info<double>;
EXPECT_EQ(
fmt::detail::const_check(double_info::max_k),
double_info::kappa - floor_log10_pow2(double_info::min_exponent -
double_info::significand_bits));
}
TEST(fp_test, get_round_direction) {
using fmt::detail::get_round_direction;
using fmt::detail::round_direction;
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10));
for (size_t i = 41; i < 60; ++i)
EXPECT_EQ(round_direction::unknown, get_round_direction(100, i, 10));
uint64_t max = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
// Check that remainder + error doesn't overflow.
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 2));
// Check that 2 * (remainder + error) doesn't overflow.
EXPECT_EQ(round_direction::unknown,
get_round_direction(max, max / 2 + 1, max / 2));
// Check that remainder - error doesn't overflow.
EXPECT_EQ(round_direction::unknown, get_round_direction(100, 40, 41));
// Check that 2 * (remainder - error) doesn't overflow.
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
}
TEST(fp_test, fixed_handler) {
struct handler : fmt::detail::fixed_handler {
char buffer[10];
handler(int prec = 0) : fmt::detail::fixed_handler() {
buf = buffer;
precision = prec;
}
};
int exp = 0;
handler().on_digit('0', 100, 99, 0, exp, false);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
assertion_failure);
namespace digits = fmt::detail::digits;
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::error);
// Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
// Check that 2 * error doesn't overflow.
uint64_t max = max_value<uint64_t>();
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false),
digits::error);
}
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
fmt::memory_buffer buf;
format_float(0.42, -1, fmt::detail::float_specs(), buf);
}
TEST(format_impl_test, format_error_code) {
TEST(FormatTest, FormatErrorCode) {
std::string msg = "error 42", sep = ": ";
{
fmt::memory_buffer buffer;
format_to(fmt::appender(buffer), "garbage");
fmt::detail::format_error_code(buffer, 42, "test");
EXPECT_EQ("test: " + msg, to_string(buffer));
fmt::MemoryWriter w;
w << "garbage";
fmt::format_error_code(w, 42, "test");
EXPECT_EQ("test: " + msg, w.str());
}
{
fmt::memory_buffer buffer;
auto prefix =
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
fmt::detail::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer));
fmt::MemoryWriter w;
std::string prefix(
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
fmt::format_error_code(w, 42, prefix);
EXPECT_EQ(msg, w.str());
}
int codes[] = {42, -1};
for (size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
// Test maximum buffer size.
msg = fmt::format("error {}", codes[i]);
fmt::memory_buffer buffer;
auto prefix =
std::string(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
size_t size = fmt::inline_buffer_size;
EXPECT_EQ(size, buffer.size());
buffer.resize(0);
fmt::MemoryWriter w;
std::string prefix(
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
fmt::format_error_code(w, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, w.str());
std::size_t size = fmt::internal::INLINE_BUFFER_SIZE;
EXPECT_EQ(size, w.size());
w.clear();
// Test with a message that doesn't fit into the buffer.
prefix += 'x';
fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(msg, to_string(buffer));
fmt::format_error_code(w, codes[i], prefix);
EXPECT_EQ(msg, w.str());
}
}
TEST(format_impl_test, compute_width) {
EXPECT_EQ(4,
fmt::detail::compute_width(
fmt::basic_string_view<fmt::detail::char8_type>(
reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
}
// Tests fmt::detail::count_digits for integer type Int.
template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10;
EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
}
}
TEST(format_impl_test, count_digits) {
test_count_digits<uint32_t>();
test_count_digits<uint64_t>();
}
TEST(format_impl_test, write_fallback_uintptr) {
std::string s;
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
EXPECT_EQ(s, "0xface");
}
#ifdef _WIN32
# include <windows.h>
#endif
#ifdef _WIN32
TEST(format_impl_test, write_console_signature) {
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW;
(void)p;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
# ignore artifacts from the build.sh script
build-*/

View File

@@ -1,30 +0,0 @@
# Copyright (c) 2019, Paul Dreik
# License: see LICENSE.rst in the fmt root directory
# Link in the main function. Useful for reproducing, kcov, gdb, afl, valgrind.
# (Note that libFuzzer can also reproduce, just pass it the files.)
option(FMT_FUZZ_LINKMAIN "Enables the reproduce mode, instead of libFuzzer" On)
# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for
# the fuzz targets, otherwise the CMake configuration step fails.
set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
# Adds a binary for reproducing, i.e. no fuzzing, just enables replaying data
# through the fuzzers.
function(add_fuzzer source)
get_filename_component(basename ${source} NAME_WE)
set(name ${basename}-fuzzer)
add_executable(${name} ${source} fuzzer-common.h)
if (FMT_FUZZ_LINKMAIN)
target_sources(${name} PRIVATE main.cc)
endif ()
target_link_libraries(${name} PRIVATE fmt)
if (FMT_FUZZ_LDFLAGS)
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
endif ()
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
endfunction()
foreach (source chrono-duration.cc float.cc named-arg.cc one-arg.cc two-args.cc)
add_fuzzer(${source})
endforeach ()

View File

@@ -1,25 +0,0 @@
# Running the fuzzers locally
There is a [helper script](build.sh) to build the fuzzers, which has only been
tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on
Windows (using clang>=8) or on Mac, but the script will probably not work out of
the box.
Something along
```sh
mkdir build
cd build
export CXX=clang++
export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build .
```
should work to build the fuzzers for all platforms which clang supports.
Execute a fuzzer with for instance
```sh
cd build
export UBSAN_OPTIONS=halt_on_error=1
mkdir out_chrono
bin/fuzzer_chrono_duration out_chrono
```

View File

@@ -1,88 +0,0 @@
#!/bin/sh
#
# Creates fuzzer builds of various kinds
# - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works)
# - libFuzzer build (you will need clang)
# - afl build (you will need afl)
#
#
# Copyright (c) 2019 Paul Dreik
#
# For the license information refer to format.h.
set -e
me=$(basename $0)
root=$(readlink -f "$(dirname "$0")/../..")
echo $me: root=$root
here=$(pwd)
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
# For performance analysis of the fuzzers.
builddir=$here/build-fuzzers-perfanalysis
mkdir -p $builddir
cd $builddir
CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL -g" cmake \
$CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=On \
-DCMAKE_BUILD_TYPE=Release
cmake --build $builddir
# Builds the fuzzers as oss-fuzz does.
builddir=$here/build-fuzzers-ossfuzz
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build $builddir
# Builds fuzzers for local fuzzing with libfuzzer with asan+usan.
builddir=$here/build-fuzzers-libfuzzer
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build $builddir
# Builds a fast fuzzer for making coverage fast.
builddir=$here/build-fuzzers-fast
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" \
-DCMAKE_BUILD_TYPE=Release
cmake --build $builddir
# Builds fuzzers for local fuzzing with afl.
builddir=$here/build-fuzzers-afl
mkdir -p $builddir
cd $builddir
CXX="afl-g++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=address,undefined" \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=On
cmake --build $builddir
echo $me: all good

View File

@@ -1,135 +0,0 @@
// Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h.
#include <cstdint>
#include <fmt/chrono.h>
#include "fuzzer-common.h"
template <typename Period, typename Rep>
void invoke_inner(fmt::string_view format_str, Rep rep) {
auto value = std::chrono::duration<Rep, Period>(rep);
try {
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(format_str, value);
#else
fmt::memory_buffer buf;
fmt::format_to(buf, format_str, value);
#endif
} catch (std::exception&) {
}
}
// Rep is a duration's representation type.
template <typename Rep>
void invoke_outer(const uint8_t* data, size_t size, int period) {
// Always use a fixed location of the data.
static_assert(sizeof(Rep) <= fixed_size, "fixed size is too small");
if (size <= fixed_size + 1) return;
const Rep rep = assign_from_buf<Rep>(data);
data += fixed_size;
size -= fixed_size;
// data is already allocated separately in libFuzzer so reading past the end
// will most likely be detected anyway.
const auto format_str = fmt::string_view(as_chars(data), size);
// yocto, zepto, zetta and yotta are not handled.
switch (period) {
case 1:
invoke_inner<std::atto>(format_str, rep);
break;
case 2:
invoke_inner<std::femto>(format_str, rep);
break;
case 3:
invoke_inner<std::pico>(format_str, rep);
break;
case 4:
invoke_inner<std::nano>(format_str, rep);
break;
case 5:
invoke_inner<std::micro>(format_str, rep);
break;
case 6:
invoke_inner<std::milli>(format_str, rep);
break;
case 7:
invoke_inner<std::centi>(format_str, rep);
break;
case 8:
invoke_inner<std::deci>(format_str, rep);
break;
case 9:
invoke_inner<std::deca>(format_str, rep);
break;
case 10:
invoke_inner<std::kilo>(format_str, rep);
break;
case 11:
invoke_inner<std::mega>(format_str, rep);
break;
case 12:
invoke_inner<std::giga>(format_str, rep);
break;
case 13:
invoke_inner<std::tera>(format_str, rep);
break;
case 14:
invoke_inner<std::peta>(format_str, rep);
break;
case 15:
invoke_inner<std::exa>(format_str, rep);
break;
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size <= 4) return 0;
const auto representation = data[0];
const auto period = data[1];
data += 2;
size -= 2;
switch (representation) {
case 1:
invoke_outer<char>(data, size, period);
break;
case 2:
invoke_outer<signed char>(data, size, period);
break;
case 3:
invoke_outer<unsigned char>(data, size, period);
break;
case 4:
invoke_outer<short>(data, size, period);
break;
case 5:
invoke_outer<unsigned short>(data, size, period);
break;
case 6:
invoke_outer<int>(data, size, period);
break;
case 7:
invoke_outer<unsigned int>(data, size, period);
break;
case 8:
invoke_outer<long>(data, size, period);
break;
case 9:
invoke_outer<unsigned long>(data, size, period);
break;
case 10:
invoke_outer<float>(data, size, period);
break;
case 11:
invoke_outer<double>(data, size, period);
break;
case 12:
invoke_outer<long double>(data, size, period);
break;
}
return 0;
}

View File

@@ -1,39 +0,0 @@
// A fuzzer for floating-point formatter.
// For the license information refer to format.h.
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
#include <limits>
#include <fmt/format.h>
#include "fuzzer-common.h"
void check_round_trip(fmt::string_view format_str, double value) {
auto buffer = fmt::memory_buffer();
fmt::format_to(buffer, format_str, value);
if (std::isnan(value)) {
auto nan = std::signbit(value) ? "-nan" : "nan";
if (fmt::string_view(buffer.data(), buffer.size()) != nan)
throw std::runtime_error("round trip failure");
return;
}
buffer.push_back('\0');
char* ptr = nullptr;
if (std::strtod(buffer.data(), &ptr) != value)
throw std::runtime_error("round trip failure");
if (ptr + 1 != buffer.end())
throw std::runtime_error("unparsed output");
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size <= sizeof(double) || !std::numeric_limits<double>::is_iec559)
return 0;
check_round_trip("{}", assign_from_buf<double>(data));
// A larger than necessary precision is used to trigger the fallback
// formatter.
check_round_trip("{:.50g}", assign_from_buf<double>(data));
return 0;
}

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h.
#ifndef FUZZER_COMMON_H
#define FUZZER_COMMON_H
#include <cstdint> // std::uint8_t
#include <cstring> // memcpy
#include <vector>
#include <fmt/core.h>
// One can format to either a string, or a buffer. The latter is faster, but
// one may be interested in formatting to a string instead to verify it works
// as intended. To avoid a combinatoric explosion, select this at compile time
// instead of dynamically from the fuzz data.
#define FMT_FUZZ_FORMAT_TO_STRING 0
// If {fmt} is given a buffer that is separately allocated, chances that address
// sanitizer detects out of bound reads is much higher. However, it slows down
// the fuzzing.
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
// The size of the largest possible type in use.
// To let the the fuzzer mutation be efficient at cross pollinating between
// different types, use a fixed size format. The same bit pattern, interpreted
// as another type, is likely interesting.
constexpr auto fixed_size = 16;
// Casts data to a char pointer.
template <typename T> inline const char* as_chars(const T* data) {
return reinterpret_cast<const char*>(data);
}
// Casts data to a byte pointer.
template <typename T> inline const std::uint8_t* as_bytes(const T* data) {
return reinterpret_cast<const std::uint8_t*>(data);
}
// Blits bytes from data to form an (assumed trivially constructible) object
// of type Item.
template <class Item> inline Item assign_from_buf(const std::uint8_t* data) {
auto item = Item();
std::memcpy(&item, data, sizeof(Item));
return item;
}
// Reads a boolean value by looking at the first byte from data.
template <> inline bool assign_from_buf<bool>(const std::uint8_t* data) {
return *data != 0;
}
struct data_to_string {
#if FMT_FUZZ_SEPARATE_ALLOCATION
std::vector<char> buffer;
data_to_string(const uint8_t* data, size_t size, bool add_terminator = false)
: buffer(size + (add_terminator ? 1 : 0)) {
std::memcpy(buffer.data(), data, size);
}
fmt::string_view get() const { return {buffer.data(), buffer.size()}; }
#else
fmt::string_view sv;
data_to_string(const uint8_t* data, size_t size, bool = false)
: str(as_chars(data), size) {}
fmt::string_view get() const { return sv; }
#endif
const char* data() const { return get().data(); }
};
#endif // FUZZER_COMMON_H

View File

@@ -1,22 +0,0 @@
#include <cassert>
#include <fstream>
#include <vector>
#include "fuzzer-common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
int main(int argc, char** argv) {
for (int i = 1; i < argc; ++i) {
std::ifstream in(argv[i]);
assert(in);
in.seekg(0, std::ios_base::end);
const auto size = in.tellg();
assert(size >= 0);
in.seekg(0, std::ios_base::beg);
std::vector<char> buf(static_cast<size_t>(size));
in.read(buf.data(), size);
assert(in.gcount() == size);
LLVMFuzzerTestOneInput(as_bytes(buf.data()), buf.size());
}
}

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