Merge pull request #629 from lioncash/fmt
externals: Update fmt to 8.0.0
This commit is contained in:
commit
7946868af4
116 changed files with 44490 additions and 47157 deletions
12
externals/fmt/.github/pull_request_template.md
vendored
12
externals/fmt/.github/pull_request_template.md
vendored
|
@ -1,6 +1,6 @@
|
||||||
<!-- Please read the contribution guidelines before submitting a pull request. -->
|
<!--
|
||||||
<!-- By submitting this pull request, you agree that your contributions are licensed under the {fmt} license,
|
Please read the contribution guidelines before submitting a pull request:
|
||||||
and agree to future changes to the licensing. -->
|
https://github.com/fmtlib/fmt/blob/master/CONTRIBUTING.md.
|
||||||
<!-- If you're a first-time contributor, please acknowledge it by leaving the statement below. -->
|
By submitting this pull request, you agree that your contributions are licensed
|
||||||
|
under the {fmt} license, and agree to future changes to the licensing.
|
||||||
I agree that my contributions are licensed under the {fmt} license, and agree to future changes to the licensing.
|
-->
|
||||||
|
|
23
externals/fmt/.github/workflows/doc.yml
vendored
Normal file
23
externals/fmt/.github/workflows/doc.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
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 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
|
78
externals/fmt/.github/workflows/linux.yml
vendored
Normal file
78
externals/fmt/.github/workflows/linux.yml
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
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
|
37
externals/fmt/.github/workflows/macos.yml
vendored
Normal file
37
externals/fmt/.github/workflows/macos.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
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
|
60
externals/fmt/.github/workflows/windows.yml
vendored
Normal file
60
externals/fmt/.github/workflows/windows.yml
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
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
|
1
externals/fmt/.gitignore
vendored
1
externals/fmt/.gitignore
vendored
|
@ -9,6 +9,7 @@ gradle/
|
||||||
gradlew*
|
gradlew*
|
||||||
local.properties
|
local.properties
|
||||||
build/
|
build/
|
||||||
|
support/.cxx
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
/_CPack_Packages
|
/_CPack_Packages
|
||||||
|
|
101
externals/fmt/.travis.yml
vendored
101
externals/fmt/.travis.yml
vendored
|
@ -1,101 +0,0 @@
|
||||||
language: cpp
|
|
||||||
dist: trusty
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
os: linux
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: 1
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- secure: |-
|
|
||||||
a1eovNn4uol9won7ghr67eD3/59oeESN+G9bWE+ecI1V6yRseG9whniGhIpC/YfMW/Qz5I
|
|
||||||
5sxSmFjaw9bxCISNwUIrL1O5x2AmRYTnFcXk4dFsUvlZg+WeF/aKyBYCNRM8C2ndbBmtAO
|
|
||||||
o1F2EwFbiso0EmtzhAPs19ujiVxkLn4=
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Documentation
|
|
||||||
- env: BUILD=Doc
|
|
||||||
sudo: required
|
|
||||||
# g++ 6 on Linux with C++14
|
|
||||||
- env: COMPILER=g++-6 BUILD=Debug STANDARD=14
|
|
||||||
compiler: gcc
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-6
|
|
||||||
- env: COMPILER=g++-6 BUILD=Release STANDARD=14
|
|
||||||
compiler: gcc
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-6
|
|
||||||
# g++ 8 on Linux with C++17
|
|
||||||
- env: COMPILER=g++-8 BUILD=Debug STANDARD=17
|
|
||||||
compiler: gcc
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-8
|
|
||||||
- env: COMPILER=g++-8 BUILD=Release STANDARD=17
|
|
||||||
compiler: gcc
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-8
|
|
||||||
|
|
||||||
# Apple clang on OS X with C++14
|
|
||||||
- env: BUILD=Debug STANDARD=14
|
|
||||||
compiler: clang
|
|
||||||
os: osx
|
|
||||||
- env: BUILD=Release STANDARD=14
|
|
||||||
compiler: clang
|
|
||||||
os: osx
|
|
||||||
# clang 6.0 on Linux with C++14 (builds the fuzzers as well)
|
|
||||||
- env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14 ENABLE_FUZZING=1
|
|
||||||
compiler: clang
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
packages:
|
|
||||||
- clang-6.0
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
- llvm-toolchain-trusty
|
|
||||||
- llvm-toolchain-trusty-6.0
|
|
||||||
# clang 4.0 on Linux with C++14
|
|
||||||
- env: COMPILER=clang++-4.0 BUILD=Debug STANDARD=11
|
|
||||||
compiler: clang
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
packages:
|
|
||||||
- clang-4.0
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
- llvm-toolchain-trusty
|
|
||||||
- llvm-toolchain-trusty-4.0
|
|
||||||
# g++ 4.8 on Linux with C++11
|
|
||||||
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
|
|
||||||
compiler: gcc
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi
|
|
||||||
- if [[ "${BUILD}" != "Doc" ]]; then ${CXX} --version; fi
|
|
||||||
|
|
||||||
script:
|
|
||||||
- support/travis-build.py
|
|
141
externals/fmt/CMakeLists.txt
vendored
141
externals/fmt/CMakeLists.txt
vendored
|
@ -1,18 +1,18 @@
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
cmake_minimum_required(VERSION 3.1...3.18)
|
||||||
|
|
||||||
# Use newer policies if available, up to most recent tested version of CMake.
|
# Fallback for using newer policies on CMake <3.12.
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.11)
|
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.11)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||||
# or if it is the master project.
|
# or if it is the master project.
|
||||||
set(MASTER_PROJECT OFF)
|
if (NOT DEFINED FMT_MASTER_PROJECT)
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
set(FMT_MASTER_PROJECT OFF)
|
||||||
set(MASTER_PROJECT ON)
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
set(FMT_MASTER_PROJECT ON)
|
||||||
|
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Joins arguments and places the results in ${result_var}.
|
# Joins arguments and places the results in ${result_var}.
|
||||||
|
@ -24,6 +24,17 @@ function(join result_var)
|
||||||
set(${result_var} "${result}" PARENT_SCOPE)
|
set(${result_var} "${result}" PARENT_SCOPE)
|
||||||
endfunction()
|
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)
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
# Sets a cache variable with a docstring joined from multiple arguments:
|
# Sets a cache variable with a docstring joined from multiple arguments:
|
||||||
|
@ -46,7 +57,7 @@ endfunction()
|
||||||
# Set the default CMAKE_BUILD_TYPE to Release.
|
# Set the default CMAKE_BUILD_TYPE to Release.
|
||||||
# This should be done before the project command since the latter can set
|
# This should be done before the project command since the latter can set
|
||||||
# CMAKE_BUILD_TYPE itself (it does so for nmake).
|
# CMAKE_BUILD_TYPE itself (it does so for nmake).
|
||||||
if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
|
if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
|
||||||
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
|
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||||
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
||||||
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
||||||
|
@ -55,20 +66,36 @@ endif ()
|
||||||
project(FMT CXX)
|
project(FMT CXX)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
|
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
|
||||||
"Installation directory for include files, a relative path "
|
"Installation directory for include files, a relative path that "
|
||||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
|
||||||
|
|
||||||
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
||||||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
||||||
OFF)
|
OFF)
|
||||||
|
|
||||||
# Options that control generation of various targets.
|
# Options that control generation of various targets.
|
||||||
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
|
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
||||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
||||||
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
||||||
|
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
|
||||||
|
|
||||||
|
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 ()
|
||||||
|
|
||||||
# Get version from core.h
|
# Get version from core.h
|
||||||
file(READ include/fmt/core.h core_h)
|
file(READ include/fmt/core.h core_h)
|
||||||
|
@ -104,24 +131,36 @@ if (${index} GREATER -1)
|
||||||
endif ()
|
endif ()
|
||||||
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
|
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)
|
||||||
|
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")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
||||||
-Wold-style-cast -Wundef
|
-Wold-style-cast -Wundef
|
||||||
-Wredundant-decls -Wwrite-strings -Wpointer-arith
|
-Wredundant-decls -Wwrite-strings -Wpointer-arith
|
||||||
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
||||||
-Wcast-align -Wnon-virtual-dtor
|
-Wcast-align
|
||||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||||
-Winvalid-pch -Woverloaded-virtual
|
-Winvalid-pch -Woverloaded-virtual
|
||||||
-Wconversion -Wswitch-enum
|
-Wconversion -Wswitch-enum -Wundef
|
||||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
|
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||||
-Wno-dangling-else -Wno-unused-local-typedefs)
|
-Wno-dangling-else -Wno-unused-local-typedefs)
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
||||||
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
||||||
-Wvector-operation-performance -Wsized-deallocation)
|
-Wvector-operation-performance -Wsized-deallocation -Wshadow)
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
||||||
|
@ -131,8 +170,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
|
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
|
||||||
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
|
-Wdeprecated -Wweak-vtables -Wshadow
|
||||||
|
-Wno-gnu-zero-variadic-macro-arguments)
|
||||||
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
||||||
if (HAS_NULLPTR_WARNING)
|
if (HAS_NULLPTR_WARNING)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||||
|
@ -146,7 +186,7 @@ if (MSVC)
|
||||||
set(WERROR_FLAG /WX)
|
set(WERROR_FLAG /WX)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||||
# If Microsoft SDK is installed create script run-msbuild.bat that
|
# If Microsoft SDK is installed create script run-msbuild.bat that
|
||||||
# calls SetEnv.cmd to set up build environment and runs msbuild.
|
# calls SetEnv.cmd to set up build environment and runs msbuild.
|
||||||
# It is useful when building Visual Studio projects with the SDK
|
# It is useful when building Visual Studio projects with the SDK
|
||||||
|
@ -185,9 +225,12 @@ function(add_headers VAR)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Define the fmt library, its includes and the needed defines.
|
# Define the fmt library, its includes and the needed defines.
|
||||||
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
|
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
|
||||||
locale.h os.h ostream.h posix.h printf.h ranges.h)
|
format-inl.h locale.h os.h ostream.h printf.h ranges.h
|
||||||
if (FMT_OS)
|
xchar.h)
|
||||||
|
if (FMT_MODULE)
|
||||||
|
set(FMT_SOURCES src/fmt.cc)
|
||||||
|
elseif (FMT_OS)
|
||||||
set(FMT_SOURCES src/format.cc src/os.cc)
|
set(FMT_SOURCES src/format.cc src/os.cc)
|
||||||
else()
|
else()
|
||||||
set(FMT_SOURCES src/format.cc)
|
set(FMT_SOURCES src/format.cc)
|
||||||
|
@ -201,7 +244,10 @@ if (HAVE_STRTOD_L)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
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 ()
|
endif ()
|
||||||
|
|
||||||
if (FMT_WERROR)
|
if (FMT_WERROR)
|
||||||
|
@ -210,6 +256,9 @@ endif ()
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC)
|
||||||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
endif ()
|
endif ()
|
||||||
|
if (FMT_MODULE)
|
||||||
|
enable_module(fmt)
|
||||||
|
endif ()
|
||||||
|
|
||||||
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||||
|
|
||||||
|
@ -231,7 +280,8 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (BUILD_SHARED_LIBS)
|
if (BUILD_SHARED_LIBS)
|
||||||
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND NOT EMSCRIPTEN)
|
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND
|
||||||
|
NOT EMSCRIPTEN)
|
||||||
# Fix rpmlint warning:
|
# Fix rpmlint warning:
|
||||||
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
|
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
|
||||||
target_link_libraries(fmt -Wl,--as-needed)
|
target_link_libraries(fmt -Wl,--as-needed)
|
||||||
|
@ -256,20 +306,22 @@ target_include_directories(fmt-header-only INTERFACE
|
||||||
if (FMT_INSTALL)
|
if (FMT_INSTALL)
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||||
"Installation directory for cmake files, a relative path "
|
"Installation directory for cmake files, a relative path that "
|
||||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
|
||||||
|
"path.")
|
||||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
||||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
||||||
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
||||||
set(targets_export_name fmt-targets)
|
set(targets_export_name fmt-targets)
|
||||||
|
|
||||||
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||||
"Installation directory for libraries, a relative path "
|
"Installation directory for libraries, a relative path that "
|
||||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
|
||||||
|
|
||||||
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
||||||
"Installation directory for pkgconfig (.pc) files, a relative path "
|
"Installation directory for pkgconfig (.pc) files, a relative "
|
||||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
|
||||||
|
"absolute path.")
|
||||||
|
|
||||||
# Generate the version, config and target files into the build directory.
|
# Generate the version, config and target files into the build directory.
|
||||||
write_basic_package_version_file(
|
write_basic_package_version_file(
|
||||||
|
@ -290,6 +342,13 @@ if (FMT_INSTALL)
|
||||||
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
|
||||||
set(INSTALL_TARGETS fmt fmt-header-only)
|
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
|
# Use a namespace because CMake provides better diagnostics for namespaced
|
||||||
# imported targets.
|
# imported targets.
|
||||||
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
||||||
|
@ -302,12 +361,6 @@ if (FMT_INSTALL)
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
||||||
NAMESPACE fmt::)
|
NAMESPACE fmt::)
|
||||||
|
|
||||||
# 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})
|
|
||||||
|
|
||||||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
||||||
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
||||||
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
|
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
|
||||||
|
@ -326,11 +379,17 @@ endif ()
|
||||||
# Control fuzzing independent of the unit tests.
|
# Control fuzzing independent of the unit tests.
|
||||||
if (FMT_FUZZ)
|
if (FMT_FUZZ)
|
||||||
add_subdirectory(test/fuzzing)
|
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)
|
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
||||||
if (MASTER_PROJECT AND EXISTS ${gitignore})
|
if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
|
||||||
# Get the list of ignored files from .gitignore.
|
# Get the list of ignored files from .gitignore.
|
||||||
file (STRINGS ${gitignore} lines)
|
file (STRINGS ${gitignore} lines)
|
||||||
list(REMOVE_ITEM lines /doc/html)
|
list(REMOVE_ITEM lines /doc/html)
|
||||||
|
|
3
externals/fmt/CONTRIBUTING.md
vendored
3
externals/fmt/CONTRIBUTING.md
vendored
|
@ -14,4 +14,7 @@ exceptions:
|
||||||
* snake_case should be used instead of UpperCamelCase for function and type
|
* snake_case should be used instead of UpperCamelCase for function and type
|
||||||
names
|
names
|
||||||
|
|
||||||
|
All documentation must adhere to the [Google Developer Documentation Style
|
||||||
|
Guide](https://developers.google.com/style).
|
||||||
|
|
||||||
Thanks for contributing!
|
Thanks for contributing!
|
||||||
|
|
1090
externals/fmt/ChangeLog.rst
vendored
1090
externals/fmt/ChangeLog.rst
vendored
File diff suppressed because it is too large
Load diff
298
externals/fmt/README.rst
vendored
298
externals/fmt/README.rst
vendored
|
@ -1,54 +1,69 @@
|
||||||
{fmt}
|
{fmt}
|
||||||
=====
|
=====
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
|
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
|
||||||
:target: https://travis-ci.org/fmtlib/fmt
|
: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://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
||||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||||
|
|
||||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
|
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||||
:alt: fmt is continuously fuzzed att oss-fuzz
|
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||||
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
|
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
|
||||||
Summary&q=proj%3Dlibfmt&can=1
|
Summary&q=proj%3Dfmt&can=1
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
||||||
:alt: Ask questions at StackOverflow with the tag fmt
|
:alt: Ask questions at StackOverflow with the tag fmt
|
||||||
:target: https://stackoverflow.com/questions/tagged/fmt
|
:target: https://stackoverflow.com/questions/tagged/fmt
|
||||||
|
|
||||||
**{fmt}** is an open-source formatting library for C++.
|
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||||
It can be used as a safe and fast alternative to (s)printf and iostreams.
|
alternative to C stdio and C++ iostreams.
|
||||||
|
|
||||||
`Documentation <https://fmt.dev/latest/>`__
|
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
|
Q&A: ask questions on `StackOverflow with the tag fmt
|
||||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||||
|
|
||||||
|
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* Simple `format API <https://fmt.dev/dev/api.html>`_ with positional arguments
|
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
|
||||||
for localization
|
for localization
|
||||||
* Implementation of `C++20 std::format
|
* Implementation of `C++20 std::format
|
||||||
<https://en.cppreference.com/w/cpp/utility/format>`__
|
<https://en.cppreference.com/w/cpp/utility/format>`__
|
||||||
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
|
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
|
||||||
of Python's
|
|
||||||
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
`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
|
||||||
* Safe `printf implementation
|
* Safe `printf implementation
|
||||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
|
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
|
||||||
the POSIX extension for positional arguments
|
extension for positional arguments
|
||||||
* Extensibility: support for user-defined types
|
* 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
|
* High performance: faster than common standard library implementations of
|
||||||
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_,
|
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
|
||||||
iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ and
|
and `Converting a hundred million integers to strings per second
|
||||||
`Converting a hundred million integers to strings per second
|
|
||||||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||||
* Small code size both in terms of source code (the minimum configuration
|
* Small code size both in terms of source code with the minimum configuration
|
||||||
consists of just three header files, ``core.h``, ``format.h`` and
|
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
|
||||||
``format-inl.h``) and compiled code. See `Compile time and code bloat`_
|
and compiled code; see `Compile time and code bloat`_
|
||||||
* Reliability: the library has an extensive set of `unit tests
|
* Reliability: the library has an extensive set of `tests
|
||||||
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed
|
<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
|
* Safety: the library is fully type safe, errors in format strings can be
|
||||||
reported at compile time, automatic memory management prevents buffer overflow
|
reported at compile time, automatic memory management prevents buffer overflow
|
||||||
errors
|
errors
|
||||||
|
@ -57,18 +72,17 @@ Features
|
||||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
||||||
consistent output across platforms and support for older compilers
|
consistent output across platforms and support for older compilers
|
||||||
* Clean warning-free codebase even on high warning levels
|
* Clean warning-free codebase even on high warning levels such as
|
||||||
(``-Wall -Wextra -pedantic``)
|
``-Wall -Wextra -pedantic``
|
||||||
* Locale-independence by default
|
* Locale-independence by default
|
||||||
* Support for wide strings
|
|
||||||
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
|
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
|
||||||
|
|
||||||
See the `documentation <https://fmt.dev/latest/>`_ for more details.
|
See the `documentation <https://fmt.dev>`_ for more details.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Print ``Hello, world!`` to ``stdout``:
|
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
|
@ -78,100 +92,95 @@ Print ``Hello, world!`` to ``stdout``:
|
||||||
fmt::print("Hello, world!\n");
|
fmt::print("Hello, world!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Format a string:
|
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
std::string s = fmt::format("The answer is {}.", 42);
|
std::string s = fmt::format("The answer is {}.", 42);
|
||||||
// s == "The answer is 42."
|
// s == "The answer is 42."
|
||||||
|
|
||||||
Format a string using positional arguments:
|
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
||||||
// s == "I'd rather be happy than right."
|
// s == "I'd rather be happy than right."
|
||||||
|
|
||||||
Print a chrono duration:
|
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
using namespace std::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
fmt::print("Elapsed time: {}", 42ms);
|
fmt::print("Default format: {} {}\n", 42s, 100ms);
|
||||||
|
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
|
||||||
}
|
}
|
||||||
|
|
||||||
prints "Elapsed time: 42ms".
|
Output::
|
||||||
|
|
||||||
Check a format string at compile time:
|
Default format: 42s 100ms
|
||||||
|
strftime-like format: 03:15:30
|
||||||
|
|
||||||
|
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
// test.cc
|
#include <vector>
|
||||||
#include <fmt/format.h>
|
#include <fmt/ranges.h>
|
||||||
std::string s = format(FMT_STRING("{:d}"), "hello");
|
|
||||||
|
|
||||||
gives a compile-time error because ``d`` is an invalid format specifier for a
|
int main() {
|
||||||
string.
|
std::vector<int> v = {1, 2, 3};
|
||||||
|
fmt::print("{}\n", v);
|
||||||
Use {fmt} as a safe portable replacement for ``itoa``
|
|
||||||
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
|
|
||||||
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
|
|
||||||
// access the string with to_string(buf) or buf.data()
|
|
||||||
|
|
||||||
Format objects of user-defined types via a simple `extension API
|
|
||||||
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
struct date {
|
|
||||||
int year, month, day;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<date> {
|
|
||||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const date& d, FormatContext& ctx) {
|
|
||||||
return format_to(ctx.out(), "{}-{}-{}", 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"
|
|
||||||
|
|
||||||
Create your own functions similar to `format
|
|
||||||
<https://fmt.dev/latest/api.html#format>`_ and
|
|
||||||
`print <https://fmt.dev/latest/api.html#print>`_
|
|
||||||
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
// Prints formatted error message.
|
|
||||||
void vreport_error(const char* format, fmt::format_args args) {
|
|
||||||
fmt::print("Error: ");
|
|
||||||
fmt::vprint(format, args);
|
|
||||||
}
|
|
||||||
template <typename... Args>
|
|
||||||
void report_error(const char* format, const Args & ... args) {
|
|
||||||
vreport_error(format, fmt::make_format_args(args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
report_error("file not found: {}", path);
|
Output::
|
||||||
|
|
||||||
Note that ``vreport_error`` is not parameterized on argument types which can
|
[1, 2, 3]
|
||||||
improve compile times and reduce code size compared to a fully parameterized
|
|
||||||
version.
|
**Check a format string at compile time**
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
Benchmarks
|
Benchmarks
|
||||||
----------
|
----------
|
||||||
|
@ -198,12 +207,14 @@ or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||||
further details refer to the `source
|
further details refer to the `source
|
||||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
||||||
|
|
||||||
{fmt} is up to 10x faster than ``std::ostringstream`` and ``sprintf`` on
|
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||||
and faster than `double-conversion <https://github.com/google/double-conversion>`_:
|
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/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
|
.. image:: https://user-images.githubusercontent.com/576385/
|
||||||
:target: https://fmt.dev/unknown_mac64_clang10.0.html
|
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
|
||||||
|
:target: https://fmt.dev/unknown_mac64_clang12.0.html
|
||||||
|
|
||||||
Compile time and code bloat
|
Compile time and code bloat
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -278,34 +289,46 @@ Then you can run the speed test::
|
||||||
or the bloat test::
|
or the bloat test::
|
||||||
|
|
||||||
$ make 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
|
Projects using this library
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform
|
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
|
||||||
real-time strategy game
|
real-time strategy game
|
||||||
|
|
||||||
|
* `2GIS <https://2gis.ru/>`_: free business listings with a city map
|
||||||
|
|
||||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||||
An open-source library for mathematical programming
|
an open-source library for mathematical programming
|
||||||
|
|
||||||
* `Aseprite <https://github.com/aseprite/aseprite>`_:
|
* `Aseprite <https://github.com/aseprite/aseprite>`_:
|
||||||
Animated sprite editor & pixel art tool
|
animated sprite editor & pixel art tool
|
||||||
|
|
||||||
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
|
* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft
|
||||||
operations suite
|
operations suite
|
||||||
|
|
||||||
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
|
* `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
|
* `Ceph <https://ceph.com/>`_: a scalable distributed storage system
|
||||||
|
|
||||||
* `ccache <https://ccache.dev/>`_: A compiler cache
|
* `ccache <https://ccache.dev/>`_: a compiler cache
|
||||||
|
|
||||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database management system
|
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
||||||
|
management system
|
||||||
|
|
||||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||||
vehicle
|
vehicle
|
||||||
|
|
||||||
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
|
||||||
for nonlinear dynamical systems (MIT)
|
for nonlinear dynamical systems (MIT)
|
||||||
|
|
||||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||||
|
@ -313,71 +336,82 @@ Projects using this library
|
||||||
|
|
||||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
* `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
|
* `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>`_:
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
Player vs Player Gaming Network with tweaks
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
* `KBEngine <https://kbengine.org/>`_: An open-source MMOG server engine
|
* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server
|
||||||
|
engine
|
||||||
|
|
||||||
* `Keypirinha <https://keypirinha.com/>`_: A semantic launcher for Windows
|
* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows
|
||||||
|
|
||||||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
|
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software
|
||||||
|
|
||||||
* `Knuth <https://kth.cash/>`_: High-performance Bitcoin full-node
|
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
|
||||||
|
|
||||||
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
||||||
Research programming language for concurrent ownership
|
research programming language for concurrent ownership
|
||||||
|
|
||||||
* `MongoDB <https://mongodb.com/>`_: Distributed document database
|
* `MongoDB <https://mongodb.com/>`_: distributed document database
|
||||||
|
|
||||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to
|
||||||
generate randomized datasets
|
generate randomized datasets
|
||||||
|
|
||||||
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
|
* `OpenSpace <https://openspaceproject.com/>`_: an open-source
|
||||||
astrovisualization framework
|
astrovisualization framework
|
||||||
|
|
||||||
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
||||||
An MMO server, compatible with most Ultima Online clients
|
an MMO server, compatible with most Ultima Online clients
|
||||||
|
|
||||||
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
|
* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine
|
||||||
learning library
|
learning library
|
||||||
|
|
||||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
|
||||||
associative database
|
associative database
|
||||||
|
|
||||||
|
* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library
|
||||||
|
|
||||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
* `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
|
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster
|
||||||
proxy
|
proxy
|
||||||
|
|
||||||
* `redpanda <https://vectorized.io/redpanda>`_: A 10x faster Kafka® replacement
|
* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement
|
||||||
for mission critical systems written in C++
|
for mission critical systems written in C++
|
||||||
|
|
||||||
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client
|
||||||
library
|
library
|
||||||
|
|
||||||
* `Salesforce Analytics Cloud
|
* `Salesforce Analytics Cloud
|
||||||
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||||
Business intelligence software
|
business intelligence software
|
||||||
|
|
||||||
* `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
|
* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store
|
||||||
that can handle 1 million transactions per second on a single server
|
that can handle 1 million transactions per second on a single server
|
||||||
|
|
||||||
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
|
* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++
|
||||||
framework for high-performance server applications on modern hardware
|
framework for high-performance server applications on modern hardware
|
||||||
|
|
||||||
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
|
* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library
|
||||||
|
|
||||||
* `Stellar <https://www.stellar.org/>`_: Financial platform
|
* `Stellar <https://www.stellar.org/>`_: financial platform
|
||||||
|
|
||||||
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
|
* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator
|
||||||
|
|
||||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
|
||||||
MMORPG framework
|
MMORPG framework
|
||||||
|
|
||||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
|
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
|
||||||
Terminal
|
terminal
|
||||||
|
|
||||||
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
||||||
|
|
||||||
|
@ -435,7 +469,7 @@ Boost Format
|
||||||
|
|
||||||
This is a very powerful library which supports both ``printf``-like format
|
This is a very powerful library which supports both ``printf``-like format
|
||||||
strings and positional arguments. Its main drawback is performance. According to
|
strings and positional arguments. Its main drawback is performance. According to
|
||||||
various benchmarks it is much slower than other methods considered here. Boost
|
various, benchmarks it is much slower than other methods considered here. Boost
|
||||||
Format also has excessive build times and severe code bloat issues (see
|
Format also has excessive build times and severe code bloat issues (see
|
||||||
`Benchmarks`_).
|
`Benchmarks`_).
|
||||||
|
|
||||||
|
|
8
externals/fmt/doc/CMakeLists.txt
vendored
8
externals/fmt/doc/CMakeLists.txt
vendored
|
@ -4,10 +4,14 @@ if (NOT DOXYGEN)
|
||||||
return ()
|
return ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
find_package(PythonInterp QUIET REQUIRED)
|
||||||
|
|
||||||
add_custom_target(doc
|
add_custom_target(doc
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
|
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)
|
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
||||||
DESTINATION share/doc/fmt OPTIONAL
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL
|
||||||
PATTERN ".doctrees" EXCLUDE)
|
PATTERN ".doctrees" EXCLUDE)
|
||||||
|
|
289
externals/fmt/doc/api.rst
vendored
289
externals/fmt/doc/api.rst
vendored
|
@ -6,17 +6,18 @@ API Reference
|
||||||
|
|
||||||
The {fmt} library API consists of the following parts:
|
The {fmt} library API consists of the following parts:
|
||||||
|
|
||||||
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling
|
* :ref:`fmt/core.h <core-api>`: the core API providing main formatting functions
|
||||||
facilities and a lightweight subset of formatting functions
|
for ``char``/UTF-8 with compile-time checks and minimal dependencies
|
||||||
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
|
* :ref:`fmt/format.h <format-api>`: the full format API providing additional
|
||||||
format string checks, wide string, output iterator and user-defined type
|
formatting functions and locale support
|
||||||
support
|
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
|
||||||
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
|
|
||||||
and tuples
|
|
||||||
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
||||||
* :ref:`fmt/compile.h <compile-api>`: format string compilation
|
* :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/ostream.h <ostream-api>`: ``std::ostream`` support
|
||||||
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
|
* :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
|
All functions and types provided by the library reside in namespace ``fmt`` and
|
||||||
macros have prefix ``FMT_``.
|
macros have prefix ``FMT_``.
|
||||||
|
@ -26,34 +27,56 @@ macros have prefix ``FMT_``.
|
||||||
Core API
|
Core API
|
||||||
========
|
========
|
||||||
|
|
||||||
``fmt/core.h`` defines the core API which provides argument handling facilities
|
``fmt/core.h`` defines the core API which provides main formatting functions for
|
||||||
and a lightweight subset of formatting functions. In the header-only mode
|
``char``/UTF-8 with compile-time checks. It has minimal include dependencies for
|
||||||
include ``fmt/format.h`` instead of ``fmt/core.h``.
|
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>`
|
The following functions use :ref:`format string syntax <syntax>`
|
||||||
similar to that of Python's `str.format
|
similar to that of Python's `str.format
|
||||||
<http://docs.python.org/3/library/stdtypes.html#str.format>`_.
|
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
|
||||||
They take *format_str* and *args* as arguments.
|
They take *fmt* and *args* as arguments.
|
||||||
|
|
||||||
*format_str* is a format string that contains literal text and replacement
|
*fmt* is a format string that contains literal text and replacement
|
||||||
fields surrounded by braces ``{}``. The fields are replaced with formatted
|
fields surrounded by braces ``{}``. The fields are replaced with formatted
|
||||||
arguments in the resulting string. A function taking *format_str* doesn't
|
arguments in the resulting string. A function taking *fmt* doesn't
|
||||||
participate in an overload resolution if the latter is not a string.
|
participate in an overload resolution if the latter is not a string.
|
||||||
|
|
||||||
*args* is an argument list representing objects to be formatted.
|
*args* is an argument list representing objects to be formatted.
|
||||||
|
|
||||||
.. _format:
|
.. _format:
|
||||||
|
|
||||||
.. doxygenfunction:: format(const S&, Args&&...)
|
.. doxygenfunction:: format(format_string<T...> fmt, T&&... args) -> std::string
|
||||||
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
|
.. 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:
|
.. _print:
|
||||||
|
|
||||||
.. doxygenfunction:: print(const S&, Args&&...)
|
.. doxygenfunction:: fmt::print(format_string<T...> fmt, T&&... args)
|
||||||
.. doxygenfunction:: vprint(string_view, format_args)
|
.. doxygenfunction:: vprint(string_view fmt, format_args args)
|
||||||
|
|
||||||
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
|
.. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
|
||||||
.. doxygenfunction:: vprint(std::FILE *, string_view, format_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
|
Named Arguments
|
||||||
---------------
|
---------------
|
||||||
|
@ -65,6 +88,35 @@ Named arguments are not supported in compile-time checks at the moment.
|
||||||
Argument Lists
|
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&...)
|
.. doxygenfunction:: fmt::make_format_args(const Args&...)
|
||||||
|
|
||||||
.. doxygenclass:: fmt::format_arg_store
|
.. doxygenclass:: fmt::format_arg_store
|
||||||
|
@ -76,11 +128,16 @@ Argument Lists
|
||||||
.. doxygenclass:: fmt::basic_format_args
|
.. doxygenclass:: fmt::basic_format_args
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. doxygenstruct:: fmt::format_args
|
.. doxygentypedef:: fmt::format_args
|
||||||
|
|
||||||
.. doxygenclass:: fmt::basic_format_arg
|
.. doxygenclass:: fmt::basic_format_arg
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::basic_format_context
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. doxygentypedef:: fmt::format_context
|
||||||
|
|
||||||
Compatibility
|
Compatibility
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -88,12 +145,11 @@ Compatibility
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. doxygentypedef:: fmt::string_view
|
.. doxygentypedef:: fmt::string_view
|
||||||
.. doxygentypedef:: fmt::wstring_view
|
|
||||||
|
|
||||||
Locale
|
Locale
|
||||||
------
|
------
|
||||||
|
|
||||||
All formatting is locale-independent by default. Use the ``'n'`` format
|
All formatting is locale-independent by default. Use the ``'L'`` format
|
||||||
specifier to insert the appropriate number separator characters from the
|
specifier to insert the appropriate number separator characters from the
|
||||||
locale::
|
locale::
|
||||||
|
|
||||||
|
@ -108,17 +164,10 @@ locale::
|
||||||
Format API
|
Format API
|
||||||
==========
|
==========
|
||||||
|
|
||||||
``fmt/format.h`` defines the full format API providing compile-time format
|
``fmt/format.h`` defines the full format API providing additional formatting
|
||||||
string checks, wide string, output iterator and user-defined type support.
|
functions and locale support.
|
||||||
|
|
||||||
Compile-time Format String Checks
|
.. _udt:
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Compile-time checks are supported for built-in and string types as well as
|
|
||||||
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
|
|
||||||
specializations.
|
|
||||||
|
|
||||||
.. doxygendefine:: FMT_STRING
|
|
||||||
|
|
||||||
Formatting User-defined Types
|
Formatting User-defined Types
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
@ -130,14 +179,12 @@ template and implement ``parse`` and ``format`` methods::
|
||||||
|
|
||||||
struct point { double x, y; };
|
struct point { double x, y; };
|
||||||
|
|
||||||
template <>
|
template <> struct fmt::formatter<point> {
|
||||||
struct fmt::formatter<point> {
|
|
||||||
// Presentation format: 'f' - fixed, 'e' - exponential.
|
// Presentation format: 'f' - fixed, 'e' - exponential.
|
||||||
char presentation = 'f';
|
char presentation = 'f';
|
||||||
|
|
||||||
// Parses format specifications of the form ['f' | 'e'].
|
// Parses format specifications of the form ['f' | 'e'].
|
||||||
constexpr auto parse(format_parse_context& ctx) {
|
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||||
// auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) // c++11
|
|
||||||
// [ctx.begin(), ctx.end()) is a character range that contains a part of
|
// [ctx.begin(), ctx.end()) is a character range that contains a part of
|
||||||
// the format string starting from the format specifications to be parsed,
|
// the format string starting from the format specifications to be parsed,
|
||||||
// e.g. in
|
// e.g. in
|
||||||
|
@ -164,8 +211,7 @@ template and implement ``parse`` and ``format`` methods::
|
||||||
// Formats the point p using the parsed format specification (presentation)
|
// Formats the point p using the parsed format specification (presentation)
|
||||||
// stored in this formatter.
|
// stored in this formatter.
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const point& p, FormatContext& ctx) {
|
auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
// auto format(const point &p, FormatContext &ctx) -> decltype(ctx.out()) // c++11
|
|
||||||
// ctx.out() is an output iterator to write to.
|
// ctx.out() is an output iterator to write to.
|
||||||
return format_to(
|
return format_to(
|
||||||
ctx.out(),
|
ctx.out(),
|
||||||
|
@ -237,44 +283,36 @@ You can also write a formatter for a hierarchy of classes::
|
||||||
fmt::print("{}", a); // prints "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::basic_format_parse_context
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
Output Iterator Support
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
|
|
||||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, const S&, const Args&...)
|
|
||||||
.. doxygenstruct:: fmt::format_to_n_result
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Literal-based API
|
Literal-based API
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The following user-defined literals are defined in ``fmt/format.h``.
|
The following user-defined literals are defined in ``fmt/format.h``.
|
||||||
|
|
||||||
.. doxygenfunction:: operator""_format(const char *, size_t)
|
.. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter<char>
|
||||||
|
|
||||||
.. doxygenfunction:: operator""_a(const char *, size_t)
|
.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg<char>
|
||||||
|
|
||||||
Utilities
|
Utilities
|
||||||
---------
|
---------
|
||||||
|
|
||||||
.. doxygenstruct:: fmt::is_char
|
.. 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*
|
||||||
|
|
||||||
.. doxygentypedef:: fmt::char_t
|
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
|
.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char>
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::to_string(const T&)
|
.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::to_wstring(const T&)
|
.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::to_string_view(const Char *)
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::join(const Range&, string_view)
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::join(It, Sentinel, string_view)
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::detail::buffer
|
.. doxygenclass:: fmt::detail::buffer
|
||||||
:members:
|
:members:
|
||||||
|
@ -286,20 +324,14 @@ Utilities
|
||||||
System Errors
|
System Errors
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
fmt does not use ``errno`` to communicate errors to the user, but it may call
|
{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
|
system functions which set ``errno``. Users should not make any assumptions
|
||||||
the value of ``errno`` being preserved by library functions.
|
about the value of ``errno`` being preserved by library functions.
|
||||||
|
|
||||||
.. doxygenclass:: fmt::system_error
|
.. doxygenfunction:: fmt::system_error
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::format_system_error
|
.. doxygenfunction:: fmt::format_system_error
|
||||||
|
|
||||||
.. doxygenclass:: fmt::windows_error
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. _formatstrings:
|
|
||||||
|
|
||||||
Custom Allocators
|
Custom Allocators
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -330,10 +362,10 @@ allocator::
|
||||||
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
The allocator will be used for the output container only. If you are using named
|
The allocator will be used for the output container only. Formatting functions
|
||||||
arguments, the container that stores pointers to them will be allocated using
|
normally don't do any allocations for built-in and string types except for
|
||||||
the default allocator. Also floating-point formatting falls back on ``sprintf``
|
non-default floating-point formatting that occasionally falls back on
|
||||||
which may do allocations.
|
``sprintf``.
|
||||||
|
|
||||||
.. _ranges-api:
|
.. _ranges-api:
|
||||||
|
|
||||||
|
@ -365,41 +397,84 @@ Using ``fmt::join``, you can separate tuple elements with a custom separator::
|
||||||
Date and Time Formatting
|
Date and Time Formatting
|
||||||
========================
|
========================
|
||||||
|
|
||||||
The library supports `strftime
|
``fmt/chrono.h`` provides formatters for
|
||||||
<http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time
|
|
||||||
formatting::
|
* `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>
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
std::time_t t = std::time(nullptr);
|
int main() {
|
||||||
// Prints "The date is 2016-04-29." (with the current date)
|
std::time_t t = std::time(nullptr);
|
||||||
fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
|
|
||||||
|
|
||||||
The format string syntax is described in the documentation of
|
// Prints "The date is 2020-11-07." (with the current date):
|
||||||
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
|
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:
|
.. _compile-api:
|
||||||
|
|
||||||
Format string compilation
|
Format string compilation
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
``fmt/compile.h`` provides format string compilation support. Format strings
|
``fmt/compile.h`` provides format string compilation support when using
|
||||||
are parsed at compile time and converted into efficient formatting code. This
|
``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient
|
||||||
supports arguments of built-in and string types as well as user-defined types
|
formatting code at compile-time. This supports arguments of built-in and string
|
||||||
with ``constexpr`` ``parse`` functions in their ``formatter`` specializations.
|
types as well as user-defined types with ``constexpr`` ``parse`` functions in
|
||||||
Format string compilation can generate more binary code compared to the default
|
their ``formatter`` specializations. Format string compilation can generate more
|
||||||
API and is only recommended in places where formatting is a performance
|
binary code compared to the default API and is only recommended in places where
|
||||||
bottleneck.
|
formatting is a performance bottleneck.
|
||||||
|
|
||||||
.. doxygendefine:: FMT_COMPILE
|
.. 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:
|
.. _ostream-api:
|
||||||
|
|
||||||
``std::ostream`` Support
|
``std::ostream`` Support
|
||||||
========================
|
========================
|
||||||
|
|
||||||
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
|
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
|
||||||
user-defined types that have overloaded ``operator<<``::
|
user-defined types that have an overloaded insertion operator (``operator<<``)::
|
||||||
|
|
||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
|
@ -416,7 +491,10 @@ user-defined types that have overloaded ``operator<<``::
|
||||||
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"
|
// s == "The date is 2012-12-9"
|
||||||
|
|
||||||
.. doxygenfunction:: print(std::basic_ostream<Char>&, const S&, Args&&...)
|
{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::basic_ostream<Char> &os, const S &format_str, Args&&... args)
|
||||||
|
|
||||||
.. _printf-api:
|
.. _printf-api:
|
||||||
|
|
||||||
|
@ -425,18 +503,32 @@ user-defined types that have overloaded ``operator<<``::
|
||||||
|
|
||||||
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
|
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
|
||||||
The following functions use `printf format string syntax
|
The following functions use `printf format string syntax
|
||||||
<http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
|
<https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
|
||||||
the POSIX extension for positional arguments. Unlike their standard
|
the POSIX extension for positional arguments. Unlike their standard
|
||||||
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
|
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
|
||||||
argument type doesn't match its format specification.
|
argument type doesn't match its format specification.
|
||||||
|
|
||||||
.. doxygenfunction:: printf(const S&, const Args&...)
|
.. doxygenfunction:: printf(const S &format_str, const T&... args)
|
||||||
|
|
||||||
.. doxygenfunction:: fprintf(std::FILE *, const S&, const Args&...)
|
.. doxygenfunction:: fprintf(std::FILE *f, const S &fmt, const T&... args) -> int
|
||||||
|
|
||||||
.. doxygenfunction:: fprintf(std::basic_ostream<Char>&, const S&, const Args&...)
|
.. doxygenfunction:: sprintf(const S&, const T&...)
|
||||||
|
|
||||||
.. doxygenfunction:: sprintf(const S&, const Args&...)
|
.. _xchar-api:
|
||||||
|
|
||||||
|
``wchar_t`` Support
|
||||||
|
===================
|
||||||
|
|
||||||
|
The optional header ``fmt/wchar_t.h`` provides support for ``wchar_t`` and
|
||||||
|
exotic character types.
|
||||||
|
|
||||||
|
.. doxygenstruct:: fmt::is_char
|
||||||
|
|
||||||
|
.. doxygentypedef:: fmt::wstring_view
|
||||||
|
|
||||||
|
.. doxygentypedef:: fmt::wformat_context
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::to_wstring(const T &value)
|
||||||
|
|
||||||
Compatibility with C++20 ``std::format``
|
Compatibility with C++20 ``std::format``
|
||||||
========================================
|
========================================
|
||||||
|
@ -447,9 +539,6 @@ differences:
|
||||||
|
|
||||||
* Names are defined in the ``fmt`` namespace instead of ``std`` to avoid
|
* Names are defined in the ``fmt`` namespace instead of ``std`` to avoid
|
||||||
collisions with standard library implementations.
|
collisions with standard library implementations.
|
||||||
* The ``'L'`` format specifier cannot be combined with presentation specifiers
|
|
||||||
yet.
|
|
||||||
* Width calculation doesn't use grapheme clusterization. The latter has been
|
* Width calculation doesn't use grapheme clusterization. The latter has been
|
||||||
implemented in a separate branch but hasn't been integrated yet.
|
implemented in a separate branch but hasn't been integrated yet.
|
||||||
* Chrono formatting doesn't support C++20 date types since they are not provided
|
* Most C++20 chrono types are not supported yet.
|
||||||
by standard library implementations.
|
|
||||||
|
|
113
externals/fmt/doc/build.py
vendored
113
externals/fmt/doc/build.py
vendored
|
@ -1,63 +1,33 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# Build the documentation.
|
# Build the documentation.
|
||||||
|
|
||||||
from __future__ import print_function
|
import errno, os, re, sys
|
||||||
import errno, os, shutil, sys, tempfile
|
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT
|
||||||
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']
|
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']
|
||||||
|
|
||||||
def pip_install(package, commit=None, **kwargs):
|
class Pip:
|
||||||
"Install package using pip."
|
def __init__(self, venv_dir):
|
||||||
min_version = kwargs.get('min_version')
|
self.path = os.path.join(venv_dir, 'bin', 'pip')
|
||||||
if min_version:
|
|
||||||
from pkg_resources import get_distribution, DistributionNotFound
|
|
||||||
try:
|
|
||||||
installed_version = get_distribution(os.path.basename(package)).version
|
|
||||||
if LooseVersion(installed_version) >= min_version:
|
|
||||||
print('{} {} already installed'.format(package, min_version))
|
|
||||||
return
|
|
||||||
except DistributionNotFound:
|
|
||||||
pass
|
|
||||||
if commit:
|
|
||||||
package = 'git+https://github.com/{0}.git@{1}'.format(package, commit)
|
|
||||||
print('Installing {0}'.format(package))
|
|
||||||
check_call(['pip', 'install', package])
|
|
||||||
|
|
||||||
def create_build_env(dirname='virtualenv'):
|
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'):
|
||||||
# Create virtualenv.
|
# Create virtualenv.
|
||||||
if not os.path.exists(dirname):
|
if not os.path.exists(venv_dir):
|
||||||
check_call(['virtualenv', dirname])
|
check_call(['python3', '-m', 'venv', venv_dir])
|
||||||
import sysconfig
|
# Install Sphinx and Breathe. Require the exact version of Sphinx which is
|
||||||
scripts_dir = os.path.basename(sysconfig.get_path('scripts'))
|
# compatible with Breathe.
|
||||||
activate_this_file = os.path.join(dirname, scripts_dir, 'activate_this.py')
|
pip = Pip(venv_dir)
|
||||||
with open(activate_this_file) as f:
|
pip.install('wheel')
|
||||||
exec(f.read(), dict(__file__=activate_this_file))
|
pip.install('six')
|
||||||
# Import get_distribution after activating virtualenv to get info about
|
pip.install('sphinx-doc/sphinx', 'v3.3.0')
|
||||||
# the correct packages.
|
pip.install('michaeljones/breathe', 'v4.16.0')
|
||||||
from pkg_resources import get_distribution, DistributionNotFound
|
|
||||||
# Upgrade pip because installation of sphinx with pip 1.1 available on Travis
|
|
||||||
# is broken (see #207) and it doesn't support the show command.
|
|
||||||
pip_version = get_distribution('pip').version
|
|
||||||
if LooseVersion(pip_version) < LooseVersion('1.5.4'):
|
|
||||||
print("Updating pip")
|
|
||||||
check_call(['pip', 'install', '--upgrade', 'pip'])
|
|
||||||
# Upgrade distribute because installation of sphinx with distribute 0.6.24
|
|
||||||
# available on Travis is broken (see #207).
|
|
||||||
try:
|
|
||||||
distribute_version = get_distribution('distribute').version
|
|
||||||
if LooseVersion(distribute_version) <= LooseVersion('0.6.24'):
|
|
||||||
print("Updating distribute")
|
|
||||||
check_call(['pip', 'install', '--upgrade', 'distribute'])
|
|
||||||
except DistributionNotFound:
|
|
||||||
pass
|
|
||||||
# Install Sphinx and Breathe.
|
|
||||||
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
|
||||||
min_version='1.4.1.dev20160531')
|
|
||||||
pip_install('michaeljones/breathe',
|
|
||||||
'129222318f7c8f865d2631e7da7b033567e7f56a',
|
|
||||||
min_version='4.2.0')
|
|
||||||
|
|
||||||
def build_docs(version='dev', **kwargs):
|
def build_docs(version='dev', **kwargs):
|
||||||
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
|
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
@ -66,16 +36,17 @@ def build_docs(version='dev', **kwargs):
|
||||||
'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt'))
|
'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt'))
|
||||||
# Build docs.
|
# Build docs.
|
||||||
cmd = ['doxygen', '-']
|
cmd = ['doxygen', '-']
|
||||||
p = Popen(cmd, stdin=PIPE)
|
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
||||||
doxyxml_dir = os.path.join(work_dir, 'doxyxml')
|
doxyxml_dir = os.path.join(work_dir, 'doxyxml')
|
||||||
p.communicate(input=r'''
|
out, _ = p.communicate(input=r'''
|
||||||
PROJECT_NAME = fmt
|
PROJECT_NAME = fmt
|
||||||
GENERATE_LATEX = NO
|
GENERATE_LATEX = NO
|
||||||
GENERATE_MAN = NO
|
GENERATE_MAN = NO
|
||||||
GENERATE_RTF = NO
|
GENERATE_RTF = NO
|
||||||
CASE_SENSE_NAMES = NO
|
CASE_SENSE_NAMES = NO
|
||||||
INPUT = {0}/core.h {0}/compile.h {0}/format.h {0}/os.h \
|
INPUT = {0}/chrono.h {0}/color.h {0}/core.h {0}/compile.h \
|
||||||
{0}/ostream.h {0}/printf.h {0}/time.h
|
{0}/format.h {0}/os.h {0}/ostream.h {0}/printf.h \
|
||||||
|
{0}/xchar.h
|
||||||
QUIET = YES
|
QUIET = YES
|
||||||
JAVADOC_AUTOBRIEF = YES
|
JAVADOC_AUTOBRIEF = YES
|
||||||
AUTOLINK_SUPPORT = NO
|
AUTOLINK_SUPPORT = NO
|
||||||
|
@ -86,6 +57,7 @@ def build_docs(version='dev', **kwargs):
|
||||||
ALIASES += "endrst=\endverbatim"
|
ALIASES += "endrst=\endverbatim"
|
||||||
MACRO_EXPANSION = YES
|
MACRO_EXPANSION = YES
|
||||||
PREDEFINED = _WIN32=1 \
|
PREDEFINED = _WIN32=1 \
|
||||||
|
__linux__=1 \
|
||||||
FMT_USE_VARIADIC_TEMPLATES=1 \
|
FMT_USE_VARIADIC_TEMPLATES=1 \
|
||||||
FMT_USE_RVALUE_REFERENCES=1 \
|
FMT_USE_RVALUE_REFERENCES=1 \
|
||||||
FMT_USE_USER_DEFINED_LITERALS=1 \
|
FMT_USE_USER_DEFINED_LITERALS=1 \
|
||||||
|
@ -94,20 +66,37 @@ def build_docs(version='dev', **kwargs):
|
||||||
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
||||||
"FMT_END_NAMESPACE=}}" \
|
"FMT_END_NAMESPACE=}}" \
|
||||||
"FMT_STRING_ALIAS=1" \
|
"FMT_STRING_ALIAS=1" \
|
||||||
"FMT_ENABLE_IF(B)="
|
"FMT_DOC=1"
|
||||||
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
|
EXCLUDE_SYMBOLS = fmt::formatter fmt::printf_formatter fmt::arg_join \
|
||||||
|
fmt::basic_format_arg::handle
|
||||||
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
|
'''.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)
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise CalledProcessError(p.returncode, cmd)
|
raise CalledProcessError(p.returncode, cmd)
|
||||||
|
|
||||||
html_dir = os.path.join(work_dir, 'html')
|
html_dir = os.path.join(work_dir, 'html')
|
||||||
main_versions = reversed(versions[-3:])
|
main_versions = reversed(versions[-3:])
|
||||||
check_call(['sphinx-build',
|
check_call([os.path.join(work_dir, 'virtualenv', 'bin', 'sphinx-build'),
|
||||||
'-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir),
|
'-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir),
|
||||||
'-Dversion=' + version, '-Drelease=' + version,
|
'-Dversion=' + version, '-Drelease=' + version,
|
||||||
'-Aversion=' + version, '-Aversions=' + ','.join(main_versions),
|
'-Aversion=' + version, '-Aversions=' + ','.join(main_versions),
|
||||||
'-b', 'html', doc_dir, html_dir])
|
'-b', 'html', doc_dir, html_dir])
|
||||||
try:
|
try:
|
||||||
check_call(['lessc', '--clean-css',
|
check_call(['lessc', '--verbose', '--clean-css',
|
||||||
'--include-path=' + os.path.join(doc_dir, 'bootstrap'),
|
'--include-path=' + os.path.join(doc_dir, 'bootstrap'),
|
||||||
os.path.join(doc_dir, 'fmt.less'),
|
os.path.join(doc_dir, 'fmt.less'),
|
||||||
os.path.join(html_dir, '_static', 'fmt.css')])
|
os.path.join(html_dir, '_static', 'fmt.css')])
|
||||||
|
|
5
externals/fmt/doc/fmt.less
vendored
5
externals/fmt/doc/fmt.less
vendored
|
@ -56,6 +56,11 @@ div.sphinxsidebar {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override center alignment in tables.
|
||||||
|
td {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
p.rubric {
|
p.rubric {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
72
externals/fmt/doc/index.rst
vendored
72
externals/fmt/doc/index.rst
vendored
|
@ -23,24 +23,26 @@ Format API
|
||||||
|
|
||||||
The format API is similar in spirit to the C ``printf`` family of function but
|
The format API is similar in spirit to the C ``printf`` family of function but
|
||||||
is safer, simpler and several times `faster
|
is safer, simpler and several times `faster
|
||||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
|
<https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||||
than common standard library implementations.
|
than common standard library implementations.
|
||||||
The `format string syntax <syntax.html>`_ is similar to the one used by
|
The `format string syntax <syntax.html>`_ is similar to the one used by
|
||||||
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
|
`str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ in
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::format("The answer is {}.", 42);
|
std::string s = fmt::format("The answer is {}.", 42);
|
||||||
|
|
||||||
The ``fmt::format`` function returns a string "The answer is 42.". You can use
|
The ``fmt::format`` function returns a string "The answer is 42.". You can use
|
||||||
``fmt::memory_buffer`` to avoid constructing ``std::string``:
|
``fmt::memory_buffer`` to avoid constructing ``std::string``:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::memory_buffer out;
|
auto out = fmt::memory_buffer();
|
||||||
format_to(out, "For a moment, {} happened.", "nothing");
|
format_to(std::back_inserter(out),
|
||||||
out.data(); // returns a pointer to the formatted data
|
"For a moment, {} happened.", "nothing");
|
||||||
|
auto data = out.data(); // pointer to the formatted data
|
||||||
|
auto size = out.size(); // size of the formatted data
|
||||||
|
|
||||||
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 stream:
|
||||||
|
|
||||||
|
@ -48,21 +50,19 @@ The ``fmt::print`` function performs formatting and writes the result to a strea
|
||||||
|
|
||||||
fmt::print(stderr, "System error code = {}\n", errno);
|
fmt::print(stderr, "System error code = {}\n", errno);
|
||||||
|
|
||||||
The file argument can be omitted in which case the function prints to
|
If you omit the file argument the function will print to ``stdout``:
|
||||||
``stdout``:
|
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::print("Don't {}\n", "panic");
|
fmt::print("Don't {}\n", "panic");
|
||||||
|
|
||||||
The Format API also supports positional arguments useful for localization:
|
The format API also supports positional arguments useful for localization:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
|
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
|
||||||
|
|
||||||
Named arguments can be created with ``fmt::arg``. This makes it easier to track
|
You can pass named arguments with ``fmt::arg``:
|
||||||
what goes where when multiple arguments are being formatted:
|
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
|
@ -91,16 +91,17 @@ time. For example, the code
|
||||||
|
|
||||||
fmt::format("The answer is {:d}", "forty-two");
|
fmt::format("The answer is {:d}", "forty-two");
|
||||||
|
|
||||||
throws a ``format_error`` exception with description "unknown format code 'd' for
|
throws the ``format_error`` exception because the argument ``"forty-two"`` is a
|
||||||
string", because the argument ``"forty-two"`` is a string while the format code
|
string while the format code ``d`` only applies to integers.
|
||||||
``d`` only applies to integers, while
|
|
||||||
|
The code
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
format(FMT_STRING("The answer is {:d}"), "forty-two");
|
format(FMT_STRING("The answer is {:d}"), "forty-two");
|
||||||
|
|
||||||
reports a compile-time error for the same reason on compilers that support
|
reports a compile-time error on compilers that support relaxed ``constexpr``.
|
||||||
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
|
See `here <api.html#c.fmt>`_ for details.
|
||||||
|
|
||||||
The following code
|
The following code
|
||||||
|
|
||||||
|
@ -109,21 +110,15 @@ The following code
|
||||||
fmt::format("Cyrillic letter {}", L'\x42e');
|
fmt::format("Cyrillic letter {}", L'\x42e');
|
||||||
|
|
||||||
produces a compile-time error because wide character ``L'\x42e'`` cannot be
|
produces a compile-time error because wide character ``L'\x42e'`` cannot be
|
||||||
formatted into a narrow string. You can use a wide format string instead:
|
formatted into a narrow string. For comparison, writing a wide character to
|
||||||
|
``std::ostream`` results in its numeric value being written to the stream
|
||||||
.. code:: c++
|
(i.e. 1070 instead of letter 'ю' which is represented by ``L'\x42e'`` if we
|
||||||
|
use Unicode) which is rarely desirable.
|
||||||
fmt::format(L"Cyrillic letter {}", L'\x42e');
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Compact Binary Code
|
Compact Binary Code
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
The library is designed to produce compact per-call compiled code. For example
|
The library produces compact per-call compiled code. For example
|
||||||
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
|
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
@ -144,8 +139,8 @@ compiles to just
|
||||||
mov rcx, rsp
|
mov rcx, rsp
|
||||||
mov edi, offset .L.str
|
mov edi, offset .L.str
|
||||||
mov esi, 17
|
mov esi, 17
|
||||||
mov edx, 2
|
mov edx, 1
|
||||||
call fmt::v5::vprint(fmt::v5::basic_string_view<char>, fmt::v5::format_args)
|
call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
|
||||||
xor eax, eax
|
xor eax, eax
|
||||||
add rsp, 24
|
add rsp, 24
|
||||||
ret
|
ret
|
||||||
|
@ -167,20 +162,19 @@ The library is highly portable and relies only on a small set of C++11 features:
|
||||||
* deleted functions
|
* deleted functions
|
||||||
* alias templates
|
* alias templates
|
||||||
|
|
||||||
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
|
These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent
|
||||||
compilers use {fmt} `version 4.x
|
compiler version. For older compilers use {fmt} `version 4.x
|
||||||
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
|
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which is maintained and
|
||||||
maintained and only requires C++98.
|
only requires C++98.
|
||||||
|
|
||||||
The output of all formatting functions is consistent across platforms. In
|
The output of all formatting functions is consistent across platforms.
|
||||||
particular, formatting a floating-point infinity always gives ``inf`` while the
|
For example,
|
||||||
output of ``printf`` is platform-dependent. For example,
|
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
fmt::print("{}", std::numeric_limits<double>::infinity());
|
fmt::print("{}", std::numeric_limits<double>::infinity());
|
||||||
|
|
||||||
always prints ``inf``.
|
always prints ``inf`` while the output of ``printf`` is platform-dependent.
|
||||||
|
|
||||||
.. _ease-of-use:
|
.. _ease-of-use:
|
||||||
|
|
||||||
|
@ -192,11 +186,13 @@ just three header files and no external dependencies.
|
||||||
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
|
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
|
||||||
using the library both in open-source and commercial projects.
|
using the library both in open-source and commercial projects.
|
||||||
|
|
||||||
|
`Learn more... <contents.html>`_
|
||||||
|
|
||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a>
|
<a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a>
|
||||||
|
|
||||||
<div class="section footer">
|
<div class="section footer">
|
||||||
<iframe src="http://ghbtns.com/github-btn.html?user=fmtlib&repo=fmt&type=watch&count=true"
|
<iframe src="https://ghbtns.com/github-btn.html?user=fmtlib&repo=fmt&type=watch&count=true"
|
||||||
class="github-btn" width="100" height="20"></iframe>
|
class="github-btn" width="100" height="20"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
86
externals/fmt/doc/syntax.rst
vendored
86
externals/fmt/doc/syntax.rst
vendored
|
@ -16,7 +16,7 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``.
|
||||||
The grammar for a replacement field is as follows:
|
The grammar for a replacement field is as follows:
|
||||||
|
|
||||||
.. productionlist:: sf
|
.. productionlist:: sf
|
||||||
replacement_field: "{" [`arg_id`] [":" `format_spec`] "}"
|
replacement_field: "{" [`arg_id`] [":" (`format_spec` | `chrono_format_spec`)] "}"
|
||||||
arg_id: `integer` | `identifier`
|
arg_id: `integer` | `identifier`
|
||||||
integer: `digit`+
|
integer: `digit`+
|
||||||
digit: "0"..."9"
|
digit: "0"..."9"
|
||||||
|
@ -27,8 +27,8 @@ The grammar for a replacement field is as follows:
|
||||||
In less formal terms, the replacement field can start with an *arg_id*
|
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
|
that specifies the argument whose value is to be formatted and inserted into
|
||||||
the output instead of the replacement field.
|
the output instead of the replacement field.
|
||||||
The *arg_id* is optionally followed by a *format_spec*, which is preceded
|
The *arg_id* is optionally followed by a *format_spec*, which is preceded by a
|
||||||
by a colon ``':'``. These specify a non-default format for the replacement value.
|
colon ``':'``. These specify a non-default format for the replacement value.
|
||||||
|
|
||||||
See also the :ref:`formatspec` section.
|
See also the :ref:`formatspec` section.
|
||||||
|
|
||||||
|
@ -75,14 +75,14 @@ although some of the formatting options are only supported by the numeric types.
|
||||||
The general form of a *standard format specifier* is:
|
The general form of a *standard format specifier* is:
|
||||||
|
|
||||||
.. productionlist:: sf
|
.. productionlist:: sf
|
||||||
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
|
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`]["L"][`type`]
|
||||||
fill: <a character other than '{' or '}'>
|
fill: <a character other than '{' or '}'>
|
||||||
align: "<" | ">" | "^"
|
align: "<" | ">" | "^"
|
||||||
sign: "+" | "-" | " "
|
sign: "+" | "-" | " "
|
||||||
width: `integer` | "{" [`arg_id`] "}"
|
width: `integer` | "{" [`arg_id`] "}"
|
||||||
precision: `integer` | "{" [`arg_id`] "}"
|
precision: `integer` | "{" [`arg_id`] "}"
|
||||||
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
|
type: "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" |
|
||||||
int_type: "b" | "B" | "d" | "o" | "x" | "X"
|
: "o" | "p" | "s" | "x" | "X"
|
||||||
|
|
||||||
The *fill* character can be any Unicode code point other than ``'{'`` or
|
The *fill* character can be any Unicode code point other than ``'{'`` or
|
||||||
``'}'``. The presence of a fill character is signaled by the character following
|
``'}'``. The presence of a fill character is signaled by the character following
|
||||||
|
@ -163,6 +163,9 @@ 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,
|
used from the field content. The *precision* is not allowed for integer,
|
||||||
character, Boolean, and pointer values.
|
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.
|
Finally, the *type* determines how the data should be presented.
|
||||||
|
|
||||||
The available string presentation types are:
|
The available string presentation types are:
|
||||||
|
@ -200,6 +203,8 @@ The available integer presentation types are:
|
||||||
| | ``'#'`` option with this type adds the prefix ``"0B"`` |
|
| | ``'#'`` option with this type adds the prefix ``"0B"`` |
|
||||||
| | to the output value. |
|
| | to the output value. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
|
| ``'c'`` | Character format. Outputs the number as a character. |
|
||||||
|
+---------+----------------------------------------------------------+
|
||||||
| ``'d'`` | Decimal integer. Outputs the number in base 10. |
|
| ``'d'`` | Decimal integer. Outputs the number in base 10. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| ``'o'`` | Octal format. Outputs the number in base 8. |
|
| ``'o'`` | Octal format. Outputs the number in base 8. |
|
||||||
|
@ -214,10 +219,6 @@ The available integer presentation types are:
|
||||||
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
|
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
|
||||||
| | to the output value. |
|
| | to the output value. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| ``'L'`` | Locale-specific format. 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'``. |
|
| none | The same as ``'d'``. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
|
|
||||||
|
@ -261,14 +262,8 @@ The available presentation types for floating-point values are:
|
||||||
| | ``'E'`` if the number gets too large. The |
|
| | ``'E'`` if the number gets too large. The |
|
||||||
| | representations of infinity and NaN are uppercased, too. |
|
| | representations of infinity and NaN are uppercased, too. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| ``'L'`` | Locale-specific format. This is the same as ``'g'``, |
|
| none | Similar to ``'g'``, except that the default precision is |
|
||||||
| | except that it uses the current locale setting to insert |
|
| | as high as needed to represent the particular value. |
|
||||||
| | the appropriate number separator characters. |
|
|
||||||
+---------+----------------------------------------------------------+
|
|
||||||
| none | Similar to ``'g'``, except that fixed-point notation, |
|
|
||||||
| | when used, has at least one digit past the decimal |
|
|
||||||
| | point. The default precision is as high as needed to |
|
|
||||||
| | represent the particular value. |
|
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
|
|
||||||
.. ifconfig:: False
|
.. ifconfig:: False
|
||||||
|
@ -303,6 +298,59 @@ The available presentation types for pointers are:
|
||||||
| none | The same as ``'p'``. |
|
| 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:
|
.. _formatexamples:
|
||||||
|
|
||||||
Format Examples
|
Format Examples
|
||||||
|
@ -391,7 +439,7 @@ Using type-specific formatting::
|
||||||
|
|
||||||
auto t = tm();
|
auto t = tm();
|
||||||
t.tm_year = 2010 - 1900;
|
t.tm_year = 2010 - 1900;
|
||||||
t.tm_mon = 6;
|
t.tm_mon = 7;
|
||||||
t.tm_mday = 4;
|
t.tm_mday = 4;
|
||||||
t.tm_hour = 12;
|
t.tm_hour = 12;
|
||||||
t.tm_min = 15;
|
t.tm_min = 15;
|
||||||
|
|
71
externals/fmt/doc/usage.rst
vendored
71
externals/fmt/doc/usage.rst
vendored
|
@ -15,7 +15,7 @@ Building the Library
|
||||||
|
|
||||||
The included `CMake build script`__ can be used to build the fmt
|
The included `CMake build script`__ can be used to build the fmt
|
||||||
library on a wide range of platforms. CMake is freely available for
|
library on a wide range of platforms. CMake is freely available for
|
||||||
download from http://www.cmake.org/download/.
|
download from https://www.cmake.org/download/.
|
||||||
|
|
||||||
__ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt
|
__ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt
|
||||||
|
|
||||||
|
@ -50,7 +50,15 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
|
||||||
|
|
||||||
cmake -DBUILD_SHARED_LIBS=TRUE ...
|
cmake -DBUILD_SHARED_LIBS=TRUE ...
|
||||||
|
|
||||||
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
|
__ https://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
|
Installing the Library
|
||||||
======================
|
======================
|
||||||
|
@ -83,6 +91,49 @@ 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)
|
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
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
@ -130,6 +181,18 @@ 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
|
contributors. If the version is out of date, please `create an issue or pull
|
||||||
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
|
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
|
Android NDK
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
@ -139,11 +202,11 @@ For an example of using fmt with Android NDK, see the
|
||||||
`android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_
|
`android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_
|
||||||
repository.
|
repository.
|
||||||
|
|
||||||
__ https://github.com/fmtlib/fmt/blob/master/Android.mk
|
__ https://github.com/fmtlib/fmt/blob/master/support/Android.mk
|
||||||
|
|
||||||
Homebrew
|
Homebrew
|
||||||
========
|
========
|
||||||
|
|
||||||
fmt can be installed on OS X using `Homebrew <http://brew.sh/>`_::
|
fmt can be installed on OS X using `Homebrew <https://brew.sh/>`_::
|
||||||
|
|
||||||
brew install fmt
|
brew install fmt
|
||||||
|
|
232
externals/fmt/include/fmt/args.h
vendored
Normal file
232
externals/fmt/include/fmt/args.h
vendored
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
// 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_
|
495
externals/fmt/include/fmt/chrono.h
vendored
495
externals/fmt/include/fmt/chrono.h
vendored
|
@ -8,13 +8,13 @@
|
||||||
#ifndef FMT_CHRONO_H_
|
#ifndef FMT_CHRONO_H_
|
||||||
#define FMT_CHRONO_H_
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "locale.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
||||||
static_assert(F::is_integer, "From must be integral");
|
static_assert(F::is_integer, "From must be integral");
|
||||||
static_assert(T::is_integer, "To must be integral");
|
static_assert(T::is_integer, "To must be integral");
|
||||||
|
|
||||||
if (F::is_signed && !T::is_signed) {
|
if (detail::const_check(F::is_signed && !T::is_signed)) {
|
||||||
// From may be negative, not allowed!
|
// From may be negative, not allowed!
|
||||||
if (fmt::detail::is_negative(from)) {
|
if (fmt::detail::is_negative(from)) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// From is positive. Can it always fit in To?
|
// From is positive. Can it always fit in To?
|
||||||
if (F::digits <= T::digits) {
|
if (F::digits > T::digits &&
|
||||||
// yes, From always fits in To.
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
} else {
|
ec = 1;
|
||||||
// from may not fit in To, we have to do a dynamic check
|
return {};
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!F::is_signed && T::is_signed) {
|
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
|
||||||
// can from be held in To?
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
if (F::digits < T::digits) {
|
ec = 1;
|
||||||
// yes, From always fits in To.
|
return {};
|
||||||
} else {
|
|
||||||
// from may not fit in To, we have to do a dynamic check
|
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
// outside range.
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return static_cast<To>(from); // Lossless conversion.
|
||||||
// reaching here means all is ok for lossless conversion.
|
}
|
||||||
return static_cast<To>(from);
|
|
||||||
|
|
||||||
} // function
|
|
||||||
|
|
||||||
template <typename To, typename From,
|
template <typename To, typename From,
|
||||||
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||||
|
@ -190,11 +174,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||||
// safe conversion to IntermediateRep
|
// safe conversion to IntermediateRep
|
||||||
IntermediateRep count =
|
IntermediateRep count =
|
||||||
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||||
if (ec) {
|
if (ec) return {};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
// multiply with Factor::num without overflow or underflow
|
// multiply with Factor::num without overflow or underflow
|
||||||
if (Factor::num != 1) {
|
if (detail::const_check(Factor::num != 1)) {
|
||||||
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
||||||
if (count > max1) {
|
if (count > max1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
|
@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||||
count *= Factor::num;
|
count *= Factor::num;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this can't go wrong, right? den>0 is checked earlier.
|
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
|
||||||
if (Factor::den != 1) {
|
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
|
||||||
count /= Factor::den;
|
return ec ? To() : To(tocount);
|
||||||
}
|
|
||||||
// convert to the to type, safely
|
|
||||||
using ToRep = typename To::rep;
|
|
||||||
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
|
||||||
if (ec) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return To{tocount};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -308,13 +282,89 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||||
#define FMT_NOMACRO
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
template <typename T = void> struct null {};
|
||||||
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||||
inline null<> localtime_s(...) { return null<>(); }
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
inline null<> gmtime_r(...) { return null<>(); }
|
inline null<> gmtime_r(...) { return null<>(); }
|
||||||
inline null<> gmtime_s(...) { return null<>(); }
|
inline null<> gmtime_s(...) { return null<>(); }
|
||||||
|
|
||||||
|
inline auto do_write(const std::tm& time, const std::locale& loc, char format,
|
||||||
|
char modifier) -> std::string {
|
||||||
|
auto&& os = std::ostringstream();
|
||||||
|
os.imbue(loc);
|
||||||
|
using iterator = std::ostreambuf_iterator<char>;
|
||||||
|
const auto& facet = std::use_facet<std::time_put<char, iterator>>(loc);
|
||||||
|
auto end = facet.put(os, os, ' ', &time, format, modifier);
|
||||||
|
if (end.failed()) FMT_THROW(format_error("failed to format time"));
|
||||||
|
auto str = os.str();
|
||||||
|
if (!detail::is_utf8() || loc == std::locale::classic()) return str;
|
||||||
|
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
|
||||||
|
// gcc-4.
|
||||||
|
#if FMT_MSC_VER != 0 || \
|
||||||
|
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
|
||||||
|
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
|
||||||
|
// and newer.
|
||||||
|
using code_unit = wchar_t;
|
||||||
|
#else
|
||||||
|
using code_unit = char32_t;
|
||||||
|
#endif
|
||||||
|
auto& f = std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
|
||||||
|
auto mb = std::mbstate_t();
|
||||||
|
const char* from_next = nullptr;
|
||||||
|
code_unit* to_next = nullptr;
|
||||||
|
constexpr size_t buf_size = 32;
|
||||||
|
code_unit buf[buf_size] = {};
|
||||||
|
auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
|
||||||
|
buf + buf_size, to_next);
|
||||||
|
if (result != std::codecvt_base::ok)
|
||||||
|
FMT_THROW(format_error("failed to format time"));
|
||||||
|
str.clear();
|
||||||
|
for (code_unit* p = buf; p != to_next; ++p) {
|
||||||
|
uint32_t c = static_cast<uint32_t>(*p);
|
||||||
|
if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
|
||||||
|
// surrogate pair
|
||||||
|
++p;
|
||||||
|
if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
|
||||||
|
FMT_THROW(format_error("failed to format time"));
|
||||||
|
}
|
||||||
|
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
|
||||||
|
}
|
||||||
|
if (c < 0x80) {
|
||||||
|
str.push_back(static_cast<char>(c));
|
||||||
|
} else if (c < 0x800) {
|
||||||
|
str.push_back(static_cast<char>(0xc0 | (c >> 6)));
|
||||||
|
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||||
|
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
|
||||||
|
str.push_back(static_cast<char>(0xe0 | (c >> 12)));
|
||||||
|
str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
|
||||||
|
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||||
|
} else if (c >= 0x10000 && c <= 0x10ffff) {
|
||||||
|
str.push_back(static_cast<char>(0xf0 | (c >> 18)));
|
||||||
|
str.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
|
||||||
|
str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
|
||||||
|
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||||
|
} else {
|
||||||
|
FMT_THROW(format_error("failed to format time"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
|
||||||
|
char format, char modifier = 0) -> OutputIt {
|
||||||
|
auto str = do_write(time, loc, format, modifier);
|
||||||
|
return std::copy(str.begin(), str.end(), out);
|
||||||
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
// Thread-safe replacement for std::localtime
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts given time since epoch as ``std::time_t`` value into calendar time,
|
||||||
|
expressed in local time. Unlike ``std::localtime``, this function is
|
||||||
|
thread-safe on most platforms.
|
||||||
|
*/
|
||||||
inline std::tm localtime(std::time_t time) {
|
inline std::tm localtime(std::time_t time) {
|
||||||
struct dispatcher {
|
struct dispatcher {
|
||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
|
@ -351,7 +401,16 @@ inline std::tm localtime(std::time_t time) {
|
||||||
return lt.tm_;
|
return lt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
inline std::tm localtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return localtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts given time since epoch as ``std::time_t`` value into calendar time,
|
||||||
|
expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
|
||||||
|
function is thread-safe on most platforms.
|
||||||
|
*/
|
||||||
inline std::tm gmtime(std::time_t time) {
|
inline std::tm gmtime(std::time_t time) {
|
||||||
struct dispatcher {
|
struct dispatcher {
|
||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
|
@ -387,33 +446,89 @@ inline std::tm gmtime(std::time_t time) {
|
||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
inline std::tm gmtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
inline size_t strftime(char* str, size_t count, const char* format,
|
inline size_t strftime(char* str, size_t count, const char* format,
|
||||||
const std::tm* time) {
|
const std::tm* time) {
|
||||||
return std::strftime(str, count, format, time);
|
// Assign to a pointer to suppress GCCs -Wformat-nonliteral
|
||||||
|
// First assign the nullptr to suppress -Wsuggest-attribute=format
|
||||||
|
std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) =
|
||||||
|
nullptr;
|
||||||
|
strftime = std::strftime;
|
||||||
|
return strftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
||||||
const std::tm* time) {
|
const std::tm* time) {
|
||||||
return std::wcsftime(str, count, format, time);
|
// See above
|
||||||
|
std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*,
|
||||||
|
const std::tm*) = nullptr;
|
||||||
|
wcsftime = std::wcsftime;
|
||||||
|
return wcsftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char, typename Duration>
|
||||||
|
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
||||||
|
Char> : formatter<std::tm, Char> {
|
||||||
|
FMT_CONSTEXPR formatter() {
|
||||||
|
this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
auto it = ctx.begin();
|
auto it = ctx.begin();
|
||||||
if (it != ctx.end() && *it == ':') ++it;
|
if (it != ctx.end() && *it == ':') ++it;
|
||||||
auto end = it;
|
auto end = it;
|
||||||
while (end != ctx.end() && *end != '}') ++end;
|
while (end != ctx.end() && *end != '}') ++end;
|
||||||
tm_format.reserve(detail::to_unsigned(end - it + 1));
|
if (end != it) this->specs = {it, detail::to_unsigned(end - it)};
|
||||||
tm_format.append(it, end);
|
|
||||||
tm_format.push_back('\0');
|
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(std::chrono::time_point<std::chrono::system_clock> val,
|
||||||
|
FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
std::tm time = localtime(val);
|
||||||
|
return formatter<std::tm, Char>::format(time, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-',
|
||||||
|
'%', 'd', ' ', '%', 'H', ':',
|
||||||
|
'%', 'M', ':', '%', 'S'};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Duration>
|
||||||
|
constexpr Char
|
||||||
|
formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
||||||
|
Char>::default_specs[];
|
||||||
|
|
||||||
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
if (it != ctx.end() && *it == ':') ++it;
|
||||||
|
auto end = it;
|
||||||
|
while (end != ctx.end() && *end != '}') ++end;
|
||||||
|
specs = {it, detail::to_unsigned(end - it)};
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::tm& tm, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> tm_format;
|
||||||
|
tm_format.append(specs.begin(), specs.end());
|
||||||
|
// By appending an extra space we can distinguish an empty result that
|
||||||
|
// indicates insufficient buffer size from a guaranteed non-empty result
|
||||||
|
// https://github.com/fmtlib/fmt/issues/2238
|
||||||
|
tm_format.push_back(' ');
|
||||||
|
tm_format.push_back('\0');
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
size_t start = buf.size();
|
size_t start = buf.size();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -423,49 +538,40 @@ template <typename Char> struct formatter<std::tm, Char> {
|
||||||
buf.resize(start + count);
|
buf.resize(start + count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (size >= tm_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 size_t MIN_GROWTH = 10;
|
const size_t MIN_GROWTH = 10;
|
||||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
}
|
}
|
||||||
return std::copy(buf.begin(), buf.end(), ctx.out());
|
// Remove the extra space.
|
||||||
|
return std::copy(buf.begin(), buf.end() - 1, ctx.out());
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_memory_buffer<Char> tm_format;
|
basic_string_view<Char> specs;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
template <typename Period> FMT_CONSTEXPR const char* get_units() {
|
|
||||||
|
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
|
||||||
|
if (std::is_same<Period, std::atto>::value) return "as";
|
||||||
|
if (std::is_same<Period, std::femto>::value) return "fs";
|
||||||
|
if (std::is_same<Period, std::pico>::value) return "ps";
|
||||||
|
if (std::is_same<Period, std::nano>::value) return "ns";
|
||||||
|
if (std::is_same<Period, std::micro>::value) return "µs";
|
||||||
|
if (std::is_same<Period, std::milli>::value) return "ms";
|
||||||
|
if (std::is_same<Period, std::centi>::value) return "cs";
|
||||||
|
if (std::is_same<Period, std::deci>::value) return "ds";
|
||||||
|
if (std::is_same<Period, std::ratio<1>>::value) return "s";
|
||||||
|
if (std::is_same<Period, std::deca>::value) return "das";
|
||||||
|
if (std::is_same<Period, std::hecto>::value) return "hs";
|
||||||
|
if (std::is_same<Period, std::kilo>::value) return "ks";
|
||||||
|
if (std::is_same<Period, std::mega>::value) return "Ms";
|
||||||
|
if (std::is_same<Period, std::giga>::value) return "Gs";
|
||||||
|
if (std::is_same<Period, std::tera>::value) return "Ts";
|
||||||
|
if (std::is_same<Period, std::peta>::value) return "Ps";
|
||||||
|
if (std::is_same<Period, std::exa>::value) return "Es";
|
||||||
|
if (std::is_same<Period, std::ratio<60>>::value) return "m";
|
||||||
|
if (std::is_same<Period, std::ratio<3600>>::value) return "h";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::atto>() { return "as"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::femto>() { return "fs"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::pico>() { return "ps"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::nano>() { return "ns"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::micro>() { return "µs"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::milli>() { return "ms"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::centi>() { return "cs"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::deci>() { return "ds"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::ratio<1>>() { return "s"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::deca>() { return "das"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::hecto>() { return "hs"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::kilo>() { return "ks"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::mega>() { return "Ms"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::giga>() { return "Gs"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::tera>() { return "Ts"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::peta>() { return "Ps"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::exa>() { return "Es"; }
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::ratio<60>>() {
|
|
||||||
return "m";
|
|
||||||
}
|
|
||||||
template <> FMT_CONSTEXPR const char* get_units<std::ratio<3600>>() {
|
|
||||||
return "h";
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class numeric_system {
|
enum class numeric_system {
|
||||||
standard,
|
standard,
|
||||||
|
@ -631,33 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct chrono_format_checker {
|
template <typename Derived> struct null_chrono_spec_handler {
|
||||||
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
|
FMT_CONSTEXPR void unsupported() {
|
||||||
|
static_cast<Derived*>(this)->unsupported();
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_full_month() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_us_date() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_iso_date() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_iso_time() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_am_pm() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_duration_value() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
|
||||||
|
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> void on_text(const Char*, const Char*) {}
|
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
||||||
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
|
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
|
||||||
FMT_NORETURN void on_full_weekday() { report_no_date(); }
|
|
||||||
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
|
template <typename Char>
|
||||||
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||||
FMT_NORETURN void on_abbr_month() { report_no_date(); }
|
FMT_CONSTEXPR void on_24_hour(numeric_system) {}
|
||||||
FMT_NORETURN void on_full_month() { report_no_date(); }
|
FMT_CONSTEXPR void on_12_hour(numeric_system) {}
|
||||||
void on_24_hour(numeric_system) {}
|
FMT_CONSTEXPR void on_minute(numeric_system) {}
|
||||||
void on_12_hour(numeric_system) {}
|
FMT_CONSTEXPR void on_second(numeric_system) {}
|
||||||
void on_minute(numeric_system) {}
|
FMT_CONSTEXPR void on_12_hour_time() {}
|
||||||
void on_second(numeric_system) {}
|
FMT_CONSTEXPR void on_24_hour_time() {}
|
||||||
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
|
FMT_CONSTEXPR void on_iso_time() {}
|
||||||
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
|
FMT_CONSTEXPR void on_am_pm() {}
|
||||||
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
|
FMT_CONSTEXPR void on_duration_value() {}
|
||||||
FMT_NORETURN void on_us_date() { report_no_date(); }
|
FMT_CONSTEXPR void on_duration_unit() {}
|
||||||
FMT_NORETURN void on_iso_date() { report_no_date(); }
|
|
||||||
void on_12_hour_time() {}
|
|
||||||
void on_24_hour_time() {}
|
|
||||||
void on_iso_time() {}
|
|
||||||
void on_am_pm() {}
|
|
||||||
void on_duration_value() {}
|
|
||||||
void on_duration_unit() {}
|
|
||||||
FMT_NORETURN void on_utc_offset() { report_no_date(); }
|
|
||||||
FMT_NORETURN void on_tz_name() { report_no_date(); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
@ -681,7 +804,8 @@ inline bool isfinite(T value) {
|
||||||
// Converts value to int and checks that it's in the range [0, upper).
|
// Converts value to int and checks that it's in the range [0, upper).
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
inline int to_nonnegative_int(T value, int upper) {
|
inline int to_nonnegative_int(T value, int upper) {
|
||||||
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
|
FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper),
|
||||||
|
"invalid value");
|
||||||
(void)upper;
|
(void)upper;
|
||||||
return static_cast<int>(value);
|
return static_cast<int>(value);
|
||||||
}
|
}
|
||||||
|
@ -759,15 +883,21 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
||||||
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
|
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename Rep, typename OutputIt>
|
template <typename Char, typename Rep, typename OutputIt,
|
||||||
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
||||||
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0};
|
OutputIt format_duration_value(OutputIt out, Rep val, int) {
|
||||||
if (precision >= 0) return format_to(out, pr_f, val, precision);
|
return write<Char>(out, val);
|
||||||
const Char fp_f[] = {'{', ':', 'g', '}', 0};
|
|
||||||
const Char format[] = {'{', '}', 0};
|
|
||||||
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
|
||||||
val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Rep, typename OutputIt,
|
||||||
|
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
|
||||||
|
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
||||||
|
auto specs = basic_format_specs<Char>();
|
||||||
|
specs.precision = precision;
|
||||||
|
specs.type = precision > 0 ? 'f' : 'g';
|
||||||
|
return write<Char>(out, val, specs);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
|
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
|
||||||
return std::copy(unit.begin(), unit.end(), out);
|
return std::copy(unit.begin(), unit.end(), out);
|
||||||
|
@ -785,10 +915,15 @@ template <typename Char, typename Period, typename OutputIt>
|
||||||
OutputIt format_duration_unit(OutputIt out) {
|
OutputIt format_duration_unit(OutputIt out) {
|
||||||
if (const char* unit = get_units<Period>())
|
if (const char* unit = get_units<Period>())
|
||||||
return copy_unit(string_view(unit), out, Char());
|
return copy_unit(string_view(unit), out, Char());
|
||||||
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
*out++ = '[';
|
||||||
if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
|
out = write<Char>(out, Period::num);
|
||||||
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
if (const_check(Period::den != 1)) {
|
||||||
return format_to(out, num_def_f, Period::num, Period::den);
|
*out++ = '/';
|
||||||
|
out = write<Char>(out, Period::den);
|
||||||
|
}
|
||||||
|
*out++ = ']';
|
||||||
|
*out++ = 's';
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, typename OutputIt, typename Rep,
|
template <typename FormatContext, typename OutputIt, typename Rep,
|
||||||
|
@ -797,6 +932,7 @@ struct chrono_formatter {
|
||||||
FormatContext& context;
|
FormatContext& context;
|
||||||
OutputIt out;
|
OutputIt out;
|
||||||
int precision;
|
int precision;
|
||||||
|
bool localized = false;
|
||||||
// rep is unsigned to avoid overflow.
|
// rep is unsigned to avoid overflow.
|
||||||
using rep =
|
using rep =
|
||||||
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
|
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
|
||||||
|
@ -891,13 +1027,9 @@ struct chrono_formatter {
|
||||||
|
|
||||||
void format_localized(const tm& time, char format, char modifier = 0) {
|
void format_localized(const tm& time, char format, char modifier = 0) {
|
||||||
if (isnan(val)) return write_nan();
|
if (isnan(val)) return write_nan();
|
||||||
auto locale = context.locale().template get<std::locale>();
|
const auto& loc = localized ? context.locale().template get<std::locale>()
|
||||||
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
|
: std::locale::classic();
|
||||||
std::basic_ostringstream<char_type> os;
|
out = detail::write(out, time, loc, format, modifier);
|
||||||
os.imbue(locale);
|
|
||||||
facet.put(os, os, ' ', &time, format, modifier);
|
|
||||||
auto str = os.str();
|
|
||||||
std::copy(str.begin(), str.end(), out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_text(const char_type* begin, const char_type* end) {
|
void on_text(const char_type* begin, const char_type* end) {
|
||||||
|
@ -1010,17 +1142,59 @@ struct chrono_formatter {
|
||||||
out = format_duration_unit<char_type, Period>(out);
|
out = format_duration_unit<char_type, Period>(out);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
|
||||||
|
using weekday = std::chrono::weekday;
|
||||||
|
#else
|
||||||
|
// A fallback version of weekday.
|
||||||
|
class weekday {
|
||||||
|
private:
|
||||||
|
unsigned char value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
weekday() = default;
|
||||||
|
explicit constexpr weekday(unsigned wd) noexcept
|
||||||
|
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
|
||||||
|
constexpr unsigned c_encoding() const noexcept { return value; }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// A rudimentary weekday formatter.
|
||||||
|
template <> struct formatter<weekday> {
|
||||||
|
private:
|
||||||
|
bool localized = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
|
if (begin != end && *begin == 'L') {
|
||||||
|
++begin;
|
||||||
|
localized = true;
|
||||||
|
}
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) {
|
||||||
|
auto time = std::tm();
|
||||||
|
time.tm_wday = static_cast<int>(wd.c_encoding());
|
||||||
|
const auto& loc = localized ? ctx.locale().template get<std::locale>()
|
||||||
|
: std::locale::classic();
|
||||||
|
return detail::write(ctx.out(), time, loc, 'a');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Rep, typename Period, typename Char>
|
template <typename Rep, typename Period, typename Char>
|
||||||
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
private:
|
private:
|
||||||
basic_format_specs<Char> specs;
|
basic_format_specs<Char> specs;
|
||||||
int precision;
|
int precision = -1;
|
||||||
using arg_ref_type = detail::arg_ref<Char>;
|
using arg_ref_type = detail::arg_ref<Char>;
|
||||||
arg_ref_type width_ref;
|
arg_ref_type width_ref;
|
||||||
arg_ref_type precision_ref;
|
arg_ref_type precision_ref;
|
||||||
mutable basic_string_view<Char> format_str;
|
bool localized = false;
|
||||||
|
basic_string_view<Char> format_str;
|
||||||
using duration = std::chrono::duration<Rep, Period>;
|
using duration = std::chrono::duration<Rep, Period>;
|
||||||
|
|
||||||
struct spec_handler {
|
struct spec_handler {
|
||||||
|
@ -1043,17 +1217,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
|
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
|
||||||
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
|
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
|
||||||
void on_align(align_t align) { f.specs.align = align; }
|
f.specs.fill = fill;
|
||||||
void on_width(int width) { f.specs.width = width; }
|
}
|
||||||
void on_precision(int _precision) { f.precision = _precision; }
|
FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
|
||||||
void end_precision() {}
|
FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
|
||||||
|
FMT_CONSTEXPR void on_precision(int _precision) {
|
||||||
|
f.precision = _precision;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR void end_precision() {}
|
||||||
|
|
||||||
template <typename Id> void on_dynamic_width(Id arg_id) {
|
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
|
||||||
f.width_ref = make_arg_ref(arg_id);
|
f.width_ref = make_arg_ref(arg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Id> void on_dynamic_precision(Id arg_id) {
|
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
|
||||||
f.precision_ref = make_arg_ref(arg_id);
|
f.precision_ref = make_arg_ref(arg_id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1078,13 +1256,15 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
else
|
else
|
||||||
handler.on_error("precision not allowed for this argument type");
|
handler.on_error("precision not allowed for this argument type");
|
||||||
}
|
}
|
||||||
|
if (begin != end && *begin == 'L') {
|
||||||
|
++begin;
|
||||||
|
localized = true;
|
||||||
|
}
|
||||||
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
|
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
|
||||||
return {begin, end};
|
return {begin, end};
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
formatter() : precision(-1) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||||
-> decltype(ctx.begin()) {
|
-> decltype(ctx.begin()) {
|
||||||
auto range = do_parse(ctx);
|
auto range = do_parse(ctx);
|
||||||
|
@ -1094,30 +1274,35 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(const duration& d, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto specs_copy = specs;
|
||||||
|
auto precision_copy = precision;
|
||||||
auto begin = format_str.begin(), end = format_str.end();
|
auto begin = format_str.begin(), end = format_str.end();
|
||||||
// As a possible future optimization, we could avoid extra copying if width
|
// As a possible future optimization, we could avoid extra copying if width
|
||||||
// is not specified.
|
// is not specified.
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
auto out = std::back_inserter(buf);
|
auto out = std::back_inserter(buf);
|
||||||
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
|
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
|
||||||
ctx);
|
width_ref, ctx);
|
||||||
detail::handle_dynamic_spec<detail::precision_checker>(precision,
|
detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
|
||||||
precision_ref, ctx);
|
precision_ref, ctx);
|
||||||
if (begin == end || *begin == '}') {
|
if (begin == end || *begin == '}') {
|
||||||
out = detail::format_duration_value<Char>(out, d.count(), precision);
|
out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
|
||||||
detail::format_duration_unit<Char, Period>(out);
|
detail::format_duration_unit<Char, Period>(out);
|
||||||
} else {
|
} else {
|
||||||
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
||||||
ctx, out, d);
|
ctx, out, d);
|
||||||
f.precision = precision;
|
f.precision = precision_copy;
|
||||||
parse_chrono_format(begin, end, f);
|
f.localized = localized;
|
||||||
|
detail::parse_chrono_format(begin, end, f);
|
||||||
}
|
}
|
||||||
return detail::write(
|
return detail::write(
|
||||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_CHRONO_H_
|
#endif // FMT_CHRONO_H_
|
||||||
|
|
173
externals/fmt/include/fmt/color.h
vendored
173
externals/fmt/include/fmt/color.h
vendored
|
@ -10,7 +10,15 @@
|
||||||
|
|
||||||
#include "format.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_BEGIN_NAMESPACE
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
enum class color : uint32_t {
|
enum class color : uint32_t {
|
||||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
@ -198,7 +206,7 @@ struct rgb {
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
// color is a struct of either a rgb color or a terminal color.
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
struct color_type {
|
struct color_type {
|
||||||
|
@ -221,9 +229,10 @@ struct color_type {
|
||||||
uint32_t rgb_color;
|
uint32_t rgb_color;
|
||||||
} value;
|
} value;
|
||||||
};
|
};
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// Experimental text formatting support.
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
/** A text style consisting of foreground and background colors and emphasis. */
|
||||||
class text_style {
|
class text_style {
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
@ -260,33 +269,14 @@ class text_style {
|
||||||
return lhs |= rhs;
|
return lhs |= rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
|
||||||
if (!set_foreground_color) {
|
const text_style& rhs) {
|
||||||
set_foreground_color = rhs.set_foreground_color;
|
return and_assign(rhs);
|
||||||
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 text_style operator&(text_style lhs,
|
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
|
||||||
const text_style& rhs) {
|
operator&(text_style lhs, const text_style& rhs) {
|
||||||
return lhs &= rhs;
|
return lhs.and_assign(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
@ -326,8 +316,34 @@ class text_style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||||
FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||||
FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
@ -338,19 +354,22 @@ class text_style {
|
||||||
emphasis ems;
|
emphasis ems;
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
/** Creates a text style from the foreground (text) color. */
|
||||||
return text_style(/*is_foreground=*/true, foreground);
|
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(true, foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
/** Creates a text style from the background color. */
|
||||||
return text_style(/*is_foreground=*/false, background);
|
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(false, background);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
|
||||||
|
emphasis rhs) FMT_NOEXCEPT {
|
||||||
return text_style(lhs) | rhs;
|
return text_style(lhs) | rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> struct ansi_color_escape {
|
template <typename Char> struct ansi_color_escape {
|
||||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||||
|
@ -358,7 +377,7 @@ template <typename Char> struct ansi_color_escape {
|
||||||
// If we have a terminal color, we need to output another escape code
|
// If we have a terminal color, we need to output another escape code
|
||||||
// sequence.
|
// sequence.
|
||||||
if (!text_color.is_rgb) {
|
if (!text_color.is_rgb) {
|
||||||
bool is_background = esc == detail::data::background_color;
|
bool is_background = esc == string_view("\x1b[48;2;");
|
||||||
uint32_t value = text_color.value.term_color;
|
uint32_t value = text_color.value.term_color;
|
||||||
// Background ASCII codes are the same as the foreground ones but with
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
// 10 more.
|
// 10 more.
|
||||||
|
@ -411,7 +430,7 @@ template <typename Char> struct ansi_color_escape {
|
||||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
|
||||||
return buffer + std::char_traits<Char>::length(buffer);
|
return buffer + std::char_traits<Char>::length(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,13 +449,13 @@ template <typename Char> struct ansi_color_escape {
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||||
detail::color_type foreground) FMT_NOEXCEPT {
|
detail::color_type foreground) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
|
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||||
detail::color_type background) FMT_NOEXCEPT {
|
detail::color_type background) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(background, detail::data::background_color);
|
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -455,24 +474,23 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||||
fputs(detail::data::reset_color, stream);
|
fputs("\x1b[0m", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||||
fputs(detail::data::wreset_color, stream);
|
fputs(L"\x1b[0m", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
const char* begin = data::reset_color;
|
auto reset_color = string_view("\x1b[0m");
|
||||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
buffer.append(reset_color.begin(), reset_color.end());
|
||||||
buffer.append(begin, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
bool has_style = false;
|
bool has_style = false;
|
||||||
if (ts.has_emphasis()) {
|
if (ts.has_emphasis()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
|
@ -492,11 +510,12 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
||||||
detail::vformat_to(buf, format_str, args);
|
detail::vformat_to(buf, format_str, args);
|
||||||
if (has_style) detail::reset_color<Char>(buf);
|
if (has_style) detail::reset_color<Char>(buf);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
buf.push_back(Char(0));
|
buf.push_back(Char(0));
|
||||||
|
@ -504,28 +523,34 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
\rst
|
||||||
Formats a string and prints it to the specified file stream using ANSI
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
escape sequences to specify text formatting.
|
escape sequences to specify text formatting.
|
||||||
Example:
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
detail::check_format_string<Args...>(format_str);
|
vprint(f, ts, format_str,
|
||||||
using context = buffer_context<char_t<S>>;
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
\rst
|
||||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
specify text formatting.
|
specify text formatting.
|
||||||
Example:
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
|
@ -557,10 +582,46 @@ inline std::basic_string<Char> vformat(
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return vformat(ts, to_string_view(format_str),
|
return fmt::vformat(ts, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
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
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_COLOR_H_
|
#endif // FMT_COLOR_H_
|
||||||
|
|
846
externals/fmt/include/fmt/compile.h
vendored
846
externals/fmt/include/fmt/compile.h
vendored
|
@ -8,13 +8,135 @@
|
||||||
#ifndef FMT_COMPILE_H_
|
#ifndef FMT_COMPILE_H_
|
||||||
#define FMT_COMPILE_H_
|
#define FMT_COMPILE_H_
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
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.
|
// A compile-time string which is compiled into fast formatting code.
|
||||||
class compiled_string {};
|
class compiled_string {};
|
||||||
|
|
||||||
|
@ -34,341 +156,36 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
|
#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>
|
template <typename T, typename... Tail>
|
||||||
const T& first(const T& value, const Tail&...) {
|
const T& first(const T& value, const Tail&...) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of a compiled format string. It can be either literal text or a
|
|
||||||
// replacement field.
|
|
||||||
template <typename Char> struct format_part {
|
|
||||||
enum class kind { arg_index, arg_name, text, replacement };
|
|
||||||
|
|
||||||
struct replacement {
|
|
||||||
arg_ref<Char> arg_id;
|
|
||||||
dynamic_format_specs<Char> specs;
|
|
||||||
};
|
|
||||||
|
|
||||||
kind part_kind;
|
|
||||||
union value {
|
|
||||||
int arg_index;
|
|
||||||
basic_string_view<Char> str;
|
|
||||||
replacement repl;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
|
|
||||||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
|
|
||||||
FMT_CONSTEXPR value(replacement r) : repl(r) {}
|
|
||||||
} val;
|
|
||||||
// Position past the end of the argument id.
|
|
||||||
const Char* arg_id_end = nullptr;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
|
|
||||||
: part_kind(k), val(v) {}
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
|
||||||
return format_part(kind::arg_index, index);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
|
|
||||||
return format_part(kind::arg_name, name);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
|
|
||||||
return format_part(kind::text, text);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
|
||||||
return format_part(kind::replacement, repl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct part_counter {
|
|
||||||
unsigned num_parts = 0;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
||||||
if (begin != end) ++num_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
|
||||||
return ++num_parts, 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
// Find the matching brace.
|
|
||||||
unsigned brace_counter = 0;
|
|
||||||
for (; begin != end; ++begin) {
|
|
||||||
if (*begin == '{') {
|
|
||||||
++brace_counter;
|
|
||||||
} else if (*begin == '}') {
|
|
||||||
if (brace_counter == 0u) break;
|
|
||||||
--brace_counter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char*) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Counts the number of parts in a format string.
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
|
||||||
part_counter<Char> counter;
|
|
||||||
parse_format_string<true>(format_str, counter);
|
|
||||||
return counter.num_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename PartHandler>
|
|
||||||
class format_string_compiler : public error_handler {
|
|
||||||
private:
|
|
||||||
using part = format_part<Char>;
|
|
||||||
|
|
||||||
PartHandler handler_;
|
|
||||||
part part_;
|
|
||||||
basic_string_view<Char> format_str_;
|
|
||||||
basic_format_parse_context<Char> parse_context_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
|
||||||
PartHandler handler)
|
|
||||||
: handler_(handler),
|
|
||||||
format_str_(format_str),
|
|
||||||
parse_context_(format_str) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
||||||
if (begin != end)
|
|
||||||
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() {
|
|
||||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int id) {
|
|
||||||
parse_context_.check_arg_id(id);
|
|
||||||
part_ = part::make_arg_index(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
|
|
||||||
part_ = part::make_arg_name(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
|
|
||||||
part_.arg_id_end = ptr;
|
|
||||||
handler_(part_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
auto repl = typename part::replacement();
|
|
||||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
|
||||||
repl.specs, parse_context_);
|
|
||||||
auto it = parse_format_specs(begin, end, handler);
|
|
||||||
if (*it != '}') on_error("missing '}' in format string");
|
|
||||||
repl.arg_id = part_.part_kind == part::kind::arg_index
|
|
||||||
? arg_ref<Char>(part_.val.arg_index)
|
|
||||||
: arg_ref<Char>(part_.val.str);
|
|
||||||
auto part = part::make_replacement(repl);
|
|
||||||
part.arg_id_end = begin;
|
|
||||||
handler_(part);
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compiles a format string and invokes handler(part) for each parsed part.
|
|
||||||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
|
|
||||||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
|
||||||
PartHandler handler) {
|
|
||||||
parse_format_string<IS_CONSTEXPR>(
|
|
||||||
format_str,
|
|
||||||
format_string_compiler<Char, PartHandler>(format_str, handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Context, typename Id>
|
|
||||||
void format_arg(
|
|
||||||
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
|
||||||
Context& ctx, Id arg_id) {
|
|
||||||
ctx.advance_to(visit_format_arg(
|
|
||||||
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
|
|
||||||
ctx.arg(arg_id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
|
||||||
namespace cf {
|
|
||||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
|
||||||
auto vformat_to(OutputIt out, CompiledFormat& cf,
|
|
||||||
basic_format_args<Context> args) -> typename Context::iterator {
|
|
||||||
using char_type = typename Context::char_type;
|
|
||||||
basic_format_parse_context<char_type> parse_ctx(
|
|
||||||
to_string_view(cf.format_str_));
|
|
||||||
Context ctx(out, args);
|
|
||||||
|
|
||||||
const auto& parts = cf.parts();
|
|
||||||
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
|
||||||
++part_it) {
|
|
||||||
const auto& part = *part_it;
|
|
||||||
const auto& value = part.val;
|
|
||||||
|
|
||||||
using format_part_t = format_part<char_type>;
|
|
||||||
switch (part.part_kind) {
|
|
||||||
case format_part_t::kind::text: {
|
|
||||||
const auto text = value.str;
|
|
||||||
auto output = ctx.out();
|
|
||||||
auto&& it = reserve(output, text.size());
|
|
||||||
it = std::copy_n(text.begin(), text.size(), it);
|
|
||||||
ctx.advance_to(output);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_index:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_name:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::replacement: {
|
|
||||||
const auto& arg_id_value = value.repl.arg_id.val;
|
|
||||||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
|
|
||||||
? ctx.arg(arg_id_value.index)
|
|
||||||
: ctx.arg(arg_id_value.name);
|
|
||||||
|
|
||||||
auto specs = value.repl.specs;
|
|
||||||
|
|
||||||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
|
|
||||||
handle_dynamic_spec<precision_checker>(specs.precision,
|
|
||||||
specs.precision_ref, ctx);
|
|
||||||
|
|
||||||
error_handler h;
|
|
||||||
numeric_specs_checker<error_handler> checker(h, arg.type());
|
|
||||||
if (specs.align == align::numeric) checker.require_numeric_argument();
|
|
||||||
if (specs.sign != sign::none) checker.check_sign();
|
|
||||||
if (specs.alt) checker.require_numeric_argument();
|
|
||||||
if (specs.precision >= 0) checker.check_precision();
|
|
||||||
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
ctx.advance_to(
|
|
||||||
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
|
|
||||||
ctx, nullptr, &specs),
|
|
||||||
arg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
} // namespace cf
|
|
||||||
|
|
||||||
struct basic_compiled_format {};
|
|
||||||
|
|
||||||
template <typename S, typename = void>
|
|
||||||
struct compiled_format_base : basic_compiled_format {
|
|
||||||
using char_type = char_t<S>;
|
|
||||||
using parts_container = std::vector<detail::format_part<char_type>>;
|
|
||||||
|
|
||||||
parts_container compiled_parts;
|
|
||||||
|
|
||||||
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
|
||||||
compile_format_string<false>(format_str,
|
|
||||||
[this](const format_part<char_type>& part) {
|
|
||||||
compiled_parts.push_back(part);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts_container& parts() const { return compiled_parts; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, unsigned N> struct format_part_array {
|
|
||||||
format_part<Char> data[N] = {};
|
|
||||||
FMT_CONSTEXPR format_part_array() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, unsigned N>
|
|
||||||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
|
||||||
basic_string_view<Char> format_str) {
|
|
||||||
format_part_array<Char, N> parts;
|
|
||||||
unsigned counter = 0;
|
|
||||||
// This is not a lambda for compatibility with older compilers.
|
|
||||||
struct {
|
|
||||||
format_part<Char>* parts;
|
|
||||||
unsigned* counter;
|
|
||||||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
|
||||||
parts[(*counter)++] = part;
|
|
||||||
}
|
|
||||||
} collector{parts.data, &counter};
|
|
||||||
compile_format_string<true>(format_str, collector);
|
|
||||||
if (counter < N) {
|
|
||||||
parts.data[counter] =
|
|
||||||
format_part<Char>::make_text(basic_string_view<Char>());
|
|
||||||
}
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
|
||||||
return (a < b) ? b : a;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S>
|
|
||||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
|
||||||
: basic_compiled_format {
|
|
||||||
using char_type = char_t<S>;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
|
||||||
|
|
||||||
// Workaround for old compilers. Format string compilation will not be
|
|
||||||
// performed there anyway.
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
|
||||||
constexpr_max(count_parts(to_string_view(S())), 1u);
|
|
||||||
#else
|
|
||||||
static const unsigned num_format_parts = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using parts_container = format_part<char_type>[num_format_parts];
|
|
||||||
|
|
||||||
const parts_container& parts() const {
|
|
||||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
|
||||||
compile_to_parts<char_type, num_format_parts>(
|
|
||||||
detail::to_string_view(S()));
|
|
||||||
return compiled_parts.data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
|
||||||
class compiled_format : private compiled_format_base<S> {
|
|
||||||
public:
|
|
||||||
using typename compiled_format_base<S>::char_type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
basic_string_view<char_type> format_str_;
|
|
||||||
|
|
||||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
|
||||||
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
|
|
||||||
basic_format_args<Context> args) ->
|
|
||||||
typename Context::iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
compiled_format() = delete;
|
|
||||||
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
|
||||||
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#ifdef __cpp_if_constexpr
|
||||||
template <typename... Args> struct type_list {};
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
template <int N, typename T, typename... Args>
|
template <int N, typename T, typename... Args>
|
||||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
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");
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
|
@ -376,6 +193,12 @@ constexpr const auto& get(const T& first, const Args&... rest) {
|
||||||
return get<N - 1>(rest...);
|
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> struct get_type_impl;
|
||||||
|
|
||||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||||
|
@ -392,7 +215,7 @@ template <typename Char> struct text {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&...) const {
|
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||||
return write<Char>(out, data);
|
return write<Char>(out, data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -406,32 +229,87 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||||
return {{&s[pos], 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.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename T, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||||
const T& arg = get<N>(args...);
|
|
||||||
return write<Char>(out, arg);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename T, int N>
|
template <typename Char, typename T, int N>
|
||||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
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.
|
// A replacement field that refers to argument N and has format specifiers.
|
||||||
template <typename Char, typename T, int N> struct spec_field {
|
template <typename Char, typename T, int N> struct spec_field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
mutable formatter<T, Char> fmt;
|
formatter<T, Char> fmt;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
const Args&... args) const {
|
||||||
const T& arg = get<N>(args...);
|
const auto& vargs =
|
||||||
basic_format_context<OutputIt, Char> ctx(out, {});
|
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
return fmt.format(arg, ctx);
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
|
return fmt.format(get_arg_checked<T, N>(args...), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -444,7 +322,7 @@ template <typename L, typename R> struct concat {
|
||||||
using char_type = typename L::char_type;
|
using char_type = typename L::char_type;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
out = lhs.format(out, args...);
|
out = lhs.format(out, args...);
|
||||||
return rhs.format(out, args...);
|
return rhs.format(out, args...);
|
||||||
}
|
}
|
||||||
|
@ -489,16 +367,80 @@ constexpr auto parse_tail(T head, S format_str) {
|
||||||
template <typename T, typename Char> struct parse_specs_result {
|
template <typename T, typename Char> struct parse_specs_result {
|
||||||
formatter<T, Char> fmt;
|
formatter<T, Char> fmt;
|
||||||
size_t end;
|
size_t end;
|
||||||
|
int next_arg_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr int manual_indexing_id = -1;
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
size_t pos) {
|
size_t pos, int next_arg_id) {
|
||||||
str.remove_prefix(pos);
|
str.remove_prefix(pos);
|
||||||
auto ctx = basic_format_parse_context<Char>(str);
|
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
|
||||||
auto f = formatter<T, Char>();
|
auto f = formatter<T, Char>();
|
||||||
auto end = f.parse(ctx);
|
auto end = f.parse(ctx);
|
||||||
return {f, pos + (end - str.data()) + 1};
|
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
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
|
@ -506,160 +448,192 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
template <typename Args, size_t POS, int ID, typename S>
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
constexpr auto compile_format_string(S format_str) {
|
constexpr auto compile_format_string(S format_str) {
|
||||||
using char_type = typename S::char_type;
|
using char_type = typename S::char_type;
|
||||||
constexpr basic_string_view<char_type> str = format_str;
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
if constexpr (str[POS] == '{') {
|
if constexpr (str[POS] == '{') {
|
||||||
if (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '{' in format string");
|
throw format_error("unmatched '{' in format string");
|
||||||
if constexpr (str[POS + 1] == '{') {
|
if constexpr (str[POS + 1] == '{') {
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else if constexpr (str[POS + 1] == '}') {
|
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||||
using type = get_type<ID, Args>;
|
static_assert(ID != manual_indexing_id,
|
||||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
"cannot switch from manual to automatic argument indexing");
|
||||||
format_str);
|
constexpr auto next_id =
|
||||||
} else if constexpr (str[POS + 1] == ':') {
|
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||||
using type = get_type<ID, Args>;
|
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||||
constexpr auto result = parse_specs<type>(str, POS + 2);
|
POS + 1, ID, next_id>(
|
||||||
return parse_tail<Args, result.end, ID + 1>(
|
format_str);
|
||||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
|
||||||
} else {
|
} else {
|
||||||
return unknown_format();
|
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] == '}') {
|
} else if constexpr (str[POS] == '}') {
|
||||||
if (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '}' in format string");
|
throw format_error("unmatched '}' in format string");
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
if constexpr (end - POS > 1) {
|
||||||
format_str);
|
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,
|
template <typename... Args, typename S,
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value ||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
detail::is_compiled_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) {
|
constexpr auto compile(S format_str) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
|
||||||
if constexpr (str.size() == 0) {
|
if constexpr (str.size() == 0) {
|
||||||
return detail::make_text(str, 0, 0);
|
return detail::make_text(str, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto result =
|
constexpr auto result =
|
||||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||||
format_str);
|
format_str);
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
return result;
|
||||||
detail::unknown_format>()) {
|
|
||||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
} else {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
template <typename... Args, typename S,
|
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
|
|
||||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
}
|
|
||||||
#endif // __cpp_if_constexpr
|
#endif // __cpp_if_constexpr
|
||||||
|
|
||||||
// Compiles the format string which must be a string literal.
|
|
||||||
template <typename... Args, typename Char, size_t N>
|
|
||||||
auto compile(const Char (&format_str)[N])
|
|
||||||
-> detail::compiled_format<const Char*, Args...> {
|
|
||||||
return detail::compiled_format<const Char*, Args...>(
|
|
||||||
basic_string_view<Char>(format_str, N - 1));
|
|
||||||
}
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
// DEPRECATED! use FMT_COMPILE instead.
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
template <typename... Args>
|
|
||||||
FMT_DEPRECATED auto compile(const Args&... args)
|
|
||||||
-> decltype(detail::compile(args...)) {
|
|
||||||
return detail::compile(args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
#ifdef __cpp_if_constexpr
|
||||||
# ifdef __cpp_if_constexpr
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto s = std::basic_string<Char>();
|
||||||
detail::buffer<Char>& base = buffer;
|
cf.format(std::back_inserter(s), args...);
|
||||||
cf.format(std::back_inserter(base), args...);
|
return s;
|
||||||
return to_string(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return cf.format(out, args...);
|
return cf.format(out, args...);
|
||||||
}
|
}
|
||||||
# endif // __cpp_if_constexpr
|
|
||||||
#endif // FMT_USE_CONSTEXPR
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
|
||||||
typename Char = typename CompiledFormat::char_type,
|
|
||||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
|
||||||
CompiledFormat>::value)>
|
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
using context = buffer_context<Char>;
|
|
||||||
detail::buffer<Char>& base = buffer;
|
|
||||||
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
return to_string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = S();
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||||
return fmt::to_string(detail::first(args...));
|
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());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
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()),
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
std::forward<Args>(args)...);
|
||||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
} else {
|
||||||
CompiledFormat>::value)>
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
}
|
||||||
const Args&... args) {
|
|
||||||
using char_type = typename CompiledFormat::char_type;
|
|
||||||
using context = format_context_t<OutputIt, char_type>;
|
|
||||||
return detail::cf::vformat_to<context>(out, cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
return format_to(out, compiled, args...);
|
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 <
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
typename OutputIt, typename CompiledFormat, typename... Args,
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
|
||||||
detail::basic_compiled_format, CompiledFormat>::value)>
|
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
const CompiledFormat& cf,
|
const S& format_str, Args&&... args) {
|
||||||
const Args&... args) {
|
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
|
||||||
auto it =
|
std::forward<Args>(args)...);
|
||||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
|
||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename S, typename... Args,
|
||||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
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
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_COMPILE_H_
|
#endif // FMT_COMPILE_H_
|
||||||
|
|
2372
externals/fmt/include/fmt/core.h
vendored
2372
externals/fmt/include/fmt/core.h
vendored
File diff suppressed because it is too large
Load diff
2306
externals/fmt/include/fmt/format-inl.h
vendored
2306
externals/fmt/include/fmt/format-inl.h
vendored
File diff suppressed because it is too large
Load diff
4168
externals/fmt/include/fmt/format.h
vendored
4168
externals/fmt/include/fmt/format.h
vendored
File diff suppressed because it is too large
Load diff
80
externals/fmt/include/fmt/locale.h
vendored
80
externals/fmt/include/fmt/locale.h
vendored
|
@ -1,78 +1,2 @@
|
||||||
// Formatting library for C++ - std::locale support
|
#include "xchar.h"
|
||||||
//
|
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_LOCALE_H_
|
|
||||||
#define FMT_LOCALE_H_
|
|
||||||
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
template <typename Char>
|
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
|
||||||
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
|
|
||||||
args, detail::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
std::basic_string<Char> vformat(
|
|
||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
detail::vformat_to(loc, buffer, format_str, args);
|
|
||||||
return fmt::to_string(buffer);
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> vformat(
|
|
||||||
const std::locale& loc, const S& format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
return detail::vformat(loc, to_string_view(format_str), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
|
||||||
const S& format_str, Args&&... args) {
|
|
||||||
return detail::vformat(
|
|
||||||
loc, to_string_view(format_str),
|
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
|
||||||
typename Char = enable_if_t<
|
|
||||||
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
|
||||||
inline OutputIt vformat_to(
|
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
|
||||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
|
||||||
using af = detail::arg_formatter<OutputIt, Char>;
|
|
||||||
return vformat_to<af>(out, to_string_view(format_str), args,
|
|
||||||
detail::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
|
||||||
detail::is_string<S>::value)>
|
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
|
||||||
const S& format_str, Args&&... args) {
|
|
||||||
detail::check_format_string<Args...>(format_str);
|
|
||||||
using context = format_context_t<OutputIt, char_t<S>>;
|
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
return vformat_to(out, loc, to_string_view(format_str),
|
|
||||||
basic_format_args<context>(as));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_LOCALE_H_
|
|
||||||
|
|
259
externals/fmt/include/fmt/os.h
vendored
259
externals/fmt/include/fmt/os.h
vendored
|
@ -8,16 +8,12 @@
|
||||||
#ifndef FMT_OS_H_
|
#ifndef FMT_OS_H_
|
||||||
#define FMT_OS_H_
|
#define FMT_OS_H_
|
||||||
|
|
||||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
||||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
|
||||||
# undef __STRICT_ANSI__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <clocale> // for locale_t
|
#include <clocale> // locale_t
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib> // for strtod_l
|
#include <cstdlib> // strtod_l
|
||||||
|
#include <system_error> // std::system_error
|
||||||
|
|
||||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
|
@ -29,7 +25,8 @@
|
||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
#endif
|
||||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# include <fcntl.h> // for O_RDONLY
|
# include <fcntl.h> // for O_RDONLY
|
||||||
# define FMT_USE_FCNTL 1
|
# define FMT_USE_FCNTL 1
|
||||||
|
@ -73,6 +70,7 @@
|
||||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
|
@ -121,19 +119,28 @@ template <typename Char> class basic_cstring_view {
|
||||||
using cstring_view = basic_cstring_view<char>;
|
using cstring_view = basic_cstring_view<char>;
|
||||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||||
|
|
||||||
// An error code.
|
template <typename Char> struct formatter<std::error_code, Char> {
|
||||||
class error_code {
|
template <typename ParseContext>
|
||||||
private:
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
int value_;
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
template <typename FormatContext>
|
||||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
int get() const FMT_NOEXCEPT { return value_; }
|
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
|
#ifdef _WIN32
|
||||||
namespace detail {
|
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
// A converter from UTF-16 to UTF-8.
|
// A converter from UTF-16 to UTF-8.
|
||||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||||
class utf16_to_utf8 {
|
class utf16_to_utf8 {
|
||||||
|
@ -142,7 +149,7 @@ class utf16_to_utf8 {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
utf16_to_utf8() {}
|
utf16_to_utf8() {}
|
||||||
FMT_API explicit utf16_to_utf8(wstring_view s);
|
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
|
||||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||||
size_t size() const { return buffer_.size() - 1; }
|
size_t size() const { return buffer_.size() - 1; }
|
||||||
const char* c_str() const { return &buffer_[0]; }
|
const char* c_str() const { return &buffer_[0]; }
|
||||||
|
@ -151,59 +158,68 @@ class utf16_to_utf8 {
|
||||||
// Performs conversion returning a system error code instead of
|
// Performs conversion returning a system error code instead of
|
||||||
// throwing exception on conversion error. This method may still throw
|
// throwing exception on conversion error. This method may still throw
|
||||||
// in case of memory allocation error.
|
// in case of memory allocation error.
|
||||||
FMT_API int convert(wstring_view s);
|
FMT_API int convert(basic_string_view<wchar_t> s);
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT;
|
const char* message) FMT_NOEXCEPT;
|
||||||
} // namespace detail
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
/** A Windows error. */
|
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||||
class windows_error : public system_error {
|
format_args args);
|
||||||
private:
|
|
||||||
FMT_API void init(int error_code, string_view format_str, format_args args);
|
|
||||||
|
|
||||||
public:
|
/**
|
||||||
/**
|
\rst
|
||||||
\rst
|
Constructs a :class:`std::system_error` object with the description
|
||||||
Constructs a :class:`fmt::windows_error` object with the description
|
of the form
|
||||||
of the form
|
|
||||||
|
|
||||||
.. parsed-literal::
|
.. parsed-literal::
|
||||||
*<message>*: *<system-message>*
|
*<message>*: *<system-message>*
|
||||||
|
|
||||||
where *<message>* is the formatted message and *<system-message>* is the
|
where *<message>* is the formatted message and *<system-message>* is the
|
||||||
system message corresponding to the error code.
|
system message corresponding to the error code.
|
||||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
*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
|
If *error_code* is not a valid error code such as -1, the system message
|
||||||
will look like "error -1".
|
will look like "error -1".
|
||||||
|
|
||||||
**Example**::
|
**Example**::
|
||||||
|
|
||||||
// This throws a windows_error with the description
|
// This throws a system_error with the description
|
||||||
// cannot open file 'madeup': The system cannot find the file specified.
|
// cannot open file 'madeup': The system cannot find the file specified.
|
||||||
// or similar (system message may vary).
|
// or similar (system message may vary).
|
||||||
const char *filename = "madeup";
|
const char *filename = "madeup";
|
||||||
LPOFSTRUCT of = LPOFSTRUCT();
|
LPOFSTRUCT of = LPOFSTRUCT();
|
||||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||||
if (file == HFILE_ERROR) {
|
if (file == HFILE_ERROR) {
|
||||||
throw fmt::windows_error(GetLastError(),
|
throw fmt::windows_error(GetLastError(),
|
||||||
"cannot open file '{}'", filename);
|
"cannot open file '{}'", filename);
|
||||||
}
|
}
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
windows_error(int error_code, string_view message, const Args&... args) {
|
std::system_error windows_error(int error_code, string_view message,
|
||||||
init(error_code, message, make_format_args(args...));
|
const Args&... args) {
|
||||||
}
|
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||||
};
|
}
|
||||||
|
|
||||||
// Reports a Windows error without throwing an exception.
|
// Reports a Windows error without throwing an exception.
|
||||||
// Can be used to report errors from destructors.
|
// Can be used to report errors from destructors.
|
||||||
FMT_API void report_windows_error(int error_code,
|
FMT_API void report_windows_error(int error_code,
|
||||||
string_view message) FMT_NOEXCEPT;
|
const char* message) FMT_NOEXCEPT;
|
||||||
|
#else
|
||||||
|
inline const std::error_category& system_category() FMT_NOEXCEPT {
|
||||||
|
return std::system_category();
|
||||||
|
}
|
||||||
#endif // _WIN32
|
#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.
|
// A buffered file.
|
||||||
class buffered_file {
|
class buffered_file {
|
||||||
private:
|
private:
|
||||||
|
@ -254,7 +270,7 @@ class buffered_file {
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
inline void print(string_view format_str, const Args&... args) {
|
inline void print(string_view format_str, const Args&... args) {
|
||||||
vprint(format_str, make_format_args(args...));
|
vprint(format_str, fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -278,7 +294,9 @@ class file {
|
||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||||
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
|
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.
|
// Constructs a file object which doesn't represent any file.
|
||||||
|
@ -293,7 +311,8 @@ class file {
|
||||||
|
|
||||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||||
|
|
||||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
// Move assignment is not noexcept because close may throw.
|
||||||
|
file& operator=(file&& other) {
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
|
@ -329,7 +348,7 @@ class file {
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
|
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
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
// and writing respectively.
|
// and writing respectively.
|
||||||
|
@ -343,62 +362,107 @@ class file {
|
||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
class direct_buffered_file;
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
struct buffer_size {
|
||||||
void print(direct_buffered_file& f, const S& format_str,
|
buffer_size() = default;
|
||||||
const Args&... args);
|
size_t value = 0;
|
||||||
|
buffer_size operator=(size_t val) const {
|
||||||
|
auto bs = buffer_size();
|
||||||
|
bs.value = val;
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// A buffered file with a direct buffer access and no synchronization.
|
struct ostream_params {
|
||||||
class direct_buffered_file {
|
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
|
||||||
|
|
||||||
|
static 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:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
enum { buffer_size = 4096 };
|
|
||||||
char buffer_[buffer_size];
|
|
||||||
int pos_;
|
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
if (pos_ == 0) return;
|
if (size() == 0) return;
|
||||||
file_.write(buffer_, pos_);
|
file_.write(data(), size());
|
||||||
pos_ = 0;
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int free_capacity() const { return buffer_size - pos_; }
|
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:
|
public:
|
||||||
direct_buffered_file(cstring_view path, int oflag)
|
ostream(ostream&& other)
|
||||||
: file_(path, oflag), pos_(0) {}
|
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||||
|
file_(std::move(other.file_)) {
|
||||||
~direct_buffered_file() {
|
other.clear();
|
||||||
flush();
|
other.set(nullptr, 0);
|
||||||
}
|
}
|
||||||
|
~ostream() {
|
||||||
|
flush();
|
||||||
|
delete[] data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
flush();
|
flush();
|
||||||
file_.close();
|
file_.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
/**
|
||||||
friend void print(direct_buffered_file& f, const S& format_str,
|
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||||
const Args&... args) {
|
output to the file.
|
||||||
// We could avoid double buffering.
|
*/
|
||||||
auto buf = fmt::memory_buffer();
|
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||||
fmt::format_to(std::back_inserter(buf), format_str, args...);
|
vformat_to(detail::buffer_appender<char>(*this), fmt,
|
||||||
auto remaining_pos = 0;
|
fmt::make_format_args(args...));
|
||||||
auto remaining_size = buf.size();
|
|
||||||
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
|
|
||||||
auto size = f.free_capacity();
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
|
|
||||||
f.pos_ += size;
|
|
||||||
f.flush();
|
|
||||||
remaining_pos += size;
|
|
||||||
remaining_size -= size;
|
|
||||||
}
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
|
|
||||||
f.pos_ += static_cast<int>(remaining_size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\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
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
@ -445,6 +509,7 @@ class locale {
|
||||||
};
|
};
|
||||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_OS_H_
|
#endif // FMT_OS_H_
|
||||||
|
|
32
externals/fmt/include/fmt/ostream.h
vendored
32
externals/fmt/include/fmt/ostream.h
vendored
|
@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct converter {
|
||||||
|
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||||
private:
|
private:
|
||||||
// Hide all operator<< from std::basic_ostream<Char>.
|
void_t<> operator<<(converter);
|
||||||
void_t<> operator<<(null<>);
|
|
||||||
void_t<> operator<<(const Char*);
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
|
||||||
!std::is_enum<T>::value)>
|
|
||||||
void_t<> operator<<(T);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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
|
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||||
// std::ostream).
|
// std::ostream).
|
||||||
template <typename T, typename Char> class is_streamable {
|
template <typename T, typename Char> class is_streamable {
|
||||||
|
@ -75,6 +85,8 @@ template <typename T, typename Char> class is_streamable {
|
||||||
using result = decltype(test<T>(0));
|
using result = decltype(test<T>(0));
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
is_streamable() = default;
|
||||||
|
|
||||||
static const bool value = result::value;
|
static const bool value = result::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,7 +115,7 @@ void format_value(buffer<Char>& buf, const T& value,
|
||||||
#endif
|
#endif
|
||||||
output << value;
|
output << value;
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
buf.resize(buf.size());
|
buf.try_resize(buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
@ -139,6 +151,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
@ -156,11 +169,12 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
fmt::print(cerr, "Don't {}!", "panic");
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
|
FMT_MODULE_EXPORT
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
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) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint(os, to_string_view(format_str),
|
vprint(os, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
|
2
externals/fmt/include/fmt/posix.h
vendored
2
externals/fmt/include/fmt/posix.h
vendored
|
@ -1,2 +0,0 @@
|
||||||
#include "os.h"
|
|
||||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
|
469
externals/fmt/include/fmt/printf.h
vendored
469
externals/fmt/include/fmt/printf.h
vendored
|
@ -10,11 +10,54 @@
|
||||||
|
|
||||||
#include <algorithm> // std::max
|
#include <algorithm> // std::max
|
||||||
#include <limits> // std::numeric_limits
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
#include "ostream.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
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
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
// signed and unsigned integers.
|
// signed and unsigned integers.
|
||||||
|
@ -178,79 +221,34 @@ template <typename Char> class printf_width_handler {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Context>
|
// The ``printf`` argument formatter.
|
||||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// For printing into memory_buffer.
|
|
||||||
template <typename Char, typename Context>
|
|
||||||
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
return detail::vprintf(buf, format, args);
|
|
||||||
}
|
|
||||||
using detail::vprintf;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
The ``printf`` argument formatter.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename OutputIt, typename Char>
|
||||||
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
class printf_arg_formatter : public arg_formatter<Char> {
|
||||||
public:
|
|
||||||
using iterator = OutputIt;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using char_type = Char;
|
using base = arg_formatter<Char>;
|
||||||
using base = detail::arg_formatter_base<OutputIt, Char>;
|
|
||||||
using context_type = basic_printf_context<OutputIt, Char>;
|
using context_type = basic_printf_context<OutputIt, Char>;
|
||||||
|
using format_specs = basic_format_specs<Char>;
|
||||||
|
|
||||||
context_type& context_;
|
context_type& context_;
|
||||||
|
|
||||||
void write_null_pointer(char) {
|
OutputIt write_null_pointer(bool is_string = false) {
|
||||||
this->specs()->type = 0;
|
auto s = this->specs;
|
||||||
this->write("(nil)");
|
s.type = 0;
|
||||||
}
|
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||||
|
|
||||||
void write_null_pointer(wchar_t) {
|
|
||||||
this->specs()->type = 0;
|
|
||||||
this->write(L"(nil)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using format_specs = typename base::format_specs;
|
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); }
|
||||||
\rst
|
|
||||||
Constructs an argument formatter object.
|
|
||||||
*buffer* is a reference to the output buffer and *specs* contains format
|
|
||||||
specifier information for standard argument types.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
|
||||||
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||||
iterator operator()(T value) {
|
OutputIt operator()(T value) {
|
||||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||||
// use std::is_same instead.
|
// std::is_same instead.
|
||||||
if (std::is_same<T, bool>::value) {
|
if (std::is_same<T, Char>::value) {
|
||||||
format_specs& fmt_specs = *this->specs();
|
format_specs fmt_specs = this->specs;
|
||||||
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
|
|
||||||
fmt_specs.type = 0;
|
|
||||||
this->write(value != 0);
|
|
||||||
} else if (std::is_same<T, char_type>::value) {
|
|
||||||
format_specs& fmt_specs = *this->specs();
|
|
||||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||||
return (*this)(static_cast<int>(value));
|
return (*this)(static_cast<int>(value));
|
||||||
fmt_specs.sign = sign::none;
|
fmt_specs.sign = sign::none;
|
||||||
|
@ -260,138 +258,49 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||||
// ignored for non-numeric types
|
// ignored for non-numeric types
|
||||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||||
fmt_specs.align = align::right;
|
fmt_specs.align = align::right;
|
||||||
return base::operator()(value);
|
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||||
} else {
|
|
||||||
return base::operator()(value);
|
|
||||||
}
|
}
|
||||||
return this->out();
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
iterator operator()(T value) {
|
OutputIt operator()(T value) {
|
||||||
return base::operator()(value);
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a null-terminated C string. */
|
/** Formats a null-terminated C string. */
|
||||||
iterator operator()(const char* value) {
|
OutputIt operator()(const char* value) {
|
||||||
if (value)
|
if (value) return base::operator()(value);
|
||||||
base::operator()(value);
|
return write_null_pointer(this->specs.type != 'p');
|
||||||
else if (this->specs()->type == 'p')
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
else
|
|
||||||
this->write("(null)");
|
|
||||||
return this->out();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a null-terminated wide C string. */
|
/** Formats a null-terminated wide C string. */
|
||||||
iterator operator()(const wchar_t* value) {
|
OutputIt operator()(const wchar_t* value) {
|
||||||
if (value)
|
if (value) return base::operator()(value);
|
||||||
base::operator()(value);
|
return write_null_pointer(this->specs.type != 'p');
|
||||||
else if (this->specs()->type == 'p')
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
else
|
|
||||||
this->write(L"(null)");
|
|
||||||
return this->out();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator operator()(basic_string_view<char_type> value) {
|
OutputIt operator()(basic_string_view<Char> value) {
|
||||||
return base::operator()(value);
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator operator()(monostate value) { return base::operator()(value); }
|
|
||||||
|
|
||||||
/** Formats a pointer. */
|
/** Formats a pointer. */
|
||||||
iterator operator()(const void* value) {
|
OutputIt operator()(const void* value) {
|
||||||
if (value) return base::operator()(value);
|
return value ? base::operator()(value) : write_null_pointer();
|
||||||
this->specs()->type = 0;
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
return this->out();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
handle.format(context_.parse_context(), context_);
|
auto parse_ctx =
|
||||||
return this->out();
|
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
||||||
|
handle.format(parse_ctx, context_);
|
||||||
|
return this->out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct printf_formatter {
|
template <typename Char>
|
||||||
printf_formatter() = delete;
|
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
||||||
|
const Char* end) {
|
||||||
template <typename ParseContext>
|
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
||||||
detail::format_value(detail::get_container(ctx.out()), value);
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
This template formats data and writes the output through an output iterator.
|
|
||||||
*/
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
|
||||||
public:
|
|
||||||
/** The character type for the output. */
|
|
||||||
using char_type = Char;
|
|
||||||
using iterator = OutputIt;
|
|
||||||
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>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using format_specs = basic_format_specs<char_type>;
|
|
||||||
|
|
||||||
OutputIt out_;
|
|
||||||
basic_format_args<basic_printf_context> args_;
|
|
||||||
parse_context_type parse_ctx_;
|
|
||||||
|
|
||||||
static void parse_flags(format_specs& specs, const Char*& it,
|
|
||||||
const Char* end);
|
|
||||||
|
|
||||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
|
||||||
// argument.
|
|
||||||
format_arg get_arg(int arg_index = -1);
|
|
||||||
|
|
||||||
// Parses argument index, flags and width and returns the argument index.
|
|
||||||
int parse_header(const Char*& it, const Char* end, format_specs& specs);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\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_string_view<char_type> format_str,
|
|
||||||
basic_format_args<basic_printf_context> args)
|
|
||||||
: out_(out), args_(args), parse_ctx_(format_str) {}
|
|
||||||
|
|
||||||
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); }
|
|
||||||
|
|
||||||
parse_context_type& parse_context() { return parse_ctx_; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
parse_ctx_.on_error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the range. */
|
|
||||||
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
|
|
||||||
OutputIt format();
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
|
||||||
const Char*& it,
|
|
||||||
const Char* end) {
|
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
switch (*it) {
|
switch (*it) {
|
||||||
case '-':
|
case '-':
|
||||||
|
@ -417,35 +326,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename Char, typename GetArg>
|
||||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
int parse_header(const Char*& it, const Char* end,
|
||||||
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
basic_format_specs<Char>& specs, GetArg get_arg) {
|
||||||
if (arg_index < 0)
|
|
||||||
arg_index = parse_ctx_.next_arg_id();
|
|
||||||
else
|
|
||||||
parse_ctx_.check_arg_id(--arg_index);
|
|
||||||
return detail::get_arg(*this, arg_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
|
||||||
const Char* end,
|
|
||||||
format_specs& specs) {
|
|
||||||
int arg_index = -1;
|
int arg_index = -1;
|
||||||
char_type c = *it;
|
Char c = *it;
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
// preceded with '0' flag(s).
|
// preceded with '0' flag(s).
|
||||||
detail::error_handler eh;
|
int value = parse_nonnegative_int(it, end, -1);
|
||||||
int value = parse_nonnegative_int(it, end, eh);
|
|
||||||
if (it != end && *it == '$') { // value is an argument index
|
if (it != end && *it == '$') { // value is an argument index
|
||||||
++it;
|
++it;
|
||||||
arg_index = value;
|
arg_index = value != -1 ? value : max_value<int>();
|
||||||
} else {
|
} else {
|
||||||
if (c == '0') specs.fill[0] = '0';
|
if (c == '0') specs.fill[0] = '0';
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
// Nonzero value means that we parsed width and don't need to
|
// Nonzero value means that we parsed width and don't need to
|
||||||
// parse it or flags again, so return now.
|
// parse it or flags again, so return now.
|
||||||
|
if (value == -1) FMT_THROW(format_error("number is too big"));
|
||||||
specs.width = value;
|
specs.width = value;
|
||||||
return arg_index;
|
return arg_index;
|
||||||
}
|
}
|
||||||
|
@ -455,58 +353,76 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||||
// Parse width.
|
// Parse width.
|
||||||
if (it != end) {
|
if (it != end) {
|
||||||
if (*it >= '0' && *it <= '9') {
|
if (*it >= '0' && *it <= '9') {
|
||||||
detail::error_handler eh;
|
specs.width = parse_nonnegative_int(it, end, -1);
|
||||||
specs.width = parse_nonnegative_int(it, end, eh);
|
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
||||||
} else if (*it == '*') {
|
} else if (*it == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.width = static_cast<int>(visit_format_arg(
|
specs.width = static_cast<int>(visit_format_arg(
|
||||||
detail::printf_width_handler<char_type>(specs), get_arg()));
|
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arg_index;
|
return arg_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename Char, typename Context>
|
||||||
template <typename ArgFormatter>
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
basic_format_args<Context> args) {
|
||||||
auto out = this->out();
|
using OutputIt = buffer_appender<Char>;
|
||||||
const Char* start = parse_ctx_.begin();
|
auto out = OutputIt(buf);
|
||||||
const Char* end = parse_ctx_.end();
|
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;
|
auto it = start;
|
||||||
while (it != end) {
|
while (it != end) {
|
||||||
char_type c = *it++;
|
if (!detail::find<false, Char>(it, end, '%', it)) {
|
||||||
if (c != '%') continue;
|
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Char c = *it++;
|
||||||
if (it != end && *it == c) {
|
if (it != end && *it == c) {
|
||||||
out = std::copy(start, it, out);
|
out = detail::write(
|
||||||
|
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
|
||||||
start = ++it;
|
start = ++it;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out = std::copy(start, it - 1, out);
|
out = detail::write(out, basic_string_view<Char>(
|
||||||
|
start, detail::to_unsigned(it - 1 - start)));
|
||||||
|
|
||||||
format_specs specs;
|
basic_format_specs<Char> specs;
|
||||||
specs.align = align::right;
|
specs.align = align::right;
|
||||||
|
|
||||||
// Parse argument index, flags and width.
|
// Parse argument index, flags and width.
|
||||||
int arg_index = parse_header(it, end, specs);
|
int arg_index = parse_header(it, end, specs, get_arg);
|
||||||
if (arg_index == 0) on_error("argument not found");
|
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
if (it != end && *it == '.') {
|
if (it != end && *it == '.') {
|
||||||
++it;
|
++it;
|
||||||
c = it != end ? *it : 0;
|
c = it != end ? *it : 0;
|
||||||
if ('0' <= c && c <= '9') {
|
if ('0' <= c && c <= '9') {
|
||||||
detail::error_handler eh;
|
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
|
||||||
} else if (c == '*') {
|
} else if (c == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.precision = static_cast<int>(
|
specs.precision = static_cast<int>(
|
||||||
visit_format_arg(detail::printf_precision_handler(), get_arg()));
|
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
|
||||||
} else {
|
} else {
|
||||||
specs.precision = 0;
|
specs.precision = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format_arg arg = get_arg(arg_index);
|
auto arg = get_arg(arg_index);
|
||||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||||
// specified, the '0' flag is ignored
|
// specified, the '0' flag is ignored
|
||||||
if (specs.precision >= 0 && arg.is_integral())
|
if (specs.precision >= 0 && arg.is_integral())
|
||||||
|
@ -516,9 +432,10 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||||
auto str_end = str + specs.precision;
|
auto str_end = str + specs.precision;
|
||||||
auto nul = std::find(str, str_end, Char());
|
auto nul = std::find(str, str_end, Char());
|
||||||
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
|
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
||||||
str,
|
basic_string_view<Char>(
|
||||||
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
|
str, detail::to_unsigned(nul != str_end ? nul - str
|
||||||
|
: specs.precision)));
|
||||||
}
|
}
|
||||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||||
specs.alt = false;
|
specs.alt = false;
|
||||||
|
@ -532,7 +449,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
// Parse length and convert the argument to the required type.
|
||||||
c = it != end ? *it++ : 0;
|
c = it != end ? *it++ : 0;
|
||||||
char_type t = it != end ? *it : 0;
|
Char t = it != end ? *it : 0;
|
||||||
using detail::convert_arg;
|
using detail::convert_arg;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
|
@ -582,8 +499,9 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
specs.type = 'd';
|
specs.type = 'd';
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
|
visit_format_arg(
|
||||||
arg);
|
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
||||||
|
arg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,14 +509,16 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
// Format argument.
|
// Format argument.
|
||||||
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
out = visit_format_arg(
|
||||||
|
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
|
||||||
}
|
}
|
||||||
return std::copy(start, it, out);
|
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||||
}
|
}
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using basic_printf_context_t =
|
using basic_printf_context_t =
|
||||||
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||||
|
|
||||||
using printf_context = basic_printf_context_t<char>;
|
using printf_context = basic_printf_context_t<char>;
|
||||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||||
|
@ -612,9 +532,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
|
||||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... T>
|
||||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
inline auto make_printf_args(const T&... args)
|
||||||
const Args&... args) {
|
-> format_arg_store<printf_context, T...> {
|
||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,18 +544,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... T>
|
||||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
inline auto make_wprintf_args(const T&... args)
|
||||||
const Args&... args) {
|
-> format_arg_store<wprintf_context, T...> {
|
||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vsprintf(
|
inline auto vsprintf(
|
||||||
const S& format,
|
const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vprintf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(fmt), args);
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,19 +569,20 @@ inline std::basic_string<Char> vsprintf(
|
||||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T,
|
||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vfprintf(
|
inline auto vfprintf(
|
||||||
std::FILE* f, const S& format,
|
std::FILE* f, const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
|
-> int {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vprintf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(fmt), args);
|
||||||
size_t size = buffer.size();
|
size_t size = buffer.size();
|
||||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||||
? -1
|
? -1
|
||||||
|
@ -676,19 +598,19 @@ inline int vfprintf(
|
||||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T, typename Char = char_t<S>>
|
||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vfprintf(f, to_string_view(format),
|
return vfprintf(f, to_string_view(fmt),
|
||||||
make_format_args<context>(args...));
|
fmt::make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vprintf(
|
inline auto vprintf(
|
||||||
const S& format,
|
const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
return vfprintf(stdout, to_string_view(format), args);
|
-> int {
|
||||||
|
return vfprintf(stdout, to_string_view(fmt), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -700,52 +622,31 @@ inline int vprintf(
|
||||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||||
inline int printf(const S& format_str, const Args&... args) {
|
return vprintf(
|
||||||
using context = basic_printf_context_t<char_t<S>>;
|
to_string_view(fmt),
|
||||||
return vprintf(to_string_view(format_str),
|
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vfprintf(
|
FMT_DEPRECATED auto vfprintf(
|
||||||
std::basic_ostream<Char>& os, const S& format,
|
std::basic_ostream<Char>& os, const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
|
-> int {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vprintf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(fmt), args);
|
||||||
detail::write_buffer(os, buffer);
|
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||||
return static_cast<int>(buffer.size());
|
return static_cast<int>(buffer.size());
|
||||||
}
|
}
|
||||||
|
template <typename S, typename... T, typename Char = char_t<S>>
|
||||||
/** Formats arguments and writes the output to the range. */
|
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
|
||||||
template <typename ArgFormatter, typename Char,
|
const T&... args) -> int {
|
||||||
typename Context =
|
return vfprintf(os, to_string_view(fmt),
|
||||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
|
||||||
typename ArgFormatter::iterator vprintf(
|
|
||||||
detail::buffer<Char>& out, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<type_identity_t<Context>> args) {
|
|
||||||
typename ArgFormatter::iterator iter(out);
|
|
||||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
|
||||||
return iter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
FMT_MODULE_EXPORT_END
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::fprintf(cerr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
|
||||||
const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
|
||||||
return vfprintf(os, to_string_view(format_str),
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_PRINTF_H_
|
#endif // FMT_PRINTF_H_
|
||||||
|
|
315
externals/fmt/include/fmt/ranges.h
vendored
315
externals/fmt/include/fmt/ranges.h
vendored
|
@ -17,41 +17,31 @@
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
// output only up to N items from the range.
|
|
||||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
|
||||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> struct formatting_base {
|
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>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
template <typename Char, typename Enable = void> struct formatting_tuple {
|
||||||
struct formatting_range : formatting_base<Char> {
|
Char prefix = '(';
|
||||||
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
|
Char postfix = ')';
|
||||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
|
||||||
// range.
|
|
||||||
Char prefix;
|
|
||||||
Char delimiter;
|
|
||||||
Char postfix;
|
|
||||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
template <typename ParseContext>
|
||||||
struct formatting_tuple : formatting_base<Char> {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
Char prefix;
|
return ctx.begin();
|
||||||
Char delimiter;
|
}
|
||||||
Char postfix;
|
|
||||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
@ -75,8 +65,14 @@ OutputIterator copy(char ch, OutputIterator out) {
|
||||||
return out;
|
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.
|
/// Return true value if T has std::string interface, like std::string_view.
|
||||||
template <typename T> class is_like_std_string {
|
template <typename T> class is_std_string_like {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p)
|
static auto check(U* p)
|
||||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||||
|
@ -88,19 +84,107 @@ template <typename T> class is_like_std_string {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
template <typename... Ts> struct conditional_helper {};
|
template <typename... Ts> struct conditional_helper {};
|
||||||
|
|
||||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
#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>
|
template <typename T>
|
||||||
struct is_range_<
|
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||||
T, conditional_t<false,
|
decltype(std::declval<T>().end())>>
|
||||||
conditional_helper<decltype(std::declval<T>().begin()),
|
: std::true_type {};
|
||||||
decltype(std::declval<T>().end())>,
|
|
||||||
void>> : 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
|
#endif
|
||||||
|
|
||||||
/// tuple_size and tuple_element check.
|
/// tuple_size and tuple_element check.
|
||||||
|
@ -157,30 +241,41 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
template <typename Range>
|
||||||
typename std::decay<Arg>::type>::value)>
|
using value_type =
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
|
||||||
return add_space ? " {}" : "{}";
|
|
||||||
|
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||||
|
*out++ = ',';
|
||||||
|
*out++ = ' ';
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
template <
|
||||||
typename std::decay<Arg>::type>::value)>
|
typename Char, typename OutputIt, typename Arg,
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||||
|
*out++ = '"';
|
||||||
|
out = write<Char>(out, v);
|
||||||
|
*out++ = '"';
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
template <typename Char, typename OutputIt, typename Arg,
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||||
}
|
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
*out++ = '\'';
|
||||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
*out++ = v;
|
||||||
|
*out++ = '\'';
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
template <
|
||||||
return add_space ? " '{}'" : "'{}'";
|
typename Char, typename OutputIt, typename Arg,
|
||||||
}
|
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
|
||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
!std::is_same<Arg, Char>::value)>
|
||||||
return add_space ? L" '{}'" : L"'{}'";
|
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||||
|
return write<Char>(out, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
@ -196,23 +291,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
// C++11 generic lambda for format()
|
// C++11 generic lambda for format()
|
||||||
template <typename FormatContext> struct format_each {
|
template <typename FormatContext> struct format_each {
|
||||||
template <typename T> void operator()(const T& v) {
|
template <typename T> void operator()(const T& v) {
|
||||||
if (i > 0) {
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
if (formatting.add_prepostfix_space) {
|
out = detail::write_range_entry<Char>(out, v);
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
out = detail::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
out = format_to(out,
|
|
||||||
detail::format_str_quoted(
|
|
||||||
(formatting.add_delimiter_spaces && i > 0), v),
|
|
||||||
v);
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatting_tuple<Char>& formatting;
|
formatting_tuple<Char>& formatting;
|
||||||
size_t& i;
|
size_t& i;
|
||||||
typename std::add_lvalue_reference<decltype(
|
typename std::add_lvalue_reference<
|
||||||
std::declval<FormatContext>().out())>::type out;
|
decltype(std::declval<FormatContext>().out())>::type out;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -227,12 +313,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
detail::copy(formatting.prefix, out);
|
|
||||||
|
|
||||||
|
detail::copy(formatting.prefix, out);
|
||||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
if (formatting.add_prepostfix_space) {
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
detail::copy(formatting.postfix, out);
|
detail::copy(formatting.postfix, out);
|
||||||
|
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
|
@ -241,14 +324,22 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
|
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
|
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename RangeT, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<RangeT, Char,
|
struct formatter<
|
||||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
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;
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
|
@ -257,75 +348,67 @@ struct formatter<RangeT, Char,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(const RangeT& values,
|
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||||
FormatContext& ctx) {
|
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
auto it = values.begin();
|
auto view = detail::range_to_view<T>::view(values);
|
||||||
auto end = values.end();
|
auto it = view.begin();
|
||||||
|
auto end = view.end();
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (i > 0) {
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
out = detail::write_range_entry<Char>(out, *it);
|
||||||
out = detail::copy(formatting.delimiter, out);
|
++i;
|
||||||
}
|
|
||||||
out = format_to(out,
|
|
||||||
detail::format_str_quoted(
|
|
||||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
|
||||||
*it);
|
|
||||||
if (++i > formatting.range_length_limit) {
|
|
||||||
out = format_to(out, " ... <other elements>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
|
||||||
return detail::copy(formatting.postfix, out);
|
return detail::copy(formatting.postfix, out);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
|
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||||
const std::tuple<T...>& tuple;
|
const std::tuple<T...>& tuple;
|
||||||
basic_string_view<Char> sep;
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||||
: tuple{t}, sep{s} {}
|
: tuple(t), sep{s} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename... T>
|
template <typename Char, typename... T>
|
||||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
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>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(
|
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
typename FormatContext::iterator {
|
||||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename FormatContext, size_t... N>
|
template <typename FormatContext, size_t... N>
|
||||||
typename FormatContext::iterator format(
|
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
detail::index_sequence<N...>) ->
|
||||||
detail::index_sequence<N...>) {
|
typename FormatContext::iterator {
|
||||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format_args(
|
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
|
||||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
typename FormatContext::iterator {
|
||||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||||
// can be replaced with a constexpr branch in the variadic overload.
|
// can be replaced with a constexpr branch in the variadic overload.
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, typename Arg, typename... Args>
|
template <typename FormatContext, typename Arg, typename... Args>
|
||||||
typename FormatContext::iterator format_args(
|
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
const Arg& arg, const Args&... args) ->
|
||||||
const Arg& arg, const Args&... args) {
|
typename FormatContext::iterator {
|
||||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||||
auto out = ctx.out();
|
auto out = base().format(arg, ctx);
|
||||||
out = base{}.format(arg, ctx);
|
|
||||||
if (sizeof...(Args) > 0) {
|
if (sizeof...(Args) > 0) {
|
||||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
|
@ -335,6 +418,8 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||||
|
@ -347,14 +432,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||||
string_view sep) {
|
-> tuple_join_view<char, T...> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||||
wstring_view sep) {
|
basic_string_view<wchar_t> sep)
|
||||||
|
-> tuple_join_view<wchar_t, T...> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,17 +456,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
|
auto join(std::initializer_list<T> list, string_view sep)
|
||||||
string_view sep) {
|
-> join_view<const T*, const T*> {
|
||||||
return join(std::begin(list), std::end(list), sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
|
|
||||||
wstring_view sep) {
|
|
||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
#endif // FMT_RANGES_H_
|
||||||
|
|
236
externals/fmt/include/fmt/xchar.h
vendored
Normal file
236
externals/fmt/include/fmt/xchar.h
vendored
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
// 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_
|
100
externals/fmt/src/fmt.cc
vendored
Normal file
100
externals/fmt/src/fmt.cc
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
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"
|
22
externals/fmt/src/format.cc
vendored
22
externals/fmt/src/format.cc
vendored
|
@ -23,9 +23,12 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
: snprintf_ptr(buf, size, format, precision, value);
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
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.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, detail::float_specs,
|
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||||
|
@ -38,15 +41,15 @@ template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||||
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
-> thousands_sep_result<char>;
|
||||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
template FMT_API void detail::vformat_to(
|
||||||
detail::buffer<char>&, string_view,
|
detail::buffer<char>&, string_view,
|
||||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
|
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||||
|
|
||||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||||
detail::buffer<char>&);
|
detail::buffer<char>&);
|
||||||
|
@ -60,10 +63,13 @@ template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||||
template FMT_API wchar_t 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 wchar_t detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||||
const wchar_t*);
|
const wchar_t*);
|
||||||
|
|
||||||
|
template struct detail::basic_data<void>;
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
127
externals/fmt/src/os.cc
vendored
127
externals/fmt/src/os.cc
vendored
|
@ -25,7 +25,6 @@
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# define WIN32_LEAN_AND_MEAN
|
||||||
# endif
|
# endif
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <windows.h>
|
|
||||||
|
|
||||||
# define O_CREAT _O_CREAT
|
# define O_CREAT _O_CREAT
|
||||||
# define O_TRUNC _O_TRUNC
|
# define O_TRUNC _O_TRUNC
|
||||||
|
@ -55,16 +54,16 @@
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Return type of read and write functions.
|
// Return type of read and write functions.
|
||||||
using RWResult = int;
|
using rwresult = int;
|
||||||
|
|
||||||
// On Windows the count argument to read and write is unsigned, so convert
|
// On Windows the count argument to read and write is unsigned, so convert
|
||||||
// it from size_t preventing integer overflow.
|
// it from size_t preventing integer overflow.
|
||||||
inline unsigned convert_rwcount(std::size_t count) {
|
inline unsigned convert_rwcount(std::size_t count) {
|
||||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||||
}
|
}
|
||||||
#else
|
#elif FMT_USE_FCNTL
|
||||||
// Return type of read and write functions.
|
// Return type of read and write functions.
|
||||||
using RWResult = ssize_t;
|
using rwresult = ssize_t;
|
||||||
|
|
||||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||||
#endif
|
#endif
|
||||||
|
@ -73,14 +72,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
|
||||||
if (int error_code = convert(s)) {
|
if (int error_code = convert(s)) {
|
||||||
FMT_THROW(windows_error(error_code,
|
FMT_THROW(windows_error(error_code,
|
||||||
"cannot convert string from UTF-16 to UTF-8"));
|
"cannot convert string from UTF-16 to UTF-8"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int detail::utf16_to_utf8::convert(wstring_view s) {
|
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
|
||||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
||||||
int s_size = static_cast<int>(s.size());
|
int s_size = static_cast<int>(s.size());
|
||||||
if (s_size == 0) {
|
if (s_size == 0) {
|
||||||
|
@ -101,45 +100,85 @@ int detail::utf16_to_utf8::convert(wstring_view s) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void windows_error::init(int err_code, string_view format_str,
|
namespace detail {
|
||||||
format_args args) {
|
|
||||||
error_code_ = err_code;
|
class system_message {
|
||||||
memory_buffer buffer;
|
system_message(const system_message&) = delete;
|
||||||
detail::format_windows_error(buffer, err_code, vformat(format_str, args));
|
void operator=(const system_message&) = delete;
|
||||||
std::runtime_error& base = *this;
|
|
||||||
base = std::runtime_error(to_string(buffer));
|
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,
|
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT {
|
const char* message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
wmemory_buffer buf;
|
system_message msg(error_code);
|
||||||
buf.resize(inline_buffer_size);
|
if (msg) {
|
||||||
for (;;) {
|
utf16_to_utf8 utf8_message;
|
||||||
wchar_t* system_message = &buf[0];
|
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||||
int result = FormatMessageW(
|
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
return;
|
||||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
|
|
||||||
static_cast<uint32_t>(buf.size()), nullptr);
|
|
||||||
if (result != 0) {
|
|
||||||
utf16_to_utf8 utf8_message;
|
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
|
||||||
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
break; // Can't get error message, report error code instead.
|
|
||||||
buf.resize(buf.size() * 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FMT_CATCH(...) {}
|
FMT_CATCH(...) {}
|
||||||
format_error_code(out, error_code, message);
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void report_windows_error(int error_code,
|
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
|
||||||
fmt::string_view message) FMT_NOEXCEPT {
|
|
||||||
report_error(detail::format_windows_error, error_code, message);
|
report_error(detail::format_windows_error, error_code, message);
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
@ -228,14 +267,14 @@ long long file::size() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t file::read(void* buffer, std::size_t count) {
|
std::size_t file::read(void* buffer, std::size_t count) {
|
||||||
RWResult result = 0;
|
rwresult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||||
return detail::to_unsigned(result);
|
return detail::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t file::write(const void* buffer, std::size_t count) {
|
std::size_t file::write(const void* buffer, std::size_t count) {
|
||||||
RWResult result = 0;
|
rwresult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
return detail::to_unsigned(result);
|
return detail::to_unsigned(result);
|
||||||
|
@ -259,10 +298,10 @@ void file::dup2(int fd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
|
void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
if (result == -1) ec = error_code(errno);
|
if (result == -1) ec = std::error_code(errno, std::generic_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
void file::pipe(file& read_end, file& write_end) {
|
void file::pipe(file& read_end, file& write_end) {
|
||||||
|
@ -288,12 +327,12 @@ void file::pipe(file& read_end, file& write_end) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffered_file file::fdopen(const char* mode) {
|
buffered_file file::fdopen(const char* mode) {
|
||||||
// Don't retry as fdopen doesn't return EINTR.
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
#if defined(__MINGW32__) && defined(_POSIX_)
|
# if defined(__MINGW32__) && defined(_POSIX_)
|
||||||
FILE* f = ::fdopen(fd_, mode);
|
FILE* f = ::fdopen(fd_, mode);
|
||||||
#else
|
# else
|
||||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
#endif
|
# endif
|
||||||
if (!f)
|
if (!f)
|
||||||
FMT_THROW(
|
FMT_THROW(
|
||||||
system_error(errno, "cannot associate stream with file descriptor"));
|
system_error(errno, "cannot associate stream with file descriptor"));
|
||||||
|
@ -313,5 +352,9 @@ long getpagesize() {
|
||||||
return size;
|
return size;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_API void ostream::grow(size_t) {
|
||||||
|
if (this->size() == this->capacity()) flush();
|
||||||
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
2
externals/fmt/support/README
vendored
2
externals/fmt/support/README
vendored
|
@ -2,5 +2,3 @@ This directory contains build support files such as
|
||||||
|
|
||||||
* CMake modules
|
* CMake modules
|
||||||
* Build scripts
|
* Build scripts
|
||||||
* qmake (static build with dynamic libc only)
|
|
||||||
|
|
||||||
|
|
10
externals/fmt/support/appveyor.yml
vendored
10
externals/fmt/support/appveyor.yml
vendored
|
@ -6,11 +6,8 @@ clone_depth: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- Visual Studio 2015
|
- Visual Studio 2015
|
||||||
- Visual Studio 2019
|
|
||||||
- Visual Studio 2017
|
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
- Win32
|
|
||||||
- x64
|
- x64
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -18,13 +15,6 @@ environment:
|
||||||
MSVC_DEFAULT_OPTIONS: ON
|
MSVC_DEFAULT_OPTIONS: ON
|
||||||
BUILD: msvc
|
BUILD: msvc
|
||||||
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
- image: Visual Studio 2015
|
|
||||||
platform: Win32
|
|
||||||
- image: Visual Studio 2019
|
|
||||||
platform: Win32
|
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
- mkdir build
|
- mkdir build
|
||||||
- cd build
|
- cd build
|
||||||
|
|
58
externals/fmt/support/build-docs.py
vendored
Executable file
58
externals/fmt/support/build-docs.py
vendored
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
#!/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)
|
73
externals/fmt/support/build.gradle
vendored
73
externals/fmt/support/build.gradle
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
// General gradle arguments for root project
|
// General gradle arguments for root project
|
||||||
buildscript {
|
buildscript {
|
||||||
|
@ -7,24 +8,25 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
//
|
//
|
||||||
// https://developer.android.com/studio/releases/gradle-plugin
|
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
|
||||||
//
|
//
|
||||||
// Notice that 3.3.0 here is the version of [Android Gradle Plugin]
|
// Notice that 4.0.0 here is the version of [Android Gradle Plugin]
|
||||||
// Accroding to URL above you will need Gradle 5.0 or higher
|
// Accroding to URL above you will need Gradle 6.1 or higher
|
||||||
//
|
//
|
||||||
// If you are using Android Studio, and it is using Gradle's lower
|
classpath "com.android.tools.build:gradle:4.1.1"
|
||||||
// version, Use the plugin version 3.1.3 ~ 3.2.0 for Gradle 4.4 ~ 4.10
|
|
||||||
classpath 'com.android.tools.build:gradle:3.3.0'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output: Shared library (.so) for Android
|
|
||||||
apply plugin: 'com.android.library'
|
|
||||||
|
|
||||||
|
// 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 {
|
android {
|
||||||
compileSdkVersion 25 // Android 7.0
|
compileSdkVersion 25 // Android 7.0
|
||||||
|
|
||||||
|
@ -41,13 +43,13 @@ android {
|
||||||
include "arm64-v8a", "armeabi-v7a", "x86_64"
|
include "arm64-v8a", "armeabi-v7a", "x86_64"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ndkVersion "21.3.6528147" // ANDROID_NDK_HOME is deprecated. Be explicit
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21 // Android 5.0+
|
minSdkVersion 21 // Android 5.0+
|
||||||
targetSdkVersion 25 // Follow Compile SDK
|
targetSdkVersion 25 // Follow Compile SDK
|
||||||
versionCode 21 // Follow release count
|
versionCode 34 // Follow release count
|
||||||
versionName "5.3.0" // Follow Official version
|
versionName "7.1.2" // Follow Official version
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
@ -56,9 +58,9 @@ android {
|
||||||
arguments "-DFMT_TEST=false" // Skip test
|
arguments "-DFMT_TEST=false" // Skip test
|
||||||
arguments "-DFMT_DOC=false" // Skip document
|
arguments "-DFMT_DOC=false" // Skip document
|
||||||
cppFlags "-std=c++17"
|
cppFlags "-std=c++17"
|
||||||
|
targets "fmt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println("Gradle CMake Plugin: ")
|
|
||||||
println(externalNativeBuild.cmake.cppFlags)
|
println(externalNativeBuild.cmake.cppFlags)
|
||||||
println(externalNativeBuild.cmake.arguments)
|
println(externalNativeBuild.cmake.arguments)
|
||||||
}
|
}
|
||||||
|
@ -69,16 +71,27 @@ android {
|
||||||
// neighbor of the top level cmake
|
// neighbor of the top level cmake
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path "../CMakeLists.txt"
|
version "3.10.0+"
|
||||||
|
path "${rootDir}/CMakeLists.txt"
|
||||||
// buildStagingDirectory "./build" // Custom path for cmake output
|
// buildStagingDirectory "./build" // Custom path for cmake output
|
||||||
}
|
}
|
||||||
//println(cmake.path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets{
|
sourceSets{
|
||||||
// Android Manifest for Gradle
|
// Android Manifest for Gradle
|
||||||
main {
|
main {
|
||||||
manifest.srcFile 'AndroidManifest.xml'
|
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,20 +101,32 @@ assemble.doLast
|
||||||
// Instead of `ninja install`, Gradle will deploy the files.
|
// Instead of `ninja install`, Gradle will deploy the files.
|
||||||
// We are doing this since FMT is dependent to the ANDROID_STL after build
|
// We are doing this since FMT is dependent to the ANDROID_STL after build
|
||||||
copy {
|
copy {
|
||||||
from 'build/intermediates/cmake'
|
from "build/intermediates/cmake"
|
||||||
into '../libs'
|
into "${rootDir}/libs"
|
||||||
}
|
}
|
||||||
// Copy debug binaries
|
// Copy debug binaries
|
||||||
copy {
|
copy {
|
||||||
from '../libs/debug/obj'
|
from "${rootDir}/libs/debug/obj"
|
||||||
into '../libs/debug'
|
into "${rootDir}/libs/debug"
|
||||||
}
|
}
|
||||||
// Copy Release binaries
|
// Copy Release binaries
|
||||||
copy {
|
copy {
|
||||||
from '../libs/release/obj'
|
from "${rootDir}/libs/release/obj"
|
||||||
into '../libs/release'
|
into "${rootDir}/libs/release"
|
||||||
}
|
}
|
||||||
// Remove empty directory
|
// Remove empty directory
|
||||||
delete '../libs/debug/obj'
|
delete "${rootDir}/libs/debug/obj"
|
||||||
delete '../libs/release/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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
27
externals/fmt/support/fmt.pro
vendored
27
externals/fmt/support/fmt.pro
vendored
|
@ -1,27 +0,0 @@
|
||||||
# Staticlib configuration for qmake builds
|
|
||||||
# For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc
|
|
||||||
# from compilation so it _MUST_ be called as qmake -nodepend
|
|
||||||
# A workaround is implemented below: a custom compiler is defined which does not track dependencies
|
|
||||||
|
|
||||||
TEMPLATE = lib
|
|
||||||
|
|
||||||
TARGET = fmt
|
|
||||||
|
|
||||||
QMAKE_EXT_CPP = .cc
|
|
||||||
|
|
||||||
CONFIG = staticlib warn_on c++11
|
|
||||||
|
|
||||||
FMT_SOURCES = \
|
|
||||||
../src/format.cc \
|
|
||||||
../src/posix.cc
|
|
||||||
|
|
||||||
fmt.name = libfmt
|
|
||||||
fmt.input = FMT_SOURCES
|
|
||||||
fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
|
||||||
fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
|
||||||
fmt.depends = ${QMAKE_FILE_IN}
|
|
||||||
# QMAKE_RUN_CXX will not be expanded
|
|
||||||
fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN}
|
|
||||||
fmt.variable_out = OBJECTS
|
|
||||||
fmt.CONFIG = no_dependencies no_link
|
|
||||||
QMAKE_EXTRA_COMPILERS += fmt
|
|
2
externals/fmt/support/rst2md.py
vendored
2
externals/fmt/support/rst2md.py
vendored
|
@ -65,7 +65,7 @@ class Translator(nodes.NodeVisitor):
|
||||||
self.write('\n\n')
|
self.write('\n\n')
|
||||||
|
|
||||||
def visit_paragraph(self, node):
|
def visit_paragraph(self, node):
|
||||||
pass
|
self.write('\n\n')
|
||||||
|
|
||||||
def depart_paragraph(self, node):
|
def depart_paragraph(self, node):
|
||||||
pass
|
pass
|
||||||
|
|
119
externals/fmt/support/travis-build.py
vendored
119
externals/fmt/support/travis-build.py
vendored
|
@ -1,119 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Build the project on Travis 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
|
|
||||||
|
|
||||||
def makedirs_if_not_exist(dir):
|
|
||||||
try:
|
|
||||||
os.makedirs(dir)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno != errno.EEXIST:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def install_dependencies():
|
|
||||||
branch = os.environ['TRAVIS_BRANCH']
|
|
||||||
if branch != 'master':
|
|
||||||
print('Branch: ' + branch)
|
|
||||||
exit(0) # Ignore non-master branches
|
|
||||||
check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' +
|
|
||||||
'| sudo apt-key add -', shell=True)
|
|
||||||
check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' +
|
|
||||||
'| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True)
|
|
||||||
check_call(['sudo', 'apt-get', 'update'])
|
|
||||||
check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs'])
|
|
||||||
check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css'])
|
|
||||||
deb_file = 'doxygen_1.8.6-2_amd64.deb'
|
|
||||||
urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' +
|
|
||||||
deb_file, deb_file)
|
|
||||||
check_call(['sudo', 'dpkg', '-i', deb_file])
|
|
||||||
|
|
||||||
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
||||||
|
|
||||||
build = os.environ['BUILD']
|
|
||||||
if build == 'Doc':
|
|
||||||
travis = 'TRAVIS' in os.environ
|
|
||||||
if travis:
|
|
||||||
install_dependencies()
|
|
||||||
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
|
|
||||||
import build
|
|
||||||
build.create_build_env()
|
|
||||||
html_dir = build.build_docs()
|
|
||||||
repo = 'fmtlib.github.io'
|
|
||||||
if travis and 'KEY' not in os.environ:
|
|
||||||
# Don't update the repo if building on Travis from an account that
|
|
||||||
# doesn't have push access.
|
|
||||||
print('Skipping update of ' + repo)
|
|
||||||
exit(0)
|
|
||||||
# Clone the fmtlib.github.io repo.
|
|
||||||
rmtree_if_exists(repo)
|
|
||||||
git_url = 'https://github.com/' if travis else 'git@github.com:'
|
|
||||||
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
|
|
||||||
# Copy docs to the repo.
|
|
||||||
target_dir = os.path.join(repo, 'dev')
|
|
||||||
rmtree_if_exists(target_dir)
|
|
||||||
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
|
|
||||||
if travis:
|
|
||||||
check_call(['git', 'config', '--global', 'user.name', 'amplbot'])
|
|
||||||
check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com'])
|
|
||||||
# Push docs to GitHub pages.
|
|
||||||
check_call(['git', 'add', '--all'], cwd=repo)
|
|
||||||
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
|
|
||||||
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
|
|
||||||
cmd = 'git push'
|
|
||||||
if travis:
|
|
||||||
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
|
|
||||||
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
|
|
||||||
# Print the output without the key.
|
|
||||||
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
|
|
||||||
if p.returncode != 0:
|
|
||||||
raise subprocess.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 the library.
|
|
||||||
makedirs_if_not_exist(build_dir)
|
|
||||||
cmake_flags = [
|
|
||||||
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
|
|
||||||
'-DCMAKE_CXX_STANDARD=' + standard
|
|
||||||
]
|
|
||||||
|
|
||||||
# Make sure the fuzzers still compile.
|
|
||||||
main_cmake_flags = list(cmake_flags)
|
|
||||||
if 'ENABLE_FUZZING' in os.environ:
|
|
||||||
main_cmake_flags += ['-DFMT_FUZZ=ON', '-DFMT_FUZZ_LINKMAIN=On']
|
|
||||||
|
|
||||||
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] +
|
|
||||||
main_cmake_flags, cwd=build_dir)
|
|
||||||
|
|
||||||
# Build the library.
|
|
||||||
check_call(['cmake', '--build','.'], cwd=build_dir)
|
|
||||||
|
|
||||||
# Test the library.
|
|
||||||
env = os.environ.copy()
|
|
||||||
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
|
|
||||||
if call(['make', 'test'], env=env, cwd=build_dir):
|
|
||||||
with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f:
|
|
||||||
print(f.read())
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# Install the library.
|
|
||||||
check_call(['make', 'install'], cwd=build_dir)
|
|
||||||
|
|
||||||
# Test installation.
|
|
||||||
makedirs_if_not_exist(test_build_dir)
|
|
||||||
check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] +
|
|
||||||
cmake_flags, cwd=test_build_dir)
|
|
||||||
check_call(['make', '-j4'], cwd=test_build_dir)
|
|
30
externals/fmt/support/update-coverity-branch.py
vendored
30
externals/fmt/support/update-coverity-branch.py
vendored
|
@ -1,30 +0,0 @@
|
||||||
#!/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)
|
|
149
externals/fmt/test/CMakeLists.txt
vendored
149
externals/fmt/test/CMakeLists.txt
vendored
|
@ -1,52 +1,10 @@
|
||||||
#------------------------------------------------------------------------------
|
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_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
|
|
||||||
target_include_directories(gmock SYSTEM PUBLIC . gmock gtest)
|
|
||||||
|
|
||||||
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 ()
|
|
||||||
|
|
||||||
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
# Workaround a bug in implementation of variadic templates in MSVC11.
|
|
||||||
target_compile_definitions(gmock PUBLIC _VARIADIC_MAX=10)
|
|
||||||
|
|
||||||
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
|
|
||||||
target_compile_definitions(gmock PRIVATE _CRT_SECURE_NO_WARNINGS)
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
# Disable MSVC warnings of POSIX functions.
|
|
||||||
target_compile_options(gmock PUBLIC -Wno-deprecated-declarations)
|
|
||||||
endif ()
|
|
||||||
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 ()
|
|
||||||
|
|
||||||
# Silence MSVC tr1 deprecation warning in gmock.
|
|
||||||
target_compile_definitions(gmock
|
|
||||||
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1)
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# Build the actual library tests
|
|
||||||
|
|
||||||
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
|
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
|
||||||
add_library(test-main STATIC ${TEST_MAIN_SRC})
|
add_library(test-main STATIC ${TEST_MAIN_SRC})
|
||||||
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
|
target_include_directories(test-main PUBLIC
|
||||||
target_link_libraries(test-main gmock fmt)
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||||
|
target_link_libraries(test-main gtest)
|
||||||
|
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
@ -73,8 +31,23 @@ endfunction()
|
||||||
# Adds a test.
|
# Adds a test.
|
||||||
# Usage: add_fmt_test(name srcs...)
|
# Usage: add_fmt_test(name srcs...)
|
||||||
function(add_fmt_test name)
|
function(add_fmt_test name)
|
||||||
add_fmt_executable(${name} ${name}.cc ${ARGN})
|
cmake_parse_arguments(ADD_FMT_TEST "HEADER_ONLY;MODULE" "" "" ${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.
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC)
|
||||||
|
@ -83,39 +56,71 @@ function(add_fmt_test name)
|
||||||
if (FMT_WERROR)
|
if (FMT_WERROR)
|
||||||
target_compile_options(${name} PRIVATE ${WERROR_FLAG})
|
target_compile_options(${name} PRIVATE ${WERROR_FLAG})
|
||||||
endif ()
|
endif ()
|
||||||
target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
|
|
||||||
add_test(NAME ${name} COMMAND ${name})
|
add_test(NAME ${name} COMMAND ${name})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
add_fmt_test(args-test)
|
||||||
add_fmt_test(assert-test)
|
add_fmt_test(assert-test)
|
||||||
add_fmt_test(chrono-test)
|
add_fmt_test(chrono-test)
|
||||||
add_fmt_test(color-test)
|
add_fmt_test(color-test)
|
||||||
add_fmt_test(core-test)
|
add_fmt_test(core-test)
|
||||||
add_fmt_test(grisu-test)
|
|
||||||
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
|
|
||||||
add_fmt_test(gtest-extra-test)
|
add_fmt_test(gtest-extra-test)
|
||||||
add_fmt_test(format-test mock-allocator.h)
|
add_fmt_test(format-test mock-allocator.h)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_compile_options(format-test PRIVATE /bigobj)
|
target_compile_options(format-test PRIVATE /bigobj)
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
||||||
add_fmt_test(format-impl-test)
|
add_fmt_test(format-impl-test HEADER_ONLY header-only-test.cc)
|
||||||
endif ()
|
endif ()
|
||||||
add_fmt_test(locale-test)
|
|
||||||
add_fmt_test(ostream-test)
|
add_fmt_test(ostream-test)
|
||||||
add_fmt_test(compile-test)
|
add_fmt_test(compile-test)
|
||||||
add_fmt_test(printf-test)
|
add_fmt_test(printf-test)
|
||||||
add_fmt_test(custom-formatter-test)
|
|
||||||
add_fmt_test(ranges-test)
|
add_fmt_test(ranges-test)
|
||||||
add_fmt_test(scan-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)
|
||||||
|
|
||||||
if (NOT MSVC_BUILD_STATIC)
|
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)
|
||||||
|
target_compile_options(module-test PRIVATE /utf-8)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC)
|
||||||
|
foreach (flag_var
|
||||||
|
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||||
|
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||||
|
if (${flag_var} MATCHES "^(/|-)(MT|MTd)")
|
||||||
|
set(MSVC_STATIC_RUNTIME ON)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT MSVC_STATIC_RUNTIME)
|
||||||
add_fmt_executable(posix-mock-test
|
add_fmt_executable(posix-mock-test
|
||||||
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
|
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_link_libraries(posix-mock-test gmock)
|
target_link_libraries(posix-mock-test gtest)
|
||||||
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
|
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC)
|
||||||
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -126,21 +131,9 @@ if (NOT MSVC_BUILD_STATIC)
|
||||||
add_fmt_test(os-test)
|
add_fmt_test(os-test)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
add_fmt_executable(header-only-test
|
|
||||||
header-only-test.cc header-only-test2.cc test-main.cc)
|
|
||||||
target_link_libraries(header-only-test gmock)
|
|
||||||
target_include_directories(header-only-test SYSTEM PUBLIC gtest 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}/include)
|
|
||||||
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
|
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
|
||||||
|
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
|
||||||
# MSVC fails to compile GMock when C++17 is enabled.
|
# MSVC fails to compile GMock when C++17 is enabled.
|
||||||
if (FMT_HAS_VARIANT AND NOT MSVC)
|
if (FMT_HAS_VARIANT AND NOT MSVC)
|
||||||
add_fmt_test(std-format-test)
|
add_fmt_test(std-format-test)
|
||||||
|
@ -195,6 +188,7 @@ if (FMT_PEDANTIC AND NOT WIN32)
|
||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
|
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
|
||||||
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||||
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
||||||
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||||
|
@ -215,6 +209,21 @@ if (FMT_PEDANTIC AND NOT WIN32)
|
||||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||||
endif ()
|
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
|
# 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
|
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
|
||||||
if (FMT_CUDA_TEST)
|
if (FMT_CUDA_TEST)
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
cmake_minimum_required(VERSION 3.1...3.18)
|
||||||
|
|
||||||
project(fmt-test)
|
project(fmt-test CXX)
|
||||||
|
|
||||||
add_subdirectory(../.. fmt)
|
add_subdirectory(../.. fmt)
|
||||||
|
|
||||||
add_executable(library-test "main.cc")
|
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_include_directories(library-test PUBLIC SYSTEM .)
|
||||||
|
target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
target_link_libraries(library-test fmt::fmt)
|
||||||
|
|
||||||
if (TARGET fmt::fmt-header-only)
|
if (TARGET fmt::fmt-header-only)
|
||||||
add_executable(header-only-test "main.cc")
|
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_include_directories(header-only-test PUBLIC SYSTEM .)
|
||||||
|
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
target_link_libraries(header-only-test fmt::fmt-header-only)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
for(int i = 0; i < argc; ++i)
|
for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]);
|
||||||
fmt::print("{}: {}\n", i, argv[i]);
|
|
||||||
}
|
}
|
||||||
|
|
173
externals/fmt/test/args-test.cc
vendored
Normal file
173
externals/fmt/test/args-test.cc
vendored
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
// 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");
|
||||||
|
}
|
15
externals/fmt/test/assert-test.cc
vendored
15
externals/fmt/test/assert-test.cc
vendored
|
@ -1,4 +1,8 @@
|
||||||
// Formatting library for C++ - assertion tests
|
// 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
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
@ -6,9 +10,9 @@
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
#include "gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
TEST(AssertTest, Fail) {
|
TEST(assert_test, fail) {
|
||||||
#if GTEST_HAS_DEATH_TEST
|
#if GTEST_HAS_DEATH_TEST
|
||||||
EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!");
|
EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!");
|
||||||
#else
|
#else
|
||||||
|
@ -16,9 +20,8 @@ TEST(AssertTest, Fail) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_condition = false;
|
TEST(assert_test, dangling_else) {
|
||||||
|
bool test_condition = false;
|
||||||
TEST(AssertTest, DanglingElse) {
|
|
||||||
bool executed_else = false;
|
bool executed_else = false;
|
||||||
if (test_condition)
|
if (test_condition)
|
||||||
FMT_ASSERT(true, "");
|
FMT_ASSERT(true, "");
|
||||||
|
|
272
externals/fmt/test/chrono-test.cc
vendored
272
externals/fmt/test/chrono-test.cc
vendored
|
@ -5,77 +5,70 @@
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
# define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fmt/chrono.h"
|
#include "fmt/chrono.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include "gtest-extra.h" // EXPECT_THROW_MSG
|
||||||
|
#include "util.h" // get_locale
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
using fmt::runtime;
|
||||||
|
|
||||||
std::tm make_tm() {
|
using testing::Contains;
|
||||||
|
|
||||||
|
auto make_tm() -> std::tm {
|
||||||
auto time = std::tm();
|
auto time = std::tm();
|
||||||
time.tm_mday = 1;
|
time.tm_mday = 1;
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tm make_hour(int h) {
|
auto make_hour(int h) -> std::tm {
|
||||||
auto time = make_tm();
|
auto time = make_tm();
|
||||||
time.tm_hour = h;
|
time.tm_hour = h;
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tm make_minute(int m) {
|
auto make_minute(int m) -> std::tm {
|
||||||
auto time = make_tm();
|
auto time = make_tm();
|
||||||
time.tm_min = m;
|
time.tm_min = m;
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tm make_second(int s) {
|
auto make_second(int s) -> std::tm {
|
||||||
auto time = make_tm();
|
auto time = make_tm();
|
||||||
time.tm_sec = s;
|
time.tm_sec = s;
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string format_tm(const std::tm& time, const char* spec,
|
TEST(chrono_test, format_tm) {
|
||||||
const std::locale& loc) {
|
auto tm = std::tm();
|
||||||
auto& facet = std::use_facet<std::time_put<char>>(loc);
|
|
||||||
std::ostringstream os;
|
|
||||||
os.imbue(loc);
|
|
||||||
facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec));
|
|
||||||
return os.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimeTest, Format) {
|
|
||||||
std::tm tm = std::tm();
|
|
||||||
tm.tm_year = 116;
|
tm.tm_year = 116;
|
||||||
tm.tm_mon = 3;
|
tm.tm_mon = 3;
|
||||||
tm.tm_mday = 25;
|
tm.tm_mday = 25;
|
||||||
EXPECT_EQ("The date is 2016-04-25.",
|
tm.tm_hour = 11;
|
||||||
fmt::format("The date is {:%Y-%m-%d}.", tm));
|
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(TimeTest, GrowBuffer) {
|
TEST(chrono_test, grow_buffer) {
|
||||||
std::string s = "{:";
|
auto s = std::string("{:");
|
||||||
for (int i = 0; i < 30; ++i) s += "%c";
|
for (int i = 0; i < 30; ++i) s += "%c";
|
||||||
s += "}\n";
|
s += "}\n";
|
||||||
std::time_t t = std::time(nullptr);
|
auto t = std::time(nullptr);
|
||||||
fmt::format(s, *std::localtime(&t));
|
fmt::format(fmt::runtime(s), *std::localtime(&t));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimeTest, FormatToEmptyContainer) {
|
TEST(chrono_test, format_to_empty_container) {
|
||||||
std::string s;
|
|
||||||
auto time = std::tm();
|
auto time = std::tm();
|
||||||
time.tm_sec = 42;
|
time.tm_sec = 42;
|
||||||
|
auto s = std::string();
|
||||||
fmt::format_to(std::back_inserter(s), "{:%S}", time);
|
fmt::format_to(std::back_inserter(s), "{:%S}", time);
|
||||||
EXPECT_EQ(s, "42");
|
EXPECT_EQ(s, "42");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimeTest, EmptyResult) { EXPECT_EQ("", fmt::format("{}", std::tm())); }
|
TEST(chrono_test, empty_result) { EXPECT_EQ(fmt::format("{}", std::tm()), ""); }
|
||||||
|
|
||||||
static bool EqualTime(const std::tm& lhs, const std::tm& rhs) {
|
auto equal(const std::tm& lhs, const std::tm& rhs) -> bool {
|
||||||
return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min &&
|
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_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday &&
|
||||||
lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year &&
|
lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year &&
|
||||||
|
@ -83,28 +76,39 @@ static bool EqualTime(const std::tm& lhs, const std::tm& rhs) {
|
||||||
lhs.tm_isdst == rhs.tm_isdst;
|
lhs.tm_isdst == rhs.tm_isdst;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimeTest, LocalTime) {
|
TEST(chrono_test, localtime) {
|
||||||
std::time_t t = std::time(nullptr);
|
auto t = std::time(nullptr);
|
||||||
std::tm tm = *std::localtime(&t);
|
auto tm = *std::localtime(&t);
|
||||||
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
|
EXPECT_TRUE(equal(tm, fmt::localtime(t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimeTest, GMTime) {
|
TEST(chrono_test, gmtime) {
|
||||||
std::time_t t = std::time(nullptr);
|
auto t = std::time(nullptr);
|
||||||
std::tm tm = *std::gmtime(&t);
|
auto tm = *std::gmtime(&t);
|
||||||
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
EXPECT_TRUE(equal(tm, fmt::gmtime(t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EXPECT_TIME(spec, time, duration) \
|
template <typename TimePoint> auto strftime(TimePoint tp) -> std::string {
|
||||||
{ \
|
auto t = std::chrono::system_clock::to_time_t(tp);
|
||||||
std::locale loc("ja_JP.utf8"); \
|
auto tm = *std::localtime(&t);
|
||||||
EXPECT_EQ(format_tm(time, spec, loc), \
|
char output[256] = {};
|
||||||
fmt::format(loc, "{:" spec "}", duration)); \
|
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
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
|
||||||
TEST(ChronoTest, FormatDefault) {
|
TEST(chrono_test, format_default) {
|
||||||
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
|
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
|
||||||
EXPECT_EQ("42as",
|
EXPECT_EQ("42as",
|
||||||
fmt::format("{}", std::chrono::duration<int, std::atto>(42)));
|
fmt::format("{}", std::chrono::duration<int, std::atto>(42)));
|
||||||
|
@ -146,49 +150,7 @@ TEST(ChronoTest, FormatDefault) {
|
||||||
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
|
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatWide) {
|
TEST(chrono_test, align) {
|
||||||
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
|
|
||||||
EXPECT_EQ(L"42as",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::atto>(42)));
|
|
||||||
EXPECT_EQ(L"42fs",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::femto>(42)));
|
|
||||||
EXPECT_EQ(L"42ps",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::pico>(42)));
|
|
||||||
EXPECT_EQ(L"42ns", fmt::format(L"{}", std::chrono::nanoseconds(42)));
|
|
||||||
EXPECT_EQ(L"42\u00B5s", fmt::format(L"{}", std::chrono::microseconds(42)));
|
|
||||||
EXPECT_EQ(L"42ms", fmt::format(L"{}", std::chrono::milliseconds(42)));
|
|
||||||
EXPECT_EQ(L"42cs",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::centi>(42)));
|
|
||||||
EXPECT_EQ(L"42ds",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::deci>(42)));
|
|
||||||
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
|
|
||||||
EXPECT_EQ(L"42das",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::deca>(42)));
|
|
||||||
EXPECT_EQ(L"42hs",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::hecto>(42)));
|
|
||||||
EXPECT_EQ(L"42ks",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::kilo>(42)));
|
|
||||||
EXPECT_EQ(L"42Ms",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::mega>(42)));
|
|
||||||
EXPECT_EQ(L"42Gs",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::giga>(42)));
|
|
||||||
EXPECT_EQ(L"42Ts",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::tera>(42)));
|
|
||||||
EXPECT_EQ(L"42Ps",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::peta>(42)));
|
|
||||||
EXPECT_EQ(L"42Es",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::exa>(42)));
|
|
||||||
EXPECT_EQ(L"42m", fmt::format(L"{}", std::chrono::minutes(42)));
|
|
||||||
EXPECT_EQ(L"42h", fmt::format(L"{}", std::chrono::hours(42)));
|
|
||||||
EXPECT_EQ(
|
|
||||||
L"42[15]s",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
|
|
||||||
EXPECT_EQ(
|
|
||||||
L"42[15/4]s",
|
|
||||||
fmt::format(L"{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ChronoTest, Align) {
|
|
||||||
auto s = std::chrono::seconds(42);
|
auto s = std::chrono::seconds(42);
|
||||||
EXPECT_EQ("42s ", fmt::format("{:5}", s));
|
EXPECT_EQ("42s ", fmt::format("{:5}", s));
|
||||||
EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
|
EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
|
||||||
|
@ -204,7 +166,7 @@ TEST(ChronoTest, Align) {
|
||||||
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
|
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatSpecs) {
|
TEST(chrono_test, format_specs) {
|
||||||
EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
|
EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
|
||||||
EXPECT_EQ("\n", fmt::format("{:%n}", 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("\t", fmt::format("{:%t}", std::chrono::seconds(0)));
|
||||||
|
@ -233,43 +195,64 @@ TEST(ChronoTest, FormatSpecs) {
|
||||||
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345)));
|
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, InvalidSpecs) {
|
TEST(chrono_test, invalid_specs) {
|
||||||
auto sec = std::chrono::seconds(0);
|
auto sec = std::chrono::seconds(0);
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%a}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%a}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%A}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%c}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%A}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%x}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%Ex}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%c}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%X}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%EX}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%x}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%D}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%F}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ex}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%Ec}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%w}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%X}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%u}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%b}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%EX}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%B}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%z}", sec), fmt::format_error, "no date");
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%D}"), sec), fmt::format_error,
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%Z}", sec), fmt::format_error, "no date");
|
"no date");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error,
|
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");
|
"invalid format");
|
||||||
EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error,
|
EXPECT_THROW_MSG(fmt::format(runtime("{:%Oq}"), sec), fmt::format_error,
|
||||||
"invalid format");
|
"invalid format");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, Locale) {
|
auto format_tm(const std::tm& time, fmt::string_view spec,
|
||||||
const char* loc_name = "ja_JP.utf8";
|
const std::locale& loc) -> std::string {
|
||||||
bool has_locale = false;
|
auto& facet = std::use_facet<std::time_put<char>>(loc);
|
||||||
std::locale loc;
|
std::ostringstream os;
|
||||||
try {
|
os.imbue(loc);
|
||||||
loc = std::locale(loc_name);
|
facet.put(os, os, ' ', &time, spec.begin(), spec.end());
|
||||||
has_locale = true;
|
return os.str();
|
||||||
} catch (const std::runtime_error&) {
|
}
|
||||||
}
|
|
||||||
if (!has_locale) {
|
TEST(chrono_test, locale) {
|
||||||
fmt::print("{} locale is missing.\n", loc_name);
|
auto loc = get_locale("ja_JP.utf8");
|
||||||
return;
|
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("%OH", make_hour(14), std::chrono::hours(14));
|
||||||
EXPECT_TIME("%OI", 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("%OM", make_minute(42), std::chrono::minutes(42));
|
||||||
|
@ -283,9 +266,9 @@ TEST(ChronoTest, Locale) {
|
||||||
EXPECT_TIME("%p", time, sec);
|
EXPECT_TIME("%p", time, sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::chrono::duration<double, std::milli> dms;
|
using dms = std::chrono::duration<double, std::milli>;
|
||||||
|
|
||||||
TEST(ChronoTest, FormatDefaultFP) {
|
TEST(chrono_test, format_default_fp) {
|
||||||
typedef std::chrono::duration<float> fs;
|
typedef std::chrono::duration<float> fs;
|
||||||
EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234)));
|
EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234)));
|
||||||
typedef std::chrono::duration<float, std::milli> fms;
|
typedef std::chrono::duration<float, std::milli> fms;
|
||||||
|
@ -295,15 +278,15 @@ TEST(ChronoTest, FormatDefaultFP) {
|
||||||
EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234)));
|
EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatPrecision) {
|
TEST(chrono_test, format_precision) {
|
||||||
EXPECT_THROW_MSG(fmt::format("{:.2}", std::chrono::seconds(42)),
|
EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), std::chrono::seconds(42)),
|
||||||
fmt::format_error,
|
fmt::format_error,
|
||||||
"precision not allowed for this argument type");
|
"precision not allowed for this argument type");
|
||||||
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
|
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
|
||||||
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
|
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatFullSpecs) {
|
TEST(chrono_test, format_full_specs) {
|
||||||
EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
|
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.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
|
||||||
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
|
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
|
||||||
|
@ -312,7 +295,7 @@ TEST(ChronoTest, FormatFullSpecs) {
|
||||||
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
|
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatSimpleQq) {
|
TEST(chrono_test, format_simple_q) {
|
||||||
typedef std::chrono::duration<float> fs;
|
typedef std::chrono::duration<float> fs;
|
||||||
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234)));
|
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234)));
|
||||||
typedef std::chrono::duration<float, std::milli> fms;
|
typedef std::chrono::duration<float, std::milli> fms;
|
||||||
|
@ -322,15 +305,15 @@ TEST(ChronoTest, FormatSimpleQq) {
|
||||||
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234)));
|
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatPrecisionQq) {
|
TEST(chrono_test, format_precision_q) {
|
||||||
EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)),
|
EXPECT_THROW_MSG(fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
|
||||||
fmt::format_error,
|
fmt::format_error,
|
||||||
"precision not allowed for this argument type");
|
"precision not allowed for this argument type");
|
||||||
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
|
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));
|
EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, FormatFullSpecsQq) {
|
TEST(chrono_test, format_full_specs_q) {
|
||||||
EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
|
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.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.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
|
||||||
|
@ -339,17 +322,17 @@ TEST(ChronoTest, FormatFullSpecsQq) {
|
||||||
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
|
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, InvalidWidthId) {
|
TEST(chrono_test, invalid_width_id) {
|
||||||
EXPECT_THROW(fmt::format("{:{o}", std::chrono::seconds(0)),
|
EXPECT_THROW(fmt::format(runtime("{:{o}"), std::chrono::seconds(0)),
|
||||||
fmt::format_error);
|
fmt::format_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, InvalidColons) {
|
TEST(chrono_test, invalid_colons) {
|
||||||
EXPECT_THROW(fmt::format("{0}=:{0::", std::chrono::seconds(0)),
|
EXPECT_THROW(fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)),
|
||||||
fmt::format_error);
|
fmt::format_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, NegativeDurations) {
|
TEST(chrono_test, negative_durations) {
|
||||||
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
|
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
|
||||||
EXPECT_EQ("-03:25:45",
|
EXPECT_EQ("-03:25:45",
|
||||||
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
|
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
|
||||||
|
@ -364,7 +347,7 @@ TEST(ChronoTest, NegativeDurations) {
|
||||||
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
|
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ChronoTest, SpecialDurations) {
|
TEST(chrono_test, special_durations) {
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
"40.",
|
"40.",
|
||||||
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
|
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
|
||||||
|
@ -384,4 +367,19 @@ TEST(ChronoTest, SpecialDurations) {
|
||||||
"03:33:20");
|
"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
|
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
|
58
externals/fmt/test/color-test.cc
vendored
58
externals/fmt/test/color-test.cc
vendored
|
@ -7,52 +7,13 @@
|
||||||
|
|
||||||
#include "fmt/color.h"
|
#include "fmt/color.h"
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
#include <iterator> // std::back_inserter
|
||||||
|
|
||||||
TEST(ColorsTest, ColorsPrint) {
|
#include "gtest-extra.h" // EXPECT_WRITE
|
||||||
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");
|
|
||||||
EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue), "blue"),
|
|
||||||
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
|
||||||
EXPECT_WRITE(
|
|
||||||
stdout,
|
|
||||||
fmt::print(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_WRITE(stdout, fmt::print(fmt::emphasis::bold, "bold"),
|
|
||||||
"\x1b[1mbold\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::italic, "italic"),
|
|
||||||
"\x1b[3mitalic\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::underline, "underline"),
|
|
||||||
"\x1b[4munderline\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout,
|
|
||||||
fmt::print(fmt::emphasis::strikethrough, "strikethrough"),
|
|
||||||
"\x1b[9mstrikethrough\x1b[0m");
|
|
||||||
EXPECT_WRITE(
|
|
||||||
stdout,
|
|
||||||
fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
|
|
||||||
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
|
|
||||||
EXPECT_WRITE(stderr, fmt::print(stderr, fmt::emphasis::bold, "bold error"),
|
|
||||||
"\x1b[1mbold error\x1b[0m");
|
|
||||||
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
|
|
||||||
"\x1b[38;2;000;000;255mblue log\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi");
|
|
||||||
EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"),
|
|
||||||
"\x1b[31mtred\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), "tcyan"),
|
|
||||||
"\x1b[46mtcyan\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout,
|
|
||||||
fmt::print(fg(fmt::terminal_color::bright_green), "tbgreen"),
|
|
||||||
"\x1b[92mtbgreen\x1b[0m");
|
|
||||||
EXPECT_WRITE(stdout,
|
|
||||||
fmt::print(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
|
|
||||||
"\x1b[105mtbmagenta\x1b[0m");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ColorsTest, Format) {
|
TEST(color_test, format) {
|
||||||
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
|
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");
|
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
|
||||||
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"),
|
|
||||||
L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m");
|
|
||||||
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
|
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
|
||||||
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
|
@ -84,3 +45,16 @@ TEST(ColorsTest, Format) {
|
||||||
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
|
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
|
||||||
"\x1b[31mfoo\x1b[0m");
|
"\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");
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Test if compile errors are produced where necessary.
|
# Test if compile errors are produced where necessary.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
cmake_minimum_required(VERSION 3.1...3.18)
|
||||||
|
|
||||||
include(CheckCXXSourceCompiles)
|
include(CheckCXXSourceCompiles)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
425
externals/fmt/test/compile-test.cc
vendored
425
externals/fmt/test/compile-test.cc
vendored
|
@ -5,138 +5,83 @@
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <cfloat>
|
|
||||||
#include <climits>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
|
||||||
#include <deque>
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Check if fmt/compile.h compiles with windows.h included before it.
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fmt/compile.h"
|
#include "fmt/compile.h"
|
||||||
#include "gmock.h"
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "fmt/chrono.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "mock-allocator.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#undef ERROR
|
TEST(iterator_test, counting_iterator) {
|
||||||
#undef min
|
auto it = fmt::detail::counting_iterator();
|
||||||
#undef max
|
auto prev = it++;
|
||||||
|
EXPECT_EQ(prev.count(), 0);
|
||||||
using testing::Return;
|
EXPECT_EQ(it.count(), 1);
|
||||||
using testing::StrictMock;
|
EXPECT_EQ((it + 41).count(), 42);
|
||||||
|
|
||||||
// compiletime_prepared_parts_type_provider is useful only with relaxed
|
|
||||||
// constexpr.
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
|
|
||||||
void check_prepared_parts_type(Format format) {
|
|
||||||
typedef fmt::detail::compiled_format_base<decltype(format)> provider;
|
|
||||||
typedef fmt::detail::format_part<char>
|
|
||||||
expected_parts_type[EXPECTED_PARTS_COUNT];
|
|
||||||
static_assert(std::is_same<typename provider::parts_container,
|
|
||||||
expected_parts_type>::value,
|
|
||||||
"CompileTimePreparedPartsTypeProvider test failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
|
TEST(iterator_test, truncating_iterator) {
|
||||||
check_prepared_parts_type<1u>(FMT_STRING("text"));
|
char* p = nullptr;
|
||||||
check_prepared_parts_type<1u>(FMT_STRING("{}"));
|
auto it = fmt::detail::truncating_iterator<char*>(p, 3);
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("text{}"));
|
auto prev = it++;
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("{}text"));
|
EXPECT_EQ(prev.base(), p);
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("text{}text"));
|
EXPECT_EQ(it.base(), p + 1);
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}"));
|
}
|
||||||
|
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}'
|
TEST(iterator_test, truncating_iterator_default_construct) {
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{'
|
auto it = fmt::detail::truncating_iterator<char*>();
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' '
|
EXPECT_EQ(nullptr, it.base());
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text
|
EXPECT_EQ(std::size_t{0}, it.count());
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text'
|
}
|
||||||
check_prepared_parts_type<4u>(
|
|
||||||
FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text'
|
#ifdef __cpp_lib_ranges
|
||||||
|
TEST(iterator_test, truncating_iterator_is_output_iterator) {
|
||||||
|
static_assert(
|
||||||
|
std::output_iterator<fmt::detail::truncating_iterator<char*>, char>);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(CompileTest, PassStringLiteralFormat) {
|
TEST(iterator_test, truncating_back_inserter) {
|
||||||
const auto prepared = fmt::detail::compile<int>("test {}");
|
auto buffer = std::string();
|
||||||
EXPECT_EQ("test 42", fmt::format(prepared, 42));
|
auto bi = std::back_inserter(buffer);
|
||||||
const auto wprepared = fmt::detail::compile<int>(L"test {}");
|
auto it = fmt::detail::truncating_iterator<decltype(bi)>(bi, 2);
|
||||||
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
|
*it++ = '4';
|
||||||
|
*it++ = '2';
|
||||||
|
*it++ = '1';
|
||||||
|
EXPECT_EQ(buffer.size(), 2);
|
||||||
|
EXPECT_EQ(buffer, "42");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatToArrayOfChars) {
|
TEST(compile_test, compile_fallback) {
|
||||||
char buffer[32] = {0};
|
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
|
||||||
const auto prepared = fmt::detail::compile<int>("4{}");
|
// not available.
|
||||||
fmt::format_to(fmt::detail::make_checked(buffer, 32), prepared, 2);
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
||||||
EXPECT_EQ(std::string("42"), buffer);
|
|
||||||
wchar_t wbuffer[32] = {0};
|
|
||||||
const auto wprepared = fmt::detail::compile<int>(L"4{}");
|
|
||||||
fmt::format_to(fmt::detail::make_checked(wbuffer, 32), wprepared, 2);
|
|
||||||
EXPECT_EQ(std::wstring(L"42"), wbuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatToIterator) {
|
#ifdef __cpp_if_constexpr
|
||||||
std::string s(2, ' ');
|
struct test_formattable {};
|
||||||
const auto prepared = fmt::detail::compile<int>("4{}");
|
|
||||||
fmt::format_to(s.begin(), prepared, 2);
|
|
||||||
EXPECT_EQ("42", s);
|
|
||||||
std::wstring ws(2, L' ');
|
|
||||||
const auto wprepared = fmt::detail::compile<int>(L"4{}");
|
|
||||||
fmt::format_to(ws.begin(), wprepared, 2);
|
|
||||||
EXPECT_EQ(L"42", ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, FormatToN) {
|
|
||||||
char buf[5];
|
|
||||||
auto f = fmt::detail::compile<int>("{:10}");
|
|
||||||
auto result = fmt::format_to_n(buf, 5, f, 42);
|
|
||||||
EXPECT_EQ(result.size, 10);
|
|
||||||
EXPECT_EQ(result.out, buf + 5);
|
|
||||||
EXPECT_EQ(fmt::string_view(buf, 5), " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, FormattedSize) {
|
|
||||||
auto f = fmt::detail::compile<int>("{:10}");
|
|
||||||
EXPECT_EQ(fmt::formatted_size(f, 42), 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, MultipleTypes) {
|
|
||||||
auto f = fmt::detail::compile<int, int>("{} {}");
|
|
||||||
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct formattable {};
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
template <> struct formatter<formattable> : formatter<const char*> {
|
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>
|
template <typename FormatContext>
|
||||||
auto format(formattable, FormatContext& ctx) -> decltype(ctx.out()) {
|
constexpr auto format(test_formattable, FormatContext& ctx) const
|
||||||
return formatter<const char*>::format("foo", ctx);
|
-> decltype(ctx.out()) {
|
||||||
|
return formatter<const char*>::format(word_spec == 'f' ? "foo" : "bar",
|
||||||
|
ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(CompileTest, FormatUserDefinedType) {
|
TEST(compile_test, format_default) {
|
||||||
auto f = fmt::detail::compile<formattable>("{}");
|
|
||||||
EXPECT_EQ(fmt::format(f, formattable()), "foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, EmptyFormatString) {
|
|
||||||
auto f = fmt::detail::compile<>("");
|
|
||||||
EXPECT_EQ(fmt::format(f), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
|
||||||
TEST(CompileTest, FormatDefault) {
|
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
|
||||||
|
@ -146,21 +91,269 @@ TEST(CompileTest, FormatDefault) {
|
||||||
EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{}"), 4.2));
|
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("{}"), "foo"));
|
||||||
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo")));
|
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo")));
|
||||||
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), formattable()));
|
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(CompileTest, FormatSpecs) {
|
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("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(CompileTest, FormatTo) {
|
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];
|
char buf[8];
|
||||||
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
|
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
EXPECT_STREQ("42", buf);
|
EXPECT_STREQ("42", buf);
|
||||||
|
end = fmt::format_to(buf, FMT_COMPILE("{:x}"), 42);
|
||||||
|
*end = '\0';
|
||||||
|
EXPECT_STREQ("2a", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, TextAndArg) {
|
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));
|
||||||
|
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
|
#endif
|
||||||
|
|
931
externals/fmt/test/core-test.cc
vendored
931
externals/fmt/test/core-test.cc
vendored
File diff suppressed because it is too large
Load diff
58
externals/fmt/test/custom-formatter-test.cc
vendored
58
externals/fmt/test/custom-formatter-test.cc
vendored
|
@ -1,58 +0,0 @@
|
||||||
// Formatting library for C++ - custom argument formatter tests
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
|
||||||
#include "gtest-extra.h"
|
|
||||||
|
|
||||||
// MSVC 2013 is known to be broken.
|
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
|
||||||
|
|
||||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
|
||||||
// rounded to 0.
|
|
||||||
class custom_arg_formatter
|
|
||||||
: public fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
|
|
||||||
public:
|
|
||||||
using base = fmt::detail::arg_formatter<fmt::format_context::iterator, char>;
|
|
||||||
|
|
||||||
custom_arg_formatter(fmt::format_context& ctx,
|
|
||||||
fmt::format_parse_context* parse_ctx,
|
|
||||||
fmt::format_specs* s = nullptr,
|
|
||||||
const char* = nullptr)
|
|
||||||
: base(ctx, parse_ctx, s) {}
|
|
||||||
|
|
||||||
using base::operator();
|
|
||||||
|
|
||||||
iterator operator()(double value) {
|
|
||||||
// Comparing a float to 0.0 is safe.
|
|
||||||
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
|
|
||||||
return base::operator()(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
|
||||||
fmt::memory_buffer buffer;
|
|
||||||
fmt::detail::buffer<char>& base = buffer;
|
|
||||||
// Pass custom argument formatter as a template arg to vwrite.
|
|
||||||
fmt::vformat_to<custom_arg_formatter>(std::back_inserter(base), format_str,
|
|
||||||
args);
|
|
||||||
return std::string(buffer.data(), buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
std::string custom_format(const char* format_str, const Args&... args) {
|
|
||||||
auto va = fmt::make_format_args(args...);
|
|
||||||
return custom_vformat(format_str, va);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CustomFormatterTest, Format) {
|
|
||||||
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
|
||||||
}
|
|
||||||
#endif
|
|
62
externals/fmt/test/enforce-checks-test.cc
vendored
Normal file
62
externals/fmt/test/enforce-checks-test.cc
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// 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();
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
cmake_minimum_required(VERSION 3.1...3.18)
|
||||||
|
|
||||||
project(fmt-test)
|
project(fmt-test)
|
||||||
|
|
||||||
|
|
7
externals/fmt/test/format
vendored
7
externals/fmt/test/format
vendored
|
@ -8,6 +8,7 @@
|
||||||
#ifndef FMT_FORMAT_
|
#ifndef FMT_FORMAT_
|
||||||
#define FMT_FORMAT_
|
#define FMT_FORMAT_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
@ -38,9 +39,9 @@ namespace std {
|
||||||
|
|
||||||
template<class Out, class charT> class basic_format_context;
|
template<class Out, class charT> class basic_format_context;
|
||||||
using format_context = basic_format_context<
|
using format_context = basic_format_context<
|
||||||
/* unspecified */ std::back_insert_iterator<fmt::detail::buffer<char>>, char>;
|
/* unspecified */ fmt::detail::buffer_appender<char>, char>;
|
||||||
using wformat_context = basic_format_context<
|
using wformat_context = basic_format_context<
|
||||||
/* unspecified */ std::back_insert_iterator<fmt::detail::buffer<wchar_t>>, wchar_t>;
|
/* unspecified */ fmt::detail::buffer_appender<wchar_t>, wchar_t>;
|
||||||
|
|
||||||
template<class T, class charT = char> struct formatter {
|
template<class T, class charT = char> struct formatter {
|
||||||
formatter() = delete;
|
formatter() = delete;
|
||||||
|
@ -714,7 +715,7 @@ string vformat(string_view fmt, format_args args) {
|
||||||
fmt::detail::buffer<char>& buf = mbuf;
|
fmt::detail::buffer<char>& buf = mbuf;
|
||||||
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
|
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
|
||||||
detail::format_handler<af, char, format_context>
|
detail::format_handler<af, char, format_context>
|
||||||
h(std::back_inserter(buf), fmt, args, {});
|
h(fmt::detail::buffer_appender<char>(buf), fmt, args, {});
|
||||||
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
|
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
|
||||||
return to_string(mbuf);
|
return to_string(mbuf);
|
||||||
}
|
}
|
||||||
|
|
6
externals/fmt/test/format-dyn-args-test.cc
vendored
6
externals/fmt/test/format-dyn-args-test.cc
vendored
|
@ -1,6 +0,0 @@
|
||||||
// Copyright (c) 2020 Vladimir Solontsov
|
|
||||||
// SPDX-License-Identifier: MIT Licence
|
|
||||||
|
|
||||||
#include <fmt/core.h>
|
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
|
256
externals/fmt/test/format-impl-test.cc
vendored
256
externals/fmt/test/format-impl-test.cc
vendored
|
@ -5,21 +5,16 @@
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#define FMT_NOEXCEPT
|
|
||||||
#undef FMT_SHARED
|
|
||||||
#include "test-assert.h"
|
|
||||||
|
|
||||||
// Include format.cc instead of format.h to test implementation.
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "../src/format.cc"
|
// clang-format off
|
||||||
#include "fmt/printf.h"
|
#include "test-assert.h"
|
||||||
#include "gmock.h"
|
// clang-format on
|
||||||
#include "gtest-extra.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#undef max
|
#include "fmt/format.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::detail::bigint;
|
using fmt::detail::bigint;
|
||||||
using fmt::detail::fp;
|
using fmt::detail::fp;
|
||||||
|
@ -28,13 +23,13 @@ using fmt::detail::max_value;
|
||||||
static_assert(!std::is_copy_constructible<bigint>::value, "");
|
static_assert(!std::is_copy_constructible<bigint>::value, "");
|
||||||
static_assert(!std::is_copy_assignable<bigint>::value, "");
|
static_assert(!std::is_copy_assignable<bigint>::value, "");
|
||||||
|
|
||||||
TEST(BigIntTest, Construct) {
|
TEST(bigint_test, construct) {
|
||||||
EXPECT_EQ("", fmt::format("{}", bigint()));
|
EXPECT_EQ("", fmt::format("{}", bigint()));
|
||||||
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
|
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
|
||||||
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
|
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, Compare) {
|
TEST(bigint_test, compare) {
|
||||||
bigint n1(42);
|
bigint n1(42);
|
||||||
bigint n2(42);
|
bigint n2(42);
|
||||||
EXPECT_EQ(compare(n1, n2), 0);
|
EXPECT_EQ(compare(n1, n2), 0);
|
||||||
|
@ -48,7 +43,7 @@ TEST(BigIntTest, Compare) {
|
||||||
EXPECT_GT(compare(n4, n2), 0);
|
EXPECT_GT(compare(n4, n2), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, AddCompare) {
|
TEST(bigint_test, add_compare) {
|
||||||
EXPECT_LT(
|
EXPECT_LT(
|
||||||
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
|
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
|
||||||
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
|
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
|
||||||
|
@ -74,7 +69,7 @@ TEST(BigIntTest, AddCompare) {
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, ShiftLeft) {
|
TEST(bigint_test, shift_left) {
|
||||||
bigint n(0x42);
|
bigint n(0x42);
|
||||||
n <<= 0;
|
n <<= 0;
|
||||||
EXPECT_EQ("42", fmt::format("{}", n));
|
EXPECT_EQ("42", fmt::format("{}", n));
|
||||||
|
@ -84,7 +79,7 @@ TEST(BigIntTest, ShiftLeft) {
|
||||||
EXPECT_EQ("108000000", fmt::format("{}", n));
|
EXPECT_EQ("108000000", fmt::format("{}", n));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, Multiply) {
|
TEST(bigint_test, multiply) {
|
||||||
bigint n(0x42);
|
bigint n(0x42);
|
||||||
EXPECT_THROW(n *= 0, assertion_failure);
|
EXPECT_THROW(n *= 0, assertion_failure);
|
||||||
n *= 1;
|
n *= 1;
|
||||||
|
@ -101,7 +96,7 @@ TEST(BigIntTest, Multiply) {
|
||||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
|
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, Accumulator) {
|
TEST(bigint_test, accumulator) {
|
||||||
fmt::detail::accumulator acc;
|
fmt::detail::accumulator acc;
|
||||||
EXPECT_EQ(acc.lower, 0);
|
EXPECT_EQ(acc.lower, 0);
|
||||||
EXPECT_EQ(acc.upper, 0);
|
EXPECT_EQ(acc.upper, 0);
|
||||||
|
@ -110,7 +105,7 @@ TEST(BigIntTest, Accumulator) {
|
||||||
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
|
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
|
||||||
acc += 56;
|
acc += 56;
|
||||||
EXPECT_EQ(acc.lower, 90);
|
EXPECT_EQ(acc.lower, 90);
|
||||||
acc += fmt::detail::max_value<uint64_t>();
|
acc += max_value<uint64_t>();
|
||||||
EXPECT_EQ(acc.upper, 13);
|
EXPECT_EQ(acc.upper, 13);
|
||||||
EXPECT_EQ(acc.lower, 89);
|
EXPECT_EQ(acc.lower, 89);
|
||||||
acc >>= 32;
|
acc >>= 32;
|
||||||
|
@ -118,7 +113,7 @@ TEST(BigIntTest, Accumulator) {
|
||||||
EXPECT_EQ(acc.lower, 13 * 0x100000000);
|
EXPECT_EQ(acc.lower, 13 * 0x100000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, Square) {
|
TEST(bigint_test, square) {
|
||||||
bigint n0(0);
|
bigint n0(0);
|
||||||
n0.square();
|
n0.square();
|
||||||
EXPECT_EQ("0", fmt::format("{}", n0));
|
EXPECT_EQ("0", fmt::format("{}", n0));
|
||||||
|
@ -136,18 +131,18 @@ TEST(BigIntTest, Square) {
|
||||||
EXPECT_EQ("2540be400", fmt::format("{}", n4));
|
EXPECT_EQ("2540be400", fmt::format("{}", n4));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, DivModAssignZeroDivisor) {
|
TEST(bigint_test, divmod_assign_zero_divisor) {
|
||||||
bigint zero(0);
|
bigint zero(0);
|
||||||
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
|
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
|
||||||
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
|
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, DivModAssignSelf) {
|
TEST(bigint_test, divmod_assign_self) {
|
||||||
bigint n(100);
|
bigint n(100);
|
||||||
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
|
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, DivModAssignUnaligned) {
|
TEST(bigint_test, divmod_assign_unaligned) {
|
||||||
// (42 << 340) / pow(10, 100):
|
// (42 << 340) / pow(10, 100):
|
||||||
bigint n1(42);
|
bigint n1(42);
|
||||||
n1 <<= 340;
|
n1 <<= 340;
|
||||||
|
@ -159,7 +154,7 @@ TEST(BigIntTest, DivModAssignUnaligned) {
|
||||||
fmt::format("{}", n1));
|
fmt::format("{}", n1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, DivModAssign) {
|
TEST(bigint_test, divmod_assign) {
|
||||||
// 100 / 10:
|
// 100 / 10:
|
||||||
bigint n1(100);
|
bigint n1(100);
|
||||||
int result = n1.divmod_assign(bigint(10));
|
int result = n1.divmod_assign(bigint(10));
|
||||||
|
@ -186,70 +181,20 @@ template <bool is_iec559> void run_double_tests() {
|
||||||
template <> void run_double_tests<true>() {
|
template <> void run_double_tests<true>() {
|
||||||
// Construct from double.
|
// Construct from double.
|
||||||
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
|
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
|
||||||
|
|
||||||
// Compute boundaries:
|
|
||||||
fp value;
|
|
||||||
// Normalized & not power of 2 - equidistant boundaries:
|
|
||||||
auto b = value.assign_with_boundaries(1.23);
|
|
||||||
EXPECT_EQ(value, fp(0x0013ae147ae147ae, -52));
|
|
||||||
EXPECT_EQ(b.lower, 0x9d70a3d70a3d6c00);
|
|
||||||
EXPECT_EQ(b.upper, 0x9d70a3d70a3d7400);
|
|
||||||
// Normalized power of 2 - lower boundary is closer:
|
|
||||||
b = value.assign_with_boundaries(1.9807040628566084e+28); // 2**94
|
|
||||||
EXPECT_EQ(value, fp(0x0010000000000000, 42));
|
|
||||||
EXPECT_EQ(b.lower, 0x7ffffffffffffe00);
|
|
||||||
EXPECT_EQ(b.upper, 0x8000000000000400);
|
|
||||||
// Smallest normalized double - equidistant boundaries:
|
|
||||||
b = value.assign_with_boundaries(2.2250738585072014e-308);
|
|
||||||
EXPECT_EQ(value, fp(0x0010000000000000, -1074));
|
|
||||||
EXPECT_EQ(b.lower, 0x7ffffffffffffc00);
|
|
||||||
EXPECT_EQ(b.upper, 0x8000000000000400);
|
|
||||||
// Subnormal - equidistant boundaries:
|
|
||||||
b = value.assign_with_boundaries(4.9406564584124654e-324);
|
|
||||||
EXPECT_EQ(value, fp(0x0000000000000001, -1074));
|
|
||||||
EXPECT_EQ(b.lower, 0x4000000000000000);
|
|
||||||
EXPECT_EQ(b.upper, 0xc000000000000000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FPTest, DoubleTests) {
|
TEST(fp_test, double_tests) {
|
||||||
run_double_tests<std::numeric_limits<double>::is_iec559>();
|
run_double_tests<std::numeric_limits<double>::is_iec559>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FPTest, Normalize) {
|
TEST(fp_test, normalize) {
|
||||||
const auto v = fp(0xbeef, 42);
|
const auto v = fp(0xbeef, 42);
|
||||||
auto normalized = normalize(v);
|
auto normalized = normalize(v);
|
||||||
EXPECT_EQ(0xbeef000000000000, normalized.f);
|
EXPECT_EQ(0xbeef000000000000, normalized.f);
|
||||||
EXPECT_EQ(-6, normalized.e);
|
EXPECT_EQ(-6, normalized.e);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FPTest, ComputeFloatBoundaries) {
|
TEST(fp_test, multiply) {
|
||||||
struct {
|
|
||||||
double x, lower, upper;
|
|
||||||
} tests[] = {
|
|
||||||
// regular
|
|
||||||
{1.5f, 1.4999999403953552, 1.5000000596046448},
|
|
||||||
// boundary
|
|
||||||
{1.0f, 0.9999999701976776, 1.0000000596046448},
|
|
||||||
// min normal
|
|
||||||
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
|
|
||||||
// max subnormal
|
|
||||||
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
|
|
||||||
// min subnormal
|
|
||||||
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
|
|
||||||
};
|
|
||||||
for (auto test : tests) {
|
|
||||||
fp vlower = normalize(fp(test.lower));
|
|
||||||
fp vupper = normalize(fp(test.upper));
|
|
||||||
vlower.f >>= vupper.e - vlower.e;
|
|
||||||
vlower.e = vupper.e;
|
|
||||||
fp value;
|
|
||||||
auto b = value.assign_float_with_boundaries(test.x);
|
|
||||||
EXPECT_EQ(vlower.f, b.lower);
|
|
||||||
EXPECT_EQ(vupper.f, b.upper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FPTest, Multiply) {
|
|
||||||
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
||||||
EXPECT_EQ(v.f, 123u * 56u);
|
EXPECT_EQ(v.f, 123u * 56u);
|
||||||
EXPECT_EQ(v.e, 4 + 7 + 64);
|
EXPECT_EQ(v.e, 4 + 7 + 64);
|
||||||
|
@ -258,19 +203,57 @@ TEST(FPTest, Multiply) {
|
||||||
EXPECT_EQ(v.e, 4 + 8 + 64);
|
EXPECT_EQ(v.e, 4 + 8 + 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FPTest, GetCachedPower) {
|
TEST(fp_test, get_cached_power) {
|
||||||
typedef std::numeric_limits<double> limits;
|
using limits = std::numeric_limits<double>;
|
||||||
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
|
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
|
||||||
int dec_exp = 0;
|
int dec_exp = 0;
|
||||||
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
|
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
|
||||||
EXPECT_LE(exp, fp.e);
|
bigint exact, cache(fp.f);
|
||||||
int dec_exp_step = 8;
|
if (dec_exp >= 0) {
|
||||||
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
|
exact.assign_pow10(dec_exp);
|
||||||
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(static_cast<double>(fp.f), fp.e));
|
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(FPTest, GetRoundDirection) {
|
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::get_round_direction;
|
||||||
using fmt::detail::round_direction;
|
using fmt::detail::round_direction;
|
||||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
|
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
|
||||||
|
@ -294,7 +277,7 @@ TEST(FPTest, GetRoundDirection) {
|
||||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
|
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FPTest, FixedHandler) {
|
TEST(fp_test, fixed_handler) {
|
||||||
struct handler : fmt::detail::fixed_handler {
|
struct handler : fmt::detail::fixed_handler {
|
||||||
char buffer[10];
|
char buffer[10];
|
||||||
handler(int prec = 0) : fmt::detail::fixed_handler() {
|
handler(int prec = 0) : fmt::detail::fixed_handler() {
|
||||||
|
@ -307,7 +290,7 @@ TEST(FPTest, FixedHandler) {
|
||||||
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
|
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
|
||||||
assertion_failure);
|
assertion_failure);
|
||||||
namespace digits = fmt::detail::digits;
|
namespace digits = fmt::detail::digits;
|
||||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done);
|
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::error);
|
||||||
// Check that divisor - error doesn't overflow.
|
// Check that divisor - error doesn't overflow.
|
||||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
|
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
|
||||||
// Check that 2 * error doesn't overflow.
|
// Check that 2 * error doesn't overflow.
|
||||||
|
@ -316,94 +299,23 @@ TEST(FPTest, FixedHandler) {
|
||||||
digits::error);
|
digits::error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
|
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
|
||||||
fmt::memory_buffer buf;
|
fmt::memory_buffer buf;
|
||||||
format_float(0.42, -1, fmt::detail::float_specs(), buf);
|
format_float(0.42, -1, fmt::detail::float_specs(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> struct value_extractor {
|
TEST(format_impl_test, format_error_code) {
|
||||||
T operator()(T value) { return value; }
|
|
||||||
|
|
||||||
template <typename U> FMT_NORETURN T operator()(U) {
|
|
||||||
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_INT128
|
|
||||||
// Apple Clang does not define typeid for __int128_t and __uint128_t.
|
|
||||||
FMT_NORETURN T operator()(fmt::detail::int128_t) {
|
|
||||||
throw std::runtime_error("invalid type __int128_t");
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_NORETURN T operator()(fmt::detail::uint128_t) {
|
|
||||||
throw std::runtime_error("invalid type __uint128_t");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(FormatTest, ArgConverter) {
|
|
||||||
long long value = max_value<long long>();
|
|
||||||
auto arg = fmt::detail::make_arg<fmt::format_context>(value);
|
|
||||||
fmt::visit_format_arg(
|
|
||||||
fmt::detail::arg_converter<long long, fmt::format_context>(arg, 'd'),
|
|
||||||
arg);
|
|
||||||
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FormatTest, FormatNegativeNaN) {
|
|
||||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
|
||||||
if (std::signbit(-nan))
|
|
||||||
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
|
||||||
else
|
|
||||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FormatTest, StrError) {
|
|
||||||
char* message = nullptr;
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = nullptr, 0),
|
|
||||||
"invalid buffer");
|
|
||||||
EXPECT_ASSERT(fmt::detail::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::detail::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
|
|
||||||
EXPECT_EQ(result, 0);
|
|
||||||
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.
|
|
||||||
#if !defined(__MINGW32__) && !defined(__sun)
|
|
||||||
result =
|
|
||||||
fmt::detail::safe_strerror(error_code, message = buffer, message_size);
|
|
||||||
EXPECT_EQ(ERANGE, result);
|
|
||||||
result = fmt::detail::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(FormatTest, FormatErrorCode) {
|
|
||||||
std::string msg = "error 42", sep = ": ";
|
std::string msg = "error 42", sep = ": ";
|
||||||
{
|
{
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
format_to(buffer, "garbage");
|
format_to(fmt::appender(buffer), "garbage");
|
||||||
fmt::detail::format_error_code(buffer, 42, "test");
|
fmt::detail::format_error_code(buffer, 42, "test");
|
||||||
EXPECT_EQ("test: " + msg, to_string(buffer));
|
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
|
auto prefix =
|
||||||
'x');
|
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
|
||||||
fmt::detail::format_error_code(buffer, 42, prefix);
|
fmt::detail::format_error_code(buffer, 42, prefix);
|
||||||
EXPECT_EQ(msg, to_string(buffer));
|
EXPECT_EQ(msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
|
@ -412,7 +324,8 @@ TEST(FormatTest, FormatErrorCode) {
|
||||||
// Test maximum buffer size.
|
// Test maximum buffer size.
|
||||||
msg = fmt::format("error {}", codes[i]);
|
msg = fmt::format("error {}", codes[i]);
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
|
auto prefix =
|
||||||
|
std::string(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
|
||||||
fmt::detail::format_error_code(buffer, codes[i], prefix);
|
fmt::detail::format_error_code(buffer, codes[i], prefix);
|
||||||
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
|
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
|
||||||
size_t size = fmt::inline_buffer_size;
|
size_t size = fmt::inline_buffer_size;
|
||||||
|
@ -425,9 +338,9 @@ TEST(FormatTest, FormatErrorCode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, CountCodePoints) {
|
TEST(format_impl_test, compute_width) {
|
||||||
EXPECT_EQ(4,
|
EXPECT_EQ(4,
|
||||||
fmt::detail::count_code_points(
|
fmt::detail::compute_width(
|
||||||
fmt::basic_string_view<fmt::detail::char8_type>(
|
fmt::basic_string_view<fmt::detail::char8_type>(
|
||||||
reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
|
reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
|
||||||
}
|
}
|
||||||
|
@ -442,15 +355,26 @@ template <typename Int> void test_count_digits() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, CountDigits) {
|
TEST(format_impl_test, count_digits) {
|
||||||
test_count_digits<uint32_t>();
|
test_count_digits<uint32_t>();
|
||||||
test_count_digits<uint64_t>();
|
test_count_digits<uint64_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, WriteFallbackUIntPtr) {
|
TEST(format_impl_test, write_fallback_uintptr) {
|
||||||
std::string s;
|
std::string s;
|
||||||
fmt::detail::write_ptr<char>(
|
fmt::detail::write_ptr<char>(
|
||||||
std::back_inserter(s),
|
std::back_inserter(s),
|
||||||
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
|
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
|
||||||
EXPECT_EQ(s, "0xface");
|
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
|
||||||
|
|
2757
externals/fmt/test/format-test.cc
vendored
2757
externals/fmt/test/format-test.cc
vendored
File diff suppressed because it is too large
Load diff
42
externals/fmt/test/fuzzing/CMakeLists.txt
vendored
42
externals/fmt/test/fuzzing/CMakeLists.txt
vendored
|
@ -1,38 +1,30 @@
|
||||||
# Copyright (c) 2019, Paul Dreik
|
# Copyright (c) 2019, Paul Dreik
|
||||||
# License: see LICENSE.rst in the fmt root directory
|
# License: see LICENSE.rst in the fmt root directory
|
||||||
|
|
||||||
# settings this links in a main. useful for reproducing,
|
# Link in the main function. Useful for reproducing, kcov, gdb, afl, valgrind.
|
||||||
# kcov, gdb, afl, valgrind.
|
# (Note that libFuzzer can also reproduce, just pass it the files.)
|
||||||
# (note that libFuzzer can also reproduce, just pass it the files)
|
option(FMT_FUZZ_LINKMAIN "Enables the reproduce mode, instead of libFuzzer" On)
|
||||||
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
|
# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for
|
||||||
# the fuzz targets, otherwise the cmake configuration step fails.
|
# the fuzz targets, otherwise the CMake configuration step fails.
|
||||||
set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
|
set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
|
||||||
|
|
||||||
# Find all fuzzers.
|
# Adds a binary for reproducing, i.e. no fuzzing, just enables replaying data
|
||||||
set(SOURCES
|
# through the fuzzers.
|
||||||
chrono_duration.cpp
|
function(add_fuzzer source)
|
||||||
named_arg.cpp
|
get_filename_component(basename ${source} NAME_WE)
|
||||||
one_arg.cpp
|
set(name ${basename}-fuzzer)
|
||||||
sprintf.cpp
|
add_executable(${name} ${source} fuzzer-common.h)
|
||||||
two_args.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
macro(implement_fuzzer sourcefile)
|
|
||||||
get_filename_component(basename ${sourcefile} NAME_WE)
|
|
||||||
set(name fuzzer_${basename})
|
|
||||||
add_executable(${name} ${sourcefile} fuzzer_common.h)
|
|
||||||
if (FMT_FUZZ_LINKMAIN)
|
if (FMT_FUZZ_LINKMAIN)
|
||||||
target_sources(${name} PRIVATE main.cpp)
|
target_sources(${name} PRIVATE main.cc)
|
||||||
endif ()
|
endif ()
|
||||||
target_link_libraries(${name} PRIVATE fmt)
|
target_link_libraries(${name} PRIVATE fmt)
|
||||||
if (FMT_FUZZ_LDFLAGS)
|
if (FMT_FUZZ_LDFLAGS)
|
||||||
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
|
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
|
||||||
endif ()
|
endif ()
|
||||||
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
|
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
|
||||||
endmacro ()
|
endfunction()
|
||||||
|
|
||||||
foreach (X IN ITEMS ${SOURCES})
|
foreach (source chrono-duration.cc float.cc named-arg.cc one-arg.cc two-args.cc)
|
||||||
implement_fuzzer(${X})
|
add_fuzzer(${source})
|
||||||
endforeach ()
|
endforeach ()
|
||||||
|
|
29
externals/fmt/test/fuzzing/README.md
vendored
29
externals/fmt/test/fuzzing/README.md
vendored
|
@ -1,27 +1,4 @@
|
||||||
# FMT Fuzzer
|
# Running the fuzzers locally
|
||||||
|
|
||||||
Fuzzing has revealed [several bugs](https://github.com/fmtlib/fmt/issues?&q=is%3Aissue+fuzz)
|
|
||||||
in fmt. It is a part of the continous fuzzing at
|
|
||||||
[oss-fuzz](https://github.com/google/oss-fuzz).
|
|
||||||
|
|
||||||
The source code is modified to make the fuzzing possible without locking up on
|
|
||||||
resource exhaustion:
|
|
||||||
```cpp
|
|
||||||
#ifdef FMT_FUZZ
|
|
||||||
if(spec.precision>100000) {
|
|
||||||
throw std::runtime_error("fuzz mode - avoiding large precision");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
```
|
|
||||||
This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
|
|
||||||
practically possible. It is used in fmt code to prevent resource exhaustion in
|
|
||||||
fuzzing mode.
|
|
||||||
The macro `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` is the
|
|
||||||
defacto standard for making fuzzing practically possible to disable certain
|
|
||||||
fuzzing-unfriendly features (for example, randomness), see [the libFuzzer
|
|
||||||
documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
|
|
||||||
|
|
||||||
## Running the fuzzers locally
|
|
||||||
|
|
||||||
There is a [helper script](build.sh) to build the fuzzers, which has only been
|
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
|
tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on
|
||||||
|
@ -34,7 +11,7 @@ mkdir build
|
||||||
cd build
|
cd build
|
||||||
export CXX=clang++
|
export CXX=clang++
|
||||||
export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
|
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 .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||||
cmake --build .
|
cmake --build .
|
||||||
```
|
```
|
||||||
should work to build the fuzzers for all platforms which clang supports.
|
should work to build the fuzzers for all platforms which clang supports.
|
||||||
|
@ -44,5 +21,5 @@ Execute a fuzzer with for instance
|
||||||
cd build
|
cd build
|
||||||
export UBSAN_OPTIONS=halt_on_error=1
|
export UBSAN_OPTIONS=halt_on_error=1
|
||||||
mkdir out_chrono
|
mkdir out_chrono
|
||||||
bin/fuzzer_chrono_duration out_chrono
|
bin/fuzzer_chrono_duration out_chrono
|
||||||
```
|
```
|
||||||
|
|
34
externals/fmt/test/fuzzing/build.sh
vendored
34
externals/fmt/test/fuzzing/build.sh
vendored
|
@ -1,7 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# Creates fuzzer builds of various kinds
|
# Creates fuzzer builds of various kinds
|
||||||
# - reproduce mode (no fuzzing, just enables replaying data through the fuzzers)
|
|
||||||
# - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works)
|
# - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works)
|
||||||
# - libFuzzer build (you will need clang)
|
# - libFuzzer build (you will need clang)
|
||||||
# - afl build (you will need afl)
|
# - afl build (you will need afl)
|
||||||
|
@ -9,7 +8,7 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2019 Paul Dreik
|
# Copyright (c) 2019 Paul Dreik
|
||||||
#
|
#
|
||||||
# License: see LICENSE.rst in the fmt root directory
|
# For the license information refer to format.h.
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
me=$(basename $0)
|
me=$(basename $0)
|
||||||
|
@ -23,16 +22,7 @@ here=$(pwd)
|
||||||
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
|
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"
|
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
|
||||||
|
|
||||||
#builds the fuzzers as one would do if using afl or just making
|
# For performance analysis of the fuzzers.
|
||||||
#binaries for reproducing.
|
|
||||||
builddir=$here/build-fuzzers-reproduce
|
|
||||||
mkdir -p $builddir
|
|
||||||
cd $builddir
|
|
||||||
CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL" cmake \
|
|
||||||
$CMAKEFLAGSALL
|
|
||||||
cmake --build $builddir
|
|
||||||
|
|
||||||
#for performance analysis of the fuzzers
|
|
||||||
builddir=$here/build-fuzzers-perfanalysis
|
builddir=$here/build-fuzzers-perfanalysis
|
||||||
mkdir -p $builddir
|
mkdir -p $builddir
|
||||||
cd $builddir
|
cd $builddir
|
||||||
|
@ -43,7 +33,7 @@ $CMAKEFLAGSALL \
|
||||||
|
|
||||||
cmake --build $builddir
|
cmake --build $builddir
|
||||||
|
|
||||||
#builds the fuzzers as oss-fuzz does
|
# Builds the fuzzers as oss-fuzz does.
|
||||||
builddir=$here/build-fuzzers-ossfuzz
|
builddir=$here/build-fuzzers-ossfuzz
|
||||||
mkdir -p $builddir
|
mkdir -p $builddir
|
||||||
cd $builddir
|
cd $builddir
|
||||||
|
@ -56,7 +46,7 @@ cmake $CMAKEFLAGSALL \
|
||||||
cmake --build $builddir
|
cmake --build $builddir
|
||||||
|
|
||||||
|
|
||||||
#builds fuzzers for local fuzzing with libfuzzer with asan+usan
|
# Builds fuzzers for local fuzzing with libfuzzer with asan+usan.
|
||||||
builddir=$here/build-fuzzers-libfuzzer
|
builddir=$here/build-fuzzers-libfuzzer
|
||||||
mkdir -p $builddir
|
mkdir -p $builddir
|
||||||
cd $builddir
|
cd $builddir
|
||||||
|
@ -68,19 +58,7 @@ cmake $CMAKEFLAGSALL \
|
||||||
|
|
||||||
cmake --build $builddir
|
cmake --build $builddir
|
||||||
|
|
||||||
#builds fuzzers for local fuzzing with libfuzzer with asan only
|
# Builds a fast fuzzer for making coverage fast.
|
||||||
builddir=$here/build-fuzzers-libfuzzer-addr
|
|
||||||
mkdir -p $builddir
|
|
||||||
cd $builddir
|
|
||||||
CXX="clang++" \
|
|
||||||
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,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
|
builddir=$here/build-fuzzers-fast
|
||||||
mkdir -p $builddir
|
mkdir -p $builddir
|
||||||
cd $builddir
|
cd $builddir
|
||||||
|
@ -94,7 +72,7 @@ cmake $CMAKEFLAGSALL \
|
||||||
cmake --build $builddir
|
cmake --build $builddir
|
||||||
|
|
||||||
|
|
||||||
#builds fuzzers for local fuzzing with afl
|
# Builds fuzzers for local fuzzing with afl.
|
||||||
builddir=$here/build-fuzzers-afl
|
builddir=$here/build-fuzzers-afl
|
||||||
mkdir -p $builddir
|
mkdir -p $builddir
|
||||||
cd $builddir
|
cd $builddir
|
||||||
|
|
135
externals/fmt/test/fuzzing/chrono-duration.cc
vendored
Normal file
135
externals/fmt/test/fuzzing/chrono-duration.cc
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// 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;
|
||||||
|
}
|
152
externals/fmt/test/fuzzing/chrono_duration.cpp
vendored
152
externals/fmt/test/fuzzing/chrono_duration.cpp
vendored
|
@ -1,152 +0,0 @@
|
||||||
// Copyright (c) 2019, Paul Dreik
|
|
||||||
// License: see LICENSE.rst in the fmt root directory
|
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
#include "fuzzer_common.h"
|
|
||||||
|
|
||||||
template <typename Item, typename Ratio>
|
|
||||||
void invoke_inner(fmt::string_view formatstring, const Item item) {
|
|
||||||
const std::chrono::duration<Item, Ratio> value(item);
|
|
||||||
try {
|
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
|
||||||
std::string message = fmt::format(formatstring, value);
|
|
||||||
#else
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
fmt::format_to(buf, formatstring, value);
|
|
||||||
#endif
|
|
||||||
} catch (std::exception& /*e*/) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Item is the underlying type for duration (int, long etc)
|
|
||||||
template <typename Item>
|
|
||||||
void invoke_outer(const uint8_t* Data, size_t Size, const int scaling) {
|
|
||||||
// always use a fixed location of the data
|
|
||||||
using fmt_fuzzer::Nfixed;
|
|
||||||
|
|
||||||
constexpr auto N = sizeof(Item);
|
|
||||||
static_assert(N <= Nfixed, "fixed size is too small");
|
|
||||||
if (Size <= Nfixed + 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
|
|
||||||
|
|
||||||
// fast forward
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
|
|
||||||
// Data is already allocated separately in libFuzzer so reading past
|
|
||||||
// the end will most likely be detected anyway
|
|
||||||
const auto formatstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
|
||||||
|
|
||||||
// doit_impl<Item,std::yocto>(buf.data(),item);
|
|
||||||
// doit_impl<Item,std::zepto>(buf.data(),item);
|
|
||||||
switch (scaling) {
|
|
||||||
case 1:
|
|
||||||
invoke_inner<Item, std::atto>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
invoke_inner<Item, std::femto>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
invoke_inner<Item, std::pico>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
invoke_inner<Item, std::nano>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
invoke_inner<Item, std::micro>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
invoke_inner<Item, std::milli>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
invoke_inner<Item, std::centi>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
invoke_inner<Item, std::deci>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
invoke_inner<Item, std::deca>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
invoke_inner<Item, std::kilo>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
invoke_inner<Item, std::mega>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
invoke_inner<Item, std::giga>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
invoke_inner<Item, std::tera>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
invoke_inner<Item, std::peta>(formatstring, item);
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
invoke_inner<Item, std::exa>(formatstring, item);
|
|
||||||
}
|
|
||||||
// doit_impl<Item,std::zeta>(buf.data(),item);
|
|
||||||
// doit_impl<Item,std::yotta>(buf.data(),item);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
|
||||||
if (Size <= 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto representation = Data[0];
|
|
||||||
const auto scaling = Data[1];
|
|
||||||
Data += 2;
|
|
||||||
Size -= 2;
|
|
||||||
|
|
||||||
switch (representation) {
|
|
||||||
case 1:
|
|
||||||
invoke_outer<char>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
invoke_outer<unsigned char>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
invoke_outer<signed char>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
invoke_outer<short>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
invoke_outer<unsigned short>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
invoke_outer<int>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
invoke_outer<unsigned int>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
invoke_outer<long>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
invoke_outer<unsigned long>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
invoke_outer<float>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
invoke_outer<double>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
invoke_outer<long double>(Data, Size, scaling);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
39
externals/fmt/test/fuzzing/float.cc
vendored
Normal file
39
externals/fmt/test/fuzzing/float.cc
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// 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;
|
||||||
|
}
|
75
externals/fmt/test/fuzzing/fuzzer-common.h
vendored
Normal file
75
externals/fmt/test/fuzzing/fuzzer-common.h
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// 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
|
67
externals/fmt/test/fuzzing/fuzzer_common.h
vendored
67
externals/fmt/test/fuzzing/fuzzer_common.h
vendored
|
@ -1,67 +0,0 @@
|
||||||
#ifndef FUZZER_COMMON_H
|
|
||||||
#define FUZZER_COMMON_H
|
|
||||||
|
|
||||||
// Copyright (c) 2019, Paul Dreik
|
|
||||||
// License: see LICENSE.rst in the fmt root directory
|
|
||||||
|
|
||||||
#include <cstdint> // std::uint8_t
|
|
||||||
#include <cstring> // memcpy
|
|
||||||
#include <type_traits> // trivially copyable
|
|
||||||
|
|
||||||
// one can format to either a string, or a buf. buf 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
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
// For this, we must know the size of the largest possible type in use.
|
|
||||||
|
|
||||||
// There are some problems on travis, claiming Nfixed is not a constant
|
|
||||||
// expression which seems to be an issue with older versions of libstdc++
|
|
||||||
#if _GLIBCXX_RELEASE >= 7
|
|
||||||
# include <algorithm>
|
|
||||||
namespace fmt_fuzzer {
|
|
||||||
constexpr auto Nfixed = std::max(sizeof(long double), sizeof(std::intmax_t));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
namespace fmt_fuzzer {
|
|
||||||
constexpr auto Nfixed = 16;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace fmt_fuzzer {
|
|
||||||
// view data as a c char pointer.
|
|
||||||
template <typename T> inline const char* as_chars(const T* data) {
|
|
||||||
return static_cast<const char*>(static_cast<const void*>(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
// view data as a byte pointer
|
|
||||||
template <typename T> inline const std::uint8_t* as_bytes(const T* data) {
|
|
||||||
return static_cast<const std::uint8_t*>(static_cast<const void*>(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
// blits bytes from Data to form an (assumed trivially constructible) object
|
|
||||||
// of type Item
|
|
||||||
template <class Item> inline Item assignFromBuf(const std::uint8_t* Data) {
|
|
||||||
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 assignFromBuf<bool>(const std::uint8_t* Data) {
|
|
||||||
return !!Data[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace fmt_fuzzer
|
|
||||||
|
|
||||||
#endif // FUZZER_COMMON_H
|
|
22
externals/fmt/test/fuzzing/main.cc
vendored
Normal file
22
externals/fmt/test/fuzzing/main.cc
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#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());
|
||||||
|
}
|
||||||
|
}
|
21
externals/fmt/test/fuzzing/main.cpp
vendored
21
externals/fmt/test/fuzzing/main.cpp
vendored
|
@ -1,21 +0,0 @@
|
||||||
#include <cassert>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#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 pos = in.tellg();
|
|
||||||
assert(pos >= 0);
|
|
||||||
in.seekg(0, std::ios_base::beg);
|
|
||||||
std::vector<char> buf(static_cast<size_t>(pos));
|
|
||||||
in.read(buf.data(), static_cast<long>(buf.size()));
|
|
||||||
assert(in.gcount() == pos);
|
|
||||||
LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size());
|
|
||||||
}
|
|
||||||
}
|
|
100
externals/fmt/test/fuzzing/named-arg.cc
vendored
Normal file
100
externals/fmt/test/fuzzing/named-arg.cc
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright (c) 2019, Paul Dreik
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
|
#include "fuzzer-common.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) {
|
||||||
|
static_assert(sizeof(T) <= fixed_size, "fixed_size too small");
|
||||||
|
if (size <= fixed_size) return;
|
||||||
|
const T value = assign_from_buf<T>(data);
|
||||||
|
data += fixed_size;
|
||||||
|
size -= fixed_size;
|
||||||
|
|
||||||
|
if (arg_name_size <= 0 || arg_name_size >= size) return;
|
||||||
|
data_to_string arg_name(data, arg_name_size, true);
|
||||||
|
data += arg_name_size;
|
||||||
|
size -= arg_name_size;
|
||||||
|
|
||||||
|
data_to_string format_str(data, size);
|
||||||
|
try {
|
||||||
|
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||||
|
std::string message =
|
||||||
|
fmt::format(format_str.get(), fmt::arg(arg_name.data(), value));
|
||||||
|
#else
|
||||||
|
fmt::memory_buffer out;
|
||||||
|
fmt::format_to(out, format_str.get(), fmt::arg(arg_name.data(), value));
|
||||||
|
#endif
|
||||||
|
} catch (std::exception&) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For dynamic dispatching to an explicit instantiation.
|
||||||
|
template <typename Callback> void invoke(int type, Callback callback) {
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
callback(bool());
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
callback(char());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
using sc = signed char;
|
||||||
|
callback(sc());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
using uc = unsigned char;
|
||||||
|
callback(uc());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
callback(short());
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
using us = unsigned short;
|
||||||
|
callback(us());
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
callback(int());
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
callback(unsigned());
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
callback(long());
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
using ul = unsigned long;
|
||||||
|
callback(ul());
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
callback(float());
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
callback(double());
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
using LD = long double;
|
||||||
|
callback(LD());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||||
|
if (size <= 3) return 0;
|
||||||
|
|
||||||
|
// Switch types depending on the first byte of the input.
|
||||||
|
const auto type = data[0] & 0x0F;
|
||||||
|
const unsigned arg_name_size = (data[0] & 0xF0) >> 4;
|
||||||
|
data++;
|
||||||
|
size--;
|
||||||
|
|
||||||
|
invoke(type, [=](auto arg) {
|
||||||
|
invoke_fmt<decltype(arg)>(data, size, arg_name_size);
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
}
|
128
externals/fmt/test/fuzzing/named_arg.cpp
vendored
128
externals/fmt/test/fuzzing/named_arg.cpp
vendored
|
@ -1,128 +0,0 @@
|
||||||
// Copyright (c) 2019, Paul Dreik
|
|
||||||
// License: see LICENSE.rst in the fmt root directory
|
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
#include "fuzzer_common.h"
|
|
||||||
|
|
||||||
template <typename Item1>
|
|
||||||
void invoke_fmt(const uint8_t* Data, size_t Size, unsigned int argsize) {
|
|
||||||
constexpr auto N1 = sizeof(Item1);
|
|
||||||
static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
|
|
||||||
if (Size <= fmt_fuzzer::Nfixed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
|
|
||||||
|
|
||||||
Data += fmt_fuzzer::Nfixed;
|
|
||||||
Size -= fmt_fuzzer::Nfixed;
|
|
||||||
|
|
||||||
// how many chars should be used for the argument name?
|
|
||||||
if (argsize <= 0 || argsize >= Size) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allocating buffers separately is slower, but increases chances
|
|
||||||
// of detecting memory errors
|
|
||||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
|
||||||
std::vector<char> argnamebuffer(argsize + 1);
|
|
||||||
std::memcpy(argnamebuffer.data(), Data, argsize);
|
|
||||||
auto argname = argnamebuffer.data();
|
|
||||||
#else
|
|
||||||
auto argname = fmt_fuzzer::as_chars(Data);
|
|
||||||
#endif
|
|
||||||
Data += argsize;
|
|
||||||
Size -= argsize;
|
|
||||||
|
|
||||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
|
||||||
// allocates as tight as possible, making it easier to catch buffer overruns.
|
|
||||||
std::vector<char> fmtstringbuffer(Size);
|
|
||||||
std::memcpy(fmtstringbuffer.data(), Data, Size);
|
|
||||||
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
|
|
||||||
#else
|
|
||||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
|
||||||
std::string message = fmt::format(fmtstring, fmt::arg(argname, item1));
|
|
||||||
#else
|
|
||||||
fmt::memory_buffer outbuf;
|
|
||||||
fmt::format_to(outbuf, fmtstring, fmt::arg(argname, item1));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// for dynamic dispatching to an explicit instantiation
|
|
||||||
template <typename Callback> void invoke(int index, Callback callback) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
callback(bool{});
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
callback(char{});
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
using sc = signed char;
|
|
||||||
callback(sc{});
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
using uc = unsigned char;
|
|
||||||
callback(uc{});
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
callback(short{});
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
using us = unsigned short;
|
|
||||||
callback(us{});
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
callback(int{});
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
callback(unsigned{});
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
callback(long{});
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
using ul = unsigned long;
|
|
||||||
callback(ul{});
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
callback(float{});
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
callback(double{});
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
using LD = long double;
|
|
||||||
callback(LD{});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
|
||||||
if (Size <= 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch types depending on the first byte of the input
|
|
||||||
const auto first = Data[0] & 0x0F;
|
|
||||||
const unsigned int second = (Data[0] & 0xF0) >> 4;
|
|
||||||
Data++;
|
|
||||||
Size--;
|
|
||||||
|
|
||||||
auto outerfcn = [=](auto param1) {
|
|
||||||
invoke_fmt<decltype(param1)>(Data, Size, second);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
invoke(first, outerfcn);
|
|
||||||
} catch (std::exception& /*e*/) {
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
91
externals/fmt/test/fuzzing/one-arg.cc
vendored
Normal file
91
externals/fmt/test/fuzzing/one-arg.cc
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright (c) 2019, Paul Dreik
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
|
#include "fuzzer-common.h"
|
||||||
|
|
||||||
|
template <typename T, typename Repr>
|
||||||
|
const T* from_repr(const Repr& r) { return &r; }
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const std::tm* from_repr<std::tm>(const std::time_t& t) {
|
||||||
|
return std::localtime(&t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Repr = T>
|
||||||
|
void invoke_fmt(const uint8_t* data, size_t size) {
|
||||||
|
static_assert(sizeof(Repr) <= fixed_size, "Nfixed is too small");
|
||||||
|
if (size <= fixed_size) return;
|
||||||
|
auto repr = assign_from_buf<Repr>(data);
|
||||||
|
const T* value = from_repr<T>(repr);
|
||||||
|
if (!value) return;
|
||||||
|
data += fixed_size;
|
||||||
|
size -= fixed_size;
|
||||||
|
data_to_string format_str(data, size);
|
||||||
|
try {
|
||||||
|
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||||
|
std::string message = fmt::format(format_str.get(), *value);
|
||||||
|
#else
|
||||||
|
fmt::memory_buffer message;
|
||||||
|
fmt::format_to(message, format_str.get(), *value);
|
||||||
|
#endif
|
||||||
|
} catch (std::exception&) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||||
|
if (size <= 3) return 0;
|
||||||
|
|
||||||
|
const auto first = data[0];
|
||||||
|
data++;
|
||||||
|
size--;
|
||||||
|
|
||||||
|
switch (first) {
|
||||||
|
case 0:
|
||||||
|
invoke_fmt<bool>(data, size);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
invoke_fmt<char>(data, size);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
invoke_fmt<unsigned char>(data, size);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
invoke_fmt<signed char>(data, size);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
invoke_fmt<short>(data, size);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
invoke_fmt<unsigned short>(data, size);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
invoke_fmt<int>(data, size);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
invoke_fmt<unsigned int>(data, size);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
invoke_fmt<long>(data, size);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
invoke_fmt<unsigned long>(data, size);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
invoke_fmt<float>(data, size);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
invoke_fmt<double>(data, size);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
invoke_fmt<long double>(data, size);
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
invoke_fmt<std::tm, std::time_t>(data, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
131
externals/fmt/test/fuzzing/one_arg.cpp
vendored
131
externals/fmt/test/fuzzing/one_arg.cpp
vendored
|
@ -1,131 +0,0 @@
|
||||||
// Copyright (c) 2019, Paul Dreik
|
|
||||||
// License: see LICENSE.rst in the fmt root directory
|
|
||||||
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#include "fuzzer_common.h"
|
|
||||||
|
|
||||||
using fmt_fuzzer::Nfixed;
|
|
||||||
|
|
||||||
template <typename Item>
|
|
||||||
void invoke_fmt(const uint8_t* Data, size_t Size) {
|
|
||||||
constexpr auto N = sizeof(Item);
|
|
||||||
static_assert(N <= Nfixed, "Nfixed is too small");
|
|
||||||
if (Size <= Nfixed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
|
|
||||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
|
||||||
// allocates as tight as possible, making it easier to catch buffer overruns.
|
|
||||||
std::vector<char> fmtstringbuffer(Size);
|
|
||||||
std::memcpy(fmtstringbuffer.data(), Data, Size);
|
|
||||||
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
|
|
||||||
#else
|
|
||||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
|
||||||
std::string message = fmt::format(fmtstring, item);
|
|
||||||
#else
|
|
||||||
fmt::memory_buffer message;
|
|
||||||
fmt::format_to(message, fmtstring, item);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void invoke_fmt_time(const uint8_t* Data, size_t Size) {
|
|
||||||
using Item = std::time_t;
|
|
||||||
constexpr auto N = sizeof(Item);
|
|
||||||
static_assert(N <= Nfixed, "Nfixed too small");
|
|
||||||
if (Size <= Nfixed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
|
||||||
// allocates as tight as possible, making it easier to catch buffer overruns.
|
|
||||||
std::vector<char> fmtstringbuffer(Size);
|
|
||||||
std::memcpy(fmtstringbuffer.data(), Data, Size);
|
|
||||||
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
|
|
||||||
#else
|
|
||||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
|
||||||
#endif
|
|
||||||
auto* b = std::localtime(&item);
|
|
||||||
if (b) {
|
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
|
||||||
std::string message = fmt::format(fmtstring, *b);
|
|
||||||
#else
|
|
||||||
fmt::memory_buffer message;
|
|
||||||
fmt::format_to(message, fmtstring, *b);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
|
||||||
if (Size <= 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto first = Data[0];
|
|
||||||
Data++;
|
|
||||||
Size--;
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (first) {
|
|
||||||
case 0:
|
|
||||||
invoke_fmt<bool>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
invoke_fmt<char>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
invoke_fmt<unsigned char>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
invoke_fmt<signed char>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
invoke_fmt<short>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
invoke_fmt<unsigned short>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
invoke_fmt<int>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
invoke_fmt<unsigned int>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
invoke_fmt<long>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
invoke_fmt<unsigned long>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
invoke_fmt<float>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
invoke_fmt<double>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
invoke_fmt<long double>(Data, Size);
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
invoke_fmt_time(Data, Size);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (std::exception& /*e*/) {
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
116
externals/fmt/test/fuzzing/sprintf.cpp
vendored
116
externals/fmt/test/fuzzing/sprintf.cpp
vendored
|
@ -1,116 +0,0 @@
|
||||||
// Copyright (c) 2019, Paul Dreik
|
|
||||||
// License: see LICENSE.rst in the fmt root directory
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <fmt/printf.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include "fuzzer_common.h"
|
|
||||||
|
|
||||||
using fmt_fuzzer::Nfixed;
|
|
||||||
|
|
||||||
template <typename Item1, typename Item2>
|
|
||||||
void invoke_fmt(const uint8_t* Data, size_t Size) {
|
|
||||||
constexpr auto N1 = sizeof(Item1);
|
|
||||||
constexpr auto N2 = sizeof(Item2);
|
|
||||||
static_assert(N1 <= Nfixed, "size1 exceeded");
|
|
||||||
static_assert(N2 <= Nfixed, "size2 exceeded");
|
|
||||||
if (Size <= Nfixed + Nfixed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
|
|
||||||
Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
|
|
||||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
|
||||||
|
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
|
||||||
std::string message = fmt::format(fmtstring, item1, item2);
|
|
||||||
#else
|
|
||||||
fmt::memory_buffer message;
|
|
||||||
fmt::format_to(message, fmtstring, item1, item2);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// for dynamic dispatching to an explicit instantiation
|
|
||||||
template <typename Callback> void invoke(int index, Callback callback) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
callback(bool{});
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
callback(char{});
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
using sc = signed char;
|
|
||||||
callback(sc{});
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
using uc = unsigned char;
|
|
||||||
callback(uc{});
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
callback(short{});
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
using us = unsigned short;
|
|
||||||
callback(us{});
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
callback(int{});
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
callback(unsigned{});
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
callback(long{});
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
using ul = unsigned long;
|
|
||||||
callback(ul{});
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
callback(float{});
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
callback(double{});
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
using LD = long double;
|
|
||||||
callback(LD{});
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
using ptr = void*;
|
|
||||||
callback(ptr{});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
|
||||||
if (Size <= 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch types depending on the first byte of the input
|
|
||||||
const auto first = Data[0] & 0x0F;
|
|
||||||
const auto second = (Data[0] & 0xF0) >> 4;
|
|
||||||
Data++;
|
|
||||||
Size--;
|
|
||||||
|
|
||||||
auto outer = [=](auto param1) {
|
|
||||||
auto inner = [=](auto param2) {
|
|
||||||
invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
|
|
||||||
};
|
|
||||||
invoke(second, inner);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
invoke(first, outer);
|
|
||||||
} catch (std::exception& /*e*/) {
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
105
externals/fmt/test/fuzzing/two-args.cc
vendored
Normal file
105
externals/fmt/test/fuzzing/two-args.cc
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright (c) 2019, Paul Dreik
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "fuzzer-common.h"
|
||||||
|
|
||||||
|
template <typename Item1, typename Item2>
|
||||||
|
void invoke_fmt(const uint8_t* data, size_t size) {
|
||||||
|
static_assert(sizeof(Item1) <= fixed_size, "size1 exceeded");
|
||||||
|
static_assert(sizeof(Item2) <= fixed_size, "size2 exceeded");
|
||||||
|
if (size <= fixed_size + fixed_size) return;
|
||||||
|
|
||||||
|
const Item1 item1 = assign_from_buf<Item1>(data);
|
||||||
|
data += fixed_size;
|
||||||
|
size -= fixed_size;
|
||||||
|
|
||||||
|
const Item2 item2 = assign_from_buf<Item2>(data);
|
||||||
|
data += fixed_size;
|
||||||
|
size -= fixed_size;
|
||||||
|
|
||||||
|
auto format_str = fmt::string_view(as_chars(data), size);
|
||||||
|
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||||
|
std::string message = fmt::format(format_str, item1, item2);
|
||||||
|
#else
|
||||||
|
fmt::memory_buffer message;
|
||||||
|
fmt::format_to(message, format_str, item1, item2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// For dynamic dispatching to an explicit instantiation.
|
||||||
|
template <typename Callback> void invoke(int index, Callback callback) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
callback(bool());
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
callback(char());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
using sc = signed char;
|
||||||
|
callback(sc());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
using uc = unsigned char;
|
||||||
|
callback(uc());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
callback(short());
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
using us = unsigned short;
|
||||||
|
callback(us());
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
callback(int());
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
callback(unsigned());
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
callback(long());
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
using ul = unsigned long;
|
||||||
|
callback(ul());
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
callback(float());
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
callback(double());
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
using LD = long double;
|
||||||
|
callback(LD());
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
using ptr = void*;
|
||||||
|
callback(ptr());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||||
|
if (size <= 3) return 0;
|
||||||
|
|
||||||
|
// Switch types depending on the first byte of the input.
|
||||||
|
const auto type1 = data[0] & 0x0F;
|
||||||
|
const auto type2 = (data[0] & 0xF0) >> 4;
|
||||||
|
data++;
|
||||||
|
size--;
|
||||||
|
try {
|
||||||
|
invoke(type1, [=](auto param1) {
|
||||||
|
invoke(type2, [=](auto param2) {
|
||||||
|
invoke_fmt<decltype(param1), decltype(param2)>(data, size);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (std::exception&) {
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
112
externals/fmt/test/fuzzing/two_args.cpp
vendored
112
externals/fmt/test/fuzzing/two_args.cpp
vendored
|
@ -1,112 +0,0 @@
|
||||||
// Copyright (c) 2019, Paul Dreik
|
|
||||||
// License: see LICENSE.rst in the fmt root directory
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "fuzzer_common.h"
|
|
||||||
|
|
||||||
constexpr auto Nfixed = fmt_fuzzer::Nfixed;
|
|
||||||
|
|
||||||
template <typename Item1, typename Item2>
|
|
||||||
void invoke_fmt(const uint8_t* Data, size_t Size) {
|
|
||||||
constexpr auto N1 = sizeof(Item1);
|
|
||||||
constexpr auto N2 = sizeof(Item2);
|
|
||||||
static_assert(N1 <= Nfixed, "size1 exceeded");
|
|
||||||
static_assert(N2 <= Nfixed, "size2 exceeded");
|
|
||||||
if (Size <= Nfixed + Nfixed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
|
|
||||||
const Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
|
|
||||||
Data += Nfixed;
|
|
||||||
Size -= Nfixed;
|
|
||||||
|
|
||||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
|
||||||
|
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
|
||||||
std::string message = fmt::format(fmtstring, item1, item2);
|
|
||||||
#else
|
|
||||||
fmt::memory_buffer message;
|
|
||||||
fmt::format_to(message, fmtstring, item1, item2);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// for dynamic dispatching to an explicit instantiation
|
|
||||||
template <typename Callback> void invoke(int index, Callback callback) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
callback(bool{});
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
callback(char{});
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
using sc = signed char;
|
|
||||||
callback(sc{});
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
using uc = unsigned char;
|
|
||||||
callback(uc{});
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
callback(short{});
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
using us = unsigned short;
|
|
||||||
callback(us{});
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
callback(int{});
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
callback(unsigned{});
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
callback(long{});
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
using ul = unsigned long;
|
|
||||||
callback(ul{});
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
callback(float{});
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
callback(double{});
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
using LD = long double;
|
|
||||||
callback(LD{});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
|
||||||
if (Size <= 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch types depending on the first byte of the input
|
|
||||||
const auto first = Data[0] & 0x0F;
|
|
||||||
const auto second = (Data[0] & 0xF0) >> 4;
|
|
||||||
Data++;
|
|
||||||
Size--;
|
|
||||||
|
|
||||||
auto outer = [=](auto param1) {
|
|
||||||
auto inner = [=](auto param2) {
|
|
||||||
invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
|
|
||||||
};
|
|
||||||
invoke(second, inner);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
invoke(first, outer);
|
|
||||||
} catch (std::exception& /*e*/) {
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
14204
externals/fmt/test/gmock/gmock.h
vendored
14204
externals/fmt/test/gmock/gmock.h
vendored
File diff suppressed because it is too large
Load diff
75
externals/fmt/test/grisu-test.cc
vendored
75
externals/fmt/test/grisu-test.cc
vendored
|
@ -1,75 +0,0 @@
|
||||||
// Formatting library for C++ - Grisu tests
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
|
||||||
#include "gtest.h"
|
|
||||||
|
|
||||||
static bool reported_skipped;
|
|
||||||
|
|
||||||
#undef TEST
|
|
||||||
#define TEST(test_fixture, test_name) \
|
|
||||||
void test_fixture##test_name(); \
|
|
||||||
GTEST_TEST(test_fixture, test_name) { \
|
|
||||||
if (FMT_USE_GRISU) { \
|
|
||||||
test_fixture##test_name(); \
|
|
||||||
} else if (!reported_skipped) { \
|
|
||||||
reported_skipped = true; \
|
|
||||||
fmt::print("Skipping Grisu tests.\n"); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
void test_fixture##test_name()
|
|
||||||
|
|
||||||
TEST(GrisuTest, NaN) {
|
|
||||||
auto nan = std::numeric_limits<double>::quiet_NaN();
|
|
||||||
EXPECT_EQ("nan", fmt::format("{}", nan));
|
|
||||||
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(GrisuTest, Inf) {
|
|
||||||
auto inf = std::numeric_limits<double>::infinity();
|
|
||||||
EXPECT_EQ("inf", fmt::format("{}", inf));
|
|
||||||
EXPECT_EQ("-inf", fmt::format("{}", -inf));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(GrisuTest, Zero) { EXPECT_EQ("0.0", fmt::format("{}", 0.0)); }
|
|
||||||
|
|
||||||
TEST(GrisuTest, Round) {
|
|
||||||
EXPECT_EQ("1.9156918820264798e-56",
|
|
||||||
fmt::format("{}", 1.9156918820264798e-56));
|
|
||||||
EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(GrisuTest, Prettify) {
|
|
||||||
EXPECT_EQ("0.0001", fmt::format("{}", 1e-4));
|
|
||||||
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
|
|
||||||
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
|
|
||||||
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
|
|
||||||
EXPECT_EQ("100000000000.0", fmt::format("{}", 1e11));
|
|
||||||
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
|
|
||||||
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
|
|
||||||
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
|
|
||||||
EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
|
|
||||||
EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }
|
|
||||||
|
|
||||||
TEST(GrisuTest, Fallback) {
|
|
||||||
EXPECT_EQ("1e+23", fmt::format("{}", 1e23));
|
|
||||||
EXPECT_EQ("9e-265", fmt::format("{}", 9e-265));
|
|
||||||
EXPECT_EQ("5.423717798060526e+125",
|
|
||||||
fmt::format("{}", 5.423717798060526e+125));
|
|
||||||
EXPECT_EQ("1.372371880954233e-288",
|
|
||||||
fmt::format("{}", 1.372371880954233e-288));
|
|
||||||
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
|
|
||||||
EXPECT_EQ("2.2506787569811123e-253",
|
|
||||||
fmt::format("{}", 2.2506787569811123e-253));
|
|
||||||
EXPECT_EQ("1103618912042992.8", fmt::format("{}", 1103618912042992.8));
|
|
||||||
// pow(2, -25) - assymetric boundaries:
|
|
||||||
EXPECT_EQ("2.9802322387695312e-08",
|
|
||||||
fmt::format("{}", 2.9802322387695312e-08));
|
|
||||||
}
|
|
124
externals/fmt/test/gtest-extra-test.cc
vendored
124
externals/fmt/test/gtest-extra-test.cc
vendored
|
@ -9,31 +9,18 @@
|
||||||
|
|
||||||
#include <gtest/gtest-spi.h>
|
#include <gtest/gtest-spi.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
#include "fmt/os.h"
|
||||||
# include <crtdbg.h> // for _CrtSetReportMode
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// This is used to suppress coverity warnings about untrusted values.
|
|
||||||
std::string sanitize(const std::string& s) {
|
|
||||||
std::string result;
|
|
||||||
for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i)
|
|
||||||
result.push_back(static_cast<char>(*i & 0xff));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that assertion macros evaluate their arguments exactly once.
|
// Tests that assertion macros evaluate their arguments exactly once.
|
||||||
class SingleEvaluationTest : public ::testing::Test {
|
namespace {
|
||||||
|
class single_evaluation_test : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
SingleEvaluationTest() {
|
single_evaluation_test() {
|
||||||
p_ = s_;
|
p_ = s_;
|
||||||
a_ = 0;
|
a_ = 0;
|
||||||
b_ = 0;
|
b_ = 0;
|
||||||
|
@ -45,11 +32,12 @@ class SingleEvaluationTest : public ::testing::Test {
|
||||||
static int a_;
|
static int a_;
|
||||||
static int b_;
|
static int b_;
|
||||||
};
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
const char* const SingleEvaluationTest::s_ = "01234";
|
const char* const single_evaluation_test::s_ = "01234";
|
||||||
const char* SingleEvaluationTest::p_;
|
const char* single_evaluation_test::p_;
|
||||||
int SingleEvaluationTest::a_;
|
int single_evaluation_test::a_;
|
||||||
int SingleEvaluationTest::b_;
|
int single_evaluation_test::b_;
|
||||||
|
|
||||||
void do_nothing() {}
|
void do_nothing() {}
|
||||||
|
|
||||||
|
@ -61,7 +49,7 @@ FMT_NORETURN void throw_system_error() {
|
||||||
|
|
||||||
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
|
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
|
||||||
// exactly once.
|
// exactly once.
|
||||||
TEST_F(SingleEvaluationTest, FailedEXPECT_THROW_MSG) {
|
TEST_F(single_evaluation_test, failed_expect_throw_msg) {
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_THROW_MSG(throw_exception(), std::exception, p_++), "01234");
|
EXPECT_THROW_MSG(throw_exception(), std::exception, p_++), "01234");
|
||||||
EXPECT_EQ(s_ + 1, p_);
|
EXPECT_EQ(s_ + 1, p_);
|
||||||
|
@ -69,14 +57,14 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_THROW_MSG) {
|
||||||
|
|
||||||
// Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument
|
// Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument
|
||||||
// exactly once.
|
// exactly once.
|
||||||
TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
|
TEST_F(single_evaluation_test, failed_expect_system_error) {
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++),
|
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++),
|
||||||
"01234");
|
"01234");
|
||||||
EXPECT_EQ(s_ + 1, p_);
|
EXPECT_EQ(s_ + 1, p_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that assertion arguments are evaluated exactly once.
|
// Tests that assertion arguments are evaluated exactly once.
|
||||||
TEST_F(SingleEvaluationTest, ExceptionTests) {
|
TEST_F(single_evaluation_test, exception_tests) {
|
||||||
// successful EXPECT_THROW_MSG
|
// successful EXPECT_THROW_MSG
|
||||||
EXPECT_THROW_MSG(
|
EXPECT_THROW_MSG(
|
||||||
{ // NOLINT
|
{ // NOLINT
|
||||||
|
@ -116,7 +104,7 @@ TEST_F(SingleEvaluationTest, ExceptionTests) {
|
||||||
EXPECT_EQ(4, b_);
|
EXPECT_EQ(4, b_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SingleEvaluationTest, SystemErrorTests) {
|
TEST_F(single_evaluation_test, system_error_tests) {
|
||||||
// successful EXPECT_SYSTEM_ERROR
|
// successful EXPECT_SYSTEM_ERROR
|
||||||
EXPECT_SYSTEM_ERROR(
|
EXPECT_SYSTEM_ERROR(
|
||||||
{ // NOLINT
|
{ // NOLINT
|
||||||
|
@ -159,14 +147,14 @@ TEST_F(SingleEvaluationTest, SystemErrorTests) {
|
||||||
#if FMT_USE_FCNTL
|
#if FMT_USE_FCNTL
|
||||||
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
|
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
|
||||||
// exactly once.
|
// exactly once.
|
||||||
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
|
TEST_F(single_evaluation_test, failed_expect_write) {
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
|
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
|
||||||
"01234");
|
"01234");
|
||||||
EXPECT_EQ(s_ + 1, p_);
|
EXPECT_EQ(s_ + 1, p_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that assertion arguments are evaluated exactly once.
|
// Tests that assertion arguments are evaluated exactly once.
|
||||||
TEST_F(SingleEvaluationTest, WriteTests) {
|
TEST_F(single_evaluation_test, write_tests) {
|
||||||
// successful EXPECT_WRITE
|
// successful EXPECT_WRITE
|
||||||
EXPECT_WRITE(
|
EXPECT_WRITE(
|
||||||
stdout,
|
stdout,
|
||||||
|
@ -192,7 +180,7 @@ TEST_F(SingleEvaluationTest, WriteTests) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests EXPECT_WRITE.
|
// Tests EXPECT_WRITE.
|
||||||
TEST(ExpectTest, EXPECT_WRITE) {
|
TEST(gtest_extra_test, expect_write) {
|
||||||
EXPECT_WRITE(stdout, do_nothing(), "");
|
EXPECT_WRITE(stdout, do_nothing(), "");
|
||||||
EXPECT_WRITE(stdout, std::printf("test"), "test");
|
EXPECT_WRITE(stdout, std::printf("test"), "test");
|
||||||
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
|
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
|
||||||
|
@ -201,7 +189,7 @@ TEST(ExpectTest, EXPECT_WRITE) {
|
||||||
" Actual: that");
|
" Actual: that");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
TEST(gtest_extra_test, expect_write_streaming) {
|
||||||
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
|
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
|
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
|
||||||
<< "expected failure",
|
<< "expected failure",
|
||||||
|
@ -211,7 +199,7 @@ TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
||||||
|
|
||||||
// Tests that the compiler will not complain about unreachable code in the
|
// Tests that the compiler will not complain about unreachable code in the
|
||||||
// EXPECT_THROW_MSG macro.
|
// EXPECT_THROW_MSG macro.
|
||||||
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
|
TEST(gtest_extra_test, expect_throw_no_unreachable_code_warning) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
using std::runtime_error;
|
using std::runtime_error;
|
||||||
EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
|
EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
|
||||||
|
@ -223,7 +211,7 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
|
||||||
|
|
||||||
// Tests that the compiler will not complain about unreachable code in the
|
// Tests that the compiler will not complain about unreachable code in the
|
||||||
// EXPECT_SYSTEM_ERROR macro.
|
// EXPECT_SYSTEM_ERROR macro.
|
||||||
TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
TEST(gtest_extra_test, expect_system_error_no_unreachable_code_warning) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test");
|
EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test");
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
|
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
|
||||||
|
@ -233,7 +221,7 @@ TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
TEST(gtest_extra_test, expect_throw_behaves_like_single_statement) {
|
||||||
if (::testing::internal::AlwaysFalse())
|
if (::testing::internal::AlwaysFalse())
|
||||||
EXPECT_THROW_MSG(do_nothing(), std::exception, "");
|
EXPECT_THROW_MSG(do_nothing(), std::exception, "");
|
||||||
|
|
||||||
|
@ -243,7 +231,7 @@ TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
||||||
do_nothing();
|
do_nothing();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AssertionSyntaxTest, SystemErrorAssertionBehavesLikeSingleStatement) {
|
TEST(gtest_extra_test, expect_system_error_behaves_like_single_statement) {
|
||||||
if (::testing::internal::AlwaysFalse())
|
if (::testing::internal::AlwaysFalse())
|
||||||
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "");
|
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "");
|
||||||
|
|
||||||
|
@ -253,7 +241,7 @@ TEST(AssertionSyntaxTest, SystemErrorAssertionBehavesLikeSingleStatement) {
|
||||||
do_nothing();
|
do_nothing();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AssertionSyntaxTest, WriteAssertionBehavesLikeSingleStatement) {
|
TEST(gtest_extra_test, expect_write_behaves_like_single_statement) {
|
||||||
if (::testing::internal::AlwaysFalse())
|
if (::testing::internal::AlwaysFalse())
|
||||||
EXPECT_WRITE(stdout, std::printf("x"), "x");
|
EXPECT_WRITE(stdout, std::printf("x"), "x");
|
||||||
|
|
||||||
|
@ -264,7 +252,7 @@ TEST(AssertionSyntaxTest, WriteAssertionBehavesLikeSingleStatement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests EXPECT_THROW_MSG.
|
// Tests EXPECT_THROW_MSG.
|
||||||
TEST(ExpectTest, EXPECT_THROW_MSG) {
|
TEST(gtest_extra_test, expect_throw_msg) {
|
||||||
EXPECT_THROW_MSG(throw_exception(), std::exception, "test");
|
EXPECT_THROW_MSG(throw_exception(), std::exception, "test");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_THROW_MSG(throw_exception(), std::logic_error, "test"),
|
EXPECT_THROW_MSG(throw_exception(), std::logic_error, "test"),
|
||||||
|
@ -282,15 +270,15 @@ TEST(ExpectTest, EXPECT_THROW_MSG) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests EXPECT_SYSTEM_ERROR.
|
// Tests EXPECT_SYSTEM_ERROR.
|
||||||
TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
|
TEST(gtest_extra_test, expect_system_error) {
|
||||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test");
|
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"),
|
EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"),
|
||||||
"Expected: throw_exception() throws an exception of "
|
"Expected: throw_exception() throws an exception of "
|
||||||
"type fmt::system_error.\n Actual: it throws a different type.");
|
"type std::system_error.\n Actual: it throws a different type.");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"),
|
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"),
|
||||||
"Expected: do_nothing() throws an exception of type fmt::system_error.\n"
|
"Expected: do_nothing() throws an exception of type std::system_error.\n"
|
||||||
" Actual: it throws nothing.");
|
" Actual: it throws nothing.");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"),
|
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"),
|
||||||
|
@ -298,11 +286,11 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
|
||||||
"throw_system_error() throws an exception with a different message.\n"
|
"throw_system_error() throws an exception with a different message.\n"
|
||||||
"Expected: {}\n"
|
"Expected: {}\n"
|
||||||
" Actual: {}",
|
" Actual: {}",
|
||||||
format_system_error(EDOM, "other"),
|
system_error_message(EDOM, "other"),
|
||||||
format_system_error(EDOM, "test")));
|
system_error_message(EDOM, "test")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
|
TEST(gtest_extra_test, expect_throw_msg_streaming) {
|
||||||
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
|
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
|
||||||
<< "unexpected failure";
|
<< "unexpected failure";
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
|
@ -311,7 +299,7 @@ TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
|
||||||
"expected failure");
|
"expected failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
|
TEST(gtest_extra_test, expect_system_error_streaming) {
|
||||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test")
|
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test")
|
||||||
<< "unexpected failure";
|
<< "unexpected failure";
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
|
@ -320,31 +308,19 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
|
||||||
"expected failure");
|
"expected failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatSystemError) {
|
|
||||||
fmt::memory_buffer out;
|
|
||||||
fmt::format_system_error(out, EDOM, "test message");
|
|
||||||
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
#if FMT_USE_FCNTL
|
||||||
|
|
||||||
using fmt::buffered_file;
|
using fmt::buffered_file;
|
||||||
using fmt::error_code;
|
|
||||||
using fmt::file;
|
using fmt::file;
|
||||||
|
|
||||||
TEST(ErrorCodeTest, Ctor) {
|
TEST(output_redirect_test, scoped_redirect) {
|
||||||
EXPECT_EQ(error_code().get(), 0);
|
|
||||||
EXPECT_EQ(error_code(42).get(), 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OutputRedirectTest, ScopedRedirect) {
|
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
{
|
{
|
||||||
buffered_file file(write_end.fdopen("w"));
|
buffered_file file(write_end.fdopen("w"));
|
||||||
std::fprintf(file.get(), "[[[");
|
std::fprintf(file.get(), "[[[");
|
||||||
{
|
{
|
||||||
OutputRedirect redir(file.get());
|
output_redirect redir(file.get());
|
||||||
std::fprintf(file.get(), "censored");
|
std::fprintf(file.get(), "censored");
|
||||||
}
|
}
|
||||||
std::fprintf(file.get(), "]]]");
|
std::fprintf(file.get(), "]]]");
|
||||||
|
@ -352,8 +328,8 @@ TEST(OutputRedirectTest, ScopedRedirect) {
|
||||||
EXPECT_READ(read_end, "[[[]]]");
|
EXPECT_READ(read_end, "[[[]]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that OutputRedirect handles errors in flush correctly.
|
// Test that output_redirect handles errors in flush correctly.
|
||||||
TEST(OutputRedirectTest, FlushErrorInCtor) {
|
TEST(output_redirect_test, flush_error_in_ctor) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
|
@ -362,47 +338,47 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
FMT_POSIX(close(write_fd));
|
FMT_POSIX(close(write_fd));
|
||||||
std::unique_ptr<OutputRedirect> redir{nullptr};
|
std::unique_ptr<output_redirect> redir{nullptr};
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EBADF,
|
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new output_redirect(f.get())), EBADF,
|
||||||
"cannot flush stream");
|
"cannot flush stream");
|
||||||
redir.reset(nullptr);
|
redir.reset(nullptr);
|
||||||
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, DupErrorInCtor) {
|
TEST(output_redirect_test, dup_error_in_ctor) {
|
||||||
buffered_file f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int fd = (f.fileno)();
|
int fd = (f.fileno)();
|
||||||
file copy = file::dup(fd);
|
file copy = file::dup(fd);
|
||||||
FMT_POSIX(close(fd));
|
FMT_POSIX(close(fd));
|
||||||
std::unique_ptr<OutputRedirect> redir{nullptr};
|
std::unique_ptr<output_redirect> redir{nullptr};
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||||
redir.reset(new OutputRedirect(f.get())), EBADF,
|
redir.reset(new output_redirect(f.get())), EBADF,
|
||||||
fmt::format("cannot duplicate file descriptor {}", fd));
|
fmt::format("cannot duplicate file descriptor {}", fd));
|
||||||
copy.dup2(fd); // "undo" close or dtor will fail
|
copy.dup2(fd); // "undo" close or dtor will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, RestoreAndRead) {
|
TEST(output_redirect_test, restore_and_read) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
buffered_file file(write_end.fdopen("w"));
|
buffered_file file(write_end.fdopen("w"));
|
||||||
std::fprintf(file.get(), "[[[");
|
std::fprintf(file.get(), "[[[");
|
||||||
OutputRedirect redir(file.get());
|
output_redirect redir(file.get());
|
||||||
std::fprintf(file.get(), "censored");
|
std::fprintf(file.get(), "censored");
|
||||||
EXPECT_EQ("censored", sanitize(redir.restore_and_read()));
|
EXPECT_EQ("censored", redir.restore_and_read());
|
||||||
EXPECT_EQ("", sanitize(redir.restore_and_read()));
|
EXPECT_EQ("", redir.restore_and_read());
|
||||||
std::fprintf(file.get(), "]]]");
|
std::fprintf(file.get(), "]]]");
|
||||||
file = buffered_file();
|
file = buffered_file();
|
||||||
EXPECT_READ(read_end, "[[[]]]");
|
EXPECT_READ(read_end, "[[[]]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that OutputRedirect handles errors in flush correctly.
|
// Test that OutputRedirect handles errors in flush correctly.
|
||||||
TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
TEST(output_redirect_test, flush_error_in_restore_and_read) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
file write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
buffered_file f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
OutputRedirect redir(f.get());
|
output_redirect redir(f.get());
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
FMT_POSIX(close(write_fd));
|
FMT_POSIX(close(write_fd));
|
||||||
|
@ -411,13 +387,13 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
||||||
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, ErrorInDtor) {
|
TEST(output_redirect_test, error_in_dtor) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
file write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
buffered_file f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
|
std::unique_ptr<output_redirect> redir(new output_redirect(f.get()));
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
EXPECT_WRITE(
|
EXPECT_WRITE(
|
||||||
|
@ -430,10 +406,8 @@ TEST(OutputRedirectTest, ErrorInDtor) {
|
||||||
FMT_POSIX(close(write_fd));
|
FMT_POSIX(close(write_fd));
|
||||||
SUPPRESS_ASSERT(redir.reset(nullptr));
|
SUPPRESS_ASSERT(redir.reset(nullptr));
|
||||||
},
|
},
|
||||||
format_system_error(EBADF, "cannot flush stream"));
|
system_error_message(EBADF, "cannot flush stream"));
|
||||||
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
46
externals/fmt/test/gtest-extra.cc
vendored
46
externals/fmt/test/gtest-extra.cc
vendored
|
@ -11,24 +11,7 @@
|
||||||
|
|
||||||
using fmt::file;
|
using fmt::file;
|
||||||
|
|
||||||
void OutputRedirect::flush() {
|
output_redirect::output_redirect(FILE* f) : file_(f) {
|
||||||
# if EOF != -1
|
|
||||||
# error "FMT_RETRY assumes return value of -1 indicating failure"
|
|
||||||
# endif
|
|
||||||
int result = 0;
|
|
||||||
FMT_RETRY(result, fflush(file_));
|
|
||||||
if (result != 0) throw fmt::system_error(errno, "cannot flush stream");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutputRedirect::restore() {
|
|
||||||
if (original_.descriptor() == -1) return; // Already restored.
|
|
||||||
flush();
|
|
||||||
// Restore the original file.
|
|
||||||
original_.dup2(FMT_POSIX(fileno(file_)));
|
|
||||||
original_.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputRedirect::OutputRedirect(FILE* f) : file_(f) {
|
|
||||||
flush();
|
flush();
|
||||||
int fd = FMT_POSIX(fileno(f));
|
int fd = FMT_POSIX(fileno(f));
|
||||||
// Create a file object referring to the original file.
|
// Create a file object referring to the original file.
|
||||||
|
@ -40,7 +23,7 @@ OutputRedirect::OutputRedirect(FILE* f) : file_(f) {
|
||||||
write_end.dup2(fd);
|
write_end.dup2(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputRedirect::~OutputRedirect() FMT_NOEXCEPT {
|
output_redirect::~output_redirect() FMT_NOEXCEPT {
|
||||||
try {
|
try {
|
||||||
restore();
|
restore();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -48,7 +31,24 @@ OutputRedirect::~OutputRedirect() FMT_NOEXCEPT {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string OutputRedirect::restore_and_read() {
|
void output_redirect::flush() {
|
||||||
|
# if EOF != -1
|
||||||
|
# error "FMT_RETRY assumes return value of -1 indicating failure"
|
||||||
|
# endif
|
||||||
|
int result = 0;
|
||||||
|
FMT_RETRY(result, fflush(file_));
|
||||||
|
if (result != 0) throw fmt::system_error(errno, "cannot flush stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_redirect::restore() {
|
||||||
|
if (original_.descriptor() == -1) return; // Already restored.
|
||||||
|
flush();
|
||||||
|
// Restore the original file.
|
||||||
|
original_.dup2(FMT_POSIX(fileno(file_)));
|
||||||
|
original_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output_redirect::restore_and_read() {
|
||||||
// Restore output.
|
// Restore output.
|
||||||
restore();
|
restore();
|
||||||
|
|
||||||
|
@ -79,9 +79,3 @@ std::string read(file& f, size_t count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::string_view message) {
|
|
||||||
fmt::memory_buffer out;
|
|
||||||
format_system_error(out, error_code, message);
|
|
||||||
return to_string(out);
|
|
||||||
}
|
|
||||||
|
|
50
externals/fmt/test/gtest-extra.h
vendored
50
externals/fmt/test/gtest-extra.h
vendored
|
@ -8,10 +8,12 @@
|
||||||
#ifndef FMT_GTEST_EXTRA_H_
|
#ifndef FMT_GTEST_EXTRA_H_
|
||||||
#define FMT_GTEST_EXTRA_H_
|
#define FMT_GTEST_EXTRA_H_
|
||||||
|
|
||||||
|
#include <stdlib.h> // _invalid_parameter_handler
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "fmt/os.h"
|
#include "fmt/os.h"
|
||||||
#include "gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
||||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
|
@ -51,30 +53,35 @@
|
||||||
FMT_TEST_THROW_(statement, expected_exception, expected_message, \
|
FMT_TEST_THROW_(statement, expected_exception, expected_message, \
|
||||||
GTEST_NONFATAL_FAILURE_)
|
GTEST_NONFATAL_FAILURE_)
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::string_view message);
|
inline std::string system_error_message(int error_code,
|
||||||
|
const std::string& message) {
|
||||||
|
auto ec = std::error_code(error_code, std::generic_category());
|
||||||
|
return std::system_error(ec, message).what();
|
||||||
|
}
|
||||||
|
|
||||||
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
|
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
|
||||||
EXPECT_THROW_MSG(statement, fmt::system_error, \
|
EXPECT_THROW_MSG(statement, std::system_error, \
|
||||||
format_system_error(error_code, message))
|
system_error_message(error_code, message))
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
#if FMT_USE_FCNTL
|
||||||
|
|
||||||
// Captures file output by redirecting it to a pipe.
|
// Captures file output by redirecting it to a pipe.
|
||||||
// The output it can handle is limited by the pipe capacity.
|
// The output it can handle is limited by the pipe capacity.
|
||||||
class OutputRedirect {
|
class output_redirect {
|
||||||
private:
|
private:
|
||||||
FILE* file_;
|
FILE* file_;
|
||||||
fmt::file original_; // Original file passed to redirector.
|
fmt::file original_; // Original file passed to redirector.
|
||||||
fmt::file read_end_; // Read end of the pipe where the output is redirected.
|
fmt::file read_end_; // Read end of the pipe where the output is redirected.
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
|
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
void restore();
|
void restore();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit OutputRedirect(FILE* file);
|
explicit output_redirect(FILE* file);
|
||||||
~OutputRedirect() FMT_NOEXCEPT;
|
~output_redirect() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
output_redirect(const output_redirect&) = delete;
|
||||||
|
void operator=(const output_redirect&) = delete;
|
||||||
|
|
||||||
// Restores the original file, reads output from the pipe into a string
|
// Restores the original file, reads output from the pipe into a string
|
||||||
// and returns it.
|
// and returns it.
|
||||||
|
@ -85,7 +92,7 @@ class OutputRedirect {
|
||||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||||
std::string gtest_expected_output = expected_output; \
|
std::string gtest_expected_output = expected_output; \
|
||||||
OutputRedirect gtest_redir(file); \
|
output_redirect gtest_redir(file); \
|
||||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||||
std::string gtest_output = gtest_redir.restore_and_read(); \
|
std::string gtest_output = gtest_redir.restore_and_read(); \
|
||||||
if (gtest_output != gtest_expected_output) { \
|
if (gtest_output != gtest_expected_output) { \
|
||||||
|
@ -106,7 +113,7 @@ class OutputRedirect {
|
||||||
|
|
||||||
// Suppresses Windows assertions on invalid file descriptors, making
|
// Suppresses Windows assertions on invalid file descriptors, making
|
||||||
// POSIX functions return proper error codes instead of crashing on Windows.
|
// POSIX functions return proper error codes instead of crashing on Windows.
|
||||||
class SuppressAssert {
|
class suppress_assert {
|
||||||
private:
|
private:
|
||||||
_invalid_parameter_handler original_handler_;
|
_invalid_parameter_handler original_handler_;
|
||||||
int original_report_mode_;
|
int original_report_mode_;
|
||||||
|
@ -115,11 +122,11 @@ class SuppressAssert {
|
||||||
const wchar_t*, unsigned, uintptr_t) {}
|
const wchar_t*, unsigned, uintptr_t) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SuppressAssert()
|
suppress_assert()
|
||||||
: original_handler_(
|
: original_handler_(
|
||||||
_set_invalid_parameter_handler(handle_invalid_parameter)),
|
_set_invalid_parameter_handler(handle_invalid_parameter)),
|
||||||
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
|
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
|
||||||
~SuppressAssert() {
|
~suppress_assert() {
|
||||||
_set_invalid_parameter_handler(original_handler_);
|
_set_invalid_parameter_handler(original_handler_);
|
||||||
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
|
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +134,7 @@ class SuppressAssert {
|
||||||
|
|
||||||
# define SUPPRESS_ASSERT(statement) \
|
# define SUPPRESS_ASSERT(statement) \
|
||||||
{ \
|
{ \
|
||||||
SuppressAssert sa; \
|
suppress_assert sa; \
|
||||||
statement; \
|
statement; \
|
||||||
}
|
}
|
||||||
# else
|
# else
|
||||||
|
@ -141,16 +148,17 @@ class SuppressAssert {
|
||||||
std::string read(fmt::file& f, size_t count);
|
std::string read(fmt::file& f, size_t count);
|
||||||
|
|
||||||
# define EXPECT_READ(file, expected_content) \
|
# define EXPECT_READ(file, expected_content) \
|
||||||
EXPECT_EQ(expected_content, \
|
EXPECT_EQ(expected_content, \
|
||||||
read(file, fmt::string_view(expected_content).size()))
|
read(file, fmt::string_view(expected_content).size()))
|
||||||
|
|
||||||
#else
|
#else
|
||||||
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
|
# define EXPECT_WRITE(file, statement, expected_output) \
|
||||||
|
do { \
|
||||||
|
(void)(file); \
|
||||||
|
(void)(statement); \
|
||||||
|
(void)(expected_output); \
|
||||||
|
SUCCEED(); \
|
||||||
|
} while (false)
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
|
|
||||||
ScopedMock() { Mock::instance = this; }
|
|
||||||
~ScopedMock() { Mock::instance = nullptr; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // FMT_GTEST_EXTRA_H_
|
#endif // FMT_GTEST_EXTRA_H_
|
||||||
|
|
3
externals/fmt/test/gtest/.clang-format
vendored
Normal file
3
externals/fmt/test/gtest/.clang-format
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Disable clang-format here
|
||||||
|
DisableFormat: true
|
||||||
|
SortIncludes: Never
|
31
externals/fmt/test/gtest/CMakeLists.txt
vendored
Normal file
31
externals/fmt/test/gtest/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# 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(gtest STATIC
|
||||||
|
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
||||||
|
target_compile_definitions(gtest PUBLIC GTEST_HAS_STD_WSTRING=1)
|
||||||
|
target_include_directories(gtest SYSTEM PUBLIC .)
|
||||||
|
|
||||||
|
find_package(Threads)
|
||||||
|
if (Threads_FOUND)
|
||||||
|
target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
else ()
|
||||||
|
target_compile_definitions(gtest PUBLIC GTEST_HAS_PTHREAD=0)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
|
||||||
|
target_compile_definitions(gtest PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
# Disable MSVC warnings of POSIX functions.
|
||||||
|
target_compile_options(gtest PUBLIC -Wno-deprecated-declarations)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Silence MSVC tr1 deprecation warning in gmock.
|
||||||
|
target_compile_definitions(gtest
|
||||||
|
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1)
|
File diff suppressed because it is too large
Load diff
11645
externals/fmt/test/gtest/gmock/gmock.h
vendored
Normal file
11645
externals/fmt/test/gtest/gmock/gmock.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
20065
externals/fmt/test/gtest/gtest.h
vendored
20065
externals/fmt/test/gtest/gtest.h
vendored
File diff suppressed because it is too large
Load diff
|
@ -26,17 +26,21 @@
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
|
||||||
// Author: wan@google.com (Zhanyong Wan)
|
|
||||||
//
|
//
|
||||||
// Utilities for testing Google Test itself and code that uses Google Test
|
// Utilities for testing Google Test itself and code that uses Google Test
|
||||||
// (e.g. frameworks built on top of Google Test).
|
// (e.g. frameworks built on top of Google Test).
|
||||||
|
|
||||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
|
// GOOGLETEST_CM0004 DO NOT DELETE
|
||||||
#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
namespace testing {
|
namespace testing {
|
||||||
|
|
||||||
// This helper class can be used to mock out Google Test failure reporting
|
// This helper class can be used to mock out Google Test failure reporting
|
||||||
|
@ -68,14 +72,15 @@ class GTEST_API_ ScopedFakeTestPartResultReporter
|
||||||
TestPartResultArray* result);
|
TestPartResultArray* result);
|
||||||
|
|
||||||
// The d'tor restores the previous test part result reporter.
|
// The d'tor restores the previous test part result reporter.
|
||||||
virtual ~ScopedFakeTestPartResultReporter();
|
~ScopedFakeTestPartResultReporter() override;
|
||||||
|
|
||||||
// Appends the TestPartResult object to the TestPartResultArray
|
// Appends the TestPartResult object to the TestPartResultArray
|
||||||
// received in the constructor.
|
// received in the constructor.
|
||||||
//
|
//
|
||||||
// This method is from the TestPartResultReporterInterface
|
// This method is from the TestPartResultReporterInterface
|
||||||
// interface.
|
// interface.
|
||||||
virtual void ReportTestPartResult(const TestPartResult& result);
|
void ReportTestPartResult(const TestPartResult& result) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
@ -97,13 +102,12 @@ class GTEST_API_ SingleFailureChecker {
|
||||||
public:
|
public:
|
||||||
// The constructor remembers the arguments.
|
// The constructor remembers the arguments.
|
||||||
SingleFailureChecker(const TestPartResultArray* results,
|
SingleFailureChecker(const TestPartResultArray* results,
|
||||||
TestPartResult::Type type,
|
TestPartResult::Type type, const std::string& substr);
|
||||||
const string& substr);
|
|
||||||
~SingleFailureChecker();
|
~SingleFailureChecker();
|
||||||
private:
|
private:
|
||||||
const TestPartResultArray* const results_;
|
const TestPartResultArray* const results_;
|
||||||
const TestPartResult::Type type_;
|
const TestPartResult::Type type_;
|
||||||
const string substr_;
|
const std::string substr_;
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
|
||||||
};
|
};
|
||||||
|
@ -112,6 +116,8 @@ class GTEST_API_ SingleFailureChecker {
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
// A set of macros for testing Google Test assertions or code that's expected
|
// A set of macros for testing Google Test assertions or code that's expected
|
||||||
// to generate Google Test fatal failures. It verifies that the given
|
// to generate Google Test fatal failures. It verifies that the given
|
||||||
// statement will cause exactly one fatal Google Test failure with 'substr'
|
// statement will cause exactly one fatal Google Test failure with 'substr'
|
||||||
|
@ -229,4 +235,4 @@ class GTEST_API_ SingleFailureChecker {
|
||||||
}\
|
}\
|
||||||
} while (::testing::internal::AlwaysFalse())
|
} while (::testing::internal::AlwaysFalse())
|
||||||
|
|
||||||
#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
|
12398
externals/fmt/test/gtest/gtest/gtest.h
vendored
Normal file
12398
externals/fmt/test/gtest/gtest/gtest.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
4
externals/fmt/test/header-only-test.cc
vendored
4
externals/fmt/test/header-only-test.cc
vendored
|
@ -1,3 +1,7 @@
|
||||||
// Header-only configuration test
|
// Header-only configuration test
|
||||||
|
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
|
#ifndef FMT_HEADER_ONLY
|
||||||
|
# error "Not in the header-only mode."
|
||||||
|
#endif
|
||||||
|
|
3
externals/fmt/test/header-only-test2.cc
vendored
3
externals/fmt/test/header-only-test2.cc
vendored
|
@ -1,3 +0,0 @@
|
||||||
// Additional translation unit for the header-only configuration test
|
|
||||||
|
|
||||||
#include "fmt/core.h"
|
|
109
externals/fmt/test/locale-test.cc
vendored
109
externals/fmt/test/locale-test.cc
vendored
|
@ -1,109 +0,0 @@
|
||||||
// Formatting library for C++ - locale tests
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#include "fmt/locale.h"
|
|
||||||
|
|
||||||
#include "gmock.h"
|
|
||||||
|
|
||||||
using fmt::detail::max_value;
|
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
|
||||||
template <typename Char> struct numpunct : std::numpunct<Char> {
|
|
||||||
protected:
|
|
||||||
Char do_decimal_point() const FMT_OVERRIDE { return '?'; }
|
|
||||||
std::string do_grouping() const FMT_OVERRIDE { return "\03"; }
|
|
||||||
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct no_grouping : std::numpunct<Char> {
|
|
||||||
protected:
|
|
||||||
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
|
|
||||||
std::string do_grouping() const FMT_OVERRIDE { return ""; }
|
|
||||||
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct special_grouping : std::numpunct<Char> {
|
|
||||||
protected:
|
|
||||||
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
|
|
||||||
std::string do_grouping() const FMT_OVERRIDE { return "\03\02"; }
|
|
||||||
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct small_grouping : std::numpunct<Char> {
|
|
||||||
protected:
|
|
||||||
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
|
|
||||||
std::string do_grouping() const FMT_OVERRIDE { return "\01"; }
|
|
||||||
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(LocaleTest, DoubleDecimalPoint) {
|
|
||||||
std::locale loc(std::locale(), new numpunct<char>());
|
|
||||||
EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LocaleTest, Format) {
|
|
||||||
std::locale loc(std::locale(), new numpunct<char>());
|
|
||||||
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567));
|
|
||||||
EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567));
|
|
||||||
EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567));
|
|
||||||
fmt::format_arg_store<fmt::format_context, int> as{1234567};
|
|
||||||
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as)));
|
|
||||||
std::string s;
|
|
||||||
fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567);
|
|
||||||
EXPECT_EQ("1~234~567", s);
|
|
||||||
|
|
||||||
std::locale no_grouping_loc(std::locale(), new no_grouping<char>());
|
|
||||||
EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567));
|
|
||||||
|
|
||||||
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
|
|
||||||
EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678));
|
|
||||||
EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345));
|
|
||||||
|
|
||||||
std::locale small_grouping_loc(std::locale(), new small_grouping<char>());
|
|
||||||
EXPECT_EQ("4,2,9,4,9,6,7,2,9,5",
|
|
||||||
fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LocaleTest, FormatDetaultAlign) {
|
|
||||||
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
|
|
||||||
EXPECT_EQ(" 12,345", fmt::format(special_grouping_loc, "{:8L}", 12345));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LocaleTest, WFormat) {
|
|
||||||
std::locale loc(std::locale(), new numpunct<wchar_t>());
|
|
||||||
EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567));
|
|
||||||
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567));
|
|
||||||
fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
|
|
||||||
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:L}", fmt::wformat_args(as)));
|
|
||||||
EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567));
|
|
||||||
|
|
||||||
std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>());
|
|
||||||
EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567));
|
|
||||||
|
|
||||||
std::locale special_grouping_loc(std::locale(),
|
|
||||||
new special_grouping<wchar_t>());
|
|
||||||
EXPECT_EQ(L"1,23,45,678",
|
|
||||||
fmt::format(special_grouping_loc, L"{:L}", 12345678));
|
|
||||||
|
|
||||||
std::locale small_grouping_loc(std::locale(), new small_grouping<wchar_t>());
|
|
||||||
EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5",
|
|
||||||
fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LocaleTest, DoubleFormatter) {
|
|
||||||
auto loc = std::locale(std::locale(), new special_grouping<char>());
|
|
||||||
auto f = fmt::formatter<int>();
|
|
||||||
auto parse_ctx = fmt::format_parse_context("L");
|
|
||||||
f.parse(parse_ctx);
|
|
||||||
char buf[10] = {};
|
|
||||||
fmt::basic_format_context<char*, char> format_ctx(
|
|
||||||
buf, {}, fmt::detail::locale_ref(loc));
|
|
||||||
*f.format(12345, format_ctx) = 0;
|
|
||||||
EXPECT_STREQ("12,345", buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
|
12
externals/fmt/test/mock-allocator.h
vendored
12
externals/fmt/test/mock-allocator.h
vendored
|
@ -8,14 +8,18 @@
|
||||||
#ifndef FMT_MOCK_ALLOCATOR_H_
|
#ifndef FMT_MOCK_ALLOCATOR_H_
|
||||||
#define FMT_MOCK_ALLOCATOR_H_
|
#define FMT_MOCK_ALLOCATOR_H_
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include <assert.h> // assert
|
||||||
#include "gmock.h"
|
#include <stddef.h> // size_t
|
||||||
|
|
||||||
|
#include <memory> // std::allocator_traits
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
template <typename T> class mock_allocator {
|
template <typename T> class mock_allocator {
|
||||||
public:
|
public:
|
||||||
mock_allocator() {}
|
mock_allocator() {}
|
||||||
mock_allocator(const mock_allocator&) {}
|
mock_allocator(const mock_allocator&) {}
|
||||||
typedef T value_type;
|
using value_type = T;
|
||||||
MOCK_METHOD1_T(allocate, T*(size_t n));
|
MOCK_METHOD1_T(allocate, T*(size_t n));
|
||||||
MOCK_METHOD2_T(deallocate, void(T* p, size_t n));
|
MOCK_METHOD2_T(deallocate, void(T* p, size_t n));
|
||||||
};
|
};
|
||||||
|
@ -30,7 +34,7 @@ template <typename Allocator> class allocator_ref {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef typename Allocator::value_type value_type;
|
using value_type = typename Allocator::value_type;
|
||||||
|
|
||||||
explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {}
|
explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {}
|
||||||
|
|
||||||
|
|
565
externals/fmt/test/module-test.cc
vendored
Normal file
565
externals/fmt/test/module-test.cc
vendored
Normal file
|
@ -0,0 +1,565 @@
|
||||||
|
// Formatting library for C++ - module tests
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 - present, Daniela Engert
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} module.
|
||||||
|
|
||||||
|
#ifdef _MSC_FULL_VER
|
||||||
|
// hide some implementation bugs in msvc
|
||||||
|
// that are not essential to users of the module.
|
||||||
|
# define FMT_HIDE_MODULE_BUGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <iterator>
|
||||||
|
#include <locale>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
|
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
|
# include <fcntl.h>
|
||||||
|
# define FMT_USE_FCNTL 1
|
||||||
|
#else
|
||||||
|
# define FMT_USE_FCNTL 0
|
||||||
|
#endif
|
||||||
|
#define FMT_NOEXCEPT noexcept
|
||||||
|
|
||||||
|
import fmt;
|
||||||
|
|
||||||
|
// check for macros leaking from BMI
|
||||||
|
static bool macro_leaked =
|
||||||
|
#if defined(FMT_CORE_H_) || defined(FMT_FORMAT_H)
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
|
// an implicitly exported namespace must be visible [module.interface]/2.2
|
||||||
|
TEST(module_test, namespace) {
|
||||||
|
using namespace fmt;
|
||||||
|
using namespace fmt::literals;
|
||||||
|
ASSERT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
bool oops_detail_namespace_is_visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
bool namespace_detail_invisible() {
|
||||||
|
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
|
||||||
|
_MSC_FULL_VER <= 192930129
|
||||||
|
// bug in msvc up to 16.11-pre1:
|
||||||
|
// the namespace is visible even when it is neither
|
||||||
|
// implicitly nor explicitly exported
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
using namespace detail;
|
||||||
|
// this fails to compile if fmt::detail is visible
|
||||||
|
return !oops_detail_namespace_is_visible;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace fmt
|
||||||
|
|
||||||
|
// the non-exported namespace 'detail' must be invisible [module.interface]/2
|
||||||
|
TEST(module_test, detail_namespace) {
|
||||||
|
EXPECT_TRUE(fmt::namespace_detail_invisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
// macros must not be imported from a *named* module [cpp.import]/5.1
|
||||||
|
TEST(module_test, macros) {
|
||||||
|
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
|
||||||
|
_MSC_FULL_VER <= 192930129
|
||||||
|
// bug in msvc up to 16.11-pre1:
|
||||||
|
// include-guard macros leak from BMI
|
||||||
|
// and even worse: they cannot be #undef-ined
|
||||||
|
macro_leaked = false;
|
||||||
|
#endif
|
||||||
|
EXPECT_FALSE(macro_leaked);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following is less about functional testing (that's done elsewhere)
|
||||||
|
// but rather visibility of all client-facing overloads, reachability of
|
||||||
|
// non-exported entities, name lookup and overload resolution within
|
||||||
|
// template instantitions.
|
||||||
|
// Excercise all exported entities of the API at least once.
|
||||||
|
// Instantiate as many code paths as possible.
|
||||||
|
|
||||||
|
TEST(module_test, to_string) {
|
||||||
|
EXPECT_EQ("42", fmt::to_string(42));
|
||||||
|
EXPECT_EQ("42", fmt::to_string(42.0));
|
||||||
|
|
||||||
|
EXPECT_EQ(L"42", fmt::to_wstring(42));
|
||||||
|
EXPECT_EQ(L"42", fmt::to_wstring(42.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, format) {
|
||||||
|
EXPECT_EQ("42", fmt::format("{:}", 42));
|
||||||
|
EXPECT_EQ("-42", fmt::format("{0}", -42.0));
|
||||||
|
|
||||||
|
EXPECT_EQ(L"42", fmt::format(L"{:}", 42));
|
||||||
|
EXPECT_EQ(L"-42", fmt::format(L"{0}", -42.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, format_to) {
|
||||||
|
std::string s;
|
||||||
|
fmt::format_to(std::back_inserter(s), "{}", 42);
|
||||||
|
EXPECT_EQ("42", s);
|
||||||
|
|
||||||
|
char buffer[4] = {0};
|
||||||
|
fmt::format_to(buffer, "{}", 42);
|
||||||
|
EXPECT_EQ("42", std::string_view(buffer));
|
||||||
|
|
||||||
|
fmt::memory_buffer mb;
|
||||||
|
fmt::format_to(mb, "{}", 42);
|
||||||
|
EXPECT_EQ("42", std::string_view(buffer));
|
||||||
|
|
||||||
|
std::wstring w;
|
||||||
|
fmt::format_to(std::back_inserter(w), L"{}", 42);
|
||||||
|
EXPECT_EQ(L"42", w);
|
||||||
|
|
||||||
|
wchar_t wbuffer[4] = {0};
|
||||||
|
fmt::format_to(wbuffer, L"{}", 42);
|
||||||
|
EXPECT_EQ(L"42", std::wstring_view(wbuffer));
|
||||||
|
|
||||||
|
fmt::wmemory_buffer wb;
|
||||||
|
fmt::format_to(wb, L"{}", 42);
|
||||||
|
EXPECT_EQ(L"42", std::wstring_view(wbuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, formatted_size) {
|
||||||
|
EXPECT_EQ(2u, fmt::formatted_size("{}", 42));
|
||||||
|
EXPECT_EQ(2u, fmt::formatted_size(L"{}", 42));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, format_to_n) {
|
||||||
|
std::string s;
|
||||||
|
auto result = fmt::format_to_n(std::back_inserter(s), 1, "{}", 42);
|
||||||
|
EXPECT_EQ(2u, result.size);
|
||||||
|
char buffer[4] = {0};
|
||||||
|
fmt::format_to_n(buffer, 3, "{}", 12345);
|
||||||
|
|
||||||
|
std::wstring w;
|
||||||
|
auto wresult = fmt::format_to_n(std::back_inserter(w), 1, L"{}", 42);
|
||||||
|
EXPECT_EQ(2u, wresult.size);
|
||||||
|
wchar_t wbuffer[4] = {0};
|
||||||
|
fmt::format_to_n(wbuffer, 3, L"{}", 12345);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, format_args) {
|
||||||
|
auto no_args = fmt::format_args();
|
||||||
|
EXPECT_FALSE(no_args.get(1));
|
||||||
|
|
||||||
|
fmt::basic_format_args args = fmt::make_format_args(42);
|
||||||
|
EXPECT_TRUE(args.max_size() > 0);
|
||||||
|
auto arg0 = args.get(0);
|
||||||
|
EXPECT_TRUE(arg0);
|
||||||
|
decltype(arg0) arg_none;
|
||||||
|
EXPECT_FALSE(arg_none);
|
||||||
|
EXPECT_TRUE(arg0.type() != arg_none.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, wformat_args) {
|
||||||
|
auto no_args = fmt::wformat_args();
|
||||||
|
EXPECT_FALSE(no_args.get(1));
|
||||||
|
fmt::basic_format_args args = fmt::make_wformat_args(42);
|
||||||
|
EXPECT_TRUE(args.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, checked_format_args) {
|
||||||
|
fmt::basic_format_args args = fmt::make_args_checked<int>("{}", 42);
|
||||||
|
EXPECT_TRUE(args.get(0));
|
||||||
|
fmt::basic_format_args wargs = fmt::make_args_checked<int>(L"{}", 42);
|
||||||
|
EXPECT_TRUE(wargs.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, dynamic_format_args) {
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
|
||||||
|
dyn_store.push_back(fmt::arg("a42", 42));
|
||||||
|
fmt::basic_format_args args = dyn_store;
|
||||||
|
EXPECT_FALSE(args.get(3));
|
||||||
|
EXPECT_TRUE(args.get(fmt::string_view("a42")));
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::wformat_context> wdyn_store;
|
||||||
|
wdyn_store.push_back(fmt::arg(L"a42", 42));
|
||||||
|
fmt::basic_format_args wargs = wdyn_store;
|
||||||
|
EXPECT_FALSE(wargs.get(3));
|
||||||
|
EXPECT_TRUE(wargs.get(fmt::wstring_view(L"a42")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vformat) {
|
||||||
|
EXPECT_EQ("42", fmt::vformat("{}", fmt::make_format_args(42)));
|
||||||
|
EXPECT_EQ(L"42", fmt::vformat(fmt::to_string_view(L"{}"),
|
||||||
|
fmt::make_wformat_args(42)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vformat_to) {
|
||||||
|
auto store = fmt::make_format_args(42);
|
||||||
|
std::string s;
|
||||||
|
fmt::vformat_to(std::back_inserter(s), "{}", store);
|
||||||
|
EXPECT_EQ("42", s);
|
||||||
|
|
||||||
|
char buffer[4] = {0};
|
||||||
|
fmt::vformat_to(buffer, "{:}", store);
|
||||||
|
EXPECT_EQ("42", std::string_view(buffer));
|
||||||
|
|
||||||
|
auto wstore = fmt::make_wformat_args(42);
|
||||||
|
std::wstring w;
|
||||||
|
fmt::vformat_to(std::back_inserter(w), L"{}", wstore);
|
||||||
|
EXPECT_EQ(L"42", w);
|
||||||
|
|
||||||
|
wchar_t wbuffer[4] = {0};
|
||||||
|
fmt::vformat_to(wbuffer, L"{:}", wstore);
|
||||||
|
EXPECT_EQ(L"42", std::wstring_view(wbuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vformat_to_n) {
|
||||||
|
auto store = fmt::make_format_args(12345);
|
||||||
|
std::string s;
|
||||||
|
auto result = fmt::vformat_to_n(std::back_inserter(s), 1, "{}", store);
|
||||||
|
char buffer[4] = {0};
|
||||||
|
fmt::vformat_to_n(buffer, 3, "{:}", store);
|
||||||
|
|
||||||
|
auto wstore = fmt::make_wformat_args(12345);
|
||||||
|
std::wstring w;
|
||||||
|
auto wresult = fmt::vformat_to_n(std::back_inserter(w), 1,
|
||||||
|
fmt::to_string_view(L"{}"), wstore);
|
||||||
|
wchar_t wbuffer[4] = {0};
|
||||||
|
fmt::vformat_to_n(wbuffer, 3, fmt::to_string_view(L"{:}"), wstore);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string as_string(std::wstring_view text) {
|
||||||
|
return {reinterpret_cast<const char*>(text.data()),
|
||||||
|
text.size() * sizeof(text[0])};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, print) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::print("{}µ", 42), "42µ");
|
||||||
|
EXPECT_WRITE(stderr, fmt::print(stderr, "{}µ", 4.2), "4.2µ");
|
||||||
|
if (false) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::print(L"{}µ", 42), as_string(L"42µ"));
|
||||||
|
EXPECT_WRITE(stderr, fmt::print(stderr, L"{}µ", 4.2), as_string(L"4.2µ"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vprint) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::vprint("{:}µ", fmt::make_format_args(42)), "42µ");
|
||||||
|
EXPECT_WRITE(stderr, fmt::vprint(stderr, "{}", fmt::make_format_args(4.2)),
|
||||||
|
"4.2");
|
||||||
|
if (false) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::vprint(L"{:}µ", fmt::make_wformat_args(42)),
|
||||||
|
as_string(L"42µ"));
|
||||||
|
EXPECT_WRITE(stderr, fmt::vprint(stderr, L"{}", fmt::make_wformat_args(42)),
|
||||||
|
as_string(L"42"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, named_args) {
|
||||||
|
EXPECT_EQ("42", fmt::format("{answer}", fmt::arg("answer", 42)));
|
||||||
|
EXPECT_EQ(L"42", fmt::format(L"{answer}", fmt::arg(L"answer", 42)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, literals) {
|
||||||
|
using namespace fmt::literals;
|
||||||
|
EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = 42));
|
||||||
|
EXPECT_EQ("42", "{}"_format(42));
|
||||||
|
EXPECT_EQ(L"42", fmt::format(L"{answer}", L"answer"_a = 42));
|
||||||
|
EXPECT_EQ(L"42", L"{}"_format(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, locale) {
|
||||||
|
auto store = fmt::make_format_args(4.2);
|
||||||
|
const auto classic = std::locale::classic();
|
||||||
|
EXPECT_EQ("4.2", fmt::format(classic, "{:L}", 4.2));
|
||||||
|
EXPECT_EQ("4.2", fmt::vformat(classic, "{:L}", store));
|
||||||
|
std::string s;
|
||||||
|
fmt::vformat_to(std::back_inserter(s), classic, "{:L}", store);
|
||||||
|
EXPECT_EQ("4.2", s);
|
||||||
|
EXPECT_EQ("4.2", fmt::format("{:L}", 4.2));
|
||||||
|
|
||||||
|
auto wstore = fmt::make_wformat_args(4.2);
|
||||||
|
EXPECT_EQ(L"4.2", fmt::format(classic, L"{:L}", 4.2));
|
||||||
|
EXPECT_EQ(L"4.2", fmt::vformat(classic, L"{:L}", wstore));
|
||||||
|
std::wstring w;
|
||||||
|
fmt::vformat_to(std::back_inserter(w), classic, L"{:L}", wstore);
|
||||||
|
EXPECT_EQ(L"4.2", w);
|
||||||
|
EXPECT_EQ(L"4.2", fmt::format(L"{:L}", 4.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, string_view) {
|
||||||
|
fmt::string_view nsv("fmt");
|
||||||
|
EXPECT_EQ("fmt", nsv);
|
||||||
|
EXPECT_TRUE(fmt::string_view("fmt") == nsv);
|
||||||
|
|
||||||
|
fmt::wstring_view wsv(L"fmt");
|
||||||
|
EXPECT_EQ(L"fmt", wsv);
|
||||||
|
EXPECT_TRUE(fmt::wstring_view(L"fmt") == wsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, memory_buffer) {
|
||||||
|
fmt::basic_memory_buffer<char, fmt::inline_buffer_size> buffer;
|
||||||
|
fmt::format_to(buffer, "{}", "42");
|
||||||
|
EXPECT_EQ("42", to_string(buffer));
|
||||||
|
fmt::memory_buffer nbuffer(std::move(buffer));
|
||||||
|
EXPECT_EQ("42", to_string(nbuffer));
|
||||||
|
buffer = std::move(nbuffer);
|
||||||
|
EXPECT_EQ("42", to_string(buffer));
|
||||||
|
nbuffer.clear();
|
||||||
|
EXPECT_EQ(0u, to_string(nbuffer).size());
|
||||||
|
|
||||||
|
fmt::wmemory_buffer wbuffer;
|
||||||
|
EXPECT_EQ(0u, to_string(wbuffer).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, is_char) {
|
||||||
|
EXPECT_TRUE(fmt::is_char<char>());
|
||||||
|
EXPECT_TRUE(fmt::is_char<wchar_t>());
|
||||||
|
EXPECT_TRUE(fmt::is_char<char8_t>());
|
||||||
|
EXPECT_TRUE(fmt::is_char<char16_t>());
|
||||||
|
EXPECT_TRUE(fmt::is_char<char32_t>());
|
||||||
|
EXPECT_FALSE(fmt::is_char<signed char>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, ptr) {
|
||||||
|
uintptr_t answer = 42;
|
||||||
|
auto p = std::bit_cast<int*>(answer);
|
||||||
|
EXPECT_EQ("0x2a", fmt::to_string(fmt::ptr(p)));
|
||||||
|
std::unique_ptr<int> up(p);
|
||||||
|
EXPECT_EQ("0x2a", fmt::to_string(fmt::ptr(up)));
|
||||||
|
up.release();
|
||||||
|
auto sp = std::make_shared<int>(0);
|
||||||
|
p = sp.get();
|
||||||
|
EXPECT_EQ(fmt::to_string(fmt::ptr(p)), fmt::to_string(fmt::ptr(sp)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, errors) {
|
||||||
|
auto store = fmt::make_format_args(42);
|
||||||
|
EXPECT_THROW(throw fmt::format_error("oops"), std::exception);
|
||||||
|
EXPECT_THROW(throw fmt::vsystem_error(0, "{}", store), std::system_error);
|
||||||
|
EXPECT_THROW(throw fmt::system_error(0, "{}", 42), std::system_error);
|
||||||
|
|
||||||
|
fmt::memory_buffer buffer;
|
||||||
|
fmt::format_system_error(buffer, 0, "oops");
|
||||||
|
auto oops = to_string(buffer);
|
||||||
|
EXPECT_TRUE(oops.size() > 0);
|
||||||
|
EXPECT_WRITE(stderr, fmt::report_system_error(0, "oops"), oops + '\n');
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
EXPECT_THROW(throw fmt::vwindows_error(0, "{}", store), std::system_error);
|
||||||
|
EXPECT_THROW(throw fmt::windows_error(0, "{}", 42), std::system_error);
|
||||||
|
output_redirect redirect(stderr);
|
||||||
|
fmt::report_windows_error(0, "oops");
|
||||||
|
EXPECT_TRUE(redirect.restore_and_read().size() > 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, error_code) {
|
||||||
|
EXPECT_EQ("generic:42",
|
||||||
|
fmt::format("{0}", std::error_code(42, std::generic_category())));
|
||||||
|
EXPECT_EQ("system:42",
|
||||||
|
fmt::format("{0}", std::error_code(42, fmt::system_category())));
|
||||||
|
EXPECT_EQ(L"generic:42",
|
||||||
|
fmt::format(L"{0}", std::error_code(42, std::generic_category())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, format_int) {
|
||||||
|
fmt::format_int sanswer(42);
|
||||||
|
EXPECT_EQ("42", fmt::string_view(sanswer.data(), sanswer.size()));
|
||||||
|
fmt::format_int uanswer(42u);
|
||||||
|
EXPECT_EQ("42", fmt::string_view(uanswer.data(), uanswer.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct test_formatter : fmt::formatter<char> {
|
||||||
|
bool check() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct test_dynamic_formatter : fmt::dynamic_formatter<> {
|
||||||
|
bool check() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(module_test, formatter) {
|
||||||
|
EXPECT_TRUE(test_formatter{}.check());
|
||||||
|
EXPECT_TRUE(test_dynamic_formatter{}.check());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, join) {
|
||||||
|
int arr[3] = {1, 2, 3};
|
||||||
|
std::vector<double> vec{1.0, 2.0, 3.0};
|
||||||
|
std::initializer_list<int> il{1, 2, 3};
|
||||||
|
auto sep = fmt::to_string_view(", ");
|
||||||
|
EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr + 0, arr + 3, sep)));
|
||||||
|
EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr, sep)));
|
||||||
|
EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec.begin(), vec.end(), sep)));
|
||||||
|
EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec, sep)));
|
||||||
|
EXPECT_EQ("1, 2, 3", to_string(fmt::join(il, sep)));
|
||||||
|
|
||||||
|
auto wsep = fmt::to_string_view(L", ");
|
||||||
|
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr + 0, arr + 3, wsep)));
|
||||||
|
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr, wsep)));
|
||||||
|
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(il, wsep)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, time) {
|
||||||
|
auto time_now = std::time(nullptr);
|
||||||
|
EXPECT_TRUE(fmt::localtime(time_now).tm_year > 120);
|
||||||
|
EXPECT_TRUE(fmt::gmtime(time_now).tm_year > 120);
|
||||||
|
auto chrono_now = std::chrono::system_clock::now();
|
||||||
|
EXPECT_TRUE(fmt::localtime(chrono_now).tm_year > 120);
|
||||||
|
EXPECT_TRUE(fmt::gmtime(chrono_now).tm_year > 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, time_point) {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
std::string_view past("2021-05-20 10:30:15");
|
||||||
|
EXPECT_TRUE(past < fmt::format("{:%Y-%m-%d %H:%M:%S}", now));
|
||||||
|
std::wstring_view wpast(L"2021-05-20 10:30:15");
|
||||||
|
EXPECT_TRUE(wpast < fmt::format(L"{:%Y-%m-%d %H:%M:%S}", now));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, time_duration) {
|
||||||
|
using us = std::chrono::duration<double, std::micro>;
|
||||||
|
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds{42}));
|
||||||
|
EXPECT_EQ("4.2µs", fmt::format("{:3.1}", us{4.234}));
|
||||||
|
EXPECT_EQ("4.2µs", fmt::format(std::locale::classic(), "{:L}", us{4.2}));
|
||||||
|
|
||||||
|
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds{42}));
|
||||||
|
EXPECT_EQ(L"4.2µs", fmt::format(L"{:3.1}", us{4.234}));
|
||||||
|
EXPECT_EQ(L"4.2µs", fmt::format(std::locale::classic(), L"{:L}", us{4.2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, weekday) {
|
||||||
|
EXPECT_EQ("Monday",
|
||||||
|
std::format(std::locale::classic(), "{:%A}", fmt::weekday(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, to_string_view) {
|
||||||
|
using fmt::to_string_view;
|
||||||
|
fmt::string_view nsv{to_string_view("42")};
|
||||||
|
EXPECT_EQ("42", nsv);
|
||||||
|
fmt::wstring_view wsv{to_string_view(L"42")};
|
||||||
|
EXPECT_EQ(L"42", wsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, printf) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::printf("%f", 42.123456), "42.123456");
|
||||||
|
EXPECT_WRITE(stdout, fmt::printf("%d", 42), "42");
|
||||||
|
if (false) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::printf(L"%f", 42.123456),
|
||||||
|
as_string(L"42.123456"));
|
||||||
|
EXPECT_WRITE(stdout, fmt::printf(L"%d", 42), as_string(L"42"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, fprintf) {
|
||||||
|
EXPECT_WRITE(stderr, fmt::fprintf(stderr, "%d", 42), "42");
|
||||||
|
std::ostringstream os;
|
||||||
|
fmt::fprintf(os, "%s", "bla");
|
||||||
|
EXPECT_EQ("bla", os.str());
|
||||||
|
|
||||||
|
EXPECT_WRITE(stderr, fmt::fprintf(stderr, L"%d", 42), as_string(L"42"));
|
||||||
|
std::wostringstream ws;
|
||||||
|
fmt::fprintf(ws, L"%s", L"bla");
|
||||||
|
EXPECT_EQ(L"bla", ws.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, sprintf) {
|
||||||
|
EXPECT_EQ("42", fmt::sprintf("%d", 42));
|
||||||
|
EXPECT_EQ(L"42", fmt::sprintf(L"%d", 42));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vprintf) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::vprintf("%d", fmt::make_printf_args(42)), "42");
|
||||||
|
if (false) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::vprintf(L"%d", fmt::make_wprintf_args(42)),
|
||||||
|
as_string(L"42"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vfprintf) {
|
||||||
|
auto args = fmt::make_printf_args(42);
|
||||||
|
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, "%d", args), "42");
|
||||||
|
std::ostringstream os;
|
||||||
|
fmt::vfprintf(os, "%d", args);
|
||||||
|
EXPECT_EQ("42", os.str());
|
||||||
|
auto wargs = fmt::make_wprintf_args(42);
|
||||||
|
if (false) {
|
||||||
|
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, L"%d", wargs), as_string(L"42"));
|
||||||
|
}
|
||||||
|
std::wostringstream ws;
|
||||||
|
fmt::vfprintf(ws, L"%d", wargs);
|
||||||
|
EXPECT_EQ(L"42", ws.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, vsprintf) {
|
||||||
|
EXPECT_EQ("42", fmt::vsprintf("%d", fmt::make_printf_args(42)));
|
||||||
|
EXPECT_EQ(L"42", fmt::vsprintf(L"%d", fmt::make_wprintf_args(42)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, color) {
|
||||||
|
auto fg_check = fg(fmt::rgb(255, 200, 30));
|
||||||
|
auto bg_check = bg(fmt::color::dark_slate_gray) | fmt::emphasis::italic;
|
||||||
|
auto emphasis_check = fmt::emphasis::underline | fmt::emphasis::bold;
|
||||||
|
EXPECT_EQ("\x1B[30m42\x1B[0m",
|
||||||
|
fmt::format(fg(fmt::terminal_color::black), "{}", 42));
|
||||||
|
EXPECT_EQ(L"\x1B[30m42\x1B[0m",
|
||||||
|
fmt::format(fg(fmt::terminal_color::black), L"{}", 42));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, cstring_view) {
|
||||||
|
auto s = "fmt";
|
||||||
|
EXPECT_EQ(s, fmt::cstring_view(s).c_str());
|
||||||
|
auto w = L"fmt";
|
||||||
|
EXPECT_EQ(w, fmt::wcstring_view(w).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, buffered_file) {
|
||||||
|
EXPECT_TRUE(fmt::buffered_file{}.get() == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, output_file) {
|
||||||
|
fmt::ostream out = fmt::output_file("module-test", fmt::buffer_size = 1);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct custom_context {
|
||||||
|
using char_type = char;
|
||||||
|
using parse_context_type = fmt::format_parse_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(module_test, custom_context) {
|
||||||
|
fmt::basic_format_arg<custom_context> custom_arg;
|
||||||
|
EXPECT_TRUE(!custom_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct disabled_formatter {};
|
||||||
|
|
||||||
|
TEST(module_test, has_formatter) {
|
||||||
|
EXPECT_FALSE(
|
||||||
|
(fmt::has_formatter<disabled_formatter, fmt::format_context>::value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, is_formattable) {
|
||||||
|
EXPECT_FALSE(fmt::is_formattable<disabled_formatter>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(module_test, compile_format_string) {
|
||||||
|
using namespace fmt::literals;
|
||||||
|
EXPECT_EQ("42", fmt::format("{0:x}"_cf, 0x42));
|
||||||
|
EXPECT_EQ(L"42", fmt::format(L"{:}"_cf, 42));
|
||||||
|
EXPECT_EQ("4.2", fmt::format("{arg:3.1f}"_cf, "arg"_a = 4.2));
|
||||||
|
EXPECT_EQ(L" 42", fmt::format(L"{arg:>3}"_cf, L"arg"_a = L"42"));
|
||||||
|
}
|
300
externals/fmt/test/os-test.cc
vendored
300
externals/fmt/test/os-test.cc
vendored
|
@ -19,20 +19,21 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using fmt::buffered_file;
|
using fmt::buffered_file;
|
||||||
using fmt::error_code;
|
using testing::HasSubstr;
|
||||||
|
using wstring_view = fmt::basic_string_view<wchar_t>;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
|
||||||
TEST(UtilTest, UTF16ToUTF8) {
|
TEST(util_test, utf16_to_utf8) {
|
||||||
std::string s = "ёжик";
|
auto s = std::string("ёжик");
|
||||||
fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
|
fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
|
||||||
EXPECT_EQ(s, u.str());
|
EXPECT_EQ(s, u.str());
|
||||||
EXPECT_EQ(s.size(), u.size());
|
EXPECT_EQ(s.size(), u.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, UTF16ToUTF8EmptyString) {
|
TEST(util_test, utf16_to_utf8_empty_string) {
|
||||||
std::string s = "";
|
std::string s = "";
|
||||||
fmt::detail::utf16_to_utf8 u(L"");
|
fmt::detail::utf16_to_utf8 u(L"");
|
||||||
EXPECT_EQ(s, u.str());
|
EXPECT_EQ(s, u.str());
|
||||||
|
@ -45,65 +46,73 @@ void check_utf_conversion_error(
|
||||||
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
||||||
fmt::memory_buffer out;
|
fmt::memory_buffer out;
|
||||||
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
||||||
fmt::system_error error(0, "");
|
auto error = std::system_error(std::error_code());
|
||||||
try {
|
try {
|
||||||
(Converter)(str);
|
(Converter)(str);
|
||||||
} catch (const fmt::system_error& e) {
|
} catch (const std::system_error& e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
|
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.code().value());
|
||||||
EXPECT_EQ(fmt::to_string(out), error.what());
|
EXPECT_THAT(error.what(), HasSubstr(fmt::to_string(out)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, UTF16ToUTF8Error) {
|
TEST(util_test, utf16_to_utf8_error) {
|
||||||
check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
|
check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
|
||||||
"cannot convert string from UTF-16 to UTF-8");
|
"cannot convert string from UTF-16 to UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, UTF16ToUTF8Convert) {
|
TEST(util_test, utf16_to_utf8_convert) {
|
||||||
fmt::detail::utf16_to_utf8 u;
|
fmt::detail::utf16_to_utf8 u;
|
||||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
|
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(0, 1)));
|
||||||
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
||||||
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
|
u.convert(wstring_view(L"foo", INT_MAX + 1u)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatWindowsError) {
|
TEST(os_test, format_std_error_code) {
|
||||||
|
EXPECT_EQ("generic:42",
|
||||||
|
fmt::format(FMT_STRING("{0}"),
|
||||||
|
std::error_code(42, std::generic_category())));
|
||||||
|
EXPECT_EQ("system:42",
|
||||||
|
fmt::format(FMT_STRING("{0}"),
|
||||||
|
std::error_code(42, fmt::system_category())));
|
||||||
|
EXPECT_EQ("system:-42",
|
||||||
|
fmt::format(FMT_STRING("{0}"),
|
||||||
|
std::error_code(-42, fmt::system_category())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(os_test, format_windows_error) {
|
||||||
LPWSTR message = 0;
|
LPWSTR message = 0;
|
||||||
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
auto result = FormatMessageW(
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
0, ERROR_FILE_EXISTS,
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
||||||
fmt::detail::utf16_to_utf8 utf8_message(message);
|
fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
|
||||||
LocalFree(message);
|
LocalFree(message);
|
||||||
fmt::memory_buffer actual_message;
|
fmt::memory_buffer actual_message;
|
||||||
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
|
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
|
||||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||||
fmt::to_string(actual_message));
|
fmt::to_string(actual_message));
|
||||||
actual_message.resize(0);
|
actual_message.resize(0);
|
||||||
auto max_size = fmt::detail::max_value<size_t>();
|
|
||||||
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS,
|
|
||||||
fmt::string_view(0, max_size));
|
|
||||||
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
|
|
||||||
fmt::to_string(actual_message));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatLongWindowsError) {
|
TEST(os_test, format_long_windows_error) {
|
||||||
LPWSTR message = 0;
|
LPWSTR message = 0;
|
||||||
// this error code is not available on all Windows platforms and
|
// this error code is not available on all Windows platforms and
|
||||||
// Windows SDKs, so do not fail the test if the error string cannot
|
// Windows SDKs, so do not fail the test if the error string cannot
|
||||||
// be retrieved.
|
// be retrieved.
|
||||||
const int provisioning_not_allowed =
|
int provisioning_not_allowed = 0x80284013L; // TBS_E_PROVISIONING_NOT_ALLOWED
|
||||||
0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
|
auto result = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
0, static_cast<DWORD>(provisioning_not_allowed),
|
||||||
0, static_cast<DWORD>(provisioning_not_allowed),
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
||||||
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
|
if (result == 0) {
|
||||||
|
LocalFree(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fmt::detail::utf16_to_utf8 utf8_message(message);
|
fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
|
||||||
LocalFree(message);
|
LocalFree(message);
|
||||||
fmt::memory_buffer actual_message;
|
fmt::memory_buffer actual_message;
|
||||||
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
|
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
|
||||||
|
@ -112,20 +121,20 @@ TEST(UtilTest, FormatLongWindowsError) {
|
||||||
fmt::to_string(actual_message));
|
fmt::to_string(actual_message));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, WindowsError) {
|
TEST(os_test, windows_error) {
|
||||||
fmt::system_error error(0, "");
|
auto error = std::system_error(std::error_code());
|
||||||
try {
|
try {
|
||||||
throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error");
|
throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error");
|
||||||
} catch (const fmt::system_error& e) {
|
} catch (const std::system_error& e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
fmt::memory_buffer message;
|
fmt::memory_buffer message;
|
||||||
fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
|
fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
|
||||||
EXPECT_EQ(to_string(message), error.what());
|
EXPECT_THAT(error.what(), HasSubstr(to_string(message)));
|
||||||
EXPECT_EQ(ERROR_FILE_EXISTS, error.error_code());
|
EXPECT_EQ(ERROR_FILE_EXISTS, error.code().value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, ReportWindowsError) {
|
TEST(os_test, report_windows_error) {
|
||||||
fmt::memory_buffer out;
|
fmt::memory_buffer out;
|
||||||
fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
|
fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
|
||||||
out.push_back('\n');
|
out.push_back('\n');
|
||||||
|
@ -140,30 +149,24 @@ TEST(UtilTest, ReportWindowsError) {
|
||||||
|
|
||||||
using fmt::file;
|
using fmt::file;
|
||||||
|
|
||||||
// Checks if the file is open by reading one character from it.
|
bool isclosed(int fd) {
|
||||||
static bool isopen(int fd) {
|
|
||||||
char buffer;
|
char buffer;
|
||||||
return FMT_POSIX(read(fd, &buffer, 1)) == 1;
|
auto result = std::streamsize();
|
||||||
}
|
|
||||||
|
|
||||||
static bool isclosed(int fd) {
|
|
||||||
char buffer;
|
|
||||||
std::streamsize result = 0;
|
|
||||||
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
|
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
|
||||||
return result == -1 && errno == EBADF;
|
return result == -1 && errno == EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opens a file for reading.
|
// Opens a file for reading.
|
||||||
static file open_file() {
|
file open_file() {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
write_end.write(file_content, std::strlen(file_content));
|
||||||
write_end.close();
|
write_end.close();
|
||||||
return read_end;
|
return read_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to write a string to a file.
|
// Attempts to write a string to a file.
|
||||||
static void write(file& f, fmt::string_view s) {
|
void write(file& f, fmt::string_view s) {
|
||||||
size_t num_chars_left = s.size();
|
size_t num_chars_left = s.size();
|
||||||
const char* ptr = s.data();
|
const char* ptr = s.data();
|
||||||
do {
|
do {
|
||||||
|
@ -175,12 +178,12 @@ static void write(file& f, fmt::string_view s) {
|
||||||
} while (num_chars_left != 0);
|
} while (num_chars_left != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, DefaultCtor) {
|
TEST(buffered_file_test, default_ctor) {
|
||||||
buffered_file f;
|
auto f = buffered_file();
|
||||||
EXPECT_TRUE(f.get() == nullptr);
|
EXPECT_TRUE(f.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveCtor) {
|
TEST(buffered_file_test, move_ctor) {
|
||||||
buffered_file bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
FILE* fp = bf.get();
|
FILE* fp = bf.get();
|
||||||
EXPECT_TRUE(fp != nullptr);
|
EXPECT_TRUE(fp != nullptr);
|
||||||
|
@ -189,7 +192,7 @@ TEST(BufferedFileTest, MoveCtor) {
|
||||||
EXPECT_TRUE(bf.get() == nullptr);
|
EXPECT_TRUE(bf.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveAssignment) {
|
TEST(buffered_file_test, move_assignment) {
|
||||||
buffered_file bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
FILE* fp = bf.get();
|
FILE* fp = bf.get();
|
||||||
EXPECT_TRUE(fp != nullptr);
|
EXPECT_TRUE(fp != nullptr);
|
||||||
|
@ -199,7 +202,7 @@ TEST(BufferedFileTest, MoveAssignment) {
|
||||||
EXPECT_TRUE(bf.get() == nullptr);
|
EXPECT_TRUE(bf.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
TEST(buffered_file_test, move_assignment_closes_file) {
|
||||||
buffered_file bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
buffered_file bf2 = open_buffered_file();
|
buffered_file bf2 = open_buffered_file();
|
||||||
int old_fd = bf2.fileno();
|
int old_fd = bf2.fileno();
|
||||||
|
@ -207,27 +210,27 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
TEST(buffered_file_test, move_from_temporary_in_ctor) {
|
||||||
FILE* fp = nullptr;
|
FILE* fp = nullptr;
|
||||||
buffered_file f(open_buffered_file(&fp));
|
buffered_file f = open_buffered_file(&fp);
|
||||||
EXPECT_EQ(fp, f.get());
|
EXPECT_EQ(fp, f.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
TEST(buffered_file_test, move_from_temporary_in_assignment) {
|
||||||
FILE* fp = nullptr;
|
FILE* fp = nullptr;
|
||||||
buffered_file f;
|
auto f = buffered_file();
|
||||||
f = open_buffered_file(&fp);
|
f = open_buffered_file(&fp);
|
||||||
EXPECT_EQ(fp, f.get());
|
EXPECT_EQ(fp, f.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) {
|
||||||
buffered_file f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int old_fd = f.fileno();
|
int old_fd = f.fileno();
|
||||||
f = open_buffered_file();
|
f = open_buffered_file();
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseFileInDtor) {
|
TEST(buffered_file_test, close_file_in_dtor) {
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
{
|
{
|
||||||
buffered_file f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
|
@ -236,8 +239,9 @@ TEST(BufferedFileTest, CloseFileInDtor) {
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseErrorInDtor) {
|
TEST(buffered_file_test, close_error_in_dtor) {
|
||||||
std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
|
auto f =
|
||||||
|
std::unique_ptr<buffered_file>(new buffered_file(open_buffered_file()));
|
||||||
EXPECT_WRITE(
|
EXPECT_WRITE(
|
||||||
stderr,
|
stderr,
|
||||||
{
|
{
|
||||||
|
@ -248,10 +252,10 @@ TEST(BufferedFileTest, CloseErrorInDtor) {
|
||||||
FMT_POSIX(close(f->fileno()));
|
FMT_POSIX(close(f->fileno()));
|
||||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||||
},
|
},
|
||||||
format_system_error(EBADF, "cannot close file") + "\n");
|
system_error_message(EBADF, "cannot close file") + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, Close) {
|
TEST(buffered_file_test, close) {
|
||||||
buffered_file f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int fd = f.fileno();
|
int fd = f.fileno();
|
||||||
f.close();
|
f.close();
|
||||||
|
@ -259,73 +263,101 @@ TEST(BufferedFileTest, Close) {
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseError) {
|
TEST(buffered_file_test, close_error) {
|
||||||
buffered_file f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
FMT_POSIX(close(f.fileno()));
|
FMT_POSIX(close(f.fileno()));
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||||
EXPECT_TRUE(f.get() == nullptr);
|
EXPECT_TRUE(f.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, Fileno) {
|
TEST(buffered_file_test, fileno) {
|
||||||
buffered_file f;
|
auto f = open_buffered_file();
|
||||||
# ifndef __COVERITY__
|
|
||||||
// fileno on a null FILE pointer either crashes or returns an error.
|
|
||||||
// Disable Coverity because this is intentional.
|
|
||||||
EXPECT_DEATH_IF_SUPPORTED(
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
f.fileno();
|
|
||||||
} catch (const fmt::system_error&) {
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"");
|
|
||||||
# endif
|
|
||||||
f = open_buffered_file();
|
|
||||||
EXPECT_TRUE(f.fileno() != -1);
|
EXPECT_TRUE(f.fileno() != -1);
|
||||||
file copy = file::dup(f.fileno());
|
file copy = file::dup(f.fileno());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, file_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DirectBufferedFileTest, Print) {
|
TEST(ostream_test, move) {
|
||||||
fmt::direct_buffered_file out(
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
|
fmt::ostream moved(std::move(out));
|
||||||
fmt::print(out, "The answer is {}.\n", 42);
|
moved.print("hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ostream_test, move_while_holding_data) {
|
||||||
|
{
|
||||||
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
|
out.print("Hello, ");
|
||||||
|
fmt::ostream moved(std::move(out));
|
||||||
|
moved.print("world!\n");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
file in("test-file", file::RDONLY);
|
||||||
|
EXPECT_READ(in, "Hello, world!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ostream_test, print) {
|
||||||
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
|
out.print("The answer is {}.\n",
|
||||||
|
fmt::join(std::initializer_list<int>{42}, ", "));
|
||||||
out.close();
|
out.close();
|
||||||
file in("test-file", file::RDONLY);
|
file in("test-file", file::RDONLY);
|
||||||
EXPECT_READ(in, "The answer is 42.\n");
|
EXPECT_READ(in, "The answer is 42.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DirectBufferedFileTest, BufferBoundary) {
|
TEST(ostream_test, buffer_boundary) {
|
||||||
auto str = std::string(4096, 'x');
|
auto str = std::string(4096, 'x');
|
||||||
fmt::direct_buffered_file out(
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
|
out.print("{}", str);
|
||||||
fmt::print(out, "{}", str);
|
out.print("{}", str);
|
||||||
fmt::print(out, "{}", str);
|
|
||||||
out.close();
|
out.close();
|
||||||
file in("test-file", file::RDONLY);
|
file in("test-file", file::RDONLY);
|
||||||
EXPECT_READ(in, str + str);
|
EXPECT_READ(in, str + str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, DefaultCtor) {
|
TEST(ostream_test, buffer_size) {
|
||||||
|
fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size = 1);
|
||||||
|
out.print("{}", "foo");
|
||||||
|
out.close();
|
||||||
|
file in("test-file", file::RDONLY);
|
||||||
|
EXPECT_READ(in, "foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ostream_test, truncate) {
|
||||||
|
{
|
||||||
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
|
out.print("0123456789");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
|
out.print("foo");
|
||||||
|
}
|
||||||
|
file in("test-file", file::RDONLY);
|
||||||
|
EXPECT_EQ("foo", read(in, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(file_test, default_ctor) {
|
||||||
file f;
|
file f;
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, OpenBufferedFileInCtor) {
|
TEST(file_test, open_buffered_file_in_ctor) {
|
||||||
FILE* fp = safe_fopen("test-file", "w");
|
FILE* fp = safe_fopen("test-file", "w");
|
||||||
std::fputs(FILE_CONTENT, fp);
|
std::fputs(file_content, fp);
|
||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
file f("test-file", file::RDONLY);
|
file f("test-file", file::RDONLY);
|
||||||
ASSERT_TRUE(isopen(f.descriptor()));
|
// Check if the file is open by reading one character from it.
|
||||||
|
char buffer;
|
||||||
|
bool isopen = FMT_POSIX(read(f.descriptor(), &buffer, 1)) == 1;
|
||||||
|
ASSERT_TRUE(isopen);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, OpenBufferedFileError) {
|
TEST(file_test, open_buffered_file_error) {
|
||||||
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
|
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
|
||||||
"cannot open file nonexistent");
|
"cannot open file nonexistent");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveCtor) {
|
TEST(file_test, move_ctor) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
EXPECT_NE(-1, fd);
|
EXPECT_NE(-1, fd);
|
||||||
|
@ -334,7 +366,7 @@ TEST(FileTest, MoveCtor) {
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveAssignment) {
|
TEST(file_test, move_assignment) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
EXPECT_NE(-1, fd);
|
EXPECT_NE(-1, fd);
|
||||||
|
@ -344,7 +376,7 @@ TEST(FileTest, MoveAssignment) {
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveAssignmentClosesFile) {
|
TEST(file_test, move_assignment_closes_file) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
file f2 = open_file();
|
file f2 = open_file();
|
||||||
int old_fd = f2.descriptor();
|
int old_fd = f2.descriptor();
|
||||||
|
@ -352,34 +384,34 @@ TEST(FileTest, MoveAssignmentClosesFile) {
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
static file OpenBufferedFile(int& fd) {
|
file open_buffered_file(int& fd) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
fd = f.descriptor();
|
fd = f.descriptor();
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInCtor) {
|
TEST(file_test, move_from_temporary_in_ctor) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
file f(OpenBufferedFile(fd));
|
file f(open_buffered_file(fd));
|
||||||
EXPECT_EQ(fd, f.descriptor());
|
EXPECT_EQ(fd, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInAssignment) {
|
TEST(file_test, move_from_temporary_in_assignment) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
file f;
|
file f;
|
||||||
f = OpenBufferedFile(fd);
|
f = open_buffered_file(fd);
|
||||||
EXPECT_EQ(fd, f.descriptor());
|
EXPECT_EQ(fd, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
TEST(file_test, move_from_temporary_in_assignment_closes_file) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
int old_fd = f.descriptor();
|
int old_fd = f.descriptor();
|
||||||
f = OpenBufferedFile(fd);
|
f = open_buffered_file(fd);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseFileInDtor) {
|
TEST(file_test, close_file_in_dtor) {
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
{
|
{
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
|
@ -388,7 +420,7 @@ TEST(FileTest, CloseFileInDtor) {
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseErrorInDtor) {
|
TEST(file_test, close_error_in_dtor) {
|
||||||
std::unique_ptr<file> f(new file(open_file()));
|
std::unique_ptr<file> f(new file(open_file()));
|
||||||
EXPECT_WRITE(
|
EXPECT_WRITE(
|
||||||
stderr,
|
stderr,
|
||||||
|
@ -400,10 +432,10 @@ TEST(FileTest, CloseErrorInDtor) {
|
||||||
FMT_POSIX(close(f->descriptor()));
|
FMT_POSIX(close(f->descriptor()));
|
||||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||||
},
|
},
|
||||||
format_system_error(EBADF, "cannot close file") + "\n");
|
system_error_message(EBADF, "cannot close file") + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Close) {
|
TEST(file_test, close) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
f.close();
|
f.close();
|
||||||
|
@ -411,19 +443,19 @@ TEST(FileTest, Close) {
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseError) {
|
TEST(file_test, close_error) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
FMT_POSIX(close(f.descriptor()));
|
FMT_POSIX(close(f.descriptor()));
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Read) {
|
TEST(file_test, read) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
EXPECT_READ(f, FILE_CONTENT);
|
EXPECT_READ(f, file_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ReadError) {
|
TEST(file_test, read_error) {
|
||||||
file f("test-file", file::WRONLY);
|
file f("test-file", file::WRONLY);
|
||||||
char buf;
|
char buf;
|
||||||
// We intentionally read from a file opened in the write-only mode to
|
// We intentionally read from a file opened in the write-only mode to
|
||||||
|
@ -431,7 +463,7 @@ TEST(FileTest, ReadError) {
|
||||||
EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file");
|
EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Write) {
|
TEST(file_test, write) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
write(write_end, "test");
|
write(write_end, "test");
|
||||||
|
@ -439,61 +471,61 @@ TEST(FileTest, Write) {
|
||||||
EXPECT_READ(read_end, "test");
|
EXPECT_READ(read_end, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, WriteError) {
|
TEST(file_test, write_error) {
|
||||||
file f("test-file", file::RDONLY);
|
file f("test-file", file::RDONLY);
|
||||||
// We intentionally write to a file opened in the read-only mode to
|
// We intentionally write to a file opened in the read-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup) {
|
TEST(file_test, dup) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
file copy = file::dup(f.descriptor());
|
file copy = file::dup(f.descriptor());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
|
EXPECT_EQ(file_content, read(copy, std::strlen(file_content)));
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifndef __COVERITY__
|
# ifndef __COVERITY__
|
||||||
TEST(FileTest, DupError) {
|
TEST(file_test, dup_error) {
|
||||||
int value = -1;
|
int value = -1;
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
|
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
|
||||||
"cannot duplicate file descriptor -1");
|
"cannot duplicate file descriptor -1");
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
TEST(FileTest, Dup2) {
|
TEST(file_test, dup2) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
file copy = open_file();
|
file copy = open_file();
|
||||||
f.dup2(copy.descriptor());
|
f.dup2(copy.descriptor());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, file_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2Error) {
|
TEST(file_test, dup2_error) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||||
f.dup2(-1), EBADF,
|
f.dup2(-1), EBADF,
|
||||||
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExcept) {
|
TEST(file_test, dup2_noexcept) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
file copy = open_file();
|
file copy = open_file();
|
||||||
error_code ec;
|
std::error_code ec;
|
||||||
f.dup2(copy.descriptor(), ec);
|
f.dup2(copy.descriptor(), ec);
|
||||||
EXPECT_EQ(ec.get(), 0);
|
EXPECT_EQ(ec.value(), 0);
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, file_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExceptError) {
|
TEST(file_test, dup2_noexcept_error) {
|
||||||
file f = open_file();
|
file f = open_file();
|
||||||
error_code ec;
|
std::error_code ec;
|
||||||
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
||||||
EXPECT_EQ(EBADF, ec.get());
|
EXPECT_EQ(EBADF, ec.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Pipe) {
|
TEST(file_test, pipe) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
EXPECT_NE(-1, read_end.descriptor());
|
EXPECT_NE(-1, read_end.descriptor());
|
||||||
|
@ -502,7 +534,7 @@ TEST(FileTest, Pipe) {
|
||||||
EXPECT_READ(read_end, "test");
|
EXPECT_READ(read_end, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Fdopen) {
|
TEST(file_test, fdopen) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int read_fd = read_end.descriptor();
|
int read_fd = read_end.descriptor();
|
||||||
|
@ -510,7 +542,7 @@ TEST(FileTest, Fdopen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifdef FMT_LOCALE
|
# ifdef FMT_LOCALE
|
||||||
TEST(LocaleTest, Strtod) {
|
TEST(locale_test, strtod) {
|
||||||
fmt::locale loc;
|
fmt::locale loc;
|
||||||
const char *start = "4.2", *ptr = start;
|
const char *start = "4.2", *ptr = start;
|
||||||
EXPECT_EQ(4.2, loc.strtod(ptr));
|
EXPECT_EQ(4.2, loc.strtod(ptr));
|
||||||
|
|
219
externals/fmt/test/ostream-test.cc
vendored
219
externals/fmt/test/ostream-test.cc
vendored
|
@ -5,17 +5,17 @@
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#define FMT_STRING_ALIAS 1
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
|
using fmt::runtime;
|
||||||
|
|
||||||
struct test {};
|
struct test {};
|
||||||
|
|
||||||
// Test that there is no issues with specializations when fmt/ostream.h is
|
// Test that there is no issues with specializations when fmt/ostream.h is
|
||||||
// included after fmt/format.h.
|
// included after fmt/format.h.
|
||||||
namespace fmt {
|
namespace fmt {
|
||||||
template <> struct formatter<test> : formatter<int> {
|
template <> struct formatter<test> : formatter<int> {
|
||||||
template <typename FormatContext>
|
auto format(const test&, format_context& ctx) -> decltype(ctx.out()) {
|
||||||
typename FormatContext::iterator format(const test&, FormatContext& ctx) {
|
|
||||||
return formatter<int>::format(42, ctx);
|
return formatter<int>::format(42, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -24,19 +24,17 @@ template <> struct formatter<test> : formatter<int> {
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "fmt/ostream.h"
|
#include "fmt/ostream.h"
|
||||||
#include "gmock.h"
|
#include "fmt/ranges.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::format;
|
std::ostream& operator<<(std::ostream& os, const date& d) {
|
||||||
using fmt::format_error;
|
|
||||||
|
|
||||||
static std::ostream& operator<<(std::ostream& os, const Date& d) {
|
|
||||||
os << d.year() << '-' << d.month() << '-' << d.day();
|
os << d.year() << '-' << d.month() << '-' << d.day();
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::wostream& operator<<(std::wostream& os, const Date& d) {
|
std::wostream& operator<<(std::wostream& os, const date& d) {
|
||||||
os << d.year() << L'-' << d.month() << L'-' << d.day();
|
os << d.year() << L'-' << d.month() << L'-' << d.day();
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
@ -44,99 +42,63 @@ static std::wostream& operator<<(std::wostream& os, const Date& d) {
|
||||||
// Make sure that overloaded comma operators do no harm to is_streamable.
|
// Make sure that overloaded comma operators do no harm to is_streamable.
|
||||||
struct type_with_comma_op {};
|
struct type_with_comma_op {};
|
||||||
template <typename T> void operator,(type_with_comma_op, const T&);
|
template <typename T> void operator,(type_with_comma_op, const T&);
|
||||||
template <typename T> type_with_comma_op operator<<(T&, const Date&);
|
template <typename T> type_with_comma_op operator<<(T&, const date&);
|
||||||
|
|
||||||
enum streamable_enum {};
|
enum streamable_enum {};
|
||||||
static std::ostream& operator<<(std::ostream& os, streamable_enum) {
|
|
||||||
return os << "streamable_enum";
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::wostream& operator<<(std::wostream& os, streamable_enum) {
|
std::ostream& operator<<(std::ostream& os, streamable_enum) {
|
||||||
return os << L"streamable_enum";
|
return os << "streamable_enum";
|
||||||
}
|
}
|
||||||
|
|
||||||
enum unstreamable_enum {};
|
enum unstreamable_enum {};
|
||||||
|
|
||||||
TEST(OStreamTest, Enum) {
|
TEST(ostream_test, enum) {
|
||||||
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
|
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
|
||||||
EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
|
EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
|
||||||
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
|
|
||||||
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct test_arg_formatter
|
TEST(ostream_test, format) {
|
||||||
: fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
|
EXPECT_EQ("a string", fmt::format("{0}", test_string("a string")));
|
||||||
fmt::format_parse_context parse_ctx;
|
EXPECT_EQ("The date is 2012-12-9",
|
||||||
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
|
fmt::format("The date is {0}", date(2012, 12, 9)));
|
||||||
: fmt::detail::arg_formatter<fmt::format_context::iterator, char>(
|
|
||||||
ctx, &parse_ctx, &s),
|
|
||||||
parse_ctx("") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(OStreamTest, CustomArg) {
|
|
||||||
fmt::memory_buffer buffer;
|
|
||||||
fmt::detail::buffer<char>& base = buffer;
|
|
||||||
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
|
|
||||||
fmt::format_specs spec;
|
|
||||||
test_arg_formatter af(ctx, spec);
|
|
||||||
fmt::visit_format_arg(
|
|
||||||
af, fmt::detail::make_arg<fmt::format_context>(streamable_enum()));
|
|
||||||
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, Format) {
|
TEST(ostream_test, format_specs) {
|
||||||
EXPECT_EQ("a string", format("{0}", TestString("a string")));
|
using fmt::format_error;
|
||||||
std::string s = format("The date is {0}", Date(2012, 12, 9));
|
EXPECT_EQ("def ", fmt::format("{0:<5}", test_string("def")));
|
||||||
EXPECT_EQ("The date is 2012-12-9", s);
|
EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def")));
|
||||||
Date date(2012, 12, 9);
|
EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def")));
|
||||||
EXPECT_EQ(L"The date is 2012-12-9",
|
EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def")));
|
||||||
format(L"The date is {0}", Date(2012, 12, 9)));
|
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), test_string()), format_error,
|
||||||
|
"format specifier requires numeric argument");
|
||||||
|
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), test_string()), format_error,
|
||||||
|
"format specifier requires numeric argument");
|
||||||
|
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), test_string()), format_error,
|
||||||
|
"format specifier requires numeric argument");
|
||||||
|
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), test_string()), format_error,
|
||||||
|
"format specifier requires numeric argument");
|
||||||
|
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), test_string()), format_error,
|
||||||
|
"format specifier requires numeric argument");
|
||||||
|
EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test")));
|
||||||
|
EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13));
|
||||||
|
EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test")));
|
||||||
|
EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, FormatSpecs) {
|
struct empty_test {};
|
||||||
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
|
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
|
||||||
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
|
|
||||||
#if FMT_DEPRECATED_NUMERIC_ALIGN
|
TEST(ostream_test, empty_custom_output) {
|
||||||
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
|
EXPECT_EQ("", fmt::format("{}", empty_test()));
|
||||||
"format specifier requires numeric argument");
|
|
||||||
#endif
|
|
||||||
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
|
|
||||||
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
|
|
||||||
EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
|
|
||||||
"format specifier requires numeric argument");
|
|
||||||
EXPECT_THROW_MSG(format("{0:-}", TestString()), format_error,
|
|
||||||
"format specifier requires numeric argument");
|
|
||||||
EXPECT_THROW_MSG(format("{0: }", TestString()), format_error,
|
|
||||||
"format specifier requires numeric argument");
|
|
||||||
EXPECT_THROW_MSG(format("{0:#}", TestString()), format_error,
|
|
||||||
"format specifier requires numeric argument");
|
|
||||||
EXPECT_THROW_MSG(format("{0:05}", TestString()), format_error,
|
|
||||||
"format specifier requires numeric argument");
|
|
||||||
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
|
|
||||||
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
|
|
||||||
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
|
|
||||||
EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmptyTest {};
|
TEST(ostream_test, print) {
|
||||||
static std::ostream& operator<<(std::ostream& os, EmptyTest) {
|
|
||||||
return os << "";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OStreamTest, EmptyCustomOutput) {
|
|
||||||
EXPECT_EQ("", fmt::format("{}", EmptyTest()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OStreamTest, Print) {
|
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
fmt::print(os, "Don't {}!", "panic");
|
fmt::print(os, "Don't {}!", "panic");
|
||||||
EXPECT_EQ("Don't panic!", os.str());
|
EXPECT_EQ("Don't panic!", os.str());
|
||||||
std::wostringstream wos;
|
|
||||||
fmt::print(wos, L"Don't {}!", L"panic");
|
|
||||||
EXPECT_EQ(L"Don't panic!", wos.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, WriteToOStream) {
|
TEST(ostream_test, write_to_ostream) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
const char* foo = "foo";
|
const char* foo = "foo";
|
||||||
|
@ -145,13 +107,14 @@ TEST(OStreamTest, WriteToOStream) {
|
||||||
EXPECT_EQ("foo", os.str());
|
EXPECT_EQ("foo", os.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, WriteToOStreamMaxSize) {
|
TEST(ostream_test, write_to_ostream_max_size) {
|
||||||
size_t max_size = fmt::detail::max_value<size_t>();
|
auto max_size = fmt::detail::max_value<size_t>();
|
||||||
std::streamsize max_streamsize = fmt::detail::max_value<std::streamsize>();
|
auto max_streamsize = fmt::detail::max_value<std::streamsize>();
|
||||||
if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
|
if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
|
||||||
|
|
||||||
struct test_buffer : fmt::detail::buffer<char> {
|
struct test_buffer final : fmt::detail::buffer<char> {
|
||||||
explicit test_buffer(size_t size) { resize(size); }
|
explicit test_buffer(size_t size)
|
||||||
|
: fmt::detail::buffer<char>(nullptr, size, size) {}
|
||||||
void grow(size_t) {}
|
void grow(size_t) {}
|
||||||
} buffer(max_size);
|
} buffer(max_size);
|
||||||
|
|
||||||
|
@ -164,12 +127,13 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
||||||
} streambuf;
|
} streambuf;
|
||||||
|
|
||||||
struct test_ostream : std::ostream {
|
struct test_ostream : std::ostream {
|
||||||
explicit test_ostream(mock_streambuf& buffer) : std::ostream(&buffer) {}
|
explicit test_ostream(mock_streambuf& output_buffer)
|
||||||
|
: std::ostream(&output_buffer) {}
|
||||||
} os(streambuf);
|
} os(streambuf);
|
||||||
|
|
||||||
testing::InSequence sequence;
|
testing::InSequence sequence;
|
||||||
const char* data = nullptr;
|
const char* data = nullptr;
|
||||||
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
|
using ustreamsize = std::make_unsigned<std::streamsize>::type;
|
||||||
ustreamsize size = max_size;
|
ustreamsize size = max_size;
|
||||||
do {
|
do {
|
||||||
auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize));
|
auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize));
|
||||||
|
@ -181,68 +145,62 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
||||||
fmt::detail::write_buffer(os, buffer);
|
fmt::detail::write_buffer(os, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, Join) {
|
TEST(ostream_test, join) {
|
||||||
int v[3] = {1, 2, 3};
|
int v[3] = {1, 2, 3};
|
||||||
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
|
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ostream_test, join_fallback_formatter) {
|
||||||
|
auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")};
|
||||||
|
EXPECT_EQ("foo, bar", fmt::format("{}", fmt::join(strs, ", ")));
|
||||||
|
}
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
#if FMT_USE_CONSTEXPR
|
||||||
TEST(OStreamTest, ConstexprString) {
|
TEST(ostream_test, constexpr_string) {
|
||||||
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
||||||
EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
|
EXPECT_EQ("a string", format(FMT_STRING("{0}"), test_string("a string")));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace fmt_test {
|
namespace fmt_test {
|
||||||
struct ABC {};
|
struct abc {};
|
||||||
|
|
||||||
template <typename Output> Output& operator<<(Output& out, ABC) {
|
template <typename Output> Output& operator<<(Output& out, abc) {
|
||||||
out << "ABC";
|
return out << "abc";
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
} // namespace fmt_test
|
} // namespace fmt_test
|
||||||
|
|
||||||
template <typename T> struct TestTemplate {};
|
template <typename T> struct test_template {};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::ostream& operator<<(std::ostream& os, TestTemplate<T>) {
|
std::ostream& operator<<(std::ostream& os, test_template<T>) {
|
||||||
return os << 1;
|
return os << 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace fmt {
|
namespace fmt {
|
||||||
template <typename T> struct formatter<TestTemplate<T>> : formatter<int> {
|
template <typename T> struct formatter<test_template<T>> : formatter<int> {
|
||||||
template <typename FormatContext>
|
auto format(test_template<T>, format_context& ctx) -> decltype(ctx.out()) {
|
||||||
typename FormatContext::iterator format(TestTemplate<T>, FormatContext& ctx) {
|
|
||||||
return formatter<int>::format(2, ctx);
|
return formatter<int>::format(2, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace fmt
|
} // namespace fmt
|
||||||
|
|
||||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407
|
TEST(ostream_test, template) {
|
||||||
TEST(OStreamTest, Template) {
|
EXPECT_EQ("2", fmt::format("{}", test_template<int>()));
|
||||||
EXPECT_EQ("2", fmt::format("{}", TestTemplate<int>()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, FormatToN) {
|
TEST(ostream_test, format_to_n) {
|
||||||
char buffer[4];
|
char buffer[4];
|
||||||
buffer[3] = 'x';
|
buffer[3] = 'x';
|
||||||
auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::ABC());
|
auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::abc());
|
||||||
EXPECT_EQ(3u, result.size);
|
EXPECT_EQ(3u, result.size);
|
||||||
EXPECT_EQ(buffer + 3, result.out);
|
EXPECT_EQ(buffer + 3, result.out);
|
||||||
EXPECT_EQ("ABCx", fmt::string_view(buffer, 4));
|
EXPECT_EQ("abcx", fmt::string_view(buffer, 4));
|
||||||
result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::ABC());
|
result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::abc());
|
||||||
EXPECT_EQ(5u, result.size);
|
EXPECT_EQ(5u, result.size);
|
||||||
EXPECT_EQ(buffer + 3, result.out);
|
EXPECT_EQ(buffer + 3, result.out);
|
||||||
EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
|
EXPECT_EQ("xabx", fmt::string_view(buffer, 4));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_USE_USER_DEFINED_LITERALS
|
|
||||||
TEST(FormatTest, UDL) {
|
|
||||||
using namespace fmt::literals;
|
|
||||||
EXPECT_EQ("{}"_format("test"), "test");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T> struct convertible {
|
template <typename T> struct convertible {
|
||||||
T value;
|
T value;
|
||||||
|
@ -250,9 +208,8 @@ template <typename T> struct convertible {
|
||||||
operator T() const { return value; }
|
operator T() const { return value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(OStreamTest, DisableBuiltinOStreamOperators) {
|
TEST(ostream_test, disable_builtin_ostream_operators) {
|
||||||
EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42)));
|
EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42)));
|
||||||
EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
|
|
||||||
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
|
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +227,7 @@ std::ostream& operator<<(std::ostream& os,
|
||||||
return os << "bar";
|
return os << "bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, FormatExplicitlyConvertibleToStringLike) {
|
TEST(ostream_test, format_explicitly_convertible_to_string_like) {
|
||||||
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,12 +243,23 @@ std::ostream& operator<<(std::ostream& os,
|
||||||
return os << "bar";
|
return os << "bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, FormatExplicitlyConvertibleToStdStringView) {
|
TEST(ostream_test, format_explicitly_convertible_to_std_string_view) {
|
||||||
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_STRING_VIEW
|
#endif // FMT_USE_STRING_VIEW
|
||||||
|
|
||||||
|
struct streamable_and_convertible_to_bool {
|
||||||
|
operator bool() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) {
|
||||||
|
return os << "foo";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ostream_test, format_convertible_to_bool) {
|
||||||
|
EXPECT_EQ("foo", fmt::format("{}", streamable_and_convertible_to_bool()));
|
||||||
|
}
|
||||||
|
|
||||||
struct copyfmt_test {};
|
struct copyfmt_test {};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
||||||
|
@ -300,10 +268,15 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
||||||
return os << "foo";
|
return os << "foo";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, CopyFmt) {
|
TEST(ostream_test, copyfmt) {
|
||||||
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
|
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, CompileTimeString) {
|
TEST(ostream_test, to_string) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
|
EXPECT_EQ("abc", fmt::to_string(fmt_test::abc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ostream_test, range) {
|
||||||
|
auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")};
|
||||||
|
EXPECT_EQ("[foo, bar]", fmt::format("{}", strs));
|
||||||
}
|
}
|
||||||
|
|
116
externals/fmt/test/posix-mock-test.cc
vendored
116
externals/fmt/test/posix-mock-test.cc
vendored
|
@ -23,20 +23,23 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# undef max
|
# undef max
|
||||||
# undef ERROR
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::buffered_file;
|
using fmt::buffered_file;
|
||||||
using fmt::error_code;
|
|
||||||
|
|
||||||
using testing::_;
|
using testing::_;
|
||||||
using testing::Return;
|
using testing::Return;
|
||||||
using testing::StrEq;
|
using testing::StrEq;
|
||||||
|
|
||||||
|
template <typename Mock> struct scoped_mock : testing::StrictMock<Mock> {
|
||||||
|
scoped_mock() { Mock::instance = this; }
|
||||||
|
~scoped_mock() { Mock::instance = nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int open_count;
|
int open_count;
|
||||||
int close_count;
|
int close_count;
|
||||||
|
@ -53,7 +56,7 @@ size_t read_nbyte;
|
||||||
size_t write_nbyte;
|
size_t write_nbyte;
|
||||||
bool sysconf_error;
|
bool sysconf_error;
|
||||||
|
|
||||||
enum { NONE, MAX_SIZE, ERROR } fstat_sim;
|
enum { none, max_size, error } fstat_sim;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define EMULATE_EINTR(func, error_result) \
|
#define EMULATE_EINTR(func, error_result) \
|
||||||
|
@ -91,7 +94,7 @@ static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
|
||||||
|
|
||||||
int test::fstat(int fd, struct stat* buf) {
|
int test::fstat(int fd, struct stat* buf) {
|
||||||
int result = ::fstat(fd, buf);
|
int result = ::fstat(fd, buf);
|
||||||
if (fstat_sim == MAX_SIZE) buf->st_size = max_file_size();
|
if (fstat_sim == max_size) buf->st_size = max_file_size();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,11 +103,11 @@ int test::fstat(int fd, struct stat* buf) {
|
||||||
static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
|
static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); }
|
||||||
|
|
||||||
DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
|
DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
|
||||||
if (fstat_sim == ERROR) {
|
if (fstat_sim == error) {
|
||||||
SetLastError(ERROR_ACCESS_DENIED);
|
SetLastError(ERROR_ACCESS_DENIED);
|
||||||
return INVALID_FILE_SIZE;
|
return INVALID_FILE_SIZE;
|
||||||
}
|
}
|
||||||
if (fstat_sim == MAX_SIZE) {
|
if (fstat_sim == max_size) {
|
||||||
DWORD max = std::numeric_limits<DWORD>::max();
|
DWORD max = std::numeric_limits<DWORD>::max();
|
||||||
*lpFileSizeHigh = max >> 1;
|
*lpFileSizeHigh = max >> 1;
|
||||||
return max;
|
return max;
|
||||||
|
@ -194,15 +197,15 @@ int(test::fileno)(FILE* stream) {
|
||||||
# define EXPECT_EQ_POSIX(expected, actual)
|
# define EXPECT_EQ_POSIX(expected, actual)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
#if FMT_USE_FCNTL
|
||||||
|
void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||||
fmt::buffered_file f(filename, "w");
|
fmt::buffered_file f(filename, "w");
|
||||||
f.print("{}", content);
|
f.print("{}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
|
||||||
using fmt::file;
|
using fmt::file;
|
||||||
|
|
||||||
TEST(UtilTest, GetPageSize) {
|
TEST(os_test, getpagesize) {
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
SYSTEM_INFO si = {};
|
SYSTEM_INFO si = {};
|
||||||
GetSystemInfo(&si);
|
GetSystemInfo(&si);
|
||||||
|
@ -216,7 +219,7 @@ TEST(UtilTest, GetPageSize) {
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, OpenRetry) {
|
TEST(file_test, open_retry) {
|
||||||
write_file("temp", "there must be something here");
|
write_file("temp", "there must be something here");
|
||||||
std::unique_ptr<file> f{nullptr};
|
std::unique_ptr<file> f{nullptr};
|
||||||
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
|
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
|
||||||
|
@ -227,7 +230,7 @@ TEST(FileTest, OpenRetry) {
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseNoRetryInDtor) {
|
TEST(file_test, close_no_retry_in_dtor) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
std::unique_ptr<file> f(new file(std::move(read_end)));
|
std::unique_ptr<file> f(new file(std::move(read_end)));
|
||||||
|
@ -240,11 +243,11 @@ TEST(FileTest, CloseNoRetryInDtor) {
|
||||||
saved_close_count = close_count;
|
saved_close_count = close_count;
|
||||||
close_count = 0;
|
close_count = 0;
|
||||||
},
|
},
|
||||||
format_system_error(EINTR, "cannot close file") + "\n");
|
system_error_message(EINTR, "cannot close file") + "\n");
|
||||||
EXPECT_EQ(2, saved_close_count);
|
EXPECT_EQ(2, saved_close_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseNoRetry) {
|
TEST(file_test, close_no_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
close_count = 1;
|
close_count = 1;
|
||||||
|
@ -253,35 +256,39 @@ TEST(FileTest, CloseNoRetry) {
|
||||||
close_count = 0;
|
close_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Size) {
|
TEST(file_test, size) {
|
||||||
std::string content = "top secret, destroy before reading";
|
std::string content = "top secret, destroy before reading";
|
||||||
write_file("temp", content);
|
write_file("temp", content);
|
||||||
file f("temp", file::RDONLY);
|
file f("temp", file::RDONLY);
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
fmt::memory_buffer message;
|
auto error_code = std::error_code();
|
||||||
fmt::detail::format_windows_error(message, ERROR_ACCESS_DENIED,
|
fstat_sim = error;
|
||||||
"cannot get file size");
|
try {
|
||||||
fstat_sim = ERROR;
|
f.size();
|
||||||
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
|
} catch (const std::system_error& e) {
|
||||||
fstat_sim = NONE;
|
error_code = e.code();
|
||||||
|
}
|
||||||
|
fstat_sim = none;
|
||||||
|
EXPECT_EQ(error_code,
|
||||||
|
std::error_code(ERROR_ACCESS_DENIED, fmt::system_category()));
|
||||||
# else
|
# else
|
||||||
f.close();
|
f.close();
|
||||||
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
|
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MaxSize) {
|
TEST(file_test, max_size) {
|
||||||
write_file("temp", "");
|
write_file("temp", "");
|
||||||
file f("temp", file::RDONLY);
|
file f("temp", file::RDONLY);
|
||||||
fstat_sim = MAX_SIZE;
|
fstat_sim = max_size;
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(max_file_size(), f.size());
|
EXPECT_EQ(max_file_size(), f.size());
|
||||||
fstat_sim = NONE;
|
fstat_sim = none;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ReadRetry) {
|
TEST(file_test, read_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
enum { SIZE = 4 };
|
enum { SIZE = 4 };
|
||||||
|
@ -294,7 +301,7 @@ TEST(FileTest, ReadRetry) {
|
||||||
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
|
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, WriteRetry) {
|
TEST(file_test, write_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
enum { SIZE = 4 };
|
enum { SIZE = 4 };
|
||||||
|
@ -312,7 +319,7 @@ TEST(FileTest, WriteRetry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
TEST(FileTest, ConvertReadCount) {
|
TEST(file_test, convert_read_count) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
char c;
|
char c;
|
||||||
|
@ -320,12 +327,12 @@ TEST(FileTest, ConvertReadCount) {
|
||||||
if (sizeof(unsigned) != sizeof(size_t)) ++size;
|
if (sizeof(unsigned) != sizeof(size_t)) ++size;
|
||||||
read_count = 1;
|
read_count = 1;
|
||||||
read_nbyte = 0;
|
read_nbyte = 0;
|
||||||
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
|
EXPECT_THROW(read_end.read(&c, size), std::system_error);
|
||||||
read_count = 0;
|
read_count = 0;
|
||||||
EXPECT_EQ(UINT_MAX, read_nbyte);
|
EXPECT_EQ(UINT_MAX, read_nbyte);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ConvertWriteCount) {
|
TEST(file_test, convert_write_count) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
char c;
|
char c;
|
||||||
|
@ -333,13 +340,13 @@ TEST(FileTest, ConvertWriteCount) {
|
||||||
if (sizeof(unsigned) != sizeof(size_t)) ++size;
|
if (sizeof(unsigned) != sizeof(size_t)) ++size;
|
||||||
write_count = 1;
|
write_count = 1;
|
||||||
write_nbyte = 0;
|
write_nbyte = 0;
|
||||||
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
|
EXPECT_THROW(write_end.write(&c, size), std::system_error);
|
||||||
write_count = 0;
|
write_count = 0;
|
||||||
EXPECT_EQ(UINT_MAX, write_nbyte);
|
EXPECT_EQ(UINT_MAX, write_nbyte);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
TEST(FileTest, DupNoRetry) {
|
TEST(file_test, dup_no_retry) {
|
||||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||||
dup_count = 1;
|
dup_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(
|
EXPECT_SYSTEM_ERROR(
|
||||||
|
@ -348,7 +355,7 @@ TEST(FileTest, DupNoRetry) {
|
||||||
dup_count = 0;
|
dup_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2Retry) {
|
TEST(file_test, dup2_retry) {
|
||||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||||
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
||||||
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
|
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
|
||||||
|
@ -356,21 +363,21 @@ TEST(FileTest, Dup2Retry) {
|
||||||
f1.descriptor(), f2.descriptor()));
|
f1.descriptor(), f2.descriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExceptRetry) {
|
TEST(file_test, dup2_no_except_retry) {
|
||||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||||
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
||||||
error_code ec;
|
std::error_code ec;
|
||||||
dup2_count = 1;
|
dup2_count = 1;
|
||||||
f1.dup2(f2.descriptor(), ec);
|
f1.dup2(f2.descriptor(), ec);
|
||||||
# ifndef _WIN32
|
# ifndef _WIN32
|
||||||
EXPECT_EQ(4, dup2_count);
|
EXPECT_EQ(4, dup2_count);
|
||||||
# else
|
# else
|
||||||
EXPECT_EQ(EINTR, ec.get());
|
EXPECT_EQ(EINTR, ec.value());
|
||||||
# endif
|
# endif
|
||||||
dup2_count = 0;
|
dup2_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, PipeNoRetry) {
|
TEST(file_test, pipe_no_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
pipe_count = 1;
|
pipe_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
|
EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
|
||||||
|
@ -378,7 +385,7 @@ TEST(FileTest, PipeNoRetry) {
|
||||||
pipe_count = 0;
|
pipe_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, FdopenNoRetry) {
|
TEST(file_test, fdopen_no_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
fdopen_count = 1;
|
fdopen_count = 1;
|
||||||
|
@ -387,7 +394,7 @@ TEST(FileTest, FdopenNoRetry) {
|
||||||
fdopen_count = 0;
|
fdopen_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, OpenRetry) {
|
TEST(buffered_file_test, open_retry) {
|
||||||
write_file("temp", "there must be something here");
|
write_file("temp", "there must be something here");
|
||||||
std::unique_ptr<buffered_file> f{nullptr};
|
std::unique_ptr<buffered_file> f{nullptr};
|
||||||
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
|
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
|
||||||
|
@ -399,7 +406,7 @@ TEST(BufferedFileTest, OpenRetry) {
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
TEST(buffered_file_test, close_no_retry_in_dtor) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
||||||
|
@ -412,11 +419,11 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
||||||
saved_fclose_count = fclose_count;
|
saved_fclose_count = fclose_count;
|
||||||
fclose_count = 0;
|
fclose_count = 0;
|
||||||
},
|
},
|
||||||
format_system_error(EINTR, "cannot close file") + "\n");
|
system_error_message(EINTR, "cannot close file") + "\n");
|
||||||
EXPECT_EQ(2, saved_fclose_count);
|
EXPECT_EQ(2, saved_fclose_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseNoRetry) {
|
TEST(buffered_file_test, close_no_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
buffered_file f = read_end.fdopen("r");
|
buffered_file f = read_end.fdopen("r");
|
||||||
|
@ -426,7 +433,7 @@ TEST(BufferedFileTest, CloseNoRetry) {
|
||||||
fclose_count = 0;
|
fclose_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, FilenoNoRetry) {
|
TEST(buffered_file_test, fileno_no_retry) {
|
||||||
file read_end, write_end;
|
file read_end, write_end;
|
||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
buffered_file f = read_end.fdopen("r");
|
buffered_file f = read_end.fdopen("r");
|
||||||
|
@ -441,9 +448,9 @@ struct test_mock {
|
||||||
static test_mock* instance;
|
static test_mock* instance;
|
||||||
} * test_mock::instance;
|
} * test_mock::instance;
|
||||||
|
|
||||||
TEST(ScopedMock, Scope) {
|
TEST(scoped_mock, scope) {
|
||||||
{
|
{
|
||||||
ScopedMock<test_mock> mock;
|
scoped_mock<test_mock> mock;
|
||||||
EXPECT_EQ(&mock, test_mock::instance);
|
EXPECT_EQ(&mock, test_mock::instance);
|
||||||
test_mock& copy = mock;
|
test_mock& copy = mock;
|
||||||
static_cast<void>(copy);
|
static_cast<void>(copy);
|
||||||
|
@ -453,7 +460,7 @@ TEST(ScopedMock, Scope) {
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
|
||||||
typedef fmt::locale::type locale_type;
|
using locale_type = fmt::locale::type;
|
||||||
|
|
||||||
struct locale_mock {
|
struct locale_mock {
|
||||||
static locale_mock* instance;
|
static locale_mock* instance;
|
||||||
|
@ -490,7 +497,8 @@ double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
||||||
# pragma warning(pop)
|
# pragma warning(pop)
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
|
# if defined(__THROW) && \
|
||||||
|
((FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408) || defined(__e2k__))
|
||||||
# define FMT_LOCALE_THROW __THROW
|
# define FMT_LOCALE_THROW __THROW
|
||||||
# else
|
# else
|
||||||
# define FMT_LOCALE_THROW
|
# define FMT_LOCALE_THROW
|
||||||
|
@ -520,20 +528,20 @@ locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
|
||||||
return locale_mock::instance->newlocale(category_mask, locale, base);
|
return locale_mock::instance->newlocale(category_mask, locale, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LocaleTest, LocaleMock) {
|
TEST(locale_test, locale_mock) {
|
||||||
ScopedMock<locale_mock> mock;
|
scoped_mock<locale_mock> mock;
|
||||||
locale_type locale = reinterpret_cast<locale_type>(11);
|
auto locale = reinterpret_cast<locale_type>(11);
|
||||||
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
|
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
|
||||||
FMT_SYSTEM(newlocale(222, "foo", locale));
|
FMT_SYSTEM(newlocale(222, "foo", locale));
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
TEST(LocaleTest, Locale) {
|
TEST(locale_test, locale) {
|
||||||
# ifndef LC_NUMERIC_MASK
|
# ifndef LC_NUMERIC_MASK
|
||||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
# endif
|
# endif
|
||||||
ScopedMock<locale_mock> mock;
|
scoped_mock<locale_mock> mock;
|
||||||
locale_type impl = reinterpret_cast<locale_type>(42);
|
auto impl = reinterpret_cast<locale_type>(42);
|
||||||
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
|
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
|
||||||
.WillOnce(Return(impl));
|
.WillOnce(Return(impl));
|
||||||
EXPECT_CALL(mock, freelocale(impl));
|
EXPECT_CALL(mock, freelocale(impl));
|
||||||
|
@ -541,8 +549,8 @@ TEST(LocaleTest, Locale) {
|
||||||
EXPECT_EQ(impl, loc.get());
|
EXPECT_EQ(impl, loc.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LocaleTest, Strtod) {
|
TEST(locale_test, strtod) {
|
||||||
ScopedMock<locale_mock> mock;
|
scoped_mock<locale_mock> mock;
|
||||||
EXPECT_CALL(mock, newlocale(_, _, _))
|
EXPECT_CALL(mock, newlocale(_, _, _))
|
||||||
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
|
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
|
||||||
EXPECT_CALL(mock, freelocale(_));
|
EXPECT_CALL(mock, freelocale(_));
|
||||||
|
|
226
externals/fmt/test/printf-test.cc
vendored
226
externals/fmt/test/printf-test.cc
vendored
|
@ -11,7 +11,8 @@
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "fmt/core.h"
|
#include "fmt/ostream.h"
|
||||||
|
#include "fmt/xchar.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ using fmt::format;
|
||||||
using fmt::format_error;
|
using fmt::format_error;
|
||||||
using fmt::detail::max_value;
|
using fmt::detail::max_value;
|
||||||
|
|
||||||
const unsigned BIG_NUM = INT_MAX + 1u;
|
const unsigned big_num = INT_MAX + 1u;
|
||||||
|
|
||||||
// Makes format string argument positional.
|
// Makes format string argument positional.
|
||||||
static std::string make_positional(fmt::string_view format) {
|
static std::string make_positional(fmt::string_view format) {
|
||||||
|
@ -28,7 +29,7 @@ static std::string make_positional(fmt::string_view format) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::wstring make_positional(fmt::wstring_view format) {
|
static std::wstring make_positional(fmt::basic_string_view<wchar_t> format) {
|
||||||
std::wstring s(format.data(), format.size());
|
std::wstring s(format.data(), format.size());
|
||||||
s.replace(s.find(L'%'), 1, L"%1$");
|
s.replace(s.find(L'%'), 1, L"%1$");
|
||||||
return s;
|
return s;
|
||||||
|
@ -41,7 +42,8 @@ std::string test_sprintf(fmt::string_view format, const Args&... args) {
|
||||||
return fmt::sprintf(format, args...);
|
return fmt::sprintf(format, args...);
|
||||||
}
|
}
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) {
|
std::wstring test_sprintf(fmt::basic_string_view<wchar_t> format,
|
||||||
|
const Args&... args) {
|
||||||
return fmt::sprintf(format, args...);
|
return fmt::sprintf(format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,12 +52,12 @@ std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) {
|
||||||
<< "format: " << format; \
|
<< "format: " << format; \
|
||||||
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
|
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
|
||||||
|
|
||||||
TEST(PrintfTest, NoArgs) {
|
TEST(printf_test, no_args) {
|
||||||
EXPECT_EQ("test", test_sprintf("test"));
|
EXPECT_EQ("test", test_sprintf("test"));
|
||||||
EXPECT_EQ(L"test", fmt::sprintf(L"test"));
|
EXPECT_EQ(L"test", fmt::sprintf(L"test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Escape) {
|
TEST(printf_test, escape) {
|
||||||
EXPECT_EQ("%", test_sprintf("%%"));
|
EXPECT_EQ("%", test_sprintf("%%"));
|
||||||
EXPECT_EQ("before %", test_sprintf("before %%"));
|
EXPECT_EQ("before %", test_sprintf("before %%"));
|
||||||
EXPECT_EQ("% after", test_sprintf("%% after"));
|
EXPECT_EQ("% after", test_sprintf("%% after"));
|
||||||
|
@ -68,7 +70,7 @@ TEST(PrintfTest, Escape) {
|
||||||
EXPECT_EQ(L"%s", fmt::sprintf(L"%%s"));
|
EXPECT_EQ(L"%s", fmt::sprintf(L"%%s"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PositionalArgs) {
|
TEST(printf_test, positional_args) {
|
||||||
EXPECT_EQ("42", test_sprintf("%1$d", 42));
|
EXPECT_EQ("42", test_sprintf("%1$d", 42));
|
||||||
EXPECT_EQ("before 42", test_sprintf("before %1$d", 42));
|
EXPECT_EQ("before 42", test_sprintf("before %1$d", 42));
|
||||||
EXPECT_EQ("42 after", test_sprintf("%1$d after", 42));
|
EXPECT_EQ("42 after", test_sprintf("%1$d after", 42));
|
||||||
|
@ -78,40 +80,40 @@ TEST(PrintfTest, PositionalArgs) {
|
||||||
EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
|
EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, AutomaticArgIndexing) {
|
TEST(printf_test, automatic_arg_indexing) {
|
||||||
EXPECT_EQ("abc", test_sprintf("%c%c%c", 'a', 'b', 'c'));
|
EXPECT_EQ("abc", test_sprintf("%c%c%c", 'a', 'b', 'c'));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
|
TEST(printf_test, number_is_too_big_in_arg_index) {
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%{}$", big_num)), format_error,
|
||||||
"number is too big");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", big_num)), format_error,
|
||||||
"number is too big");
|
"argument not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, SwitchArgIndexing) {
|
TEST(printf_test, switch_arg_indexing) {
|
||||||
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
|
||||||
"cannot switch from manual to automatic argument indexing");
|
"cannot switch from manual to automatic argument indexing");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", big_num), 1, 2),
|
||||||
format_error, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
|
||||||
"cannot switch from manual to automatic argument indexing");
|
"cannot switch from manual to automatic argument indexing");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error,
|
||||||
"cannot switch from automatic to manual argument indexing");
|
"cannot switch from automatic to manual argument indexing");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", big_num), 1, 2), format_error,
|
||||||
"number is too big");
|
"cannot switch from automatic to manual argument indexing");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error,
|
||||||
"cannot switch from automatic to manual argument indexing");
|
"cannot switch from automatic to manual argument indexing");
|
||||||
|
|
||||||
// Indexing errors override width errors.
|
// Indexing errors override width errors.
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", big_num), 1, 2),
|
||||||
format_error, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", big_num), 1, 2),
|
||||||
format_error, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, InvalidArgIndex) {
|
TEST(printf_test, invalid_arg_index) {
|
||||||
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
|
||||||
"argument not found");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
|
||||||
|
@ -120,16 +122,16 @@ TEST(PrintfTest, InvalidArgIndex) {
|
||||||
"argument not found");
|
"argument not found");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, "argument not found");
|
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, "argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", big_num), 42), format_error,
|
||||||
"number is too big");
|
"argument not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DefaultAlignRight) {
|
TEST(printf_test, default_align_right) {
|
||||||
EXPECT_PRINTF(" 42", "%5d", 42);
|
EXPECT_PRINTF(" 42", "%5d", 42);
|
||||||
EXPECT_PRINTF(" abc", "%5s", "abc");
|
EXPECT_PRINTF(" abc", "%5s", "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, ZeroFlag) {
|
TEST(printf_test, zero_flag) {
|
||||||
EXPECT_PRINTF("00042", "%05d", 42);
|
EXPECT_PRINTF("00042", "%05d", 42);
|
||||||
EXPECT_PRINTF("-0042", "%05d", -42);
|
EXPECT_PRINTF("-0042", "%05d", -42);
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ TEST(PrintfTest, ZeroFlag) {
|
||||||
EXPECT_PRINTF(" x", "%05c", 'x');
|
EXPECT_PRINTF(" x", "%05c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PlusFlag) {
|
TEST(printf_test, plus_flag) {
|
||||||
EXPECT_PRINTF("+42", "%+d", 42);
|
EXPECT_PRINTF("+42", "%+d", 42);
|
||||||
EXPECT_PRINTF("-42", "%+d", -42);
|
EXPECT_PRINTF("-42", "%+d", -42);
|
||||||
EXPECT_PRINTF("+0042", "%+05d", 42);
|
EXPECT_PRINTF("+0042", "%+05d", 42);
|
||||||
|
@ -168,7 +170,7 @@ TEST(PrintfTest, PlusFlag) {
|
||||||
EXPECT_PRINTF("x", "% +c", 'x');
|
EXPECT_PRINTF("x", "% +c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, MinusFlag) {
|
TEST(printf_test, minus_flag) {
|
||||||
EXPECT_PRINTF("abc ", "%-5s", "abc");
|
EXPECT_PRINTF("abc ", "%-5s", "abc");
|
||||||
EXPECT_PRINTF("abc ", "%0--5s", "abc");
|
EXPECT_PRINTF("abc ", "%0--5s", "abc");
|
||||||
|
|
||||||
|
@ -188,7 +190,7 @@ TEST(PrintfTest, MinusFlag) {
|
||||||
EXPECT_PRINTF(" 42", "%- d", 42);
|
EXPECT_PRINTF(" 42", "%- d", 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, SpaceFlag) {
|
TEST(printf_test, space_flag) {
|
||||||
EXPECT_PRINTF(" 42", "% d", 42);
|
EXPECT_PRINTF(" 42", "% d", 42);
|
||||||
EXPECT_PRINTF("-42", "% d", -42);
|
EXPECT_PRINTF("-42", "% d", -42);
|
||||||
EXPECT_PRINTF(" 0042", "% 05d", 42);
|
EXPECT_PRINTF(" 0042", "% 05d", 42);
|
||||||
|
@ -198,7 +200,7 @@ TEST(PrintfTest, SpaceFlag) {
|
||||||
EXPECT_PRINTF("x", "% c", 'x');
|
EXPECT_PRINTF("x", "% c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, HashFlag) {
|
TEST(printf_test, hash_flag) {
|
||||||
EXPECT_PRINTF("042", "%#o", 042);
|
EXPECT_PRINTF("042", "%#o", 042);
|
||||||
EXPECT_PRINTF(fmt::format("0{:o}", static_cast<unsigned>(-042)), "%#o", -042);
|
EXPECT_PRINTF(fmt::format("0{:o}", static_cast<unsigned>(-042)), "%#o", -042);
|
||||||
EXPECT_PRINTF("0", "%#o", 0);
|
EXPECT_PRINTF("0", "%#o", 0);
|
||||||
|
@ -215,7 +217,7 @@ TEST(PrintfTest, HashFlag) {
|
||||||
EXPECT_PRINTF("-42.000000", "%#f", -42.0);
|
EXPECT_PRINTF("-42.000000", "%#f", -42.0);
|
||||||
EXPECT_PRINTF("-42.000000", "%#F", -42.0);
|
EXPECT_PRINTF("-42.000000", "%#F", -42.0);
|
||||||
|
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[256];
|
||||||
safe_sprintf(buffer, "%#e", -42.0);
|
safe_sprintf(buffer, "%#e", -42.0);
|
||||||
EXPECT_PRINTF(buffer, "%#e", -42.0);
|
EXPECT_PRINTF(buffer, "%#e", -42.0);
|
||||||
safe_sprintf(buffer, "%#E", -42.0);
|
safe_sprintf(buffer, "%#E", -42.0);
|
||||||
|
@ -233,30 +235,30 @@ TEST(PrintfTest, HashFlag) {
|
||||||
EXPECT_PRINTF("x", "%#c", 'x');
|
EXPECT_PRINTF("x", "%#c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Width) {
|
TEST(printf_test, width) {
|
||||||
EXPECT_PRINTF(" abc", "%5s", "abc");
|
EXPECT_PRINTF(" abc", "%5s", "abc");
|
||||||
|
|
||||||
// Width cannot be specified twice.
|
// Width cannot be specified twice.
|
||||||
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
|
||||||
"invalid type specifier");
|
"invalid type specifier");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%{}d", big_num), 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", big_num), 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DynamicWidth) {
|
TEST(printf_test, dynamic_width) {
|
||||||
EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42));
|
EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42));
|
||||||
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
|
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
|
||||||
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
|
||||||
"width is not integer");
|
"width is not integer");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, "argument not found");
|
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, "argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%*d", big_num, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, IntPrecision) {
|
TEST(printf_test, int_precision) {
|
||||||
EXPECT_PRINTF("00042", "%.5d", 42);
|
EXPECT_PRINTF("00042", "%.5d", 42);
|
||||||
EXPECT_PRINTF("-00042", "%.5d", -42);
|
EXPECT_PRINTF("-00042", "%.5d", -42);
|
||||||
EXPECT_PRINTF("00042", "%.5x", 0x42);
|
EXPECT_PRINTF("00042", "%.5x", 0x42);
|
||||||
|
@ -277,8 +279,8 @@ TEST(PrintfTest, IntPrecision) {
|
||||||
EXPECT_PRINTF("00042 ", "%-#10.5o", 042);
|
EXPECT_PRINTF("00042 ", "%-#10.5o", 042);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, FloatPrecision) {
|
TEST(printf_test, float_precision) {
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[256];
|
||||||
safe_sprintf(buffer, "%.3e", 1234.5678);
|
safe_sprintf(buffer, "%.3e", 1234.5678);
|
||||||
EXPECT_PRINTF(buffer, "%.3e", 1234.5678);
|
EXPECT_PRINTF(buffer, "%.3e", 1234.5678);
|
||||||
EXPECT_PRINTF("1234.568", "%.3f", 1234.5678);
|
EXPECT_PRINTF("1234.568", "%.3f", 1234.5678);
|
||||||
|
@ -287,22 +289,22 @@ TEST(PrintfTest, FloatPrecision) {
|
||||||
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
|
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, StringPrecision) {
|
TEST(printf_test, string_precision) {
|
||||||
char test[] = {'H', 'e', 'l', 'l', 'o'};
|
char test[] = {'H', 'e', 'l', 'l', 'o'};
|
||||||
EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell");
|
EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
|
TEST(printf_test, ignore_precision_for_non_numeric_arg) {
|
||||||
EXPECT_PRINTF("abc", "%.5s", "abc");
|
EXPECT_PRINTF("abc", "%.5s", "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DynamicPrecision) {
|
TEST(printf_test, dynamic_precision) {
|
||||||
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
|
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
|
||||||
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
|
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found");
|
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%.*d", big_num, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
if (sizeof(long long) != sizeof(int)) {
|
if (sizeof(long long) != sizeof(int)) {
|
||||||
long long prec = static_cast<long long>(INT_MIN) - 1;
|
long long prec = static_cast<long long>(INT_MIN) - 1;
|
||||||
|
@ -325,7 +327,7 @@ SPECIALIZE_MAKE_SIGNED(unsigned long long, long long);
|
||||||
|
|
||||||
// Test length format specifier ``length_spec``.
|
// Test length format specifier ``length_spec``.
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
void TestLength(const char* length_spec, U value) {
|
void test_length(const char* length_spec, U value) {
|
||||||
long long signed_value = 0;
|
long long signed_value = 0;
|
||||||
unsigned long long unsigned_value = 0;
|
unsigned long long unsigned_value = 0;
|
||||||
// Apply integer promotion to the argument.
|
// Apply integer promotion to the argument.
|
||||||
|
@ -364,54 +366,54 @@ void TestLength(const char* length_spec, U value) {
|
||||||
EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value);
|
EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void TestLength(const char* length_spec) {
|
template <typename T> void test_length(const char* length_spec) {
|
||||||
T min = std::numeric_limits<T>::min(), max = max_value<T>();
|
T min = std::numeric_limits<T>::min(), max = max_value<T>();
|
||||||
TestLength<T>(length_spec, 42);
|
test_length<T>(length_spec, 42);
|
||||||
TestLength<T>(length_spec, -42);
|
test_length<T>(length_spec, -42);
|
||||||
TestLength<T>(length_spec, min);
|
test_length<T>(length_spec, min);
|
||||||
TestLength<T>(length_spec, max);
|
test_length<T>(length_spec, max);
|
||||||
long long long_long_min = std::numeric_limits<long long>::min();
|
long long long_long_min = std::numeric_limits<long long>::min();
|
||||||
if (static_cast<long long>(min) > long_long_min)
|
if (static_cast<long long>(min) > long_long_min)
|
||||||
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
|
test_length<T>(length_spec, static_cast<long long>(min) - 1);
|
||||||
unsigned long long long_long_max = max_value<long long>();
|
unsigned long long long_long_max = max_value<long long>();
|
||||||
if (static_cast<unsigned long long>(max) < long_long_max)
|
if (static_cast<unsigned long long>(max) < long_long_max)
|
||||||
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
|
test_length<T>(length_spec, static_cast<long long>(max) + 1);
|
||||||
TestLength<T>(length_spec, std::numeric_limits<short>::min());
|
test_length<T>(length_spec, std::numeric_limits<short>::min());
|
||||||
TestLength<T>(length_spec, max_value<unsigned short>());
|
test_length<T>(length_spec, max_value<unsigned short>());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<int>::min());
|
test_length<T>(length_spec, std::numeric_limits<int>::min());
|
||||||
TestLength<T>(length_spec, max_value<int>());
|
test_length<T>(length_spec, max_value<int>());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<unsigned>::min());
|
test_length<T>(length_spec, std::numeric_limits<unsigned>::min());
|
||||||
TestLength<T>(length_spec, max_value<unsigned>());
|
test_length<T>(length_spec, max_value<unsigned>());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<long long>::min());
|
test_length<T>(length_spec, std::numeric_limits<long long>::min());
|
||||||
TestLength<T>(length_spec, max_value<long long>());
|
test_length<T>(length_spec, max_value<long long>());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::min());
|
test_length<T>(length_spec, std::numeric_limits<unsigned long long>::min());
|
||||||
TestLength<T>(length_spec, max_value<unsigned long long>());
|
test_length<T>(length_spec, max_value<unsigned long long>());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Length) {
|
TEST(printf_test, length) {
|
||||||
TestLength<char>("hh");
|
test_length<char>("hh");
|
||||||
TestLength<signed char>("hh");
|
test_length<signed char>("hh");
|
||||||
TestLength<unsigned char>("hh");
|
test_length<unsigned char>("hh");
|
||||||
TestLength<short>("h");
|
test_length<short>("h");
|
||||||
TestLength<unsigned short>("h");
|
test_length<unsigned short>("h");
|
||||||
TestLength<long>("l");
|
test_length<long>("l");
|
||||||
TestLength<unsigned long>("l");
|
test_length<unsigned long>("l");
|
||||||
TestLength<long long>("ll");
|
test_length<long long>("ll");
|
||||||
TestLength<unsigned long long>("ll");
|
test_length<unsigned long long>("ll");
|
||||||
TestLength<intmax_t>("j");
|
test_length<intmax_t>("j");
|
||||||
TestLength<size_t>("z");
|
test_length<size_t>("z");
|
||||||
TestLength<std::ptrdiff_t>("t");
|
test_length<std::ptrdiff_t>("t");
|
||||||
long double max = max_value<long double>();
|
long double max = max_value<long double>();
|
||||||
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
|
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
|
||||||
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
|
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Bool) {
|
TEST(printf_test, bool) {
|
||||||
EXPECT_PRINTF("1", "%d", true);
|
EXPECT_PRINTF("1", "%d", true);
|
||||||
EXPECT_PRINTF("true", "%s", true);
|
EXPECT_PRINTF("true", "%s", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Int) {
|
TEST(printf_test, int) {
|
||||||
EXPECT_PRINTF("-42", "%d", -42);
|
EXPECT_PRINTF("-42", "%d", -42);
|
||||||
EXPECT_PRINTF("-42", "%i", -42);
|
EXPECT_PRINTF("-42", "%i", -42);
|
||||||
unsigned u = 0 - 42u;
|
unsigned u = 0 - 42u;
|
||||||
|
@ -421,20 +423,20 @@ TEST(PrintfTest, Int) {
|
||||||
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
|
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, long_long) {
|
TEST(printf_test, long_long) {
|
||||||
// fmt::printf allows passing long long arguments to %d without length
|
// fmt::printf allows passing long long arguments to %d without length
|
||||||
// specifiers.
|
// specifiers.
|
||||||
long long max = max_value<long long>();
|
long long max = max_value<long long>();
|
||||||
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Float) {
|
TEST(printf_test, float) {
|
||||||
EXPECT_PRINTF("392.650000", "%f", 392.65);
|
EXPECT_PRINTF("392.650000", "%f", 392.65);
|
||||||
EXPECT_PRINTF("392.65", "%.2f", 392.65);
|
EXPECT_PRINTF("392.65", "%.2f", 392.65);
|
||||||
EXPECT_PRINTF("392.6", "%.1f", 392.65);
|
EXPECT_PRINTF("392.6", "%.1f", 392.65);
|
||||||
EXPECT_PRINTF("393", "%.f", 392.65);
|
EXPECT_PRINTF("393", "%.f", 392.65);
|
||||||
EXPECT_PRINTF("392.650000", "%F", 392.65);
|
EXPECT_PRINTF("392.650000", "%F", 392.65);
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[256];
|
||||||
safe_sprintf(buffer, "%e", 392.65);
|
safe_sprintf(buffer, "%e", 392.65);
|
||||||
EXPECT_PRINTF(buffer, "%e", 392.65);
|
EXPECT_PRINTF(buffer, "%e", 392.65);
|
||||||
safe_sprintf(buffer, "%E", 392.65);
|
safe_sprintf(buffer, "%E", 392.65);
|
||||||
|
@ -450,7 +452,7 @@ TEST(PrintfTest, Float) {
|
||||||
EXPECT_EQ(buffer, format("{:A}", -392.65));
|
EXPECT_EQ(buffer, format("{:A}", -392.65));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Inf) {
|
TEST(printf_test, inf) {
|
||||||
double inf = std::numeric_limits<double>::infinity();
|
double inf = std::numeric_limits<double>::infinity();
|
||||||
for (const char* type = "fega"; *type; ++type) {
|
for (const char* type = "fega"; *type; ++type) {
|
||||||
EXPECT_PRINTF("inf", fmt::format("%{}", *type), inf);
|
EXPECT_PRINTF("inf", fmt::format("%{}", *type), inf);
|
||||||
|
@ -459,7 +461,7 @@ TEST(PrintfTest, Inf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Char) {
|
TEST(printf_test, char) {
|
||||||
EXPECT_PRINTF("x", "%c", 'x');
|
EXPECT_PRINTF("x", "%c", 'x');
|
||||||
int max = max_value<int>();
|
int max = max_value<int>();
|
||||||
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
||||||
|
@ -468,7 +470,7 @@ TEST(PrintfTest, Char) {
|
||||||
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
|
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, String) {
|
TEST(printf_test, string) {
|
||||||
EXPECT_PRINTF("abc", "%s", "abc");
|
EXPECT_PRINTF("abc", "%s", "abc");
|
||||||
const char* null_str = nullptr;
|
const char* null_str = nullptr;
|
||||||
EXPECT_PRINTF("(null)", "%s", null_str);
|
EXPECT_PRINTF("(null)", "%s", null_str);
|
||||||
|
@ -479,13 +481,13 @@ TEST(PrintfTest, String) {
|
||||||
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, UCharString) {
|
TEST(printf_test, uchar_string) {
|
||||||
unsigned char str[] = "test";
|
unsigned char str[] = "test";
|
||||||
unsigned char* pstr = str;
|
unsigned char* pstr = str;
|
||||||
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
|
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Pointer) {
|
TEST(printf_test, pointer) {
|
||||||
int n;
|
int n;
|
||||||
void* p = &n;
|
void* p = &n;
|
||||||
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
|
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
|
||||||
|
@ -508,20 +510,16 @@ TEST(PrintfTest, Pointer) {
|
||||||
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
|
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Location) {
|
|
||||||
// TODO: test %n
|
|
||||||
}
|
|
||||||
|
|
||||||
enum test_enum { answer = 42 };
|
enum test_enum { answer = 42 };
|
||||||
|
|
||||||
TEST(PrintfTest, Enum) {
|
TEST(printf_test, enum) {
|
||||||
EXPECT_PRINTF("42", "%d", answer);
|
EXPECT_PRINTF("42", "%d", answer);
|
||||||
volatile test_enum volatile_enum = answer;
|
volatile test_enum volatile_enum = answer;
|
||||||
EXPECT_PRINTF("42", "%d", volatile_enum);
|
EXPECT_PRINTF("42", "%d", volatile_enum);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
#if FMT_USE_FCNTL
|
||||||
TEST(PrintfTest, Examples) {
|
TEST(printf_test, examples) {
|
||||||
const char* weekday = "Thursday";
|
const char* weekday = "Thursday";
|
||||||
const char* month = "August";
|
const char* month = "August";
|
||||||
int day = 21;
|
int day = 21;
|
||||||
|
@ -529,7 +527,7 @@ TEST(PrintfTest, Examples) {
|
||||||
"Thursday, 21 August");
|
"Thursday, 21 August");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PrintfError) {
|
TEST(printf_test, printf_error) {
|
||||||
fmt::file read_end, write_end;
|
fmt::file read_end, write_end;
|
||||||
fmt::file::pipe(read_end, write_end);
|
fmt::file::pipe(read_end, write_end);
|
||||||
int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
|
int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
|
||||||
|
@ -537,26 +535,20 @@ TEST(PrintfTest, PrintfError) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
|
TEST(printf_test, wide_string) {
|
||||||
|
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
|
||||||
TEST(PrintfTest, PrintfCustom) {
|
|
||||||
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, OStream) {
|
TEST(printf_test, printf_custom) {
|
||||||
std::ostringstream os;
|
EXPECT_EQ("abc", test_sprintf("%s", test_string("abc")));
|
||||||
int ret = fmt::fprintf(os, "Don't %s!", "panic");
|
|
||||||
EXPECT_EQ("Don't panic!", os.str());
|
|
||||||
EXPECT_EQ(12, ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, VPrintf) {
|
TEST(printf_test, vprintf) {
|
||||||
fmt::format_arg_store<fmt::printf_context, int> as{42};
|
fmt::format_arg_store<fmt::printf_context, int> as{42};
|
||||||
fmt::basic_format_args<fmt::printf_context> args(as);
|
fmt::basic_format_args<fmt::printf_context> args(as);
|
||||||
EXPECT_EQ(fmt::vsprintf("%d", args), "42");
|
EXPECT_EQ(fmt::vsprintf("%d", args), "42");
|
||||||
EXPECT_WRITE(stdout, fmt::vprintf("%d", args), "42");
|
EXPECT_WRITE(stdout, fmt::vprintf("%d", args), "42");
|
||||||
EXPECT_WRITE(stdout, fmt::vfprintf(stdout, "%d", args), "42");
|
EXPECT_WRITE(stdout, fmt::vfprintf(stdout, "%d", args), "42");
|
||||||
EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
|
@ -564,15 +556,15 @@ void check_format_string_regression(fmt::string_view s, const Args&... args) {
|
||||||
fmt::sprintf(s, args...);
|
fmt::sprintf(s, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, CheckFormatStringRegression) {
|
TEST(printf_test, check_format_string_regression) {
|
||||||
check_format_string_regression("%c%s", 'x', "");
|
check_format_string_regression("%c%s", 'x', "");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, FixedLargeExponent) {
|
TEST(printf_test, fixed_large_exponent) {
|
||||||
EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21));
|
EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, VSPrintfMakeArgsExample) {
|
TEST(printf_test, vsprintf_make_args_example) {
|
||||||
fmt::format_arg_store<fmt::printf_context, int, const char*> as{42,
|
fmt::format_arg_store<fmt::printf_context, int, const char*> as{42,
|
||||||
"something"};
|
"something"};
|
||||||
fmt::basic_format_args<fmt::printf_context> args(as);
|
fmt::basic_format_args<fmt::printf_context> args(as);
|
||||||
|
@ -581,15 +573,12 @@ TEST(PrintfTest, VSPrintfMakeArgsExample) {
|
||||||
fmt::basic_format_args<fmt::printf_context> args2(as2);
|
fmt::basic_format_args<fmt::printf_context> args2(as2);
|
||||||
EXPECT_EQ("[42] something happened",
|
EXPECT_EQ("[42] something happened",
|
||||||
fmt::vsprintf("[%d] %s happened", args2));
|
fmt::vsprintf("[%d] %s happened", args2));
|
||||||
// The older gcc versions can't cast the return value.
|
|
||||||
#if !defined(__GNUC__) || (__GNUC__ > 4)
|
|
||||||
EXPECT_EQ("[42] something happened",
|
EXPECT_EQ("[42] something happened",
|
||||||
fmt::vsprintf("[%d] %s happened",
|
fmt::vsprintf("[%d] %s happened",
|
||||||
{fmt::make_printf_args(42, "something")}));
|
{fmt::make_printf_args(42, "something")}));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, VSPrintfMakeWArgsExample) {
|
TEST(printf_test, vsprintf_make_wargs_example) {
|
||||||
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{
|
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{
|
||||||
42, L"something"};
|
42, L"something"};
|
||||||
fmt::basic_format_args<fmt::wprintf_context> args(as);
|
fmt::basic_format_args<fmt::wprintf_context> args(as);
|
||||||
|
@ -599,30 +588,7 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
|
||||||
fmt::basic_format_args<fmt::wprintf_context> args2(as2);
|
fmt::basic_format_args<fmt::wprintf_context> args2(as2);
|
||||||
EXPECT_EQ(L"[42] something happened",
|
EXPECT_EQ(L"[42] something happened",
|
||||||
fmt::vsprintf(L"[%d] %s happened", args2));
|
fmt::vsprintf(L"[%d] %s happened", args2));
|
||||||
// the older gcc versions can't cast the return value
|
|
||||||
#if !defined(__GNUC__) || (__GNUC__ > 4)
|
|
||||||
EXPECT_EQ(L"[42] something happened",
|
EXPECT_EQ(L"[42] something happened",
|
||||||
fmt::vsprintf(L"[%d] %s happened",
|
fmt::vsprintf(L"[%d] %s happened",
|
||||||
{fmt::make_wprintf_args(42, L"something")}));
|
{fmt::make_wprintf_args(42, L"something")}));
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PrintfTest, PrintfDetermineOutputSize) {
|
|
||||||
using backit = std::back_insert_iterator<std::vector<char>>;
|
|
||||||
using truncated_printf_context =
|
|
||||||
fmt::basic_printf_context<fmt::detail::truncating_iterator<backit>, char>;
|
|
||||||
|
|
||||||
auto v = std::vector<char>{};
|
|
||||||
auto it = std::back_inserter(v);
|
|
||||||
|
|
||||||
const auto format_string = "%s";
|
|
||||||
const auto format_arg = "Hello";
|
|
||||||
const auto expected_size = fmt::sprintf(format_string, format_arg).size();
|
|
||||||
|
|
||||||
EXPECT_EQ((truncated_printf_context(
|
|
||||||
fmt::detail::truncating_iterator<backit>(it, 0), format_string,
|
|
||||||
fmt::make_format_args<truncated_printf_context>(format_arg))
|
|
||||||
.format()
|
|
||||||
.count()),
|
|
||||||
expected_size);
|
|
||||||
}
|
}
|
||||||
|
|
267
externals/fmt/test/ranges-test.cc
vendored
267
externals/fmt/test/ranges-test.cc
vendored
|
@ -11,106 +11,104 @@
|
||||||
|
|
||||||
#include "fmt/ranges.h"
|
#include "fmt/ranges.h"
|
||||||
|
|
||||||
#include "gtest.h"
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// Check if 'if constexpr' is supported.
|
#include "gtest/gtest.h"
|
||||||
#if (__cplusplus > 201402L) || \
|
|
||||||
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
|
|
||||||
|
|
||||||
# include <array>
|
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
|
||||||
# include <map>
|
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||||
# include <string>
|
#endif
|
||||||
# include <vector>
|
|
||||||
|
|
||||||
TEST(RangesTest, FormatVector) {
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1910
|
||||||
std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
|
# define FMT_RANGES_TEST_ENABLE_JOIN
|
||||||
auto ivf = fmt::format("{}", iv);
|
# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||||
EXPECT_EQ("{1, 2, 3, 5, 7, 11}", ivf);
|
#endif
|
||||||
|
|
||||||
|
#ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||||
|
TEST(ranges_test, format_array) {
|
||||||
|
int arr[] = {1, 2, 3, 5, 7, 11};
|
||||||
|
EXPECT_EQ(fmt::format("{}", arr), "[1, 2, 3, 5, 7, 11]");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, FormatVector2) {
|
TEST(ranges_test, format_2d_array) {
|
||||||
std::vector<std::vector<int32_t>> ivv{{1, 2}, {3, 5}, {7, 11}};
|
int arr[][2] = {{1, 2}, {3, 5}, {7, 11}};
|
||||||
auto ivf = fmt::format("{}", ivv);
|
EXPECT_EQ(fmt::format("{}", arr), "[[1, 2], [3, 5], [7, 11]]");
|
||||||
EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, FormatMap) {
|
TEST(ranges_test, format_array_of_literals) {
|
||||||
std::map<std::string, int32_t> simap{{"one", 1}, {"two", 2}};
|
const char* arr[] = {"1234", "abcd"};
|
||||||
EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format("{}", simap));
|
EXPECT_EQ(fmt::format("{}", arr), "[\"1234\", \"abcd\"]");
|
||||||
|
}
|
||||||
|
#endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||||
|
|
||||||
|
TEST(ranges_test, format_vector) {
|
||||||
|
auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
|
||||||
|
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, FormatPair) {
|
TEST(ranges_test, format_vector2) {
|
||||||
std::pair<int64_t, float> pa1{42, 1.5f};
|
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
|
||||||
EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
|
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, FormatTuple) {
|
TEST(ranges_test, format_map) {
|
||||||
std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
|
auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
|
||||||
'i'};
|
EXPECT_EQ(fmt::format("{}", m), "[(\"one\", 1), (\"two\", 2)]");
|
||||||
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", t));
|
|
||||||
EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, JoinTuple) {
|
TEST(ranges_test, format_pair) {
|
||||||
// Value tuple args
|
auto p = std::pair<int, float>(42, 1.5f);
|
||||||
std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
|
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
|
||||||
EXPECT_EQ("(a, 1, 2.0)", fmt::format("({})", fmt::join(t1, ", ")));
|
|
||||||
|
|
||||||
// Testing lvalue tuple args
|
|
||||||
int x = 4;
|
|
||||||
std::tuple<char, int&> t2{'b', x};
|
|
||||||
EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + ")));
|
|
||||||
|
|
||||||
// Empty tuple
|
|
||||||
std::tuple<> t3;
|
|
||||||
EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|")));
|
|
||||||
|
|
||||||
// Single element tuple
|
|
||||||
std::tuple<float> t4{4.0f};
|
|
||||||
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, JoinInitializerList) {
|
TEST(ranges_test, format_tuple) {
|
||||||
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
|
auto t =
|
||||||
EXPECT_EQ("fmt rocks !",
|
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
|
||||||
fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
|
EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct my_struct {
|
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||||
int32_t i;
|
struct tuple_like {
|
||||||
std::string str; // can throw
|
int i;
|
||||||
template <size_t N> decltype(auto) get() const noexcept {
|
std::string str;
|
||||||
if constexpr (N == 0)
|
|
||||||
return i;
|
template <size_t N> fmt::enable_if_t<N == 0, int> get() const noexcept {
|
||||||
else if constexpr (N == 1)
|
return i;
|
||||||
return fmt::string_view{str};
|
}
|
||||||
|
template <size_t N>
|
||||||
|
fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept {
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
|
template <size_t N>
|
||||||
return s.get<N>();
|
auto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) {
|
||||||
|
return t.get<N>();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {};
|
||||||
|
|
||||||
template <> struct tuple_size<my_struct> : std::integral_constant<size_t, 2> {};
|
template <size_t N> struct tuple_element<N, tuple_like> {
|
||||||
|
using type = decltype(std::declval<tuple_like>().get<N>());
|
||||||
template <size_t N> struct tuple_element<N, my_struct> {
|
|
||||||
using type = decltype(std::declval<my_struct>().get<N>());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
TEST(RangesTest, FormatStruct) {
|
TEST(ranges_test, format_struct) {
|
||||||
my_struct mst{13, "my struct"};
|
auto t = tuple_like{42, "foo"};
|
||||||
EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
|
EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")");
|
||||||
}
|
}
|
||||||
|
#endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||||
|
|
||||||
TEST(RangesTest, FormatTo) {
|
TEST(ranges_test, format_to) {
|
||||||
char buf[10];
|
char buf[10];
|
||||||
auto end = fmt::format_to(buf, "{}", std::vector{1, 2, 3});
|
auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3});
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
EXPECT_STREQ(buf, "{1, 2, 3}");
|
EXPECT_STREQ(buf, "[1, 2, 3]");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct path_like {
|
struct path_like {
|
||||||
|
@ -120,13 +118,10 @@ struct path_like {
|
||||||
operator std::string() const;
|
operator std::string() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(RangesTest, PathLike) {
|
TEST(ranges_test, path_like) {
|
||||||
EXPECT_FALSE((fmt::is_range<path_like, char>::value));
|
EXPECT_FALSE((fmt::is_range<path_like, char>::value));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
|
|
||||||
// 201402L && _MSC_VER >= 1910)
|
|
||||||
|
|
||||||
#ifdef FMT_USE_STRING_VIEW
|
#ifdef FMT_USE_STRING_VIEW
|
||||||
struct string_like {
|
struct string_like {
|
||||||
const char* begin();
|
const char* begin();
|
||||||
|
@ -135,21 +130,135 @@ struct string_like {
|
||||||
explicit operator std::string_view() const { return "foo"; }
|
explicit operator std::string_view() const { return "foo"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(RangesTest, FormatStringLike) {
|
TEST(ranges_test, format_string_like) {
|
||||||
EXPECT_EQ("foo", fmt::format("{}", string_like()));
|
EXPECT_EQ(fmt::format("{}", string_like()), "foo");
|
||||||
}
|
}
|
||||||
#endif // FMT_USE_STRING_VIEW
|
#endif // FMT_USE_STRING_VIEW
|
||||||
|
|
||||||
|
// A range that provides non-const only begin()/end() to test fmt::join handles
|
||||||
|
// that.
|
||||||
|
//
|
||||||
|
// Some ranges (e.g. those produced by range-v3's views::filter()) can cache
|
||||||
|
// information during iteration so they only provide non-const begin()/end().
|
||||||
|
template <typename T> class non_const_only_range {
|
||||||
|
private:
|
||||||
|
std::vector<T> vec;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using const_iterator = typename ::std::vector<T>::const_iterator;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
explicit non_const_only_range(Args&&... args)
|
||||||
|
: vec(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
const_iterator begin() { return vec.begin(); }
|
||||||
|
const_iterator end() { return vec.end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class noncopyable_range {
|
||||||
|
private:
|
||||||
|
std::vector<T> vec;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using const_iterator = typename ::std::vector<T>::const_iterator;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
explicit noncopyable_range(Args&&... args)
|
||||||
|
: vec(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
noncopyable_range(noncopyable_range const&) = delete;
|
||||||
|
noncopyable_range(noncopyable_range&) = delete;
|
||||||
|
|
||||||
|
const_iterator begin() const { return vec.begin(); }
|
||||||
|
const_iterator end() const { return vec.end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(ranges_test, range) {
|
||||||
|
noncopyable_range<int> w(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", w), "[0, 0, 0]");
|
||||||
|
EXPECT_EQ(fmt::format("{}", noncopyable_range<int>(3u, 0)), "[0, 0, 0]");
|
||||||
|
|
||||||
|
non_const_only_range<int> x(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", x), "[0, 0, 0]");
|
||||||
|
EXPECT_EQ(fmt::format("{}", non_const_only_range<int>(3u, 0)), "[0, 0, 0]");
|
||||||
|
|
||||||
|
auto y = std::vector<int>(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", y), "[0, 0, 0]");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::vector<int>(3u, 0)), "[0, 0, 0]");
|
||||||
|
|
||||||
|
const auto z = std::vector<int>(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||||
|
struct unformattable {};
|
||||||
|
|
||||||
|
TEST(ranges_test, unformattable_range) {
|
||||||
|
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
|
||||||
|
fmt::format_context>::value));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FMT_RANGES_TEST_ENABLE_JOIN
|
||||||
|
TEST(ranges_test, join_tuple) {
|
||||||
|
// Value tuple args.
|
||||||
|
auto t1 = std::tuple<char, int, float>('a', 1, 2.0f);
|
||||||
|
EXPECT_EQ(fmt::format("({})", fmt::join(t1, ", ")), "(a, 1, 2)");
|
||||||
|
|
||||||
|
// Testing lvalue tuple args.
|
||||||
|
int x = 4;
|
||||||
|
auto t2 = std::tuple<char, int&>('b', x);
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(t2, " + ")), "b + 4");
|
||||||
|
|
||||||
|
// Empty tuple.
|
||||||
|
auto t3 = std::tuple<>();
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(t3, "|")), "");
|
||||||
|
|
||||||
|
// Single element tuple.
|
||||||
|
auto t4 = std::tuple<float>(4.0f);
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ranges_test, join_initializer_list) {
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3");
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")),
|
||||||
|
"fmt rocks !");
|
||||||
|
}
|
||||||
|
|
||||||
struct zstring_sentinel {};
|
struct zstring_sentinel {};
|
||||||
|
|
||||||
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
|
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
|
||||||
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
|
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
|
||||||
|
|
||||||
struct zstring {
|
struct zstring {
|
||||||
const char* p;
|
const char* p;
|
||||||
const char* begin() const { return p; }
|
const char* begin() const { return p; }
|
||||||
zstring_sentinel end() const { return {}; }
|
zstring_sentinel end() const { return {}; }
|
||||||
};
|
};
|
||||||
TEST(RangesTest, JoinSentinel) {
|
|
||||||
zstring hello{"hello"};
|
TEST(ranges_test, join_sentinel) {
|
||||||
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
|
auto hello = zstring{"hello"};
|
||||||
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
|
EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']");
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ranges_test, join_range) {
|
||||||
|
noncopyable_range<int> w(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(w, ",")), "0,0,0");
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")),
|
||||||
|
"0,0,0");
|
||||||
|
|
||||||
|
non_const_only_range<int> x(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(x, ",")), "0,0,0");
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")),
|
||||||
|
"0,0,0");
|
||||||
|
|
||||||
|
auto y = std::vector<int>(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(y, ",")), "0,0,0");
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")),
|
||||||
|
"0,0,0");
|
||||||
|
|
||||||
|
const auto z = std::vector<int>(3u, 0);
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0");
|
||||||
|
}
|
||||||
|
#endif // FMT_RANGES_TEST_ENABLE_JOIN
|
||||||
|
|
38
externals/fmt/test/scan-test.cc
vendored
38
externals/fmt/test/scan-test.cc
vendored
|
@ -11,25 +11,25 @@
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
#include "gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
TEST(ScanTest, ReadText) {
|
TEST(scan_test, read_text) {
|
||||||
fmt::string_view s = "foo";
|
auto s = fmt::string_view("foo");
|
||||||
auto end = fmt::scan(s, "foo");
|
auto end = fmt::scan(s, "foo");
|
||||||
EXPECT_EQ(end, s.end());
|
EXPECT_EQ(end, s.end());
|
||||||
EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input");
|
EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, ReadInt) {
|
TEST(scan_test, read_int) {
|
||||||
int n = 0;
|
auto n = int();
|
||||||
fmt::scan("42", "{}", n);
|
fmt::scan("42", "{}", n);
|
||||||
EXPECT_EQ(n, 42);
|
EXPECT_EQ(n, 42);
|
||||||
fmt::scan("-42", "{}", n);
|
fmt::scan("-42", "{}", n);
|
||||||
EXPECT_EQ(n, -42);
|
EXPECT_EQ(n, -42);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, ReadLongLong) {
|
TEST(scan_test, read_longlong) {
|
||||||
long long n = 0;
|
long long n = 0;
|
||||||
fmt::scan("42", "{}", n);
|
fmt::scan("42", "{}", n);
|
||||||
EXPECT_EQ(n, 42);
|
EXPECT_EQ(n, 42);
|
||||||
|
@ -37,15 +37,15 @@ TEST(ScanTest, ReadLongLong) {
|
||||||
EXPECT_EQ(n, -42);
|
EXPECT_EQ(n, -42);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, ReadUInt) {
|
TEST(scan_test, read_uint) {
|
||||||
unsigned n = 0;
|
auto n = unsigned();
|
||||||
fmt::scan("42", "{}", n);
|
fmt::scan("42", "{}", n);
|
||||||
EXPECT_EQ(n, 42);
|
EXPECT_EQ(n, 42);
|
||||||
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
|
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
|
||||||
"invalid input");
|
"invalid input");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, ReadULongLong) {
|
TEST(scan_test, read_ulonglong) {
|
||||||
unsigned long long n = 0;
|
unsigned long long n = 0;
|
||||||
fmt::scan("42", "{}", n);
|
fmt::scan("42", "{}", n);
|
||||||
EXPECT_EQ(n, 42);
|
EXPECT_EQ(n, 42);
|
||||||
|
@ -53,14 +53,14 @@ TEST(ScanTest, ReadULongLong) {
|
||||||
"invalid input");
|
"invalid input");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, ReadString) {
|
TEST(scan_test, read_string) {
|
||||||
std::string s;
|
auto s = std::string();
|
||||||
fmt::scan("foo", "{}", s);
|
fmt::scan("foo", "{}", s);
|
||||||
EXPECT_EQ(s, "foo");
|
EXPECT_EQ(s, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, ReadStringView) {
|
TEST(scan_test, read_string_view) {
|
||||||
fmt::string_view s;
|
auto s = fmt::string_view();
|
||||||
fmt::scan("foo", "{}", s);
|
fmt::scan("foo", "{}", s);
|
||||||
EXPECT_EQ(s, "foo");
|
EXPECT_EQ(s, "foo");
|
||||||
}
|
}
|
||||||
|
@ -90,8 +90,8 @@ template <> struct scanner<tm> {
|
||||||
};
|
};
|
||||||
} // namespace fmt
|
} // namespace fmt
|
||||||
|
|
||||||
TEST(ScanTest, ReadCustom) {
|
TEST(scan_test, read_custom) {
|
||||||
const char* input = "Date: 1985-10-25";
|
auto input = "Date: 1985-10-25";
|
||||||
auto t = tm();
|
auto t = tm();
|
||||||
fmt::scan(input, "Date: {0:%Y-%m-%d}", t);
|
fmt::scan(input, "Date: {0:%Y-%m-%d}", t);
|
||||||
EXPECT_EQ(t.tm_year, 85);
|
EXPECT_EQ(t.tm_year, 85);
|
||||||
|
@ -100,16 +100,16 @@ TEST(ScanTest, ReadCustom) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(ScanTest, InvalidFormat) {
|
TEST(scan_test, invalid_format) {
|
||||||
EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error,
|
EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::scan("", "{"), fmt::format_error,
|
EXPECT_THROW_MSG(fmt::scan("", "{"), fmt::format_error,
|
||||||
"invalid format string");
|
"invalid format string");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ScanTest, Example) {
|
TEST(scan_test, example) {
|
||||||
std::string key;
|
auto key = std::string();
|
||||||
int value;
|
auto value = int();
|
||||||
fmt::scan("answer = 42", "{} = {}", key, value);
|
fmt::scan("answer = 42", "{} = {}", key, value);
|
||||||
EXPECT_EQ(key, "answer");
|
EXPECT_EQ(key, "answer");
|
||||||
EXPECT_EQ(value, 42);
|
EXPECT_EQ(value, 42);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue