externals: Update fmt to 6.2.0
Merge commit 'c9dec5da8cb2893219c069f92728fb3997c7d81e'
This commit is contained in:
commit
591e7667f2
92 changed files with 18603 additions and 9815 deletions
8
externals/fmt/.clang-format
vendored
Normal file
8
externals/fmt/.clang-format
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Run manually to reformat a file:
|
||||
# clang-format -i --style=file <file>
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentCaseLabels: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
DerivePointerAlignment: false
|
6
externals/fmt/.github/pull_request_template.md
vendored
Normal file
6
externals/fmt/.github/pull_request_template.md
vendored
Normal file
|
@ -0,0 +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,
|
||||
and agree to future changes to the licensing. -->
|
||||
<!-- If you're a first-time contributor, please acknowledge it by leaving the statement below. -->
|
||||
|
||||
I agree that my contributions are licensed under the {fmt} license, and agree to future changes to the licensing.
|
1
externals/fmt/.gitignore
vendored
1
externals/fmt/.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
.vscode/
|
||||
.vs/
|
||||
|
||||
*.iml
|
||||
.idea/
|
||||
|
|
71
externals/fmt/.travis.yml
vendored
71
externals/fmt/.travis.yml
vendored
|
@ -7,7 +7,6 @@ os: linux
|
|||
git:
|
||||
depth: 1
|
||||
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: |-
|
||||
|
@ -39,6 +38,26 @@ matrix:
|
|||
- 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
|
||||
|
@ -46,8 +65,8 @@ matrix:
|
|||
- env: BUILD=Release STANDARD=14
|
||||
compiler: clang
|
||||
os: osx
|
||||
# clang 6.0 on Linux with C++14
|
||||
- env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14
|
||||
# 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:
|
||||
|
@ -73,52 +92,6 @@ matrix:
|
|||
# g++ 4.8 on Linux with C++11
|
||||
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
|
||||
compiler: gcc
|
||||
# g++ 4.4 on Linux with C++11
|
||||
- env: COMPILER=g++-4.4 BUILD=Debug STANDARD=11
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- g++-4.4
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
# Android
|
||||
- language: android
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- wget
|
||||
- unzip
|
||||
- tree
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- android-21
|
||||
env:
|
||||
- ANDROID=true
|
||||
before_install:
|
||||
# Download/Install Gradle
|
||||
- wget https://services.gradle.org/distributions/gradle-4.10.2-bin.zip
|
||||
- mkdir -p gradle
|
||||
- unzip -q -d ./gradle gradle-4.10.2-bin.zip
|
||||
- export GRADLE=gradle/gradle-4.10.2/bin/gradle
|
||||
- bash $GRADLE --version
|
||||
install:
|
||||
# Accept SDK Licenses + Install NDK
|
||||
- yes | sdkmanager --update > /dev/null 2>&1
|
||||
- sdkmanager ndk-bundle > /dev/null 2>&1
|
||||
before_script:
|
||||
- pushd ./support
|
||||
script:
|
||||
- bash ../$GRADLE clean assemble
|
||||
after_success:
|
||||
- popd;
|
||||
- tree ./libs
|
||||
|
||||
before_script:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi
|
||||
|
|
123
externals/fmt/CMakeLists.txt
vendored
123
externals/fmt/CMakeLists.txt
vendored
|
@ -24,24 +24,38 @@ function(join result_var)
|
|||
set(${result_var} "${result}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Sets a cache variable with a docstring joined from multiple arguments:
|
||||
# set(<variable> <value>... CACHE <type> <docstring>...)
|
||||
# This allows splitting a long docstring for readability.
|
||||
function(set_verbose)
|
||||
cmake_parse_arguments(SET_VERBOSE "" "" "CACHE" ${ARGN})
|
||||
list(GET SET_VERBOSE_CACHE 0 type)
|
||||
list(REMOVE_AT SET_VERBOSE_CACHE 0)
|
||||
join(doc ${SET_VERBOSE_CACHE})
|
||||
set(${SET_VERBOSE_UNPARSED_ARGUMENTS} CACHE ${type} ${doc})
|
||||
endfunction()
|
||||
|
||||
# Set the default CMAKE_BUILD_TYPE to Release.
|
||||
# This should be done before the project command since the latter can set
|
||||
# CMAKE_BUILD_TYPE itself (it does so for nmake).
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
||||
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
|
||||
if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
|
||||
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
||||
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
||||
endif ()
|
||||
|
||||
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
||||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF)
|
||||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
||||
OFF)
|
||||
|
||||
# Options that control generation of various targets.
|
||||
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
|
||||
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
||||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
||||
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
||||
|
||||
project(FMT)
|
||||
project(FMT CXX)
|
||||
|
||||
# Get version from core.h
|
||||
file(READ include/fmt/core.h core_h)
|
||||
|
@ -58,7 +72,9 @@ message(STATUS "Version: ${FMT_VERSION}")
|
|||
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
endif ()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
|
||||
|
@ -66,6 +82,14 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
|||
include(cxx14)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
|
||||
if (${index} GREATER -1)
|
||||
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
|
||||
# compatibility with older CMake versions.
|
||||
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
|
||||
endif ()
|
||||
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
||||
-Wold-style-cast -Wundef
|
||||
|
@ -74,7 +98,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|||
-Wcast-align -Wnon-virtual-dtor
|
||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||
-Winvalid-pch -Woverloaded-virtual
|
||||
-Wconversion
|
||||
-Wconversion -Wswitch-enum
|
||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
|
||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
|
||||
|
@ -93,7 +117,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|||
endif ()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion)
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
|
||||
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
|
||||
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
||||
if (HAS_NULLPTR_WARNING)
|
||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||
|
@ -117,17 +142,24 @@ if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
|||
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
|
||||
endif ()
|
||||
# Set FrameworkPathOverride to get rid of MSB3644 warnings.
|
||||
set(netfxpath "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.0")
|
||||
join(netfxpath
|
||||
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
|
||||
".NETFramework\\v4.0")
|
||||
file(WRITE run-msbuild.bat "
|
||||
${MSBUILD_SETUP}
|
||||
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
|
||||
endif ()
|
||||
|
||||
set(strtod_l_headers stdlib.h)
|
||||
if (APPLE)
|
||||
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
|
||||
endif ()
|
||||
|
||||
include(CheckSymbolExists)
|
||||
if (WIN32)
|
||||
check_symbol_exists(open io.h HAVE_OPEN)
|
||||
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
|
||||
else ()
|
||||
check_symbol_exists(open fcntl.h HAVE_OPEN)
|
||||
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
|
||||
endif ()
|
||||
|
||||
function(add_headers VAR)
|
||||
|
@ -139,17 +171,17 @@ function(add_headers VAR)
|
|||
endfunction()
|
||||
|
||||
# Define the fmt library, its includes and the needed defines.
|
||||
add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h
|
||||
ostream.h printf.h time.h ranges.h)
|
||||
set(FMT_SOURCES src/format.cc)
|
||||
if (HAVE_OPEN)
|
||||
add_headers(FMT_HEADERS posix.h)
|
||||
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
|
||||
endif ()
|
||||
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
|
||||
locale.h os.h ostream.h posix.h printf.h ranges.h)
|
||||
set(FMT_SOURCES src/format.cc src/os.cc)
|
||||
|
||||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
||||
add_library(fmt::fmt ALIAS fmt)
|
||||
|
||||
if (HAVE_STRTOD_L)
|
||||
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
|
||||
endif ()
|
||||
|
||||
if (FMT_WERROR)
|
||||
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
|
||||
endif ()
|
||||
|
@ -157,27 +189,42 @@ if (FMT_PEDANTIC)
|
|||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
|
||||
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||
|
||||
target_include_directories(fmt PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
|
||||
|
||||
set_target_properties(fmt PROPERTIES
|
||||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
||||
DEBUG_POSTFIX d)
|
||||
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
|
||||
|
||||
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
|
||||
# property because it's not set by default.
|
||||
set(FMT_LIB_NAME fmt)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
|
||||
endif ()
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
|
||||
# Fix rpmlint warning:
|
||||
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
|
||||
target_link_libraries(fmt -Wl,--as-needed)
|
||||
endif ()
|
||||
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
|
||||
endif ()
|
||||
if (FMT_SAFE_DURATION_CAST)
|
||||
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
|
||||
endif()
|
||||
|
||||
add_library(fmt-header-only INTERFACE)
|
||||
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
||||
|
||||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
||||
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||
|
||||
target_include_directories(fmt-header-only INTERFACE
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||
|
@ -187,8 +234,9 @@ target_include_directories(fmt-header-only INTERFACE
|
|||
if (FMT_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||
"Installation directory for cmake files, relative to "
|
||||
"${CMAKE_INSTALL_PREFIX}.")
|
||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
||||
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
||||
|
@ -199,14 +247,17 @@ if (FMT_INSTALL)
|
|||
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
|
||||
endif ()
|
||||
|
||||
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||
"Installation directory for libraries, relative to "
|
||||
"${CMAKE_INSTALL_PREFIX}.")
|
||||
|
||||
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
||||
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
||||
"Installation directory for include files, relative to "
|
||||
"${CMAKE_INSTALL_PREFIX}.")
|
||||
|
||||
set(FMT_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH
|
||||
"Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
||||
"Installation directory for pkgconfig (.pc) files, relative to "
|
||||
"${CMAKE_INSTALL_PREFIX}.")
|
||||
|
||||
# Generate the version, config and target files into the build directory.
|
||||
write_basic_package_version_file(
|
||||
|
@ -235,9 +286,12 @@ if (FMT_INSTALL)
|
|||
|
||||
# Install the library and headers.
|
||||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
||||
DESTINATION ${FMT_LIB_DIR})
|
||||
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
||||
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
||||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
||||
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
||||
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
|
||||
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
|
||||
endif ()
|
||||
|
@ -251,11 +305,16 @@ if (FMT_TEST)
|
|||
add_subdirectory(test)
|
||||
endif ()
|
||||
|
||||
# Control fuzzing independent of the unit tests.
|
||||
if (FMT_FUZZ)
|
||||
add_subdirectory(test/fuzzing)
|
||||
endif ()
|
||||
|
||||
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
||||
if (MASTER_PROJECT AND EXISTS ${gitignore})
|
||||
# Get the list of ignored files from .gitignore.
|
||||
file (STRINGS ${gitignore} lines)
|
||||
LIST(REMOVE_ITEM lines /doc/html)
|
||||
list(REMOVE_ITEM lines /doc/html)
|
||||
foreach (line ${lines})
|
||||
string(REPLACE "." "[.]" line "${line}")
|
||||
string(REPLACE "*" ".*" line "${line}")
|
||||
|
|
17
externals/fmt/CONTRIBUTING.md
vendored
Normal file
17
externals/fmt/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
Contributing to {fmt}
|
||||
=====================
|
||||
|
||||
By submitting a pull request or a patch, you represent that you have the right
|
||||
to license your contribution to the {fmt} project owners and the community,
|
||||
agree that your contributions are licensed under the {fmt} license, and agree
|
||||
to future changes to the licensing.
|
||||
|
||||
All C++ code must adhere to [Google C++ Style Guide](
|
||||
https://google.github.io/styleguide/cppguide.html) with the following
|
||||
exceptions:
|
||||
|
||||
* Exceptions are permitted
|
||||
* snake_case should be used instead of UpperCamelCase for function and type
|
||||
names
|
||||
|
||||
Thanks for contributing!
|
12
externals/fmt/CONTRIBUTING.rst
vendored
12
externals/fmt/CONTRIBUTING.rst
vendored
|
@ -1,12 +0,0 @@
|
|||
Contributing to fmt
|
||||
===================
|
||||
|
||||
All C++ code must adhere to `Google C++ Style Guide
|
||||
<https://google.github.io/styleguide/cppguide.html>`_ with the following
|
||||
exceptions:
|
||||
|
||||
* Exceptions are permitted
|
||||
* snake_case should be used instead of UpperCamelCase for function and type
|
||||
names
|
||||
|
||||
Thanks for contributing!
|
920
externals/fmt/ChangeLog.rst
vendored
920
externals/fmt/ChangeLog.rst
vendored
File diff suppressed because it is too large
Load diff
42
externals/fmt/LICENSE.rst
vendored
42
externals/fmt/LICENSE.rst
vendored
|
@ -1,23 +1,27 @@
|
|||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
|
|
516
externals/fmt/README.rst
vendored
516
externals/fmt/README.rst
vendored
|
@ -6,90 +6,90 @@
|
|||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||
|
||||
.. image:: https://badges.gitter.im/Join%20Chat.svg
|
||||
:alt: Join the chat at https://gitter.im/fmtlib/fmt
|
||||
:target: https://gitter.im/fmtlib/fmt
|
||||
|
||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
|
||||
:alt: fmt is continuously fuzzed att oss-fuzz
|
||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1
|
||||
|
||||
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
||||
:alt: Ask questions at StackOverflow with the tag fmt
|
||||
:target: https://stackoverflow.com/questions/tagged/fmt
|
||||
|
||||
**{fmt}** is an open-source formatting library for C++.
|
||||
It can be used as a safe and fast alternative to (s)printf and IOStreams.
|
||||
It can be used as a safe and fast alternative to (s)printf and iostreams.
|
||||
|
||||
`Documentation <http://fmtlib.net/latest/>`__
|
||||
`Documentation <https://fmt.dev/latest/>`__
|
||||
|
||||
This is a development branch that implements the C++ standards proposal `P0645
|
||||
Text Formatting <http://fmtlib.net/Text%20Formatting.html>`__.
|
||||
Released versions are available from the `Releases page
|
||||
<https://github.com/fmtlib/fmt/releases>`__.
|
||||
Q&A: ask questions on `StackOverflow with the tag fmt <https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Replacement-based `format API <http://fmtlib.net/dev/api.html>`_ with
|
||||
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
|
||||
positional arguments for localization.
|
||||
* `Format string syntax <http://fmtlib.net/dev/syntax.html>`_ similar to the one
|
||||
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
|
||||
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
|
||||
of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
||||
in Python.
|
||||
* Safe `printf implementation
|
||||
<http://fmtlib.net/latest/api.html#printf-formatting>`_ including
|
||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
|
||||
the POSIX extension for positional arguments.
|
||||
* Implementation of `C++20 std::format <https://en.cppreference.com/w/cpp/utility/format>`__.
|
||||
* Support for user-defined types.
|
||||
* High speed: performance of the format API is close to that of glibc's `printf
|
||||
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the
|
||||
performance of IOStreams. See `Speed tests`_ and
|
||||
`Fast integer to string conversion in C++
|
||||
* High performance: faster than common standard library implementations of
|
||||
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_ and
|
||||
iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||
* Small code size both in terms of source code (the minimum configuration
|
||||
consists of just three header files, ``core.h``, ``format.h`` and
|
||||
``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
|
||||
* Reliability: the library has an extensive set of `unit tests
|
||||
<https://github.com/fmtlib/fmt/tree/master/test>`_.
|
||||
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed.
|
||||
* Safety: the library is fully type safe, errors in format strings can be
|
||||
reported at compile time, automatic memory management prevents buffer overflow
|
||||
errors.
|
||||
* Ease of use: small self-contained code base, no external dependencies,
|
||||
permissive BSD `license
|
||||
permissive MIT `license
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with
|
||||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
||||
consistent output across platforms and support for older compilers.
|
||||
* Clean warning-free codebase even on high warning levels
|
||||
(``-Wall -Wextra -pedantic``).
|
||||
* Support for wide strings.
|
||||
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
|
||||
|
||||
See the `documentation <http://fmtlib.net/latest/>`_ for more details.
|
||||
See the `documentation <https://fmt.dev/latest/>`_ for more details.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
This prints ``Hello, world!`` to stdout:
|
||||
Print ``Hello, world!`` to ``stdout``:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax
|
||||
fmt::printf("Hello, %s!", "world"); // uses printf format string syntax
|
||||
fmt::print("Hello, {}!", "world"); // Python-like format string syntax
|
||||
fmt::printf("Hello, %s!", "world"); // printf format string syntax
|
||||
|
||||
Arguments can be accessed by position and arguments' indices can be repeated:
|
||||
Format a string and use positional arguments:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
|
||||
// s == "abracadabra"
|
||||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
||||
// s == "I'd rather be happy than right."
|
||||
|
||||
Format strings can be checked at compile time:
|
||||
Check a format string at compile time:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
// test.cc
|
||||
#define FMT_STRING_ALIAS 1
|
||||
#include <fmt/format.h>
|
||||
std::string s = format(fmt("{2}"), 42);
|
||||
std::string s = format(FMT_STRING("{2}"), 42);
|
||||
|
||||
.. code::
|
||||
|
||||
$ c++ -Iinclude -std=c++14 test.cc
|
||||
...
|
||||
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
|
||||
std::string s = format(fmt("{2}"), 42);
|
||||
std::string s = format(FMT_STRING("{2}"), 42);
|
||||
^
|
||||
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
|
||||
ErrorHandler::on_error(message);
|
||||
|
@ -98,7 +98,7 @@ Format strings can be checked at compile time:
|
|||
context_.on_error("argument index out of range");
|
||||
^
|
||||
|
||||
{fmt} can be used as a safe portable replacement for ``itoa``
|
||||
Use {fmt} as a safe portable replacement for ``itoa``
|
||||
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
|
||||
|
||||
.. code:: c++
|
||||
|
@ -106,10 +106,10 @@ Format strings can be checked at compile time:
|
|||
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 using to_string(buf) or buf.data()
|
||||
// access the string with to_string(buf) or buf.data()
|
||||
|
||||
Formatting of user-defined types is supported via a simple
|
||||
`extension API <http://fmtlib.net/latest/api.html#formatting-user-defined-types>`_:
|
||||
Format objects of user-defined types via a simple `extension API
|
||||
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -121,11 +121,10 @@ Formatting of user-defined types is supported via a simple
|
|||
|
||||
template <>
|
||||
struct fmt::formatter<date> {
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const date &d, FormatContext &ctx) {
|
||||
auto format(const date& d, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
|
||||
}
|
||||
};
|
||||
|
@ -133,243 +132,60 @@ Formatting of user-defined types is supported via a simple
|
|||
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
|
||||
// s == "The date is 2012-12-9"
|
||||
|
||||
You can create your own functions similar to `format
|
||||
<http://fmtlib.net/latest/api.html#format>`_ and
|
||||
`print <http://fmtlib.net/latest/api.html#print>`_
|
||||
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) {
|
||||
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) {
|
||||
void report_error(const char* format, const Args & ... args) {
|
||||
vreport_error(format, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
report_error("file not found: {}", path);
|
||||
|
||||
Note that ``vreport_error`` is not parameterized on argument types which can
|
||||
improve compile times and reduce code size compared to fully parameterized
|
||||
improve compile times and reduce code size compared to a fully parameterized
|
||||
version.
|
||||
|
||||
Projects using this library
|
||||
---------------------------
|
||||
|
||||
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time
|
||||
strategy game
|
||||
|
||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||
An open-source library for mathematical programming
|
||||
|
||||
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
|
||||
operations suite
|
||||
|
||||
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
|
||||
|
||||
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
|
||||
|
||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||
vehicle
|
||||
|
||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||
Player vs Player Gaming Network with tweaks
|
||||
|
||||
* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine
|
||||
|
||||
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
|
||||
|
||||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
|
||||
|
||||
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
||||
|
||||
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
||||
for nonlinear dynamical systems (MIT)
|
||||
|
||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||
(Lyft)
|
||||
|
||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||
|
||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
||||
generate randomized datasets
|
||||
|
||||
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization
|
||||
framework
|
||||
|
||||
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
|
||||
An MMO server, compatible with most Ultima Online clients
|
||||
|
||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||
associative database
|
||||
|
||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
||||
|
||||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
||||
proxy
|
||||
|
||||
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
||||
library
|
||||
|
||||
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
|
||||
Small crossplatform 2D graphic engine
|
||||
|
||||
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||
Business intelligence software
|
||||
|
||||
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
|
||||
that can handle 1 million transactions per second on a single server
|
||||
|
||||
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
|
||||
framework for high-performance server applications on modern hardware
|
||||
|
||||
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
|
||||
|
||||
* `Stellar <https://www.stellar.org/>`_: Financial platform
|
||||
|
||||
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
|
||||
|
||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
||||
MMORPG framework
|
||||
|
||||
`More... <https://github.com/search?q=cppformat&type=Code>`_
|
||||
|
||||
If you are aware of other projects using this library, please let me know
|
||||
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
|
||||
`issue <https://github.com/fmtlib/fmt/issues>`_.
|
||||
|
||||
Motivation
|
||||
----------
|
||||
|
||||
So why yet another formatting library?
|
||||
|
||||
There are plenty of methods for doing this task, from standard ones like
|
||||
the printf family of function and IOStreams to Boost Format library and
|
||||
FastFormat. The reason for creating a new library is that every existing
|
||||
solution that I found either had serious issues or didn't provide
|
||||
all the features I needed.
|
||||
|
||||
Printf
|
||||
~~~~~~
|
||||
|
||||
The good thing about printf is that it is pretty fast and readily available
|
||||
being a part of the C standard library. The main drawback is that it
|
||||
doesn't support user-defined types. Printf also has safety issues although
|
||||
they are mostly solved with `__attribute__ ((format (printf, ...))
|
||||
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
|
||||
There is a POSIX extension that adds positional arguments required for
|
||||
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
|
||||
to printf but it is not a part of C99 and may not be available on some
|
||||
platforms.
|
||||
|
||||
IOStreams
|
||||
~~~~~~~~~
|
||||
|
||||
The main issue with IOStreams is best illustrated with an example:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
|
||||
|
||||
which is a lot of typing compared to printf:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
printf("%.2f\n", 1.23456);
|
||||
|
||||
Matthew Wilson, the author of FastFormat, referred to this situation with
|
||||
IOStreams as "chevron hell". IOStreams doesn't support positional arguments
|
||||
by design.
|
||||
|
||||
The good part is that IOStreams supports user-defined types and is safe
|
||||
although error reporting is awkward.
|
||||
|
||||
Boost Format library
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is a very powerful library which supports both printf-like format
|
||||
strings and positional arguments. Its main drawback is performance.
|
||||
According to various benchmarks it is much slower than other methods
|
||||
considered here. Boost Format also has excessive build times and severe
|
||||
code bloat issues (see `Benchmarks`_).
|
||||
|
||||
FastFormat
|
||||
~~~~~~~~~~
|
||||
|
||||
This is an interesting library which is fast, safe and has positional
|
||||
arguments. However it has significant limitations, citing its author:
|
||||
|
||||
Three features that have no hope of being accommodated within the
|
||||
current design are:
|
||||
|
||||
* Leading zeros (or any other non-space padding)
|
||||
* Octal/hexadecimal encoding
|
||||
* Runtime width/alignment specification
|
||||
|
||||
It is also quite big and has a heavy dependency, STLSoft, which might be
|
||||
too restrictive for using it in some projects.
|
||||
|
||||
Loki SafeFormat
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
SafeFormat is a formatting library which uses printf-like format strings
|
||||
and is type safe. It doesn't support user-defined types or positional
|
||||
arguments. It makes unconventional use of ``operator()`` for passing
|
||||
format arguments.
|
||||
|
||||
Tinyformat
|
||||
~~~~~~~~~~
|
||||
|
||||
This library supports printf-like format strings and is very small and
|
||||
fast. Unfortunately it doesn't support positional arguments and wrapping
|
||||
it in C++98 is somewhat difficult. Also its performance and code compactness
|
||||
are limited by IOStreams.
|
||||
|
||||
Boost Spirit.Karma
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is not really a formatting library but I decided to include it here
|
||||
for completeness. As IOStreams it suffers from the problem of mixing
|
||||
verbatim text with arguments. The library is pretty fast, but slower
|
||||
on integer formatting than ``fmt::Writer`` on Karma's own benchmark,
|
||||
see `Fast integer to string conversion in C++
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
Speed tests
|
||||
~~~~~~~~~~~
|
||||
|
||||
The following speed tests results were generated by building
|
||||
``tinyformat_test.cpp`` on Ubuntu GNU/Linux 14.04.1 with
|
||||
``g++-4.8.2 -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of three
|
||||
runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` or
|
||||
equivalent is filled 2000000 times with output sent to ``/dev/null``; for
|
||||
further details see the `source
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
||||
|
||||
================= ============= ===========
|
||||
Library Method Run Time, s
|
||||
================= ============= ===========
|
||||
libc printf 1.35
|
||||
libc++ std::ostream 3.42
|
||||
fmt 534bff7 fmt::print 1.56
|
||||
tinyformat 2.0.1 tfm::printf 3.73
|
||||
Boost Format 1.54 boost::format 8.44
|
||||
Folly Format folly::format 2.54
|
||||
libc printf 1.04
|
||||
libc++ std::ostream 3.05
|
||||
{fmt} 6.1.1 fmt::print 0.75
|
||||
Boost Format 1.67 boost::format 7.24
|
||||
Folly Format folly::format 2.23
|
||||
================= ============= ===========
|
||||
|
||||
As you can see ``boost::format`` is much slower than the alternative methods; this
|
||||
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
|
||||
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat
|
||||
cannot be faster than the IOStreams because it uses them internally.
|
||||
Performance of fmt is close to that of printf, being `faster than printf on integer
|
||||
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_,
|
||||
but slower on floating-point formatting which dominates this benchmark.
|
||||
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
|
||||
|
||||
The above results were generated by building ``tinyformat_test.cpp`` on macOS
|
||||
10.14.6 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
|
||||
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
|
||||
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||
further details refer to the `source
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
||||
|
||||
{fmt} is 10x faster than ``std::ostringstream`` and ``sprintf`` on floating-point
|
||||
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
|
||||
:target: https://fmt.dev/unknown_mac64_clang10.0.html
|
||||
|
||||
Compile time and code bloat
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -390,15 +206,14 @@ Method Compile Time, s Executable size, KiB Stripped size, KiB
|
|||
============= =============== ==================== ==================
|
||||
printf 2.6 29 26
|
||||
printf+string 16.4 29 26
|
||||
IOStreams 31.1 59 55
|
||||
fmt 19.0 37 34
|
||||
tinyformat 44.0 103 97
|
||||
iostreams 31.1 59 55
|
||||
{fmt} 19.0 37 34
|
||||
Boost Format 91.9 226 203
|
||||
Folly Format 115.7 101 88
|
||||
============= =============== ==================== ==================
|
||||
|
||||
As you can see, fmt has 60% less overhead in terms of resulting binary code
|
||||
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
|
||||
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
|
||||
size compared to iostreams and comes pretty close to ``printf``. Boost Format
|
||||
and Folly Format have the largest overheads.
|
||||
|
||||
``printf+string`` is the same as ``printf`` but with extra ``<string>``
|
||||
|
@ -411,17 +226,15 @@ Method Compile Time, s Executable size, KiB Stripped size, KiB
|
|||
============= =============== ==================== ==================
|
||||
printf 2.2 33 30
|
||||
printf+string 16.0 33 30
|
||||
IOStreams 28.3 56 52
|
||||
fmt 18.2 59 50
|
||||
tinyformat 32.6 88 82
|
||||
iostreams 28.3 56 52
|
||||
{fmt} 18.2 59 50
|
||||
Boost Format 54.1 365 303
|
||||
Folly Format 79.9 445 430
|
||||
============= =============== ==================== ==================
|
||||
|
||||
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared
|
||||
libraries to compare formatting function overhead only. Boost Format
|
||||
and tinyformat are header-only libraries so they don't provide any
|
||||
linkage options.
|
||||
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
|
||||
compare formatting function overhead only. Boost Format is a
|
||||
header-only library so it doesn't provide any linkage options.
|
||||
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
@ -429,7 +242,7 @@ Running the tests
|
|||
Please refer to `Building the library`__ for the instructions on how to build
|
||||
the library and run the unit tests.
|
||||
|
||||
__ http://fmtlib.net/latest/usage.html#building-the-library
|
||||
__ https://fmt.dev/latest/usage.html#building-the-library
|
||||
|
||||
Benchmarks reside in a separate repository,
|
||||
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
|
||||
|
@ -448,6 +261,175 @@ or the bloat test::
|
|||
|
||||
$ make bloat-test
|
||||
|
||||
Projects using this library
|
||||
---------------------------
|
||||
|
||||
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform real-time
|
||||
strategy game
|
||||
|
||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||
An open-source library for mathematical programming
|
||||
|
||||
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
|
||||
operations suite
|
||||
|
||||
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
|
||||
|
||||
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
|
||||
|
||||
* `ccache <https://ccache.dev/>`_: A compiler cache
|
||||
|
||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||
vehicle
|
||||
|
||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||
Player vs Player Gaming Network with tweaks
|
||||
|
||||
* `KBEngine <https://kbengine.org/>`_: An open-source MMOG server engine
|
||||
|
||||
* `Keypirinha <https://keypirinha.com/>`_: A semantic launcher for Windows
|
||||
|
||||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
|
||||
|
||||
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
||||
|
||||
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
||||
for nonlinear dynamical systems (MIT)
|
||||
|
||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||
(Lyft)
|
||||
|
||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||
|
||||
* `MongoDB <https://mongodb.com/>`_: Distributed document database
|
||||
|
||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
||||
generate randomized datasets
|
||||
|
||||
* `OpenSpace <https://openspaceproject.com/>`_: An open-source astrovisualization
|
||||
framework
|
||||
|
||||
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
||||
An MMO server, compatible with most Ultima Online clients
|
||||
|
||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||
associative database
|
||||
|
||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
||||
|
||||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
||||
proxy
|
||||
|
||||
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
||||
library
|
||||
|
||||
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
|
||||
Small crossplatform 2D graphic engine
|
||||
|
||||
* `Salesforce Analytics Cloud <https://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||
Business intelligence software
|
||||
|
||||
* `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
|
||||
that can handle 1 million transactions per second on a single server
|
||||
|
||||
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
|
||||
framework for high-performance server applications on modern hardware
|
||||
|
||||
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
|
||||
|
||||
* `Stellar <https://www.stellar.org/>`_: Financial platform
|
||||
|
||||
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
|
||||
|
||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
||||
MMORPG framework
|
||||
|
||||
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
||||
|
||||
If you are aware of other projects using this library, please let me know
|
||||
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
|
||||
`issue <https://github.com/fmtlib/fmt/issues>`_.
|
||||
|
||||
Motivation
|
||||
----------
|
||||
|
||||
So why yet another formatting library?
|
||||
|
||||
There are plenty of methods for doing this task, from standard ones like
|
||||
the printf family of function and iostreams to Boost Format and FastFormat
|
||||
libraries. The reason for creating a new library is that every existing
|
||||
solution that I found either had serious issues or didn't provide
|
||||
all the features I needed.
|
||||
|
||||
printf
|
||||
~~~~~~
|
||||
|
||||
The good thing about ``printf`` is that it is pretty fast and readily available
|
||||
being a part of the C standard library. The main drawback is that it
|
||||
doesn't support user-defined types. ``printf`` also has safety issues although
|
||||
they are somewhat mitigated with `__attribute__ ((format (printf, ...))
|
||||
<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
|
||||
There is a POSIX extension that adds positional arguments required for
|
||||
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
|
||||
to ``printf`` but it is not a part of C99 and may not be available on some
|
||||
platforms.
|
||||
|
||||
iostreams
|
||||
~~~~~~~~~
|
||||
|
||||
The main issue with iostreams is best illustrated with an example:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
|
||||
|
||||
which is a lot of typing compared to printf:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
printf("%.2f\n", 1.23456);
|
||||
|
||||
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
|
||||
don't support positional arguments by design.
|
||||
|
||||
The good part is that iostreams support user-defined types and are safe although
|
||||
error handling is awkward.
|
||||
|
||||
Boost Format
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This is a very powerful library which supports both ``printf``-like format
|
||||
strings and positional arguments. Its main drawback is performance. According to
|
||||
various benchmarks it is much slower than other methods considered here. Boost
|
||||
Format also has excessive build times and severe code bloat issues (see
|
||||
`Benchmarks`_).
|
||||
|
||||
FastFormat
|
||||
~~~~~~~~~~
|
||||
|
||||
This is an interesting library which is fast, safe and has positional
|
||||
arguments. However it has significant limitations, citing its author:
|
||||
|
||||
Three features that have no hope of being accommodated within the
|
||||
current design are:
|
||||
|
||||
* Leading zeros (or any other non-space padding)
|
||||
* Octal/hexadecimal encoding
|
||||
* Runtime width/alignment specification
|
||||
|
||||
It is also quite big and has a heavy dependency, STLSoft, which might be
|
||||
too restrictive for using it in some projects.
|
||||
|
||||
Boost Spirit.Karma
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is not really a formatting library but I decided to include it here for
|
||||
completeness. As iostreams, it suffers from the problem of mixing verbatim text
|
||||
with arguments. The library is pretty fast, but slower on integer formatting
|
||||
than ``fmt::format_int`` on Karma's own benchmark,
|
||||
see `Fast integer to string conversion in C++
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
|
@ -474,11 +456,11 @@ A: use ``std::tuple``:
|
|||
License
|
||||
-------
|
||||
|
||||
fmt is distributed under the BSD `license
|
||||
{fmt} is distributed under the MIT `license
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
|
||||
|
||||
The `Format String Syntax
|
||||
<http://fmtlib.net/latest/syntax.html>`_
|
||||
<https://fmt.dev/latest/syntax.html>`_
|
||||
section in the documentation is based on the one from Python `string module
|
||||
documentation <https://docs.python.org/3/library/string.html#module-string>`_
|
||||
adapted for the current library. For this reason the documentation is
|
||||
|
@ -490,7 +472,7 @@ It only applies if you distribute the documentation of fmt.
|
|||
Acknowledgments
|
||||
---------------
|
||||
|
||||
The fmt library is maintained by Victor Zverovich (`vitaut
|
||||
The {fmt} library is maintained by Victor Zverovich (`vitaut
|
||||
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||
<https://github.com/foonathan>`_) with contributions from many other people.
|
||||
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
|
||||
|
@ -504,10 +486,10 @@ written by Chris Foster. Boost Format library is acknowledged transitively
|
|||
since it had some influence on tinyformat.
|
||||
Some ideas used in the implementation are borrowed from `Loki
|
||||
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
|
||||
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
|
||||
`Clang <http://clang.llvm.org/>`_.
|
||||
<https://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
|
||||
`Clang <https://clang.llvm.org/>`_.
|
||||
Format string syntax and the documentation are based on Python's `str.format
|
||||
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
|
||||
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
|
||||
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
|
||||
comments and contribution to the design of the type-safe API and
|
||||
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
|
||||
|
|
2
externals/fmt/doc/CMakeLists.txt
vendored
2
externals/fmt/doc/CMakeLists.txt
vendored
|
@ -6,7 +6,7 @@ endif ()
|
|||
|
||||
add_custom_target(doc
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
|
||||
SOURCES api.rst syntax.rst build.py conf.py _templates/layout.html)
|
||||
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
||||
DESTINATION share/doc/fmt OPTIONAL)
|
||||
|
|
4
externals/fmt/doc/_templates/layout.html
vendored
4
externals/fmt/doc/_templates/layout.html
vendored
|
@ -58,7 +58,7 @@
|
|||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% for v in versions.split(',') %}
|
||||
<li><a href="http://fmtlib.net/{{v}}">{{v}}</a></li>
|
||||
<li><a href="https://fmt.dev/{{v}}">{{v}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -84,7 +84,7 @@
|
|||
<div class="jumbotron">
|
||||
<div class="tb-container">
|
||||
<h1>{fmt}</h1>
|
||||
<p class="lead">Small, safe and fast formatting library</p>
|
||||
<p class="lead">A modern formatting library</p>
|
||||
<div class="btn-group" role="group">
|
||||
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
|
||||
<a class="btn btn-success"
|
||||
|
|
187
externals/fmt/doc/api.rst
vendored
187
externals/fmt/doc/api.rst
vendored
|
@ -10,12 +10,14 @@ The {fmt} library API consists of the following parts:
|
|||
facilities and a lightweight subset of formatting functions
|
||||
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
|
||||
format string checks, output iterator and user-defined type support
|
||||
* :ref:`fmt/time.h <time-api>`: date and time formatting
|
||||
* :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/ostream.h <ostream-api>`: ``std::ostream`` support
|
||||
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
|
||||
|
||||
All functions and types provided by the library reside in namespace ``fmt`` and
|
||||
macros have prefix ``FMT_`` or ``fmt``.
|
||||
macros have prefix ``FMT_``.
|
||||
|
||||
.. _core-api:
|
||||
|
||||
|
@ -23,7 +25,8 @@ Core API
|
|||
========
|
||||
|
||||
``fmt/core.h`` defines the core API which provides argument handling facilities
|
||||
and a lightweight subset of formatting functions.
|
||||
and a lightweight subset of formatting functions. In the header-only mode
|
||||
include ``fmt/format.h`` instead of ``fmt/core.h``.
|
||||
|
||||
The following functions use :ref:`format string syntax <syntax>`
|
||||
similar to that of Python's `str.format
|
||||
|
@ -39,24 +42,25 @@ participate in an overload resolution if the latter is not a string.
|
|||
|
||||
.. _format:
|
||||
|
||||
.. doxygenfunction:: format(const S&, const Args&...)
|
||||
.. doxygenfunction:: vformat(const S&, basic_format_args<typename buffer_context<Char>::type>)
|
||||
.. doxygenfunction:: format(const S&, Args&&...)
|
||||
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>)
|
||||
|
||||
.. _print:
|
||||
|
||||
.. doxygenfunction:: print(const S&, const Args&...)
|
||||
.. doxygenfunction:: print(const S&, Args&&...)
|
||||
.. doxygenfunction:: vprint(string_view, format_args)
|
||||
|
||||
.. doxygenfunction:: print(std::FILE *, const S&, const Args&...)
|
||||
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
|
||||
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
|
||||
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
|
||||
|
||||
Named arguments
|
||||
Named Arguments
|
||||
---------------
|
||||
|
||||
.. doxygenfunction:: fmt::arg(string_view, const T&)
|
||||
.. doxygenfunction:: fmt::arg(const S&, const T&)
|
||||
|
||||
Argument lists
|
||||
Named arguments are not supported in compile-time checks at the moment.
|
||||
|
||||
Argument Lists
|
||||
--------------
|
||||
|
||||
.. doxygenfunction:: fmt::make_format_args(const Args&...)
|
||||
|
@ -81,6 +85,19 @@ Compatibility
|
|||
.. doxygentypedef:: fmt::string_view
|
||||
.. doxygentypedef:: fmt::wstring_view
|
||||
|
||||
Locale
|
||||
------
|
||||
|
||||
All formatting is locale-independent by default. Use the ``'n'`` format
|
||||
specifier to insert the appropriate number separator characters from the
|
||||
locale::
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <locale>
|
||||
|
||||
std::locale::global(std::locale("en_US.UTF-8"));
|
||||
auto s = fmt::format("{:n}", 1000000); // s == "1,000,000"
|
||||
|
||||
.. _format-api:
|
||||
|
||||
Format API
|
||||
|
@ -89,12 +106,16 @@ Format API
|
|||
``fmt/format.h`` defines the full format API providing compile-time format
|
||||
string checks, output iterator and user-defined type support.
|
||||
|
||||
Compile-time format string checks
|
||||
Compile-time Format String Checks
|
||||
---------------------------------
|
||||
|
||||
.. doxygendefine:: fmt
|
||||
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.
|
||||
|
||||
Formatting user-defined types
|
||||
.. doxygendefine:: FMT_STRING
|
||||
|
||||
Formatting User-defined Types
|
||||
-----------------------------
|
||||
|
||||
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
|
||||
|
@ -104,32 +125,56 @@ template and implement ``parse`` and ``format`` methods::
|
|||
|
||||
struct point { double x, y; };
|
||||
|
||||
namespace fmt {
|
||||
template <>
|
||||
struct formatter<point> {
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||
struct fmt::formatter<point> {
|
||||
// Presentation format: 'f' - fixed, 'e' - exponential.
|
||||
char presentation = 'f';
|
||||
|
||||
// Parses format specifications of the form ['f' | 'e'].
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
// [ctx.begin(), ctx.end()) is a character range that contains a part of
|
||||
// the format string starting from the format specifications to be parsed,
|
||||
// e.g. in
|
||||
//
|
||||
// fmt::format("{:f} - point of interest", point{1, 2});
|
||||
//
|
||||
// the range will contain "f} - point of interest". The formatter should
|
||||
// parse specifiers until '}' or the end of the range. In this example
|
||||
// the formatter should parse the 'f' specifier and return an iterator
|
||||
// pointing to '}'.
|
||||
|
||||
// Parse the presentation format and store it in the formatter:
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
|
||||
|
||||
// Check if reached the end of the range:
|
||||
if (it != end && *it != '}')
|
||||
throw format_error("invalid format");
|
||||
|
||||
// Return an iterator past the end of the parsed range:
|
||||
return it;
|
||||
}
|
||||
|
||||
// Formats the point p using the parsed format specification (presentation)
|
||||
// stored in this formatter.
|
||||
template <typename FormatContext>
|
||||
auto format(const point &p, FormatContext &ctx) {
|
||||
return format_to(ctx.begin(), "({:.1f}, {:.1f})", p.x, p.y);
|
||||
auto format(const point& p, FormatContext& ctx) {
|
||||
// ctx.out() is an output iterator to write to.
|
||||
return format_to(
|
||||
ctx.out(),
|
||||
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
|
||||
p.x, p.y);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Then you can pass objects of type ``point`` to any formatting function::
|
||||
|
||||
point p = {1, 2};
|
||||
std::string s = fmt::format("{}", p);
|
||||
std::string s = fmt::format("{:f}", p);
|
||||
// s == "(1.0, 2.0)"
|
||||
|
||||
In the example above the ``formatter<point>::parse`` function ignores the
|
||||
contents of the format string referred to by ``ctx.begin()`` so the object will
|
||||
always be formatted in the same way. See ``formatter<tm>::parse`` in
|
||||
:file:`fmt/time.h` for an advanced example of how to parse the format string and
|
||||
customize the formatted output.
|
||||
|
||||
You can also reuse existing formatters, for example::
|
||||
You can also reuse existing formatters via inheritance or composition, for
|
||||
example::
|
||||
|
||||
enum class color {red, green, blue};
|
||||
|
||||
|
@ -137,7 +182,7 @@ You can also reuse existing formatters, for example::
|
|||
struct fmt::formatter<color>: formatter<string_view> {
|
||||
// parse is inherited from formatter<string_view>.
|
||||
template <typename FormatContext>
|
||||
auto format(color c, FormatContext &ctx) {
|
||||
auto format(color c, FormatContext& ctx) {
|
||||
string_view name = "unknown";
|
||||
switch (c) {
|
||||
case color::red: name = "red"; break;
|
||||
|
@ -177,15 +222,14 @@ You can also write a formatter for a hierarchy of classes::
|
|||
fmt::print("{}", a); // prints "B"
|
||||
}
|
||||
|
||||
This section shows how to define a custom format function for a user-defined
|
||||
type. The next section describes how to get ``fmt`` to use a conventional stream
|
||||
output ``operator<<`` when one is defined for a user-defined type.
|
||||
.. doxygenclass:: fmt::basic_format_parse_context
|
||||
:members:
|
||||
|
||||
Output iterator support
|
||||
Output Iterator Support
|
||||
-----------------------
|
||||
|
||||
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, const Args&...)
|
||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...)
|
||||
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
|
||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, Args&&...)
|
||||
.. doxygenstruct:: fmt::format_to_n_result
|
||||
:members:
|
||||
|
||||
|
@ -201,6 +245,8 @@ The following user-defined literals are defined in ``fmt/format.h``.
|
|||
Utilities
|
||||
---------
|
||||
|
||||
.. doxygenstruct:: fmt::is_char
|
||||
|
||||
.. doxygentypedef:: fmt::char_t
|
||||
|
||||
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
|
||||
|
@ -209,13 +255,17 @@ Utilities
|
|||
|
||||
.. doxygenfunction:: fmt::to_wstring(const T&)
|
||||
|
||||
.. doxygenfunction:: fmt::to_string_view(basic_string_view<Char>)
|
||||
.. doxygenfunction:: fmt::to_string_view(const Char *)
|
||||
|
||||
.. doxygenfunction:: fmt::join(const Range&, string_view)
|
||||
|
||||
.. doxygenfunction:: fmt::join(It, It, string_view)
|
||||
|
||||
.. doxygenclass:: fmt::basic_memory_buffer
|
||||
:protected-members:
|
||||
:members:
|
||||
|
||||
System errors
|
||||
System Errors
|
||||
-------------
|
||||
|
||||
fmt does not use ``errno`` to communicate errors to the user, but it may call
|
||||
|
@ -232,7 +282,7 @@ the value of ``errno`` being preserved by library functions.
|
|||
|
||||
.. _formatstrings:
|
||||
|
||||
Custom allocators
|
||||
Custom Allocators
|
||||
-----------------
|
||||
|
||||
The {fmt} library supports custom dynamic memory allocators.
|
||||
|
@ -258,7 +308,7 @@ allocator::
|
|||
template <typename ...Args>
|
||||
inline custom_string format(custom_allocator alloc,
|
||||
fmt::string_view format_str,
|
||||
const Args & ... args) {
|
||||
const Args& ... args) {
|
||||
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
|
@ -267,27 +317,27 @@ arguments, the container that stores pointers to them will be allocated using
|
|||
the default allocator. Also floating-point formatting falls back on ``sprintf``
|
||||
which may do allocations.
|
||||
|
||||
Custom formatting of built-in types
|
||||
Custom Formatting of Built-in Types
|
||||
-----------------------------------
|
||||
|
||||
It is possible to change the way arguments are formatted by providing a
|
||||
custom argument formatter class::
|
||||
|
||||
using arg_formatter =
|
||||
fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>>;
|
||||
using arg_formatter = fmt::arg_formatter<fmt::buffer_range<char>>;
|
||||
|
||||
// A custom argument formatter that formats negative integers as unsigned
|
||||
// with the ``x`` format specifier.
|
||||
class custom_arg_formatter : public arg_formatter {
|
||||
public:
|
||||
custom_arg_formatter(fmt::format_context &ctx,
|
||||
fmt::format_specs *spec = nullptr)
|
||||
: arg_formatter(ctx, spec) {}
|
||||
custom_arg_formatter(fmt::format_context& ctx,
|
||||
fmt::format_parse_context* parse_ctx = nullptr,
|
||||
fmt::format_specs* spec = nullptr)
|
||||
: arg_formatter(ctx, parse_ctx, spec) {}
|
||||
|
||||
using arg_formatter::operator();
|
||||
|
||||
auto operator()(int value) {
|
||||
if (spec().type() == 'x')
|
||||
if (specs() && specs()->type == 'x')
|
||||
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
|
||||
return arg_formatter::operator()(value);
|
||||
}
|
||||
|
@ -302,7 +352,7 @@ custom argument formatter class::
|
|||
|
||||
template <typename ...Args>
|
||||
inline std::string custom_format(
|
||||
fmt::string_view format_str, const Args &... args) {
|
||||
fmt::string_view format_str, const Args&... args) {
|
||||
return custom_vformat(format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
|
@ -311,16 +361,41 @@ custom argument formatter class::
|
|||
.. doxygenclass:: fmt::arg_formatter
|
||||
:members:
|
||||
|
||||
.. _time-api:
|
||||
.. _ranges-api:
|
||||
|
||||
Date and time formatting
|
||||
Ranges and Tuple Formatting
|
||||
===========================
|
||||
|
||||
The library also supports convenient formatting of ranges and tuples::
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
std::tuple<char, int, float> t{'a', 1, 2.0f};
|
||||
// Prints "('a', 1, 2.0)"
|
||||
fmt::print("{}", t);
|
||||
|
||||
|
||||
NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main
|
||||
``format.h`` header, but expect this to change in the future.
|
||||
|
||||
Using ``fmt::join``, you can separate tuple elements with a custom separator::
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
std::tuple<int, char> t = {1, 'a'};
|
||||
// Prints "1, a"
|
||||
fmt::print("{}", fmt::join(t, ", "));
|
||||
|
||||
.. _chrono-api:
|
||||
|
||||
Date and Time Formatting
|
||||
========================
|
||||
|
||||
The library supports `strftime
|
||||
<http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time
|
||||
formatting::
|
||||
|
||||
#include <fmt/time.h>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
std::time_t t = std::time(nullptr);
|
||||
// Prints "The date is 2016-04-29." (with the current date)
|
||||
|
@ -331,7 +406,7 @@ The format string syntax is described in the documentation of
|
|||
|
||||
.. _ostream-api:
|
||||
|
||||
``std::ostream`` support
|
||||
``std::ostream`` Support
|
||||
========================
|
||||
|
||||
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
|
||||
|
@ -344,7 +419,7 @@ user-defined types that have overloaded ``operator<<``::
|
|||
public:
|
||||
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const date &d) {
|
||||
friend std::ostream& operator<<(std::ostream& os, const date& d) {
|
||||
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
|
||||
}
|
||||
};
|
||||
|
@ -352,11 +427,11 @@ user-defined types that have overloaded ``operator<<``::
|
|||
std::string s = fmt::format("The date is {}", date(2012, 12, 9));
|
||||
// s == "The date is 2012-12-9"
|
||||
|
||||
.. doxygenfunction:: print(std::basic_ostream<fmt::char_t<S>>&, const S&, const Args&...)
|
||||
.. doxygenfunction:: print(std::basic_ostream<Char>&, const S&, Args&&...)
|
||||
|
||||
.. _printf-api:
|
||||
|
||||
``printf`` formatting
|
||||
``printf`` Formatting
|
||||
=====================
|
||||
|
||||
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
|
||||
|
@ -370,6 +445,6 @@ argument type doesn't match its format specification.
|
|||
|
||||
.. doxygenfunction:: fprintf(std::FILE *, const S&, const Args&...)
|
||||
|
||||
.. doxygenfunction:: fprintf(std::basic_ostream<fmt::char_t<S>>&, const S&, const Args&...)
|
||||
.. doxygenfunction:: fprintf(std::basic_ostream<Char>&, const S&, const Args&...)
|
||||
|
||||
.. doxygenfunction:: sprintf(const S&, const Args&...)
|
||||
|
|
6
externals/fmt/doc/build.py
vendored
6
externals/fmt/doc/build.py
vendored
|
@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
|
|||
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0']
|
||||
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']
|
||||
|
||||
def pip_install(package, commit=None, **kwargs):
|
||||
"Install package using pip."
|
||||
|
@ -74,7 +74,7 @@ def build_docs(version='dev', **kwargs):
|
|||
GENERATE_MAN = NO
|
||||
GENERATE_RTF = NO
|
||||
CASE_SENSE_NAMES = NO
|
||||
INPUT = {0}/core.h {0}/format.h {0}/ostream.h \
|
||||
INPUT = {0}/core.h {0}/format.h {0}/os.h {0}/ostream.h \
|
||||
{0}/printf.h {0}/time.h
|
||||
QUIET = YES
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
|
@ -94,7 +94,7 @@ def build_docs(version='dev', **kwargs):
|
|||
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
||||
"FMT_END_NAMESPACE=}}" \
|
||||
"FMT_STRING_ALIAS=1" \
|
||||
"FMT_ENABLE_IF_T(B, T)=T"
|
||||
"FMT_ENABLE_IF(B)="
|
||||
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
|
||||
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
|
||||
if p.returncode != 0:
|
||||
|
|
54
externals/fmt/doc/index.rst
vendored
54
externals/fmt/doc/index.rst
vendored
|
@ -1,17 +1,18 @@
|
|||
Overview
|
||||
========
|
||||
|
||||
**fmt** (formerly cppformat) is an open-source formatting library.
|
||||
It can be used as a fast and safe alternative to printf and IOStreams.
|
||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||
alternative to C stdio and C++ iostreams.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">What users say:</div>
|
||||
<div class="panel-body">
|
||||
Thanks for creating this library. It’s been a hole in C++ for a long
|
||||
time. I’ve used both boost::format and loki::SPrintf, and neither felt
|
||||
like the right answer. This does.
|
||||
Thanks for creating this library. It’s been a hole in C++ for
|
||||
a long time. I’ve used both <code>boost::format</code> and
|
||||
<code>loki::SPrintf</code>, and neither felt like the right answer.
|
||||
This does.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -20,12 +21,13 @@ It can be used as a fast and safe alternative to printf and IOStreams.
|
|||
Format API
|
||||
----------
|
||||
|
||||
The replacement-based Format API provides a safe alternative to ``printf``,
|
||||
``sprintf`` and friends with comparable or `better performance
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||
The format API is similar in spirit to the C ``printf`` family of function but
|
||||
is safer, simpler and serveral times `faster
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
|
||||
than common standard library implementations.
|
||||
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 Python:
|
||||
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
|
||||
Python:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -60,7 +62,7 @@ The Format API also supports positional arguments useful for localization:
|
|||
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
|
||||
what goes where when multiple values are being inserted:
|
||||
what goes where when multiple arguments are being formatted:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -72,21 +74,10 @@ an alternative, slightly terser syntax for named arguments:
|
|||
|
||||
.. code:: c++
|
||||
|
||||
using namespace fmt::literals;
|
||||
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
|
||||
"name"_a="World", "number"_a=42);
|
||||
|
||||
The ``_format`` suffix may be used to format string literals similar to Python:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string message = "{0}{1}{0}"_format("abra", "cad");
|
||||
|
||||
Other than the placement of the format string on the left of the operator,
|
||||
``_format`` is functionally identical to ``fmt::format``. In order to use the
|
||||
literal operators, they must be made visible with the directive
|
||||
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
|
||||
``_format`` but nothing else from the ``fmt`` namespace.
|
||||
|
||||
.. _safety:
|
||||
|
||||
Safety
|
||||
|
@ -106,10 +97,10 @@ string", because the argument ``"forty-two"`` is a string while the format code
|
|||
|
||||
.. code:: c++
|
||||
|
||||
format(fmt("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
|
||||
relaxed ``constexpr``.
|
||||
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
|
||||
|
||||
The following code
|
||||
|
||||
|
@ -129,7 +120,7 @@ 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
|
||||
|
@ -174,15 +165,16 @@ The library is highly portable and relies only on a small set of C++11 features:
|
|||
* decltype
|
||||
* trailing return types
|
||||
* deleted functions
|
||||
* alias templates
|
||||
|
||||
These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older
|
||||
compilers use fmt `version 4.x
|
||||
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
|
||||
compilers use {fmt} `version 4.x
|
||||
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
|
||||
maintained and only requires C++98.
|
||||
|
||||
The output of all formatting functions is consistent across platforms. In
|
||||
particular, formatting a floating-point infinity always gives ``inf`` while the
|
||||
output of ``printf`` is platform-dependent in this case. For example,
|
||||
output of ``printf`` is platform-dependent. For example,
|
||||
|
||||
.. code::
|
||||
|
||||
|
@ -195,9 +187,9 @@ always prints ``inf``.
|
|||
Ease of Use
|
||||
-----------
|
||||
|
||||
fmt has a small self-contained code base with the core library consisting of
|
||||
{fmt} has a small self-contained code base with the core library consisting of
|
||||
just three header files and no external dependencies.
|
||||
A permissive BSD `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.
|
||||
|
||||
.. raw:: html
|
||||
|
|
133
externals/fmt/doc/syntax.rst
vendored
133
externals/fmt/doc/syntax.rst
vendored
|
@ -76,19 +76,19 @@ The general form of a *standard format specifier* is:
|
|||
|
||||
.. productionlist:: sf
|
||||
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
|
||||
fill: <a character other than '{', '}' or '\0'>
|
||||
align: "<" | ">" | "=" | "^"
|
||||
fill: <a character other than '{' or '}'>
|
||||
align: "<" | ">" | "^"
|
||||
sign: "+" | "-" | " "
|
||||
width: `integer` | "{" `arg_id` "}"
|
||||
precision: `integer` | "{" `arg_id` "}"
|
||||
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
|
||||
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
|
||||
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
|
||||
int_type: "b" | "B" | "d" | "o" | "x" | "X"
|
||||
|
||||
The *fill* character can be any character other than '{', '}' or '\\0'. The
|
||||
presence of a fill character is signaled by the character following it, which
|
||||
must be one of the alignment options. If the second character of *format_spec*
|
||||
is not a valid alignment option, then it is assumed that both the fill character
|
||||
and the alignment option are absent.
|
||||
The *fill* character can be any Unicode code point other than ``'{'`` or
|
||||
``'}'``. The presence of a fill character is signaled by the character following
|
||||
it, which must be one of the alignment options. If the second character of
|
||||
*format_spec* is not a valid alignment option, then it is assumed that both the
|
||||
fill character and the alignment option are absent.
|
||||
|
||||
The meaning of the various alignment options is as follows:
|
||||
|
||||
|
@ -101,11 +101,6 @@ The meaning of the various alignment options is as follows:
|
|||
| ``'>'`` | Forces the field to be right-aligned within the |
|
||||
| | available space (this is the default for numbers). |
|
||||
+---------+----------------------------------------------------------+
|
||||
| ``'='`` | Forces the padding to be placed after the sign (if any) |
|
||||
| | but before the digits. This is used for printing fields |
|
||||
| | in the form '+000000120'. This alignment option is only |
|
||||
| | valid for numeric types. |
|
||||
+---------+----------------------------------------------------------+
|
||||
| ``'^'`` | Forces the field to be centered within the available |
|
||||
| | space. |
|
||||
+---------+----------------------------------------------------------+
|
||||
|
@ -148,15 +143,17 @@ conversions, trailing zeros are not removed from the result.
|
|||
.. ifconfig:: False
|
||||
|
||||
The ``','`` option signals the use of a comma for a thousands separator.
|
||||
For a locale aware separator, use the ``'n'`` integer presentation type
|
||||
For a locale aware separator, use the ``'L'`` integer presentation type
|
||||
instead.
|
||||
|
||||
*width* is a decimal integer defining the minimum field width. If not
|
||||
specified, then the field width will be determined by the content.
|
||||
|
||||
Preceding the *width* field by a zero (``'0'``) character enables
|
||||
sign-aware zero-padding for numeric types. This is equivalent to a *fill*
|
||||
character of ``'0'`` with an *alignment* type of ``'='``.
|
||||
Preceding the *width* field by a zero (``'0'``) character enables sign-aware
|
||||
zero-padding for numeric types. It forces the padding to be placed after the
|
||||
sign or base (if any) but before the digits. This is used for printing fields in
|
||||
the form '+000000120'. This option is only valid for numeric types and it has no
|
||||
effect on formatting of infinity and NaN.
|
||||
|
||||
The *precision* is a decimal number indicating how many digits should be
|
||||
displayed after the decimal point for a floating-point value formatted with
|
||||
|
@ -217,9 +214,9 @@ The available integer presentation types are:
|
|||
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
|
||||
| | to the output value. |
|
||||
+---------+----------------------------------------------------------+
|
||||
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
|
||||
| | the current locale setting to insert the appropriate |
|
||||
| | number separator characters. |
|
||||
| ``'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'``. |
|
||||
+---------+----------------------------------------------------------+
|
||||
|
@ -244,7 +241,7 @@ The available presentation types for floating-point values are:
|
|||
| | notation using the letter 'e' to indicate the exponent. |
|
||||
+---------+----------------------------------------------------------+
|
||||
| ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an |
|
||||
| | upper-case 'E' as the separator character. |
|
||||
| | upper-case ``'E'`` as the separator character. |
|
||||
+---------+----------------------------------------------------------+
|
||||
| ``'f'`` | Fixed point. Displays the number as a fixed-point |
|
||||
| | number. |
|
||||
|
@ -264,10 +261,15 @@ The available presentation types for floating-point values are:
|
|||
| | ``'E'`` if the number gets too large. The |
|
||||
| | representations of infinity and NaN are uppercased, too. |
|
||||
+---------+----------------------------------------------------------+
|
||||
| none | The same as ``'g'``. |
|
||||
| ``'L'`` | Locale-specific format. This is the same as ``'g'``, |
|
||||
| | except that it uses the current locale setting to insert |
|
||||
| | 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. |
|
||||
+---------+----------------------------------------------------------+
|
||||
|
||||
Floating-point formatting is locale-dependent.
|
||||
|
||||
.. ifconfig:: False
|
||||
|
||||
|
@ -303,7 +305,7 @@ The available presentation types for pointers are:
|
|||
|
||||
.. _formatexamples:
|
||||
|
||||
Format examples
|
||||
Format Examples
|
||||
===============
|
||||
|
||||
This section contains examples of the format syntax and comparison with
|
||||
|
@ -318,72 +320,94 @@ following examples.
|
|||
|
||||
Accessing arguments by position::
|
||||
|
||||
format("{0}, {1}, {2}", 'a', 'b', 'c');
|
||||
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
|
||||
// Result: "a, b, c"
|
||||
format("{}, {}, {}", 'a', 'b', 'c');
|
||||
fmt::format("{}, {}, {}", 'a', 'b', 'c');
|
||||
// Result: "a, b, c"
|
||||
format("{2}, {1}, {0}", 'a', 'b', 'c');
|
||||
fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
|
||||
// Result: "c, b, a"
|
||||
format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
|
||||
fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
|
||||
// Result: "abracadabra"
|
||||
|
||||
Aligning the text and specifying a width::
|
||||
|
||||
format("{:<30}", "left aligned");
|
||||
fmt::format("{:<30}", "left aligned");
|
||||
// Result: "left aligned "
|
||||
format("{:>30}", "right aligned");
|
||||
fmt::format("{:>30}", "right aligned");
|
||||
// Result: " right aligned"
|
||||
format("{:^30}", "centered");
|
||||
fmt::format("{:^30}", "centered");
|
||||
// Result: " centered "
|
||||
format("{:*^30}", "centered"); // use '*' as a fill char
|
||||
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
|
||||
// Result: "***********centered***********"
|
||||
|
||||
Dynamic width::
|
||||
|
||||
format("{:<{}}", "left aligned", 30);
|
||||
fmt::format("{:<{}}", "left aligned", 30);
|
||||
// Result: "left aligned "
|
||||
|
||||
Dynamic precision::
|
||||
|
||||
format("{:.{}f}", 3.14, 1);
|
||||
fmt::format("{:.{}f}", 3.14, 1);
|
||||
// Result: "3.1"
|
||||
|
||||
Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::
|
||||
|
||||
format("{:+f}; {:+f}", 3.14, -3.14); // show it always
|
||||
fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
|
||||
// Result: "+3.140000; -3.140000"
|
||||
format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
|
||||
fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
|
||||
// Result: " 3.140000; -3.140000"
|
||||
format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
|
||||
fmt::format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
|
||||
// Result: "3.140000; -3.140000"
|
||||
|
||||
Replacing ``%x`` and ``%o`` and converting the value to different bases::
|
||||
|
||||
format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
|
||||
// with 0x or 0 or 0b as prefix:
|
||||
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
|
||||
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
|
||||
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
|
||||
|
||||
Padded hex byte with prefix and always prints both hex characters::
|
||||
|
||||
format("{:#04x}", 0);
|
||||
fmt::format("{:#04x}", 0);
|
||||
// Result: "0x00"
|
||||
|
||||
Box drawing using Unicode fill::
|
||||
|
||||
fmt::print(
|
||||
"┌{0:─^{2}}┐\n"
|
||||
"│{1: ^{2}}│\n"
|
||||
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
|
||||
|
||||
prints::
|
||||
|
||||
┌────────────────────┐
|
||||
│ Hello, world! │
|
||||
└────────────────────┘
|
||||
|
||||
Using type-specific formatting::
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
auto t = tm();
|
||||
t.tm_year = 2010 - 1900;
|
||||
t.tm_mon = 6;
|
||||
t.tm_mday = 4;
|
||||
t.tm_hour = 12;
|
||||
t.tm_min = 15;
|
||||
t.tm_sec = 58;
|
||||
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
|
||||
// Prints: 2010-08-04 12:15:58
|
||||
|
||||
Using the comma as a thousands separator::
|
||||
|
||||
#include <fmt/locale.h>
|
||||
|
||||
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
|
||||
// s == "1,234,567,890"
|
||||
|
||||
.. ifconfig:: False
|
||||
|
||||
Using the comma as a thousands separator::
|
||||
|
||||
format("{:,}", 1234567890);
|
||||
'1,234,567,890'
|
||||
|
||||
Using type-specific formatting::
|
||||
|
||||
>>> import datetime
|
||||
>>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)
|
||||
Format("{:%Y-%m-%d %H:%M:%S}") << d)
|
||||
'2010-07-04 12:15:58'
|
||||
|
||||
Nesting arguments and more complex examples::
|
||||
|
||||
>>> for align, text in zip('<^>', ['left', 'center', 'right']):
|
||||
|
@ -412,4 +436,3 @@ Padded hex byte with prefix and always prints both hex characters::
|
|||
9 9 11 1001
|
||||
10 A 12 1010
|
||||
11 B 13 1011
|
||||
|
||||
|
|
62
externals/fmt/doc/usage.rst
vendored
62
externals/fmt/doc/usage.rst
vendored
|
@ -2,21 +2,15 @@
|
|||
Usage
|
||||
*****
|
||||
|
||||
To use the fmt library, add :file:`format.h` and :file:`format.cc` from
|
||||
a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_
|
||||
or the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
|
||||
To use the {fmt} library, add :file:`fmt/core.h`, :file:`fmt/format.h`,
|
||||
:file:`fmt/format-inl.h`, :file:`src/format.cc` and optionally other headers
|
||||
from a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_ or
|
||||
the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
|
||||
Alternatively, you can :ref:`build the library with CMake <building>`.
|
||||
|
||||
If you are using Visual C++ with precompiled headers, you might need to add
|
||||
the line ::
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
before other includes in :file:`format.cc`.
|
||||
|
||||
.. _building:
|
||||
|
||||
Building the library
|
||||
Building the Library
|
||||
====================
|
||||
|
||||
The included `CMake build script`__ can be used to build the fmt
|
||||
|
@ -31,7 +25,7 @@ workflow starts with::
|
|||
|
||||
mkdir build # Create a directory to hold the build output.
|
||||
cd build
|
||||
cmake <path/to/fmt> # Generate native build scripts.
|
||||
cmake .. # Generate native build scripts.
|
||||
|
||||
where :file:`{<path/to/fmt>}` is a path to the ``fmt`` repository.
|
||||
|
||||
|
@ -58,8 +52,14 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
|
|||
|
||||
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
|
||||
|
||||
Header-only usage with CMake
|
||||
============================
|
||||
Installing the Library
|
||||
======================
|
||||
|
||||
After building the library you can install it on a Unix-like system by running
|
||||
:command:`sudo make install`.
|
||||
|
||||
Usage with CMake
|
||||
================
|
||||
|
||||
You can add the ``fmt`` library directory into your project and include it in
|
||||
your ``CMakeLists.txt`` file::
|
||||
|
@ -79,11 +79,11 @@ You can detect and use an installed version of {fmt} as follows::
|
|||
find_package(fmt)
|
||||
target_link_libraries(<your-target> fmt::fmt)
|
||||
|
||||
Setting up your target to use a header-only version of ``fmt`` is equaly easy::
|
||||
Setting up your target to use a header-only version of ``fmt`` is equally easy::
|
||||
|
||||
target_link_libraries(<your-target> PRIVATE fmt-header-only)
|
||||
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
|
||||
|
||||
Building the documentation
|
||||
Building the Documentation
|
||||
==========================
|
||||
|
||||
To build the documentation you need the following software installed on your
|
||||
|
@ -103,7 +103,33 @@ the previous section. Then compile the ``doc`` target/project, for example::
|
|||
make doc
|
||||
|
||||
This will generate the HTML documentation in ``doc/html``.
|
||||
|
||||
|
||||
Conda
|
||||
=====
|
||||
|
||||
fmt can be installed on Linux, macOS and Windows with
|
||||
`Conda <https://docs.conda.io/en/latest/>`__, using its
|
||||
`conda-forge <https://conda-forge.org>`__
|
||||
`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows::
|
||||
|
||||
conda install -c conda-forge fmt
|
||||
|
||||
Vcpkg
|
||||
=====
|
||||
|
||||
You can download and install fmt using the `vcpkg
|
||||
<https://github.com/Microsoft/vcpkg>`__ dependency manager::
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install fmt
|
||||
|
||||
The fmt port in vcpkg is kept up to date by Microsoft team members and community
|
||||
contributors. If the version is out of date, please `create an issue or pull
|
||||
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
|
||||
|
||||
Android NDK
|
||||
===========
|
||||
|
||||
|
|
973
externals/fmt/include/fmt/chrono.h
vendored
973
externals/fmt/include/fmt/chrono.h
vendored
File diff suppressed because it is too large
Load diff
591
externals/fmt/include/fmt/color.h
vendored
591
externals/fmt/include/fmt/color.h
vendored
|
@ -12,184 +12,149 @@
|
|||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef FMT_DEPRECATED_COLORS
|
||||
|
||||
// color and (v)print_colored are deprecated.
|
||||
enum color { black, red, green, yellow, blue, magenta, cyan, white };
|
||||
FMT_API void vprint_colored(color c, string_view format, format_args args);
|
||||
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
|
||||
template <typename... Args>
|
||||
inline void print_colored(color c, string_view format_str,
|
||||
const Args & ... args) {
|
||||
vprint_colored(c, format_str, make_format_args(args...));
|
||||
}
|
||||
template <typename... Args>
|
||||
inline void print_colored(color c, wstring_view format_str,
|
||||
const Args & ... args) {
|
||||
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
|
||||
}
|
||||
|
||||
inline void vprint_colored(color c, string_view format, format_args args) {
|
||||
char escape[] = "\x1b[30m";
|
||||
escape[3] = static_cast<char>('0' + c);
|
||||
std::fputs(escape, stdout);
|
||||
vprint(format, args);
|
||||
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||
}
|
||||
|
||||
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
|
||||
wchar_t escape[] = L"\x1b[30m";
|
||||
escape[3] = static_cast<wchar_t>('0' + c);
|
||||
std::fputws(escape, stdout);
|
||||
vprint(format, args);
|
||||
std::fputws(internal::data::WRESET_COLOR, stdout);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
|
@ -208,27 +173,26 @@ enum class terminal_color : uint8_t {
|
|||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
}; // enum class terminal_color
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
italic = 1 << 1,
|
||||
underline = 1 << 2,
|
||||
strikethrough = 1 << 3
|
||||
}; // enum class emphasis
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// We use rgb as name because some editors will show it as color direct in the
|
||||
// editor.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
|
||||
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
||||
: r(r_), g(g_), b(b_) {}
|
||||
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
|
||||
FMT_CONSTEXPR_DECL rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||
FMT_CONSTEXPR rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||
g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
|
@ -238,19 +202,17 @@ namespace internal {
|
|||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT
|
||||
: is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
|
||||
: is_rgb(true), value{} {
|
||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||
value{} {
|
||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||
}
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
|
||||
: is_rgb(true), value{} {
|
||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
|
||||
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
}
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
|
||||
: is_rgb(), value{} {
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||
value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
|
@ -259,21 +221,23 @@ struct color_type {
|
|||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
} // namespace internal
|
||||
} // namespace internal
|
||||
|
||||
// Experimental text formatting support.
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
throw format_error("can't OR a terminal color");
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
|
@ -282,7 +246,7 @@ class text_style {
|
|||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
throw format_error("can't OR a terminal color");
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
|
@ -291,18 +255,18 @@ class text_style {
|
|||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR
|
||||
text_style operator|(text_style lhs, const text_style &rhs) {
|
||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
|
||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
throw format_error("can't AND a terminal color");
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
|
@ -311,7 +275,7 @@ class text_style {
|
|||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
throw format_error("can't AND a terminal color");
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
|
@ -320,8 +284,8 @@ class text_style {
|
|||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR
|
||||
text_style operator&(text_style lhs, const text_style &rhs) {
|
||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs &= rhs;
|
||||
}
|
||||
|
||||
|
@ -335,32 +299,32 @@ class text_style {
|
|||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||
assert(has_foreground() && "no foreground specified for this style");
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||
assert(has_background() && "no background specified for this style");
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||
assert(has_emphasis() && "no emphasis specified for this style");
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
internal::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
internal::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
|
@ -388,19 +352,17 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
|||
|
||||
namespace internal {
|
||||
|
||||
template <typename Char>
|
||||
struct ansi_color_escape {
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||
const char * esc) FMT_NOEXCEPT {
|
||||
const char* esc) FMT_NOEXCEPT {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == internal::data::BACKGROUND_COLOR;
|
||||
bool is_background = esc == internal::data::background_color;
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background)
|
||||
value += 10u;
|
||||
if (is_background) value += 10u;
|
||||
|
||||
std::size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
|
@ -422,7 +384,7 @@ struct ansi_color_escape {
|
|||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value.rgb_color);
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.g, buffer + 11, ';');
|
||||
to_esc(color.b, buffer + 15, 'm');
|
||||
buffer[19] = static_cast<Char>(0);
|
||||
|
@ -430,19 +392,15 @@ struct ansi_color_escape {
|
|||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||
uint8_t em_codes[4] = {};
|
||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::bold))
|
||||
em_codes[0] = 1;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::italic))
|
||||
em_codes[1] = 3;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::underline))
|
||||
em_codes[2] = 4;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||
em_codes[3] = 9;
|
||||
|
||||
std::size_t index = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!em_codes[i])
|
||||
continue;
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||
|
@ -450,12 +408,17 @@ struct ansi_color_escape {
|
|||
}
|
||||
buffer[index++] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
|
||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
Char buffer[7u + 3u * 4u + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) FMT_NOEXCEPT {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
|
@ -465,77 +428,81 @@ private:
|
|||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
internal::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||
make_background_color(internal::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
internal::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, internal::data::background_color);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||
make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
|
||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputs(chars, stream);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
|
||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputws(chars, stream);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::reset_color, stream);
|
||||
}
|
||||
|
||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::wreset_color, stream);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::RESET_COLOR, stream);
|
||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
const char* begin = data::reset_color;
|
||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||
buffer.append(begin, end);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::WRESET_COLOR, stream);
|
||||
}
|
||||
|
||||
// The following specialiazation disables using std::FILE as a character type,
|
||||
// which is needed because or else
|
||||
// fmt::print(stderr, fmt::emphasis::bold, "");
|
||||
// would take stderr (a std::FILE *) as the format string.
|
||||
template <>
|
||||
struct is_string<std::FILE *> : std::false_type {};
|
||||
template <>
|
||||
struct is_string<const std::FILE *> : std::false_type {};
|
||||
} // namespace internal
|
||||
|
||||
template <
|
||||
typename S, typename Char = typename internal::char_t<S>::type>
|
||||
void vprint(std::FILE *f, const text_style &ts, const S &format,
|
||||
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||
template <typename Char>
|
||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
internal::fputs<Char>(
|
||||
internal::make_emphasis<Char>(ts.get_emphasis()), f);
|
||||
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
internal::fputs<Char>(
|
||||
internal::make_foreground_color<Char>(ts.get_foreground()), f);
|
||||
auto foreground =
|
||||
internal::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
internal::fputs<Char>(
|
||||
internal::make_background_color<Char>(ts.get_background()), f);
|
||||
}
|
||||
vprint(f, format, args);
|
||||
if (has_style) {
|
||||
internal::reset_color<Char>(f);
|
||||
auto background =
|
||||
internal::make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
internal::vformat_to(buf, format_str, args);
|
||||
if (has_style) internal::reset_color<Char>(buf);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
internal::vformat_to(buf, ts, to_string_view(format), args);
|
||||
buf.push_back(Char(0));
|
||||
internal::fputs(buf.data(), f);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -545,15 +512,14 @@ void vprint(std::FILE *f, const text_style &ts, const S &format,
|
|||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename String, typename... Args>
|
||||
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||
std::FILE *f, const text_style &ts, const String &format_str,
|
||||
const Args &... args) {
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
typedef typename internal::char_t<String>::type char_t;
|
||||
typedef typename buffer_context<char_t>::type context_t;
|
||||
format_arg_store<context_t, Args...> as{args...};
|
||||
vprint(f, ts, format_str, basic_format_args<context_t>(as));
|
||||
using context = buffer_context<char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,14 +529,39 @@ typename std::enable_if<internal::is_string<String>::value>::type print(
|
|||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename String, typename... Args>
|
||||
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||
const text_style &ts, const String &format_str,
|
||||
const Args &... args) {
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
|
||||
#endif
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/color.h>
|
||||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return vformat(ts, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
|
595
externals/fmt/include/fmt/compile.h
vendored
Normal file
595
externals/fmt/include/fmt/compile.h
vendored
Normal file
|
@ -0,0 +1,595 @@
|
|||
// Formatting library for C++ - experimental format string compilation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
// 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 void on_arg_id() { ++num_parts; }
|
||||
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
|
||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(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 void on_arg_id() {
|
||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_arg_id(int id) {
|
||||
parse_context_.check_arg_id(id);
|
||||
part_ = part::make_arg_index(id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
|
||||
part_ = part::make_arg_name(id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
||||
part_.arg_id_end = ptr;
|
||||
handler_(part_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(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 Range, typename Context, typename Id>
|
||||
void format_arg(
|
||||
basic_format_parse_context<typename Range::value_type>& parse_ctx,
|
||||
Context& ctx, Id arg_id) {
|
||||
ctx.advance_to(
|
||||
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
|
||||
}
|
||||
|
||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
||||
namespace cf {
|
||||
template <typename Context, typename Range, typename CompiledFormat>
|
||||
auto vformat_to(Range 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.begin(), 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);
|
||||
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
|
||||
break;
|
||||
|
||||
case format_part_t::kind::arg_name:
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
internal::format_arg<Range>(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<Range>(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<internal::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>(
|
||||
internal::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 Range, typename CompiledFormat>
|
||||
friend auto cf::vformat_to(Range 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
|
||||
template <typename... Args> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
else
|
||||
return get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
||||
};
|
||||
|
||||
template <int N, typename T>
|
||||
using get_type = typename get_type_impl<N, T>::type;
|
||||
|
||||
template <typename T> struct is_compiled_format : std::false_type {};
|
||||
|
||||
template <typename Char> struct text {
|
||||
basic_string_view<Char> data;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&...) const {
|
||||
// TODO: reserve
|
||||
return copy_str<Char>(data.begin(), data.end(), out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||
size_t size) {
|
||||
return {{&s[pos], size}};
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
||||
OutputIt format_default(OutputIt out, T value) {
|
||||
// TODO: reserve
|
||||
format_int fi(value);
|
||||
return std::copy(fi.data(), fi.data() + fi.size(), out);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt format_default(OutputIt out, double value) {
|
||||
writer w(out);
|
||||
w.write(value);
|
||||
return w.out();
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt format_default(OutputIt out, Char value) {
|
||||
*out++ = value;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt format_default(OutputIt out, const Char* value) {
|
||||
auto length = std::char_traits<Char>::length(value);
|
||||
return copy_str<Char>(value, value + length, out);
|
||||
}
|
||||
|
||||
// A replacement field that refers to argument N.
|
||||
template <typename Char, typename T, int N> struct field {
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
const T& arg = get<N>(args...);
|
||||
return format_default<Char>(out, arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R> struct concat {
|
||||
L lhs;
|
||||
R rhs;
|
||||
using char_type = typename L::char_type;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
out = lhs.format(out, args...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||
return {lhs, rhs};
|
||||
}
|
||||
|
||||
struct unknown_format {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||
for (size_t size = str.size(); pos != size; ++pos) {
|
||||
if (str[pos] == '{' || str[pos] == '}') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str);
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||
constexpr auto parse_tail(T head, S format_str) {
|
||||
if constexpr (POS != to_string_view(format_str).size()) {
|
||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||
unknown_format>())
|
||||
return tail;
|
||||
else
|
||||
return make_concat(head, tail);
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// or unknown_format() on unrecognized input.
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr basic_string_view<char_type> str = format_str;
|
||||
if constexpr (str[POS] == '{') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '{' in format string");
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}') {
|
||||
using type = get_type<ID, Args>;
|
||||
if constexpr (std::is_same<type, int>::value) {
|
||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||
format_str);
|
||||
} else {
|
||||
return unknown_format();
|
||||
}
|
||||
} else {
|
||||
return unknown_format();
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '}' in format string");
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
#endif // __cpp_if_constexpr
|
||||
} // namespace internal
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
# ifdef __cpp_if_constexpr
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
constexpr auto compile(S format_str) {
|
||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
||||
if constexpr (str.size() == 0) {
|
||||
return internal::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||
internal::unknown_format>()) {
|
||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
cf.format(std::back_inserter(buffer), args...);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
# else
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
|
||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
}
|
||||
# endif // __cpp_if_constexpr
|
||||
#endif // FMT_USE_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])
|
||||
-> internal::compiled_format<const Char*, Args...> {
|
||||
return internal::compiled_format<const Char*, Args...>(
|
||||
basic_string_view<Char>(format_str, N - 1));
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
using range = buffer_range<Char>;
|
||||
using context = buffer_context<Char>;
|
||||
internal::cf::vformat_to<context>(range(buffer), cf,
|
||||
make_format_args<context>(args...));
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
using char_type = typename CompiledFormat::char_type;
|
||||
using range = internal::output_range<OutputIt, char_type>;
|
||||
using context = format_context_t<OutputIt, char_type>;
|
||||
return internal::cf::vformat_to<context>(range(out), cf,
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
auto it =
|
||||
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args>
|
||||
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||
return format_to(internal::counting_iterator(), cf, args...).count();
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
2123
externals/fmt/include/fmt/core.h
vendored
2123
externals/fmt/include/fmt/core.h
vendored
File diff suppressed because it is too large
Load diff
1793
externals/fmt/include/fmt/format-inl.h
vendored
1793
externals/fmt/include/fmt/format-inl.h
vendored
File diff suppressed because it is too large
Load diff
4417
externals/fmt/include/fmt/format.h
vendored
4417
externals/fmt/include/fmt/format.h
vendored
File diff suppressed because it is too large
Load diff
65
externals/fmt/include/fmt/locale.h
vendored
65
externals/fmt/include/fmt/locale.h
vendored
|
@ -8,65 +8,66 @@
|
|||
#ifndef FMT_LOCALE_H_
|
||||
#define FMT_LOCALE_H_
|
||||
|
||||
#include "format.h"
|
||||
#include <locale>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace internal {
|
||||
template <typename Char>
|
||||
typename buffer_context<Char>::type::iterator vformat_to(
|
||||
const std::locale &loc, basic_buffer<Char> &buf,
|
||||
typename buffer_context<Char>::iterator vformat_to(
|
||||
const std::locale& loc, buffer<Char>& buf,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||
typedef back_insert_range<basic_buffer<Char> > range;
|
||||
return vformat_to<arg_formatter<range>>(
|
||||
buf, to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
using range = buffer_range<Char>;
|
||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||
internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
std::basic_string<Char> vformat(
|
||||
const std::locale &loc, basic_string_view<Char> format_str,
|
||||
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||
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;
|
||||
internal::vformat_to(loc, buffer, format_str, args);
|
||||
return fmt::to_string(buffer);
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename S, typename Char = FMT_CHAR(S)>
|
||||
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<typename buffer_context<Char>::type> args) {
|
||||
const std::locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
return internal::vformat(loc, to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args>
|
||||
inline std::basic_string<FMT_CHAR(S)> format(
|
||||
const std::locale &loc, const S &format_str, const Args &... 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 internal::vformat(
|
||||
loc, to_string_view(format_str),
|
||||
*internal::checked_args<S, Args...>(format_str, args...));
|
||||
loc, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
template <typename String, typename OutputIt, typename... Args>
|
||||
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
|
||||
OutputIt>::type
|
||||
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
|
||||
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
|
||||
typedef output_range<OutputIt, FMT_CHAR(String)> range;
|
||||
template <typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<
|
||||
internal::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 range = internal::output_range<OutputIt, Char>;
|
||||
return vformat_to<arg_formatter<range>>(
|
||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args>
|
||||
inline typename std::enable_if<
|
||||
internal::is_string<S>::value &&
|
||||
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
|
||||
format_to(OutputIt out, const std::locale &loc, const S &format_str,
|
||||
const Args &... args) {
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
||||
internal::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
|
||||
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));
|
||||
|
|
392
externals/fmt/include/fmt/os.h
vendored
Normal file
392
externals/fmt/include/fmt/os.h
vendored
Normal file
|
@ -0,0 +1,392 @@
|
|||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||
# undef __STRICT_ANSI__
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // for locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // for strtod_l
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// UWP doesn't provide _pipe.
|
||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#endif
|
||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following type aliases for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char* c_str() const { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
// An error code.
|
||||
class error_code {
|
||||
private:
|
||||
int value_;
|
||||
|
||||
public:
|
||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||
|
||||
int get() const FMT_NOEXCEPT { return value_; }
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace internal {
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
private:
|
||||
memory_buffer buffer_;
|
||||
|
||||
public:
|
||||
utf16_to_utf8() {}
|
||||
FMT_API explicit utf16_to_utf8(wstring_view s);
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a system error code instead of
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// in case of memory allocation error.
|
||||
FMT_API int convert(wstring_view s);
|
||||
};
|
||||
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
} // namespace internal
|
||||
|
||||
/** A Windows error. */
|
||||
class windows_error : public system_error {
|
||||
private:
|
||||
FMT_API void init(int error_code, string_view format_str, format_args args);
|
||||
|
||||
public:
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`fmt::windows_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||
If *error_code* is not a valid error code such as -1, the system message
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a windows_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
windows_error(int error_code, string_view message, const Args&... args) {
|
||||
init(error_code, message, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
#endif // _WIN32
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int(fileno)() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args&... args) {
|
||||
vprint(format_str, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API std::size_t read(void* buffer, std::size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API std::size_t write(const void* buffer, std::size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(file& read_end, file& write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
FMT_API buffered_file fdopen(const char* mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class locale {
|
||||
private:
|
||||
# ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
|
||||
static void freelocale(locale_t loc) { _free_locale(loc); }
|
||||
|
||||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
|
||||
return _strtod_l(nptr, endptr, loc);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
public:
|
||||
using type = locale_t;
|
||||
locale(const locale&) = delete;
|
||||
void operator=(const locale&) = delete;
|
||||
|
||||
locale() {
|
||||
# ifndef _WIN32
|
||||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
|
||||
# else
|
||||
locale_ = _create_locale(LC_NUMERIC, "C");
|
||||
# endif
|
||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~locale() { freelocale(locale_); }
|
||||
|
||||
type get() const { return locale_; }
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char*& str) const {
|
||||
char* end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||
#endif // FMT_LOCALE
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
110
externals/fmt/include/fmt/ostream.h
vendored
110
externals/fmt/include/fmt/ostream.h
vendored
|
@ -8,22 +8,21 @@
|
|||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include "format.h"
|
||||
#include <ostream>
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
template <class Char>
|
||||
class formatbuf : public std::basic_streambuf<Char> {
|
||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
private:
|
||||
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||
|
||||
basic_buffer<Char> &buffer_;
|
||||
buffer<Char>& buffer_;
|
||||
|
||||
public:
|
||||
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||
|
||||
protected:
|
||||
// The put-area is actually always empty. This makes the implementation
|
||||
|
@ -39,33 +38,36 @@ class formatbuf : public std::basic_streambuf<Char> {
|
|||
return ch;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
||||
buffer_.append(s, s + count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct test_stream : std::basic_ostream<Char> {
|
||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||
private:
|
||||
struct null;
|
||||
// Hide all operator<< from std::basic_ostream<Char>.
|
||||
void operator<<(null);
|
||||
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);
|
||||
};
|
||||
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
|
||||
template <typename T, typename Char>
|
||||
class is_streamable {
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static decltype(
|
||||
internal::declval<test_stream<Char>&>()
|
||||
<< internal::declval<U>(), std::true_type()) test(int);
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||
<< std::declval<U>()),
|
||||
void_t<>>::value>
|
||||
test(int);
|
||||
|
||||
template <typename>
|
||||
static std::false_type test(...);
|
||||
template <typename> static std::false_type test(...);
|
||||
|
||||
typedef decltype(test<T>(0)) result;
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
static const bool value = result::value;
|
||||
|
@ -73,65 +75,54 @@ class is_streamable {
|
|||
|
||||
// Write the content of buf to os.
|
||||
template <typename Char>
|
||||
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
||||
const Char *data = buf.data();
|
||||
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
|
||||
UnsignedStreamSize size = buf.size();
|
||||
UnsignedStreamSize max_size =
|
||||
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do {
|
||||
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
||||
os.write(data, static_cast<std::streamsize>(n));
|
||||
data += n;
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
template <typename Char, typename T>
|
||||
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||
internal::formatbuf<Char> format_buf(buffer);
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
output << value;
|
||||
buffer.resize(buffer.size());
|
||||
buf.resize(buf.size());
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
// Disable conversion to int if T has an overloaded operator<< which is a free
|
||||
// function (not a member of std::ostream).
|
||||
template <typename T, typename Char>
|
||||
struct convert_to_int<T, Char, void> {
|
||||
static const bool value =
|
||||
convert_to_int<T, Char, int>::value &&
|
||||
!internal::is_streamable<T, Char>::value;
|
||||
};
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char,
|
||||
typename std::enable_if<
|
||||
internal::is_streamable<T, Char>::value &&
|
||||
!internal::format_type<
|
||||
typename buffer_context<Char>::type, T>::value>::type>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: formatter<basic_string_view<Char>, Char> {
|
||||
|
||||
template <typename Context>
|
||||
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
|
||||
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::format_value(buffer, value);
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename Char>
|
||||
inline void vprint(std::basic_ostream<Char> &os,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(buffer, format_str, args);
|
||||
internal::write(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
@ -141,12 +132,11 @@ inline void vprint(std::basic_ostream<Char> &os,
|
|||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args>
|
||||
inline typename std::enable_if<internal::is_string<S>::value>::type
|
||||
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
|
||||
const Args & ... args) {
|
||||
internal::checked_args<S, Args...> ca(format_str, args...);
|
||||
vprint(os, to_string_view(format_str), *ca);
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
|
326
externals/fmt/include/fmt/posix.h
vendored
326
externals/fmt/include/fmt/posix.h
vendored
|
@ -1,324 +1,2 @@
|
|||
// A C++ interface to POSIX functions.
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_POSIX_H_
|
||||
#define FMT_POSIX_H_
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||
# undef __STRICT_ANSI__
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> // for O_RDONLY
|
||||
#include <locale.h> // for locale_t
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // for strtod_l
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
result = (expression); \
|
||||
} while (result == error_result && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following typedefs for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char>
|
||||
class basic_cstring_view {
|
||||
private:
|
||||
const Char *data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char *s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char *c_str() const { return data_; }
|
||||
};
|
||||
|
||||
typedef basic_cstring_view<char> cstring_view;
|
||||
typedef basic_cstring_view<wchar_t> wcstring_view;
|
||||
|
||||
// An error code.
|
||||
class error_code {
|
||||
private:
|
||||
int value_;
|
||||
|
||||
public:
|
||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||
|
||||
int get() const FMT_NOEXCEPT { return value_; }
|
||||
};
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE *file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE *f) : file_(f) {}
|
||||
|
||||
public:
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
buffered_file(const buffered_file &) = delete;
|
||||
void operator=(const buffered_file &) = delete;
|
||||
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = FMT_NULL;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file &&other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = FMT_NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE *get() const FMT_NOEXCEPT { return file_; }
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int (fileno)() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args & ... args) {
|
||||
vprint(format_str, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
private:
|
||||
file(const file &) = delete;
|
||||
void operator=(const file &) = delete;
|
||||
|
||||
public:
|
||||
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
file& operator=(file &&other) {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API std::size_t write(const void *buffer, std::size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(file &read_end, file &write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
FMT_API buffered_file fdopen(const char *mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||
!defined(__NEWLIB_H__)
|
||||
# define FMT_LOCALE
|
||||
#endif
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class Locale {
|
||||
private:
|
||||
# ifdef _MSC_VER
|
||||
typedef _locale_t locale_t;
|
||||
|
||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||
|
||||
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
|
||||
return _create_locale(category_mask, locale);
|
||||
}
|
||||
|
||||
static void freelocale(locale_t locale) {
|
||||
_free_locale(locale);
|
||||
}
|
||||
|
||||
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||
return _strtod_l(nptr, endptr, locale);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
Locale(const Locale &) = delete;
|
||||
void operator=(const Locale &) = delete;
|
||||
|
||||
public:
|
||||
typedef locale_t Type;
|
||||
|
||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
||||
if (!locale_)
|
||||
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~Locale() { freelocale(locale_); }
|
||||
|
||||
Type get() const { return locale_; }
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char *&str) const {
|
||||
char *end = FMT_NULL;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
#endif // FMT_LOCALE
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_POSIX_H_
|
||||
#include "os.h"
|
||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
814
externals/fmt/include/fmt/printf.h
vendored
814
externals/fmt/include/fmt/printf.h
vendored
File diff suppressed because it is too large
Load diff
293
externals/fmt/include/fmt/ranges.h
vendored
293
externals/fmt/include/fmt/ranges.h
vendored
|
@ -1,4 +1,4 @@
|
|||
// Formatting library for C++ - the core API
|
||||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
|
@ -12,20 +12,21 @@
|
|||
#ifndef FMT_RANGES_H_
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include "format.h"
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
#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
|
||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char>
|
||||
struct formatting_base {
|
||||
template <typename Char> struct formatting_base {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
@ -33,7 +34,8 @@ struct formatting_base {
|
|||
template <typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char> {
|
||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
|
@ -55,87 +57,75 @@ struct formatting_tuple : formatting_base<Char> {
|
|||
namespace internal {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
void copy(const RangeT &range, OutputIterator out) {
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
void copy(const char *str, OutputIterator out) {
|
||||
const char *p_curr = str;
|
||||
while (*p_curr) {
|
||||
*out++ = *p_curr++;
|
||||
}
|
||||
OutputIterator copy(const char* str, OutputIterator out) {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
void copy(char ch, OutputIterator out) {
|
||||
OutputIterator copy(char ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Return true value if T has std::string interface, like std::string_view.
|
||||
template <typename T>
|
||||
class is_like_std_string {
|
||||
template <typename T> class is_like_std_string {
|
||||
template <typename U>
|
||||
static auto check(U *p) ->
|
||||
decltype(p->find('a'), p->length(), p->data(), int());
|
||||
template <typename>
|
||||
static void check(...);
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_like_std_string<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
|
||||
template <typename T>
|
||||
struct is_range_<T, typename std::conditional<
|
||||
false,
|
||||
conditional_helper<decltype(internal::declval<T>().begin()),
|
||||
decltype(internal::declval<T>().end())>,
|
||||
void>::type> : std::true_type {};
|
||||
struct is_range_<
|
||||
T, conditional_t<false,
|
||||
conditional_helper<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>,
|
||||
void>> : std::true_type {};
|
||||
#endif
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
template <typename T>
|
||||
class is_tuple_like_ {
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U *p) ->
|
||||
decltype(std::tuple_size<U>::value,
|
||||
internal::declval<typename std::tuple_element<0, U>::type>(), int());
|
||||
template <typename>
|
||||
static void check(...);
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <std::size_t... N>
|
||||
using index_sequence = std::index_sequence<N...>;
|
||||
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <std::size_t N>
|
||||
using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N>
|
||||
struct integer_sequence {
|
||||
typedef T value_type;
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR std::size_t size() {
|
||||
return sizeof...(N);
|
||||
}
|
||||
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <std::size_t... N>
|
||||
|
@ -151,7 +141,7 @@ using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
|||
#endif
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
|
||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
|
@ -159,26 +149,25 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
|
|||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
|
||||
get_indexes(T const &) { return {}; }
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||
T const&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple, class F>
|
||||
void for_each(Tuple &&tup, F &&f) {
|
||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename Arg>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||
typename std::enable_if<
|
||||
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " {}" : "{}";
|
||||
}
|
||||
|
||||
template<typename Arg>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||
typename std::enable_if<
|
||||
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
|
||||
|
@ -186,61 +175,58 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
|||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
struct is_tuple_like {
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char,
|
||||
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
|
||||
private:
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
template <typename FormatContext>
|
||||
struct format_each {
|
||||
template <typename T>
|
||||
void operator()(const T& v) {
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.delimiter, out);
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
++i;
|
||||
}
|
||||
|
||||
formatting_tuple<Char>& formatting;
|
||||
std::size_t& i;
|
||||
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
|
||||
typename std::add_lvalue_reference<decltype(
|
||||
std::declval<FormatContext>().out())>::type out;
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
|
@ -255,54 +241,147 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_range {
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
|
||||
internal::is_range_<T>::value &&
|
||||
!internal::is_like_std_string<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char,
|
||||
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
|
||||
|
||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const RangeT &values, FormatContext &ctx) {
|
||||
auto out = ctx.out();
|
||||
internal::copy(formatting.prefix, out);
|
||||
typename FormatContext::iterator format(const RangeT& values,
|
||||
FormatContext& ctx) {
|
||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||
std::size_t i = 0;
|
||||
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.delimiter, out);
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||
*it);
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||
*it);
|
||||
if (++i > formatting.range_length_limit) {
|
||||
format_to(out, " ... <other elements>");
|
||||
out = format_to(out, " ... <other elements>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.postfix, out);
|
||||
return ctx.out();
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
return internal::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple{t}, sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
internal::index_sequence<N...>) {
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return format_args(value, ctx, args...);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::tuple<int, char> t = {1, 'a'};
|
||||
fmt::print("{}", fmt::join(t, ", "));
|
||||
// Output: "1, a"
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||
string_view sep) {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
wstring_view sep) {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `initializer_list` with elements separated by
|
||||
`sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||
// Output: "1, 2, 3"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
||||
std::initializer_list<T> list, string_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
||||
std::initializer_list<T> list, wstring_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
|
|
160
externals/fmt/include/fmt/time.h
vendored
160
externals/fmt/include/fmt/time.h
vendored
|
@ -1,160 +0,0 @@
|
|||
// Formatting library for C++ - time formatting
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_TIME_H_
|
||||
#define FMT_TIME_H_
|
||||
|
||||
#include "format.h"
|
||||
#include <ctime>
|
||||
#include <locale>
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
// Prevents expansion of a preceding token as a function-style macro.
|
||||
// Usage: f FMT_NOMACRO()
|
||||
#define FMT_NOMACRO
|
||||
|
||||
namespace internal{
|
||||
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||
inline null<> localtime_s(...) { return null<>(); }
|
||||
inline null<> gmtime_r(...) { return null<>(); }
|
||||
inline null<> gmtime_s(...) { return null<>(); }
|
||||
} // namespace internal
|
||||
|
||||
// Thread-safe replacement for std::localtime
|
||||
inline std::tm localtime(std::time_t time) {
|
||||
struct dispatcher {
|
||||
std::time_t time_;
|
||||
std::tm tm_;
|
||||
|
||||
dispatcher(std::time_t t): time_(t) {}
|
||||
|
||||
bool run() {
|
||||
using namespace fmt::internal;
|
||||
return handle(localtime_r(&time_, &tm_));
|
||||
}
|
||||
|
||||
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
||||
|
||||
bool handle(internal::null<>) {
|
||||
using namespace fmt::internal;
|
||||
return fallback(localtime_s(&tm_, &time_));
|
||||
}
|
||||
|
||||
bool fallback(int res) { return res == 0; }
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
bool fallback(internal::null<>) {
|
||||
using namespace fmt::internal;
|
||||
std::tm *tm = std::localtime(&time_);
|
||||
if (tm) tm_ = *tm;
|
||||
return tm != FMT_NULL;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
dispatcher lt(time);
|
||||
// Too big time values may be unsupported.
|
||||
if (!lt.run())
|
||||
FMT_THROW(format_error("time_t value out of range"));
|
||||
return lt.tm_;
|
||||
}
|
||||
|
||||
// Thread-safe replacement for std::gmtime
|
||||
inline std::tm gmtime(std::time_t time) {
|
||||
struct dispatcher {
|
||||
std::time_t time_;
|
||||
std::tm tm_;
|
||||
|
||||
dispatcher(std::time_t t): time_(t) {}
|
||||
|
||||
bool run() {
|
||||
using namespace fmt::internal;
|
||||
return handle(gmtime_r(&time_, &tm_));
|
||||
}
|
||||
|
||||
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
||||
|
||||
bool handle(internal::null<>) {
|
||||
using namespace fmt::internal;
|
||||
return fallback(gmtime_s(&tm_, &time_));
|
||||
}
|
||||
|
||||
bool fallback(int res) { return res == 0; }
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
bool fallback(internal::null<>) {
|
||||
std::tm *tm = std::gmtime(&time_);
|
||||
if (tm) tm_ = *tm;
|
||||
return tm != FMT_NULL;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
dispatcher gt(time);
|
||||
// Too big time values may be unsupported.
|
||||
if (!gt.run())
|
||||
FMT_THROW(format_error("time_t value out of range"));
|
||||
return gt.tm_;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
inline std::size_t strftime(char *str, std::size_t count, const char *format,
|
||||
const std::tm *time) {
|
||||
return std::strftime(str, count, format, time);
|
||||
}
|
||||
|
||||
inline std::size_t strftime(wchar_t *str, std::size_t count,
|
||||
const wchar_t *format, const std::tm *time) {
|
||||
return std::wcsftime(str, count, format, time);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
struct formatter<std::tm, Char> {
|
||||
template <typename ParseContext>
|
||||
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;
|
||||
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
||||
tm_format.append(it, end);
|
||||
tm_format.push_back('\0');
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
std::size_t start = buf.size();
|
||||
for (;;) {
|
||||
std::size_t size = buf.capacity() - start;
|
||||
std::size_t count =
|
||||
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||
if (count != 0) {
|
||||
buf.resize(start + count);
|
||||
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 std::size_t MIN_GROWTH = 10;
|
||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||
}
|
||||
return std::copy(buf.begin(), buf.end(), ctx.out());
|
||||
}
|
||||
|
||||
basic_memory_buffer<Char> tm_format;
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_TIME_H_
|
169
externals/fmt/src/format.cc
vendored
169
externals/fmt/src/format.cc
vendored
|
@ -8,51 +8,168 @@
|
|||
#include "fmt/format-inl.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template struct internal::basic_data<void>;
|
||||
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||
T value) {
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
if (precision > 100000)
|
||||
throw std::runtime_error(
|
||||
"fuzz mode - avoid large allocation inside snprintf");
|
||||
#endif
|
||||
// Suppress the warning about nonliteral format string.
|
||||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||
: snprintf_ptr(buf, size, format, precision, value);
|
||||
}
|
||||
struct sprintf_specs {
|
||||
int precision;
|
||||
char type;
|
||||
bool alt : 1;
|
||||
|
||||
template <typename Char>
|
||||
constexpr sprintf_specs(basic_format_specs<Char> specs)
|
||||
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
|
||||
|
||||
constexpr bool has_precision() const { return precision >= 0; }
|
||||
};
|
||||
|
||||
// This is deprecated and is kept only to preserve ABI compatibility.
|
||||
template <typename Double>
|
||||
char* sprintf_format(Double value, internal::buffer<char>& buf,
|
||||
sprintf_specs specs) {
|
||||
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
||||
|
||||
// Build format string.
|
||||
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
|
||||
char format[max_format_size];
|
||||
char* format_ptr = format;
|
||||
*format_ptr++ = '%';
|
||||
if (specs.alt || !specs.type) *format_ptr++ = '#';
|
||||
if (specs.precision >= 0) {
|
||||
*format_ptr++ = '.';
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
|
||||
|
||||
char type = specs.type;
|
||||
|
||||
if (type == '%')
|
||||
type = 'f';
|
||||
else if (type == 0 || type == 'n')
|
||||
type = 'g';
|
||||
#if FMT_MSC_VER
|
||||
if (type == 'F') {
|
||||
// MSVC's printf doesn't support 'F'.
|
||||
type = 'f';
|
||||
}
|
||||
#endif
|
||||
*format_ptr++ = type;
|
||||
*format_ptr = '\0';
|
||||
|
||||
// Format using snprintf.
|
||||
char* start = nullptr;
|
||||
char* decimal_point_pos = nullptr;
|
||||
for (;;) {
|
||||
std::size_t buffer_size = buf.capacity();
|
||||
start = &buf[0];
|
||||
int result =
|
||||
format_float(start, buffer_size, format, specs.precision, value);
|
||||
if (result >= 0) {
|
||||
unsigned n = internal::to_unsigned(result);
|
||||
if (n < buf.capacity()) {
|
||||
// Find the decimal point.
|
||||
auto p = buf.data(), end = p + n;
|
||||
if (*p == '+' || *p == '-') ++p;
|
||||
if (specs.type != 'a' && specs.type != 'A') {
|
||||
while (p < end && *p >= '0' && *p <= '9') ++p;
|
||||
if (p < end && *p != 'e' && *p != 'E') {
|
||||
decimal_point_pos = p;
|
||||
if (!specs.type) {
|
||||
// Keep only one trailing zero after the decimal point.
|
||||
++p;
|
||||
if (*p == '0') ++p;
|
||||
while (p != end && *p >= '1' && *p <= '9') ++p;
|
||||
char* where = p;
|
||||
while (p != end && *p == '0') ++p;
|
||||
if (p == end || *p < '0' || *p > '9') {
|
||||
if (p != end) std::memmove(where, p, to_unsigned(end - p));
|
||||
n -= static_cast<unsigned>(p - where);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.resize(n);
|
||||
break; // The buffer is large enough - continue with formatting.
|
||||
}
|
||||
buf.reserve(n + 1);
|
||||
} else {
|
||||
// If result is negative we ask to increase the capacity by at least 1,
|
||||
// but as std::vector, the buffer grows exponentially.
|
||||
buf.reserve(buf.capacity() + 1);
|
||||
}
|
||||
}
|
||||
return decimal_point_pos;
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
|
||||
sprintf_specs);
|
||||
template FMT_API char* internal::sprintf_format(long double,
|
||||
internal::buffer<char>&,
|
||||
sprintf_specs);
|
||||
|
||||
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
|
||||
|
||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||
int (*instantiate_format_float)(double, int, internal::float_specs,
|
||||
internal::buffer<char>&) =
|
||||
internal::format_float;
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
|
||||
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
|
||||
#endif
|
||||
|
||||
// Explicit instantiations for char.
|
||||
|
||||
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
|
||||
template FMT_API char internal::thousands_sep_impl(locale_ref);
|
||||
template FMT_API char internal::decimal_point_impl(locale_ref);
|
||||
|
||||
template FMT_API void internal::basic_buffer<char>::append(const char *, const char *);
|
||||
template FMT_API void internal::buffer<char>::append(const char*, const char*);
|
||||
|
||||
template FMT_API void internal::arg_map<format_context>::init(
|
||||
const basic_format_args<format_context> &args);
|
||||
|
||||
template FMT_API int internal::char_traits<char>::format_float(
|
||||
char *, std::size_t, const char *, int, double);
|
||||
|
||||
template FMT_API int internal::char_traits<char>::format_float(
|
||||
char *, std::size_t, const char *, int, long double);
|
||||
const basic_format_args<format_context>& args);
|
||||
|
||||
template FMT_API std::string internal::vformat<char>(
|
||||
string_view, basic_format_args<format_context>);
|
||||
|
||||
template FMT_API format_context::iterator internal::vformat_to(
|
||||
internal::buffer &, string_view, basic_format_args<format_context>);
|
||||
internal::buffer<char>&, string_view, basic_format_args<format_context>);
|
||||
|
||||
template FMT_API void internal::sprintf_format(
|
||||
double, internal::buffer &, core_format_specs);
|
||||
template FMT_API void internal::sprintf_format(
|
||||
long double, internal::buffer &, core_format_specs);
|
||||
template FMT_API int internal::snprintf_float(double, int,
|
||||
internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
template FMT_API int internal::snprintf_float(long double, int,
|
||||
internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
template FMT_API int internal::format_float(double, int, internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
template FMT_API int internal::format_float(long double, int,
|
||||
internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
|
||||
// Explicit instantiations for wchar_t.
|
||||
|
||||
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
|
||||
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
|
||||
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
|
||||
|
||||
template FMT_API void internal::basic_buffer<wchar_t>::append(
|
||||
const wchar_t *, const wchar_t *);
|
||||
|
||||
template FMT_API void internal::arg_map<wformat_context>::init(
|
||||
const basic_format_args<wformat_context> &);
|
||||
|
||||
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||
wchar_t *, std::size_t, const wchar_t *, int, double);
|
||||
|
||||
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||
wchar_t *, std::size_t, const wchar_t *, int, long double);
|
||||
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
|
||||
const wchar_t*);
|
||||
|
||||
template FMT_API std::wstring internal::vformat<wchar_t>(
|
||||
wstring_view, basic_format_args<wformat_context>);
|
||||
|
|
316
externals/fmt/src/os.cc
vendored
Normal file
316
externals/fmt/src/os.cc
vendored
Normal file
|
@ -0,0 +1,316 @@
|
|||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/os.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
||||
# ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
|
||||
# define O_CREAT _O_CREAT
|
||||
# define O_TRUNC _O_TRUNC
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# define _SH_DENYNO 0x40
|
||||
# endif
|
||||
# endif // _WIN32
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
using RWResult = int;
|
||||
|
||||
// On Windows the count argument to read and write is unsigned, so convert
|
||||
// it from size_t preventing integer overflow.
|
||||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#else
|
||||
// Return type of read and write functions.
|
||||
using RWResult = ssize_t;
|
||||
|
||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef _WIN32
|
||||
internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||
if (int error_code = convert(s)) {
|
||||
FMT_THROW(windows_error(error_code,
|
||||
"cannot convert string from UTF-16 to UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
int internal::utf16_to_utf8::convert(wstring_view s) {
|
||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
if (s_size == 0) {
|
||||
// WideCharToMultiByte does not support zero length, handle separately.
|
||||
buffer_.resize(1);
|
||||
buffer_[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
|
||||
nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_.resize(length + 1);
|
||||
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
|
||||
length, nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_[length] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void windows_error::init(int err_code, string_view format_str,
|
||||
format_args args) {
|
||||
error_code_ = err_code;
|
||||
memory_buffer buffer;
|
||||
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||
std::runtime_error& base = *this;
|
||||
base = std::runtime_error(to_string(buffer));
|
||||
}
|
||||
|
||||
void internal::format_windows_error(internal::buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
wmemory_buffer buf;
|
||||
buf.resize(inline_buffer_size);
|
||||
for (;;) {
|
||||
wchar_t* system_message = &buf[0];
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||
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) {
|
||||
internal::writer w(out);
|
||||
w.write(message);
|
||||
w.write(": ");
|
||||
w.write(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(...) {}
|
||||
format_error_code(out, error_code, message);
|
||||
}
|
||||
|
||||
void report_windows_error(int error_code,
|
||||
fmt::string_view message) FMT_NOEXCEPT {
|
||||
report_error(internal::format_windows_error, error_code, message);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
|
||||
nullptr);
|
||||
if (!file_)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||
}
|
||||
|
||||
void buffered_file::close() {
|
||||
if (!file_) return;
|
||||
int result = FMT_SYSTEM(fclose(file_));
|
||||
file_ = nullptr;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||
#define FMT_ARGS
|
||||
|
||||
int buffered_file::fileno() const {
|
||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
file::file(cstring_view path, int oflag) {
|
||||
int mode = S_IRUSR | S_IWUSR;
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
# else
|
||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||
# endif
|
||||
if (fd_ == -1)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||
}
|
||||
|
||||
file::~file() FMT_NOEXCEPT {
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
void file::close() {
|
||||
if (fd_ == -1) return;
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
int result = FMT_POSIX_CALL(close(fd_));
|
||||
fd_ = -1;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
long long file::size() const {
|
||||
# ifdef _WIN32
|
||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||
// Both functions support large file sizes.
|
||||
DWORD size_upper = 0;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||
if (size_lower == INVALID_FILE_SIZE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != NO_ERROR)
|
||||
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||
}
|
||||
unsigned long long long_size = size_upper;
|
||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||
# else
|
||||
using Stat = struct stat;
|
||||
Stat file_stat = Stat();
|
||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||
"return type of file::size is not large enough");
|
||||
return file_stat.st_size;
|
||||
# endif
|
||||
}
|
||||
|
||||
std::size_t file::read(void* buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
std::size_t file::write(const void* buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
file file::dup(int fd) {
|
||||
// Don't retry as dup doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||
if (new_fd == -1)
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||
return file(new_fd);
|
||||
}
|
||||
|
||||
void file::dup2(int fd) {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) {
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
|
||||
fd_, fd));
|
||||
}
|
||||
}
|
||||
|
||||
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) ec = error_code(errno);
|
||||
}
|
||||
|
||||
void file::pipe(file& read_end, file& write_end) {
|
||||
// Close the descriptors first to make sure that assignments don't throw
|
||||
// and there are no leaks.
|
||||
read_end.close();
|
||||
write_end.close();
|
||||
int fds[2] = {};
|
||||
# ifdef _WIN32
|
||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||
enum { DEFAULT_CAPACITY = 65536 };
|
||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||
# else
|
||||
// Don't retry as the pipe function doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||
int result = FMT_POSIX_CALL(pipe(fds));
|
||||
# endif
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||
// The following assignments don't throw because read_fd and write_fd
|
||||
// are closed.
|
||||
read_end = file(fds[0]);
|
||||
write_end = file(fds[1]);
|
||||
}
|
||||
|
||||
buffered_file file::fdopen(const char* mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
if (!f)
|
||||
FMT_THROW(
|
||||
system_error(errno, "cannot associate stream with file descriptor"));
|
||||
buffered_file bf(f);
|
||||
fd_ = -1;
|
||||
return bf;
|
||||
}
|
||||
|
||||
long getpagesize() {
|
||||
# ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
# else
|
||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||
return size;
|
||||
# endif
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
FMT_END_NAMESPACE
|
244
externals/fmt/src/posix.cc
vendored
244
externals/fmt/src/posix.cc
vendored
|
@ -1,244 +0,0 @@
|
|||
// A C++ interface to POSIX functions.
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/posix.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
|
||||
# define O_CREAT _O_CREAT
|
||||
# define O_TRUNC _O_TRUNC
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# define _SH_DENYNO 0x40
|
||||
# endif
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
typedef int RWResult;
|
||||
|
||||
// On Windows the count argument to read and write is unsigned, so convert
|
||||
// it from size_t preventing integer overflow.
|
||||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#else
|
||||
// Return type of read and write functions.
|
||||
typedef ssize_t RWResult;
|
||||
|
||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||
#endif
|
||||
}
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||
FMT_RETRY_VAL(file_,
|
||||
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
|
||||
if (!file_)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||
}
|
||||
|
||||
void buffered_file::close() {
|
||||
if (!file_)
|
||||
return;
|
||||
int result = FMT_SYSTEM(fclose(file_));
|
||||
file_ = FMT_NULL;
|
||||
if (result != 0)
|
||||
FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||
#define FMT_ARGS
|
||||
|
||||
int buffered_file::fileno() const {
|
||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||
if (fd == -1)
|
||||
FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
||||
file::file(cstring_view path, int oflag) {
|
||||
int mode = S_IRUSR | S_IWUSR;
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
#else
|
||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||
#endif
|
||||
if (fd_ == -1)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||
}
|
||||
|
||||
file::~file() FMT_NOEXCEPT {
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
void file::close() {
|
||||
if (fd_ == -1)
|
||||
return;
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
int result = FMT_POSIX_CALL(close(fd_));
|
||||
fd_ = -1;
|
||||
if (result != 0)
|
||||
FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
long long file::size() const {
|
||||
#ifdef _WIN32
|
||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||
// Both functions support large file sizes.
|
||||
DWORD size_upper = 0;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||
if (size_lower == INVALID_FILE_SIZE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != NO_ERROR)
|
||||
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||
}
|
||||
unsigned long long long_size = size_upper;
|
||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||
#else
|
||||
typedef struct stat Stat;
|
||||
Stat file_stat = Stat();
|
||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||
"return type of file::size is not large enough");
|
||||
return file_stat.st_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t file::read(void *buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0)
|
||||
FMT_THROW(system_error(errno, "cannot read from file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
std::size_t file::write(const void *buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0)
|
||||
FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
file file::dup(int fd) {
|
||||
// Don't retry as dup doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||
if (new_fd == -1)
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||
return file(new_fd);
|
||||
}
|
||||
|
||||
void file::dup2(int fd) {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) {
|
||||
FMT_THROW(system_error(errno,
|
||||
"cannot duplicate file descriptor {} to {}", fd_, fd));
|
||||
}
|
||||
}
|
||||
|
||||
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1)
|
||||
ec = error_code(errno);
|
||||
}
|
||||
|
||||
void file::pipe(file &read_end, file &write_end) {
|
||||
// Close the descriptors first to make sure that assignments don't throw
|
||||
// and there are no leaks.
|
||||
read_end.close();
|
||||
write_end.close();
|
||||
int fds[2] = {};
|
||||
#ifdef _WIN32
|
||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||
enum { DEFAULT_CAPACITY = 65536 };
|
||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||
#else
|
||||
// Don't retry as the pipe function doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||
int result = FMT_POSIX_CALL(pipe(fds));
|
||||
#endif
|
||||
if (result != 0)
|
||||
FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||
// The following assignments don't throw because read_fd and write_fd
|
||||
// are closed.
|
||||
read_end = file(fds[0]);
|
||||
write_end = file(fds[1]);
|
||||
}
|
||||
|
||||
buffered_file file::fdopen(const char *mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
if (!f)
|
||||
FMT_THROW(system_error(errno,
|
||||
"cannot associate stream with file descriptor"));
|
||||
buffered_file bf(f);
|
||||
fd_ = -1;
|
||||
return bf;
|
||||
}
|
||||
|
||||
long getpagesize() {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
#else
|
||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||
if (size < 0)
|
||||
FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
2061
externals/fmt/support/C++.sublime-syntax
vendored
Normal file
2061
externals/fmt/support/C++.sublime-syntax
vendored
Normal file
File diff suppressed because it is too large
Load diff
19
externals/fmt/support/Vagrantfile
vendored
Normal file
19
externals/fmt/support/Vagrantfile
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# A vagrant config for testing against gcc-4.8.
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.memory = "4096"
|
||||
end
|
||||
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
apt-get update
|
||||
apt-get install -y g++ make wget git
|
||||
wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz
|
||||
tar xzf cmake-3.14.4-Linux-x86_64.tar.gz
|
||||
ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin
|
||||
SHELL
|
||||
end
|
19
externals/fmt/support/appveyor-build.py
vendored
19
externals/fmt/support/appveyor-build.py
vendored
|
@ -23,14 +23,17 @@ else:
|
|||
# Add MSBuild 14.0 to PATH as described in
|
||||
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
|
||||
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
|
||||
if image == 'Visual Studio 2013':
|
||||
generator = 'Visual Studio 12 2013'
|
||||
elif image == 'Visual Studio 2015':
|
||||
generator = 'Visual Studio 14 2015'
|
||||
elif image == 'Visual Studio 2017':
|
||||
generator = 'Visual Studio 15 2017'
|
||||
if platform == 'x64':
|
||||
generator += ' Win64'
|
||||
if image == 'Visual Studio 2019':
|
||||
generator = 'Visual Studio 16 2019'
|
||||
if platform == 'x64':
|
||||
cmake_command.extend(['-A', 'x64'])
|
||||
else:
|
||||
if image == 'Visual Studio 2015':
|
||||
generator = 'Visual Studio 14 2015'
|
||||
elif image == 'Visual Studio 2017':
|
||||
generator = 'Visual Studio 15 2017'
|
||||
if platform == 'x64':
|
||||
generator += ' Win64'
|
||||
cmake_command.append('-G' + generator)
|
||||
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
|
||||
test_command = ['ctest', '-C', config]
|
||||
|
|
17
externals/fmt/support/appveyor.yml
vendored
17
externals/fmt/support/appveyor.yml
vendored
|
@ -4,20 +4,27 @@ configuration:
|
|||
|
||||
clone_depth: 1
|
||||
|
||||
image:
|
||||
- Visual Studio 2015
|
||||
- Visual Studio 2019
|
||||
- Visual Studio 2017
|
||||
|
||||
platform:
|
||||
- Win32
|
||||
- x64
|
||||
|
||||
image:
|
||||
- Visual Studio 2013
|
||||
- Visual Studio 2015
|
||||
- Visual Studio 2017
|
||||
|
||||
environment:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
MSVC_DEFAULT_OPTIONS: ON
|
||||
BUILD: msvc
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- image: Visual Studio 2015
|
||||
platform: Win32
|
||||
- image: Visual Studio 2019
|
||||
platform: Win32
|
||||
|
||||
before_build:
|
||||
- mkdir build
|
||||
- cd build
|
||||
|
|
12
externals/fmt/support/build.gradle
vendored
12
externals/fmt/support/build.gradle
vendored
|
@ -9,10 +9,12 @@ buildscript {
|
|||
//
|
||||
// https://developer.android.com/studio/releases/gradle-plugin
|
||||
//
|
||||
// Notice that 3.1.3 here is the version of [Android Gradle Plugin]
|
||||
// Accroding to URL above you will need Gradle 4.4 or higher
|
||||
// Notice that 3.3.0 here is the version of [Android Gradle Plugin]
|
||||
// Accroding to URL above you will need Gradle 5.0 or higher
|
||||
//
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
// If you are using Android Studio, and it is using Gradle's lower
|
||||
// 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 {
|
||||
|
@ -43,8 +45,8 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 21 // Android 5.0+
|
||||
targetSdkVersion 25 // Follow Compile SDK
|
||||
versionCode 20 // Follow release count
|
||||
versionName "5.2.1" // Follow Official version
|
||||
versionCode 21 // Follow release count
|
||||
versionName "5.3.0" // Follow Official version
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
externalNativeBuild {
|
||||
|
|
47
externals/fmt/support/cmake/cxx14.cmake
vendored
47
externals/fmt/support/cmake/cxx14.cmake
vendored
|
@ -48,43 +48,6 @@ endif ()
|
|||
|
||||
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
|
||||
|
||||
# Check if variadic templates are working and not affected by GCC bug 39653:
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
||||
check_cxx_source_compiles("
|
||||
template <class T, class ...Types>
|
||||
struct S { typedef typename S<Types...>::type type; };
|
||||
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
|
||||
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
|
||||
set (SUPPORTS_VARIADIC_TEMPLATES OFF)
|
||||
endif ()
|
||||
|
||||
# Check if initializer lists are supported.
|
||||
check_cxx_source_compiles("
|
||||
#include <initializer_list>
|
||||
int main() {}" SUPPORTS_INITIALIZER_LIST)
|
||||
if (NOT SUPPORTS_INITIALIZER_LIST)
|
||||
set (SUPPORTS_INITIALIZER_LIST OFF)
|
||||
endif ()
|
||||
|
||||
# Check if enum bases are available
|
||||
check_cxx_source_compiles("
|
||||
enum C : char {A};
|
||||
int main() {}"
|
||||
SUPPORTS_ENUM_BASE)
|
||||
if (NOT SUPPORTS_ENUM_BASE)
|
||||
set (SUPPORTS_ENUM_BASE OFF)
|
||||
endif ()
|
||||
|
||||
# Check if type traits are available
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
class C { void operator=(const C&); };
|
||||
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
|
||||
SUPPORTS_TYPE_TRAITS)
|
||||
if (NOT SUPPORTS_TYPE_TRAITS)
|
||||
set (SUPPORTS_TYPE_TRAITS OFF)
|
||||
endif ()
|
||||
|
||||
# Check if user-defined literals are available
|
||||
check_cxx_source_compiles("
|
||||
void operator\"\" _udl(long double);
|
||||
|
@ -94,4 +57,14 @@ if (NOT SUPPORTS_USER_DEFINED_LITERALS)
|
|||
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
|
||||
endif ()
|
||||
|
||||
# Check if <variant> is available
|
||||
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
|
||||
check_cxx_source_compiles("
|
||||
#include <variant>
|
||||
int main() {}"
|
||||
FMT_HAS_VARIANT)
|
||||
if (NOT FMT_HAS_VARIANT)
|
||||
set (FMT_HAS_VARIANT OFF)
|
||||
endif ()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS )
|
||||
|
|
6
externals/fmt/support/cmake/fmt.pc.in
vendored
6
externals/fmt/support/cmake/fmt.pc.in
vendored
|
@ -1,11 +1,11 @@
|
|||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: fmt
|
||||
Description: A modern formatting library
|
||||
Version: @FMT_VERSION@
|
||||
Libs: -L${libdir} -lfmt
|
||||
Libs: -L${libdir} -l@FMT_LIB_NAME@
|
||||
Cflags: -I${includedir}
|
||||
|
||||
|
|
11
externals/fmt/support/cmake/run-cmake.bat
vendored
11
externals/fmt/support/cmake/run-cmake.bat
vendored
|
@ -1,11 +0,0 @@
|
|||
@echo on
|
||||
rem This scripts configures build environment and runs CMake.
|
||||
rem Use it instead of running CMake directly when building with
|
||||
rem the Microsoft SDK toolchain rather than Visual Studio.
|
||||
rem It is used in the same way as cmake, for example:
|
||||
rem
|
||||
rem run-cmake -G "Visual Studio 10 Win64" .
|
||||
|
||||
for /F "delims=" %%i IN ('cmake "-DPRINT_PATH=1" -P %~dp0/FindSetEnv.cmake') DO set setenv=%%i
|
||||
if NOT "%setenv%" == "" call "%setenv%"
|
||||
cmake %*
|
7
externals/fmt/support/manage.py
vendored
7
externals/fmt/support/manage.py
vendored
|
@ -5,6 +5,9 @@
|
|||
Usage:
|
||||
manage.py release [<branch>]
|
||||
manage.py site
|
||||
|
||||
For the release command $FMT_TOKEN should contain a GitHub personal access token
|
||||
obtained from https://github.com/settings/tokens.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
@ -134,7 +137,7 @@ def update_site(env):
|
|||
if not os.path.exists(contents):
|
||||
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
|
||||
# Fix issues in reference.rst/api.rst.
|
||||
for filename in ['reference.rst', 'api.rst']:
|
||||
for filename in ['reference.rst', 'api.rst', 'index.rst']:
|
||||
pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
|
||||
with rewrite(os.path.join(target_doc_dir, filename)) as b:
|
||||
b.data = b.data.replace('std::ostream &', 'std::ostream&')
|
||||
|
@ -142,6 +145,8 @@ def update_site(env):
|
|||
b.data = b.data.replace('std::FILE*', 'std::FILE *')
|
||||
b.data = b.data.replace('unsigned int', 'unsigned')
|
||||
b.data = b.data.replace('operator""_', 'operator"" _')
|
||||
b.data = b.data.replace(', size_t', ', std::size_t')
|
||||
b.data = b.data.replace('aa long', 'a long')
|
||||
# Fix a broken link in index.rst.
|
||||
index = os.path.join(target_doc_dir, 'index.rst')
|
||||
with rewrite(index) as b:
|
||||
|
|
2
externals/fmt/support/rtd/index.rst
vendored
2
externals/fmt/support/rtd/index.rst
vendored
|
@ -1,2 +1,2 @@
|
|||
If you are not redirected automatically, follow the
|
||||
`link to the fmt documentation <http://fmtlib.net/latest/>`_.
|
||||
`link to the fmt documentation <https://fmt.dev/latest/>`_.
|
||||
|
|
6
externals/fmt/support/rtd/theme/layout.html
vendored
6
externals/fmt/support/rtd/theme/layout.html
vendored
|
@ -2,15 +2,15 @@
|
|||
|
||||
{% block extrahead %}
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/">
|
||||
<meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/">
|
||||
<script type="text/javascript">
|
||||
window.location.href = "http://fmtlib.net/latest/"
|
||||
window.location.href = "https://fmt.dev/latest/"
|
||||
</script>
|
||||
<title>Page Redirection</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block document %}
|
||||
If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>.
|
||||
If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>.
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
|
|
18
externals/fmt/support/travis-build.py
vendored
18
externals/fmt/support/travis-build.py
vendored
|
@ -83,19 +83,25 @@ install_dir = os.path.join(fmt_dir, "_install")
|
|||
build_dir = os.path.join(fmt_dir, "_build")
|
||||
test_build_dir = os.path.join(fmt_dir, "_build_test")
|
||||
|
||||
# Configure library.
|
||||
# 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] +
|
||||
cmake_flags, cwd=build_dir)
|
||||
main_cmake_flags, cwd=build_dir)
|
||||
|
||||
# Build library.
|
||||
check_call(['make', '-j4'], cwd=build_dir)
|
||||
# Build the library.
|
||||
check_call(['cmake', '--build','.'], cwd=build_dir)
|
||||
|
||||
# Test library.
|
||||
# Test the library.
|
||||
env = os.environ.copy()
|
||||
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
|
||||
if call(['make', 'test'], env=env, cwd=build_dir):
|
||||
|
@ -103,7 +109,7 @@ if call(['make', 'test'], env=env, cwd=build_dir):
|
|||
print(f.read())
|
||||
sys.exit(-1)
|
||||
|
||||
# Install library.
|
||||
# Install the library.
|
||||
check_call(['make', 'install'], cwd=build_dir)
|
||||
|
||||
# Test installation.
|
||||
|
|
118
externals/fmt/test/CMakeLists.txt
vendored
118
externals/fmt/test/CMakeLists.txt
vendored
|
@ -17,13 +17,18 @@ else ()
|
|||
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
|
||||
endif ()
|
||||
|
||||
if (NOT SUPPORTS_VARIADIC_TEMPLATES OR NOT SUPPORTS_INITIALIZER_LIST)
|
||||
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
|
||||
endif ()
|
||||
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
|
||||
|
||||
# Workaround a bug in implementation of variadic templates in MSVC11.
|
||||
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.
|
||||
|
@ -33,15 +38,13 @@ endif ()
|
|||
|
||||
# Silence MSVC tr1 deprecation warning in gmock.
|
||||
target_compile_definitions(gmock
|
||||
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=0)
|
||||
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)
|
||||
add_library(test-main STATIC ${TEST_MAIN_SRC})
|
||||
target_compile_definitions(test-main PUBLIC
|
||||
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
|
||||
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
|
||||
target_link_libraries(test-main gmock fmt)
|
||||
|
||||
|
@ -74,44 +77,53 @@ function(add_fmt_test name)
|
|||
target_link_libraries(${name} test-main)
|
||||
|
||||
# Define if certain C++ features can be used.
|
||||
target_compile_definitions(${name} PRIVATE
|
||||
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
|
||||
FMT_USE_ENUM_BASE=$<BOOL:${SUPPORTS_ENUM_BASE}>)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
if (FMT_WERROR)
|
||||
target_compile_options(${name} PRIVATE ${WERROR_FLAG})
|
||||
endif ()
|
||||
target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
|
||||
add_test(NAME ${name} COMMAND ${name})
|
||||
endfunction()
|
||||
|
||||
add_fmt_test(assert-test)
|
||||
add_fmt_test(chrono-test)
|
||||
add_fmt_test(color-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(format-test mock-allocator.h)
|
||||
if (MSVC)
|
||||
target_compile_options(format-test PRIVATE /bigobj)
|
||||
endif ()
|
||||
if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
||||
add_fmt_test(format-impl-test)
|
||||
endif ()
|
||||
add_fmt_test(locale-test)
|
||||
add_fmt_test(ostream-test)
|
||||
add_fmt_test(compile-test)
|
||||
add_fmt_test(printf-test)
|
||||
add_fmt_test(time-test)
|
||||
add_fmt_test(custom-formatter-test)
|
||||
add_fmt_test(ranges-test)
|
||||
add_fmt_test(scan-test)
|
||||
|
||||
if (HAVE_OPEN)
|
||||
if (NOT MSVC_BUILD_STATIC)
|
||||
add_fmt_executable(posix-mock-test
|
||||
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
|
||||
target_include_directories(
|
||||
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
|
||||
target_link_libraries(posix-mock-test gmock)
|
||||
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
if (HAVE_STRTOD_L)
|
||||
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
|
||||
endif ()
|
||||
add_test(NAME posix-mock-test COMMAND posix-mock-test)
|
||||
add_fmt_test(posix-test)
|
||||
add_fmt_test(os-test)
|
||||
endif ()
|
||||
|
||||
add_fmt_executable(header-only-test
|
||||
|
@ -126,41 +138,41 @@ else ()
|
|||
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
|
||||
endif ()
|
||||
|
||||
# Test that the library can be compiled with exceptions disabled.
|
||||
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
|
||||
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
||||
endif ()
|
||||
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
||||
add_library(noexception-test ../src/format.cc)
|
||||
target_include_directories(
|
||||
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
target_include_directories(noexception-test SYSTEM PUBLIC gtest gmock)
|
||||
endif ()
|
||||
|
||||
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
|
||||
|
||||
if (FMT_PEDANTIC)
|
||||
# Test that the library compiles without windows.h.
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_library(no-windows-h-test ../src/format.cc)
|
||||
target_include_directories(
|
||||
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(no-windows-h-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
target_include_directories(no-windows-h-test SYSTEM PUBLIC gtest gmock)
|
||||
# MSVC fails to compile GMock when C++17 is enabled.
|
||||
if (FMT_HAS_VARIANT AND NOT MSVC)
|
||||
add_fmt_test(std-format-test)
|
||||
set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
|
||||
endif ()
|
||||
|
||||
add_test(compile-test ${CMAKE_CTEST_COMMAND}
|
||||
# Test that the library can be compiled with exceptions disabled.
|
||||
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
|
||||
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
||||
endif ()
|
||||
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
||||
add_library(noexception-test ../src/format.cc)
|
||||
target_include_directories(
|
||||
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Test that the library compiles without locale.
|
||||
add_library(nolocale-test ../src/format.cc)
|
||||
target_include_directories(
|
||||
nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(
|
||||
nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1)
|
||||
|
||||
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
|
||||
--build-and-test
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/compile-test"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/compile-test"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/compile-error-test"
|
||||
--build-generator ${CMAKE_GENERATOR}
|
||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||
--build-options
|
||||
|
@ -202,3 +214,23 @@ if (FMT_PEDANTIC AND NOT WIN32)
|
|||
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||
endif ()
|
||||
|
||||
# Activate optional CUDA tests if CUDA is found. For version selection see
|
||||
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
|
||||
if (FMT_CUDA_TEST)
|
||||
if (${CMAKE_VERSION} VERSION_LESS 3.15)
|
||||
find_package(CUDA 9.0)
|
||||
else ()
|
||||
include(CheckLanguage)
|
||||
check_language(CUDA)
|
||||
if (CMAKE_CUDA_COMPILER)
|
||||
enable_language(CUDA OPTIONAL)
|
||||
set(CUDA_FOUND TRUE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (CUDA_FOUND)
|
||||
add_subdirectory(cuda-test)
|
||||
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
26
externals/fmt/test/assert-test.cc
vendored
26
externals/fmt/test/assert-test.cc
vendored
|
@ -8,15 +8,21 @@
|
|||
#include "fmt/core.h"
|
||||
#include "gtest.h"
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
EXPECT_DEBUG_DEATH(statement, regex)
|
||||
#else
|
||||
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
|
||||
#endif
|
||||
|
||||
TEST(AssertTest, Fail) {
|
||||
EXPECT_DEBUG_DEATH_IF_SUPPORTED(
|
||||
FMT_ASSERT(false, "don't panic!"), "don't panic!");
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!");
|
||||
#else
|
||||
fmt::print("warning: death tests are not supported\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool test_condition = false;
|
||||
|
||||
TEST(AssertTest, DanglingElse) {
|
||||
bool executed_else = false;
|
||||
if (test_condition)
|
||||
FMT_ASSERT(true, "");
|
||||
else
|
||||
executed_else = true;
|
||||
EXPECT_TRUE(executed_else);
|
||||
}
|
||||
|
|
235
externals/fmt/test/chrono-test.cc
vendored
235
externals/fmt/test/chrono-test.cc
vendored
|
@ -5,6 +5,10 @@
|
|||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifdef WIN32
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/chrono.h"
|
||||
#include "gtest-extra.h"
|
||||
|
||||
|
@ -34,21 +38,71 @@ std::tm make_second(int s) {
|
|||
return time;
|
||||
}
|
||||
|
||||
std::string format_tm(const std::tm &time, const char *spec,
|
||||
const std::locale &loc) {
|
||||
auto &facet = std::use_facet<std::time_put<char>>(loc);
|
||||
std::string format_tm(const std::tm& time, const char* spec,
|
||||
const std::locale& loc) {
|
||||
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();
|
||||
}
|
||||
|
||||
#define EXPECT_TIME(spec, time, duration) { \
|
||||
std::locale loc("ja_JP.utf8"); \
|
||||
EXPECT_EQ(format_tm(time, spec, loc), \
|
||||
TEST(TimeTest, Format) {
|
||||
std::tm tm = std::tm();
|
||||
tm.tm_year = 116;
|
||||
tm.tm_mon = 3;
|
||||
tm.tm_mday = 25;
|
||||
EXPECT_EQ("The date is 2016-04-25.",
|
||||
fmt::format("The date is {:%Y-%m-%d}.", tm));
|
||||
}
|
||||
|
||||
TEST(TimeTest, GrowBuffer) {
|
||||
std::string s = "{:";
|
||||
for (int i = 0; i < 30; ++i) s += "%c";
|
||||
s += "}\n";
|
||||
std::time_t t = std::time(nullptr);
|
||||
fmt::format(s, *std::localtime(&t));
|
||||
}
|
||||
|
||||
TEST(TimeTest, FormatToEmptyContainer) {
|
||||
std::string s;
|
||||
auto time = std::tm();
|
||||
time.tm_sec = 42;
|
||||
fmt::format_to(std::back_inserter(s), "{:%S}", time);
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
|
||||
TEST(TimeTest, EmptyResult) { EXPECT_EQ("", fmt::format("{}", std::tm())); }
|
||||
|
||||
static bool EqualTime(const std::tm& lhs, const std::tm& rhs) {
|
||||
return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min &&
|
||||
lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday &&
|
||||
lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year &&
|
||||
lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday &&
|
||||
lhs.tm_isdst == rhs.tm_isdst;
|
||||
}
|
||||
|
||||
TEST(TimeTest, LocalTime) {
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
|
||||
}
|
||||
|
||||
TEST(TimeTest, GMTime) {
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::gmtime(&t);
|
||||
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
||||
}
|
||||
|
||||
#define EXPECT_TIME(spec, time, duration) \
|
||||
{ \
|
||||
std::locale loc("ja_JP.utf8"); \
|
||||
EXPECT_EQ(format_tm(time, spec, loc), \
|
||||
fmt::format(loc, "{:" spec "}", duration)); \
|
||||
}
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
||||
TEST(ChronoTest, FormatDefault) {
|
||||
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
|
||||
EXPECT_EQ("42as",
|
||||
|
@ -83,12 +137,54 @@ TEST(ChronoTest, FormatDefault) {
|
|||
fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
|
||||
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
|
||||
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
|
||||
EXPECT_EQ("42[15]s",
|
||||
fmt::format("{}",
|
||||
std::chrono::duration<int, std::ratio<15, 1>>(42)));
|
||||
EXPECT_EQ("42[15/4]s",
|
||||
fmt::format("{}",
|
||||
std::chrono::duration<int, std::ratio<15, 4>>(42)));
|
||||
EXPECT_EQ(
|
||||
"42[15]s",
|
||||
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
|
||||
EXPECT_EQ(
|
||||
"42[15/4]s",
|
||||
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatWide) {
|
||||
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) {
|
||||
|
@ -103,7 +199,8 @@ TEST(ChronoTest, Align) {
|
|||
fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)));
|
||||
EXPECT_EQ("~~03:25:45~~",
|
||||
fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)));
|
||||
|
||||
EXPECT_EQ("03:25:45 ",
|
||||
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatSpecs) {
|
||||
|
@ -131,6 +228,8 @@ TEST(ChronoTest, FormatSpecs) {
|
|||
fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
|
||||
EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345)));
|
||||
EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345)));
|
||||
EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345)));
|
||||
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, InvalidSpecs) {
|
||||
|
@ -151,8 +250,6 @@ TEST(ChronoTest, InvalidSpecs) {
|
|||
EXPECT_THROW_MSG(fmt::format("{:%B}", sec), fmt::format_error, "no date");
|
||||
EXPECT_THROW_MSG(fmt::format("{:%z}", sec), fmt::format_error, "no date");
|
||||
EXPECT_THROW_MSG(fmt::format("{:%Z}", sec), fmt::format_error, "no date");
|
||||
EXPECT_THROW_MSG(fmt::format("{:%q}", sec), fmt::format_error,
|
||||
"invalid format");
|
||||
EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error,
|
||||
"invalid format");
|
||||
EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error,
|
||||
|
@ -160,13 +257,14 @@ TEST(ChronoTest, InvalidSpecs) {
|
|||
}
|
||||
|
||||
TEST(ChronoTest, Locale) {
|
||||
const char *loc_name = "ja_JP.utf8";
|
||||
const char* loc_name = "ja_JP.utf8";
|
||||
bool has_locale = false;
|
||||
std::locale loc;
|
||||
try {
|
||||
loc = std::locale(loc_name);
|
||||
has_locale = true;
|
||||
} catch (const std::runtime_error &) {}
|
||||
} catch (const std::runtime_error&) {
|
||||
}
|
||||
if (!has_locale) {
|
||||
fmt::print("{} locale is missing.\n", loc_name);
|
||||
return;
|
||||
|
@ -183,3 +281,106 @@ TEST(ChronoTest, Locale) {
|
|||
EXPECT_TIME("%r", time, sec);
|
||||
EXPECT_TIME("%p", time, sec);
|
||||
}
|
||||
|
||||
typedef std::chrono::duration<double, std::milli> dms;
|
||||
|
||||
TEST(ChronoTest, FormatDefaultFP) {
|
||||
typedef std::chrono::duration<float> fs;
|
||||
EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234)));
|
||||
typedef std::chrono::duration<float, std::milli> fms;
|
||||
EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234)));
|
||||
typedef std::chrono::duration<double> ds;
|
||||
EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234)));
|
||||
EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatPrecision) {
|
||||
EXPECT_THROW_MSG(fmt::format("{:.2}", std::chrono::seconds(42)),
|
||||
fmt::format_error,
|
||||
"precision not allowed for this argument type");
|
||||
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
|
||||
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatFullSpecs) {
|
||||
EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
|
||||
EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
|
||||
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
|
||||
EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8));
|
||||
EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3));
|
||||
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatSimpleQq) {
|
||||
typedef std::chrono::duration<float> fs;
|
||||
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234)));
|
||||
typedef std::chrono::duration<float, std::milli> fms;
|
||||
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234)));
|
||||
typedef std::chrono::duration<double> ds;
|
||||
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234)));
|
||||
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatPrecisionQq) {
|
||||
EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)),
|
||||
fmt::format_error,
|
||||
"precision not allowed for this argument type");
|
||||
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
|
||||
EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, FormatFullSpecsQq) {
|
||||
EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
|
||||
EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2));
|
||||
EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
|
||||
EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9));
|
||||
EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3));
|
||||
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, InvalidWidthId) {
|
||||
EXPECT_THROW(fmt::format("{:{o}", std::chrono::seconds(0)),
|
||||
fmt::format_error);
|
||||
}
|
||||
|
||||
TEST(ChronoTest, InvalidColons) {
|
||||
EXPECT_THROW(fmt::format("{0}=:{0::", std::chrono::seconds(0)),
|
||||
fmt::format_error);
|
||||
}
|
||||
|
||||
TEST(ChronoTest, NegativeDurations) {
|
||||
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
|
||||
EXPECT_EQ("-03:25:45",
|
||||
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
|
||||
EXPECT_EQ("-00:01",
|
||||
fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
|
||||
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345)));
|
||||
EXPECT_EQ("-00.127",
|
||||
fmt::format("{:%S}",
|
||||
std::chrono::duration<signed char, std::milli>{-127}));
|
||||
auto min = std::numeric_limits<int>::min();
|
||||
EXPECT_EQ(fmt::format("{}", min),
|
||||
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
|
||||
}
|
||||
|
||||
TEST(ChronoTest, SpecialDurations) {
|
||||
EXPECT_EQ(
|
||||
"40.",
|
||||
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
|
||||
auto nan = std::numeric_limits<double>::quiet_NaN();
|
||||
EXPECT_EQ(
|
||||
"nan nan nan nan nan:nan nan",
|
||||
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
|
||||
fmt::format("{:%S}",
|
||||
std::chrono::duration<float, std::atto>(1.79400457e+31f));
|
||||
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)),
|
||||
"1Es");
|
||||
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
|
||||
"1as");
|
||||
EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration<char, std::mega>{2}),
|
||||
"03:33");
|
||||
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
|
||||
"03:33:20");
|
||||
}
|
||||
|
||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
|
85
externals/fmt/test/color-test.cc
vendored
Normal file
85
externals/fmt/test/color-test.cc
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Formatting library for C++ - color tests
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/color.h"
|
||||
#include "gtest-extra.h"
|
||||
|
||||
TEST(ColorsTest, ColorsPrint) {
|
||||
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) {
|
||||
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
|
||||
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fg(fmt::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"),
|
||||
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
||||
EXPECT_EQ(
|
||||
fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
|
||||
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"),
|
||||
"\x1b[3mitalic\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"),
|
||||
"\x1b[4munderline\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"),
|
||||
"\x1b[9mstrikethrough\x1b[0m");
|
||||
EXPECT_EQ(
|
||||
fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
|
||||
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
|
||||
"\x1b[1mbold error\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"),
|
||||
"\x1b[38;2;000;000;255mblue log\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fmt::text_style(), "hi"), "hi");
|
||||
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "tred"),
|
||||
"\x1b[31mtred\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::cyan), "tcyan"),
|
||||
"\x1b[46mtcyan\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::bright_green), "tbgreen"),
|
||||
"\x1b[92mtbgreen\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
|
||||
"\x1b[105mtbmagenta\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
|
||||
"\x1b[31mfoo\x1b[0m");
|
||||
}
|
143
externals/fmt/test/compile-test.cc
vendored
Normal file
143
externals/fmt/test/compile-test.cc
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
// 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 <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 "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "mock-allocator.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef ERROR
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
|
||||
// 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::internal::compiled_format_base<decltype(format)> provider;
|
||||
typedef fmt::internal::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) {
|
||||
check_prepared_parts_type<1u>(FMT_STRING("text"));
|
||||
check_prepared_parts_type<1u>(FMT_STRING("{}"));
|
||||
check_prepared_parts_type<2u>(FMT_STRING("text{}"));
|
||||
check_prepared_parts_type<2u>(FMT_STRING("{}text"));
|
||||
check_prepared_parts_type<3u>(FMT_STRING("text{}text"));
|
||||
check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}"));
|
||||
|
||||
check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}'
|
||||
check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{'
|
||||
check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' '
|
||||
check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text
|
||||
check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text'
|
||||
check_prepared_parts_type<4u>(
|
||||
FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text'
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(CompileTest, PassStringLiteralFormat) {
|
||||
const auto prepared = fmt::compile<int>("test {}");
|
||||
EXPECT_EQ("test 42", fmt::format(prepared, 42));
|
||||
const auto wprepared = fmt::compile<int>(L"test {}");
|
||||
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
TEST(CompileTest, PassCompileString) {
|
||||
const auto prepared = fmt::compile<int>(FMT_STRING("test {}"));
|
||||
EXPECT_EQ("test 42", fmt::format(prepared, 42));
|
||||
const auto wprepared = fmt::compile<int>(FMT_STRING(L"test {}"));
|
||||
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(CompileTest, FormatToArrayOfChars) {
|
||||
char buffer[32] = {0};
|
||||
const auto prepared = fmt::compile<int>("4{}");
|
||||
fmt::format_to(fmt::internal::make_checked(buffer, 32), prepared, 2);
|
||||
EXPECT_EQ(std::string("42"), buffer);
|
||||
wchar_t wbuffer[32] = {0};
|
||||
const auto wprepared = fmt::compile<int>(L"4{}");
|
||||
fmt::format_to(fmt::internal::make_checked(wbuffer, 32), wprepared, 2);
|
||||
EXPECT_EQ(std::wstring(L"42"), wbuffer);
|
||||
}
|
||||
|
||||
TEST(CompileTest, FormatToIterator) {
|
||||
std::string s(2, ' ');
|
||||
const auto prepared = fmt::compile<int>("4{}");
|
||||
fmt::format_to(s.begin(), prepared, 2);
|
||||
EXPECT_EQ("42", s);
|
||||
std::wstring ws(2, L' ');
|
||||
const auto wprepared = fmt::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::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::compile<int>("{:10}");
|
||||
EXPECT_EQ(fmt::formatted_size(f, 42), 10);
|
||||
}
|
||||
|
||||
TEST(CompileTest, MultipleTypes) {
|
||||
auto f = fmt::compile<int, int>("{} {}");
|
||||
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
|
||||
}
|
||||
|
||||
struct formattable {};
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<formattable> : formatter<const char*> {
|
||||
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
|
||||
return formatter<const char*>::format("foo", ctx);
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(CompileTest, FormatUserDefinedType) {
|
||||
auto f = fmt::compile<formattable>("{}");
|
||||
EXPECT_EQ(fmt::format(f, formattable()), "foo");
|
||||
}
|
||||
|
||||
TEST(CompileTest, EmptyFormatString) {
|
||||
auto f = fmt::compile<>("");
|
||||
EXPECT_EQ(fmt::format(f), "");
|
||||
}
|
497
externals/fmt/test/core-test.cc
vendored
497
externals/fmt/test/core-test.cc
vendored
|
@ -11,17 +11,16 @@
|
|||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
#include "test-assert.h"
|
||||
|
||||
#include "gmock.h"
|
||||
#include "test-assert.h"
|
||||
|
||||
// Check if fmt/core.h compiles with windows.h included before it.
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
@ -30,9 +29,9 @@
|
|||
#undef max
|
||||
|
||||
using fmt::basic_format_arg;
|
||||
using fmt::internal::basic_buffer;
|
||||
using fmt::internal::value;
|
||||
using fmt::string_view;
|
||||
using fmt::internal::buffer;
|
||||
using fmt::internal::value;
|
||||
|
||||
using testing::_;
|
||||
using testing::StrictMock;
|
||||
|
@ -42,24 +41,23 @@ namespace {
|
|||
struct test_struct {};
|
||||
|
||||
template <typename Context, typename T>
|
||||
basic_format_arg<Context> make_arg(const T &value) {
|
||||
basic_format_arg<Context> make_arg(const T& value) {
|
||||
return fmt::internal::make_arg<Context>(value);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char>
|
||||
struct formatter<test_struct, Char> {
|
||||
template <typename Char> struct formatter<test_struct, Char> {
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
typedef std::back_insert_iterator<basic_buffer<Char>> iterator;
|
||||
typedef std::back_insert_iterator<buffer<Char>> iterator;
|
||||
|
||||
auto format(test_struct, basic_format_context<iterator, char> &ctx)
|
||||
auto format(test_struct, basic_format_context<iterator, char>& ctx)
|
||||
-> decltype(ctx.out()) {
|
||||
const Char *test = "test";
|
||||
const Char* test = "test";
|
||||
return std::copy_n(test, std::strlen(test), ctx.out());
|
||||
}
|
||||
};
|
||||
|
@ -67,31 +65,29 @@ FMT_END_NAMESPACE
|
|||
|
||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
|
||||
TEST(BufferTest, Noncopyable) {
|
||||
EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char> >::value);
|
||||
#if !FMT_MSC_VER
|
||||
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
|
||||
# if !FMT_MSC_VER
|
||||
// std::is_copy_assignable is broken in MSVC2013.
|
||||
EXPECT_FALSE(std::is_copy_assignable<basic_buffer<char> >::value);
|
||||
#endif
|
||||
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(BufferTest, Nonmoveable) {
|
||||
EXPECT_FALSE(std::is_move_constructible<basic_buffer<char> >::value);
|
||||
#if !FMT_MSC_VER
|
||||
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
|
||||
# if !FMT_MSC_VER
|
||||
// std::is_move_assignable is broken in MSVC2013.
|
||||
EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value);
|
||||
#endif
|
||||
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// A test buffer with a dummy grow method.
|
||||
template <typename T>
|
||||
struct test_buffer : basic_buffer<T> {
|
||||
void grow(std::size_t capacity) { this->set(FMT_NULL, capacity); }
|
||||
template <typename T> struct test_buffer : buffer<T> {
|
||||
void grow(std::size_t capacity) { this->set(nullptr, capacity); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct mock_buffer : basic_buffer<T> {
|
||||
MOCK_METHOD1(do_grow, void (std::size_t capacity));
|
||||
template <typename T> struct mock_buffer : buffer<T> {
|
||||
MOCK_METHOD1(do_grow, void(std::size_t capacity));
|
||||
|
||||
void grow(std::size_t capacity) {
|
||||
this->set(this->data(), capacity);
|
||||
|
@ -99,14 +95,14 @@ struct mock_buffer : basic_buffer<T> {
|
|||
}
|
||||
|
||||
mock_buffer() {}
|
||||
mock_buffer(T *data) { this->set(data, 0); }
|
||||
mock_buffer(T *data, std::size_t capacity) { this->set(data, capacity); }
|
||||
mock_buffer(T* data) { this->set(data, 0); }
|
||||
mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); }
|
||||
};
|
||||
|
||||
TEST(BufferTest, Ctor) {
|
||||
{
|
||||
mock_buffer<int> buffer;
|
||||
EXPECT_EQ(FMT_NULL, &buffer[0]);
|
||||
EXPECT_EQ(nullptr, buffer.data());
|
||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
||||
}
|
||||
|
@ -130,13 +126,18 @@ TEST(BufferTest, Ctor) {
|
|||
struct dying_buffer : test_buffer<int> {
|
||||
MOCK_METHOD0(die, void());
|
||||
~dying_buffer() { die(); }
|
||||
|
||||
private:
|
||||
virtual void avoid_weak_vtable();
|
||||
};
|
||||
|
||||
void dying_buffer::avoid_weak_vtable() {}
|
||||
|
||||
TEST(BufferTest, VirtualDtor) {
|
||||
typedef StrictMock<dying_buffer> stict_mock_buffer;
|
||||
stict_mock_buffer *mock_buffer = new stict_mock_buffer();
|
||||
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
|
||||
EXPECT_CALL(*mock_buffer, die());
|
||||
basic_buffer<int> *buffer = mock_buffer;
|
||||
buffer<int>* buffer = mock_buffer;
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
|
@ -147,7 +148,7 @@ TEST(BufferTest, Access) {
|
|||
EXPECT_EQ(11, buffer[0]);
|
||||
buffer[3] = 42;
|
||||
EXPECT_EQ(42, *(&buffer[0] + 3));
|
||||
const basic_buffer<char> &const_buffer = buffer;
|
||||
const fmt::internal::buffer<char>& const_buffer = buffer;
|
||||
EXPECT_EQ(42, const_buffer[3]);
|
||||
}
|
||||
|
||||
|
@ -182,7 +183,7 @@ TEST(BufferTest, Clear) {
|
|||
TEST(BufferTest, Append) {
|
||||
char data[15];
|
||||
mock_buffer<char> buffer(data, 10);
|
||||
const char *test = "test";
|
||||
const char* test = "test";
|
||||
buffer.append(test, test + 5);
|
||||
EXPECT_STREQ(test, &buffer[0]);
|
||||
EXPECT_EQ(5u, buffer.size());
|
||||
|
@ -197,7 +198,7 @@ TEST(BufferTest, Append) {
|
|||
TEST(BufferTest, AppendAllocatesEnoughStorage) {
|
||||
char data[19];
|
||||
mock_buffer<char> buffer(data, 10);
|
||||
const char *test = "abcdefgh";
|
||||
const char* test = "abcdefgh";
|
||||
buffer.resize(10);
|
||||
EXPECT_CALL(buffer, do_grow(19));
|
||||
buffer.append(test, test + 9);
|
||||
|
@ -211,35 +212,31 @@ TEST(ArgTest, FormatArgs) {
|
|||
struct custom_context {
|
||||
typedef char char_type;
|
||||
|
||||
template <typename T>
|
||||
struct formatter_type {
|
||||
struct type {
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename T> struct formatter_type {
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
const char *format(const T &, custom_context& ctx) {
|
||||
ctx.called = true;
|
||||
return FMT_NULL;
|
||||
}
|
||||
};
|
||||
const char* format(const T&, custom_context& ctx) {
|
||||
ctx.called = true;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
bool called;
|
||||
fmt::format_parse_context ctx;
|
||||
|
||||
fmt::format_parse_context parse_context() {
|
||||
return fmt::format_parse_context("");
|
||||
}
|
||||
void advance_to(const char *) {}
|
||||
fmt::format_parse_context& parse_context() { return ctx; }
|
||||
void advance_to(const char*) {}
|
||||
};
|
||||
|
||||
TEST(ArgTest, MakeValueWithCustomContext) {
|
||||
test_struct t;
|
||||
fmt::internal::value<custom_context> arg =
|
||||
fmt::internal::make_value<custom_context>(t);
|
||||
custom_context ctx = {false};
|
||||
arg.custom.format(&t, ctx);
|
||||
fmt::internal::value<custom_context> arg(
|
||||
fmt::internal::arg_mapper<custom_context>().map(t));
|
||||
custom_context ctx = {false, fmt::format_parse_context("")};
|
||||
arg.custom.format(&t, ctx.parse_context(), ctx);
|
||||
EXPECT_TRUE(ctx.called);
|
||||
}
|
||||
|
||||
|
@ -249,40 +246,35 @@ template <typename Char>
|
|||
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
// Use a unique result type to make sure that there are no undesirable
|
||||
// conversions.
|
||||
struct test_result {};
|
||||
|
||||
template <typename T>
|
||||
struct mock_visitor {
|
||||
template <typename U>
|
||||
struct result { typedef test_result type; };
|
||||
template <typename T> struct mock_visitor {
|
||||
template <typename U> struct result { typedef test_result type; };
|
||||
|
||||
mock_visitor() {
|
||||
ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
|
||||
}
|
||||
|
||||
MOCK_METHOD1_T(visit, test_result (T value));
|
||||
MOCK_METHOD0_T(unexpected, void ());
|
||||
MOCK_METHOD1_T(visit, test_result(T value));
|
||||
MOCK_METHOD0_T(unexpected, void());
|
||||
|
||||
test_result operator()(T value) { return visit(value); }
|
||||
|
||||
template <typename U>
|
||||
test_result operator()(U) {
|
||||
template <typename U> test_result operator()(U) {
|
||||
unexpected();
|
||||
return test_result();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct visit_type { typedef T Type; };
|
||||
template <typename T> struct visit_type { typedef T Type; };
|
||||
|
||||
#define VISIT_TYPE(Type_, visit_type_) \
|
||||
template <> \
|
||||
struct visit_type<Type_> { typedef visit_type_ Type; }
|
||||
template <> struct visit_type<Type_> { typedef visit_type_ Type; }
|
||||
|
||||
VISIT_TYPE(signed char, int);
|
||||
VISIT_TYPE(unsigned char, unsigned);
|
||||
|
@ -297,30 +289,30 @@ VISIT_TYPE(long, long long);
|
|||
VISIT_TYPE(unsigned long, unsigned long long);
|
||||
#endif
|
||||
|
||||
VISIT_TYPE(float, double);
|
||||
#define CHECK_ARG_(Char, expected, value) \
|
||||
{ \
|
||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
typedef std::back_insert_iterator<buffer<Char>> iterator; \
|
||||
fmt::visit_format_arg( \
|
||||
visitor, make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
|
||||
}
|
||||
|
||||
#define CHECK_ARG_(Char, expected, value) { \
|
||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
typedef std::back_insert_iterator<basic_buffer<Char>> iterator; \
|
||||
fmt::visit(visitor, \
|
||||
make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
|
||||
}
|
||||
#define CHECK_ARG(value, typename_) \
|
||||
{ \
|
||||
typedef decltype(value) value_type; \
|
||||
typename_ visit_type<value_type>::Type expected = value; \
|
||||
CHECK_ARG_(char, expected, value) \
|
||||
CHECK_ARG_(wchar_t, expected, value) \
|
||||
}
|
||||
|
||||
#define CHECK_ARG(value, typename_) { \
|
||||
typedef decltype(value) value_type; \
|
||||
typename_ visit_type<value_type>::Type expected = value; \
|
||||
CHECK_ARG_(char, expected, value) \
|
||||
CHECK_ARG_(wchar_t, expected, value) \
|
||||
}
|
||||
template <typename T> class NumericArgTest : public testing::Test {};
|
||||
|
||||
template <typename T>
|
||||
class NumericArgTest : public testing::Test {};
|
||||
|
||||
typedef ::testing::Types<
|
||||
bool, signed char, unsigned char, signed, unsigned short,
|
||||
int, unsigned, long, unsigned long, long long, unsigned long long,
|
||||
float, double, long double> Types;
|
||||
typedef ::testing::Types<bool, signed char, unsigned char, signed,
|
||||
unsigned short, int, unsigned, long, unsigned long,
|
||||
long long, unsigned long long, float, double,
|
||||
long double>
|
||||
Types;
|
||||
TYPED_TEST_CASE(NumericArgTest, Types);
|
||||
|
||||
template <typename T>
|
||||
|
@ -330,7 +322,7 @@ typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
|
|||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_floating_point<T>::value, T>::type
|
||||
test_value() {
|
||||
test_value() {
|
||||
return static_cast<T>(4.2);
|
||||
}
|
||||
|
||||
|
@ -348,8 +340,8 @@ TEST(ArgTest, CharArg) {
|
|||
|
||||
TEST(ArgTest, StringArg) {
|
||||
char str_data[] = "test";
|
||||
char *str = str_data;
|
||||
const char *cstr = str;
|
||||
char* str = str_data;
|
||||
const char* cstr = str;
|
||||
CHECK_ARG_(char, cstr, str);
|
||||
|
||||
string_view sref(str);
|
||||
|
@ -358,8 +350,8 @@ TEST(ArgTest, StringArg) {
|
|||
|
||||
TEST(ArgTest, WStringArg) {
|
||||
wchar_t str_data[] = L"test";
|
||||
wchar_t *str = str_data;
|
||||
const wchar_t *cstr = str;
|
||||
wchar_t* str = str_data;
|
||||
const wchar_t* cstr = str;
|
||||
|
||||
fmt::wstring_view sref(str);
|
||||
CHECK_ARG_(wchar_t, cstr, str);
|
||||
|
@ -369,8 +361,8 @@ TEST(ArgTest, WStringArg) {
|
|||
}
|
||||
|
||||
TEST(ArgTest, PointerArg) {
|
||||
void *p = FMT_NULL;
|
||||
const void *cp = FMT_NULL;
|
||||
void* p = nullptr;
|
||||
const void* cp = nullptr;
|
||||
CHECK_ARG_(char, cp, p);
|
||||
CHECK_ARG_(wchar_t, cp, p);
|
||||
CHECK_ARG(cp, );
|
||||
|
@ -379,14 +371,15 @@ TEST(ArgTest, PointerArg) {
|
|||
struct check_custom {
|
||||
test_result operator()(
|
||||
fmt::basic_format_arg<fmt::format_context>::handle h) const {
|
||||
struct test_buffer : fmt::internal::basic_buffer<char> {
|
||||
struct test_buffer : fmt::internal::buffer<char> {
|
||||
char data[10];
|
||||
test_buffer() : fmt::internal::basic_buffer<char>(data, 0, 10) {}
|
||||
test_buffer() : fmt::internal::buffer<char>(data, 0, 10) {}
|
||||
void grow(std::size_t) {}
|
||||
} buffer;
|
||||
fmt::internal::basic_buffer<char> &base = buffer;
|
||||
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
|
||||
h.format(ctx);
|
||||
fmt::internal::buffer<char>& base = buffer;
|
||||
fmt::format_parse_context parse_ctx("");
|
||||
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
|
||||
h.format(parse_ctx, ctx);
|
||||
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
|
||||
return test_result();
|
||||
}
|
||||
|
@ -395,30 +388,135 @@ struct check_custom {
|
|||
TEST(ArgTest, CustomArg) {
|
||||
test_struct test;
|
||||
typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
|
||||
visitor;
|
||||
visitor;
|
||||
testing::StrictMock<visitor> v;
|
||||
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom()));
|
||||
fmt::visit(v, make_arg<fmt::format_context>(test));
|
||||
fmt::visit_format_arg(v, make_arg<fmt::format_context>(test));
|
||||
}
|
||||
|
||||
TEST(ArgTest, VisitInvalidArg) {
|
||||
testing::StrictMock< mock_visitor<fmt::monostate> > visitor;
|
||||
testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
|
||||
EXPECT_CALL(visitor, visit(_));
|
||||
fmt::basic_format_arg<fmt::format_context> arg;
|
||||
visit(visitor, arg);
|
||||
fmt::visit_format_arg(visitor, arg);
|
||||
}
|
||||
|
||||
TEST(FormatDynArgsTest, Basic) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(42);
|
||||
store.push_back("abc1");
|
||||
store.push_back(1.5f);
|
||||
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
EXPECT_EQ("42 and abc1 and 1.5", result);
|
||||
}
|
||||
|
||||
TEST(FormatDynArgsTest, StringsAndRefs) {
|
||||
// Unfortunately the tests are compiled with old ABI so strings use COW.
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
char str[] = "1234567890";
|
||||
store.push_back(str);
|
||||
store.push_back(std::cref(str));
|
||||
store.push_back(fmt::string_view{str});
|
||||
str[0] = 'X';
|
||||
|
||||
std::string 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(FormatDynArgsTest, CustomFormat) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
custom_type c{};
|
||||
store.push_back(c);
|
||||
++c.i;
|
||||
store.push_back(c);
|
||||
++c.i;
|
||||
store.push_back(std::cref(c));
|
||||
++c.i;
|
||||
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
|
||||
}
|
||||
|
||||
TEST(FormatDynArgsTest, NamedArgByRef) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
|
||||
// Note: fmt::arg() constructs an object which holds a reference
|
||||
// to its value. It's not an aggregate, so it doesn't extend the
|
||||
// reference lifetime. As a result, it's a very bad idea passing temporary
|
||||
// as a named argument value. Only GCC with optimization level >0
|
||||
// complains about this.
|
||||
//
|
||||
// A real life usecase is when you have both name and value alive
|
||||
// guarantee their lifetime and thus don't want them to be copied into
|
||||
// storages.
|
||||
int a1_val{42};
|
||||
auto a1 = fmt::arg("a1_", a1_val);
|
||||
store.push_back(std::cref(a1));
|
||||
|
||||
std::string result = fmt::vformat("{a1_}", // and {} and {}",
|
||||
store);
|
||||
|
||||
EXPECT_EQ("42", 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(FormatDynArgsTest, ThrowOnCopy) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(std::string("foo"));
|
||||
try {
|
||||
store.push_back(copy_throwable());
|
||||
} catch (...) {
|
||||
}
|
||||
EXPECT_EQ(fmt::vformat("{}", store), "foo");
|
||||
}
|
||||
|
||||
TEST(StringViewTest, ValueType) {
|
||||
static_assert(std::is_same<string_view::value_type, char>::value, "");
|
||||
}
|
||||
|
||||
TEST(StringViewTest, Length) {
|
||||
// Test that StringRef::size() returns string length, not buffer size.
|
||||
// Test that string_view::size() returns string length, not buffer size.
|
||||
char str[100] = "some string";
|
||||
EXPECT_EQ(std::strlen(str), string_view(str).size());
|
||||
EXPECT_LT(std::strlen(str), sizeof(str));
|
||||
}
|
||||
|
||||
// Check string_view's comparison operator.
|
||||
template <template <typename> class Op>
|
||||
void check_op() {
|
||||
const char *inputs[] = {"foo", "fop", "fo"};
|
||||
template <template <typename> class Op> void check_op() {
|
||||
const char* inputs[] = {"foo", "fop", "fo"};
|
||||
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
|
||||
for (std::size_t i = 0; i < num_inputs; ++i) {
|
||||
for (std::size_t j = 0; j < num_inputs; ++j) {
|
||||
|
@ -442,63 +540,105 @@ TEST(StringViewTest, Compare) {
|
|||
check_op<std::greater_equal>();
|
||||
}
|
||||
|
||||
enum basic_enum {};
|
||||
struct enabled_formatter {};
|
||||
struct disabled_formatter {};
|
||||
struct disabled_formatter_convertible {
|
||||
operator int() const { return 42; }
|
||||
};
|
||||
|
||||
TEST(CoreTest, ConvertToInt) {
|
||||
EXPECT_FALSE((fmt::convert_to_int<char, char>::value));
|
||||
EXPECT_FALSE((fmt::convert_to_int<const char *, char>::value));
|
||||
EXPECT_TRUE((fmt::convert_to_int<basic_enum, char>::value));
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<enabled_formatter> {
|
||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) {
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(CoreTest, HasFormatter) {
|
||||
using fmt::has_formatter;
|
||||
using context = fmt::format_context;
|
||||
static_assert(has_formatter<enabled_formatter, context>::value, "");
|
||||
static_assert(!has_formatter<disabled_formatter, context>::value, "");
|
||||
static_assert(!has_formatter<disabled_formatter_convertible, context>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
enum enum_with_underlying_type : char {};
|
||||
struct convertible_to_int {
|
||||
operator int() const { return 42; }
|
||||
};
|
||||
|
||||
TEST(CoreTest, IsEnumConvertibleToInt) {
|
||||
EXPECT_TRUE((fmt::convert_to_int<enum_with_underlying_type, char>::value));
|
||||
struct convertible_to_c_string {
|
||||
operator const char*() const { return "foo"; }
|
||||
};
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<convertible_to_int> {
|
||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) {
|
||||
return std::copy_n("foo", 3, ctx.out());
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct formatter<convertible_to_c_string> {
|
||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(convertible_to_c_string, format_context& ctx)
|
||||
-> decltype(ctx.out()) {
|
||||
return std::copy_n("bar", 3, ctx.out());
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(CoreTest, FormatterOverridesImplicitConversion) {
|
||||
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
|
||||
EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar");
|
||||
}
|
||||
|
||||
namespace my_ns {
|
||||
template <typename Char>
|
||||
class my_string {
|
||||
template <typename Char> class my_string {
|
||||
public:
|
||||
my_string(const Char *s) : s_(s) {}
|
||||
const Char * data() const FMT_NOEXCEPT { return s_.data(); }
|
||||
my_string(const Char* s) : s_(s) {}
|
||||
const Char* data() const FMT_NOEXCEPT { return s_.data(); }
|
||||
std::size_t length() const FMT_NOEXCEPT { return s_.size(); }
|
||||
operator const Char*() const { return s_.c_str(); }
|
||||
|
||||
private:
|
||||
std::basic_string<Char> s_;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
inline fmt::basic_string_view<Char>
|
||||
to_string_view(const my_string<Char> &s) FMT_NOEXCEPT {
|
||||
return { s.data(), s.length() };
|
||||
inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
|
||||
FMT_NOEXCEPT {
|
||||
return {s.data(), s.length()};
|
||||
}
|
||||
|
||||
struct non_string {};
|
||||
}
|
||||
} // namespace my_ns
|
||||
|
||||
namespace FakeQt {
|
||||
class QString {
|
||||
public:
|
||||
QString(const wchar_t *s) : s_(std::make_shared<std::wstring>(s)) {}
|
||||
const wchar_t *utf16() const FMT_NOEXCEPT { return s_->data(); }
|
||||
QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
|
||||
const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
|
||||
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
|
||||
#ifdef FMT_STRING_VIEW
|
||||
operator FMT_STRING_VIEW<wchar_t>() const FMT_NOEXCEPT { return *s_; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::wstring> s_;
|
||||
};
|
||||
|
||||
inline fmt::basic_string_view<wchar_t> to_string_view(
|
||||
const QString &s) FMT_NOEXCEPT {
|
||||
return {s.utf16(),
|
||||
static_cast<std::size_t>(s.size())};
|
||||
}
|
||||
inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
|
||||
FMT_NOEXCEPT {
|
||||
return {s.utf16(), static_cast<std::size_t>(s.size())};
|
||||
}
|
||||
} // namespace FakeQt
|
||||
|
||||
template <typename T>
|
||||
class IsStringTest : public testing::Test {};
|
||||
template <typename T> class IsStringTest : public testing::Test {};
|
||||
|
||||
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
|
||||
TYPED_TEST_CASE(IsStringTest, StringCharTypes);
|
||||
|
@ -506,30 +646,30 @@ TYPED_TEST_CASE(IsStringTest, StringCharTypes);
|
|||
namespace {
|
||||
template <typename Char>
|
||||
struct derived_from_string_view : fmt::basic_string_view<Char> {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TYPED_TEST(IsStringTest, IsString) {
|
||||
EXPECT_TRUE((fmt::internal::is_string<TypeParam *>::value));
|
||||
EXPECT_TRUE((fmt::internal::is_string<const TypeParam *>::value));
|
||||
EXPECT_TRUE((fmt::internal::is_string<TypeParam[2]>::value));
|
||||
EXPECT_TRUE((fmt::internal::is_string<const TypeParam[2]>::value));
|
||||
EXPECT_TRUE((fmt::internal::is_string<std::basic_string<TypeParam>>::value));
|
||||
EXPECT_TRUE(fmt::internal::is_string<TypeParam*>::value);
|
||||
EXPECT_TRUE(fmt::internal::is_string<const TypeParam*>::value);
|
||||
EXPECT_TRUE(fmt::internal::is_string<TypeParam[2]>::value);
|
||||
EXPECT_TRUE(fmt::internal::is_string<const TypeParam[2]>::value);
|
||||
EXPECT_TRUE(fmt::internal::is_string<std::basic_string<TypeParam>>::value);
|
||||
EXPECT_TRUE(
|
||||
(fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value));
|
||||
fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value);
|
||||
EXPECT_TRUE(
|
||||
(fmt::internal::is_string<derived_from_string_view<TypeParam>>::value));
|
||||
#ifdef FMT_STRING_VIEW
|
||||
EXPECT_TRUE((fmt::internal::is_string<FMT_STRING_VIEW<TypeParam>>::value));
|
||||
#endif
|
||||
EXPECT_TRUE((fmt::internal::is_string<my_ns::my_string<TypeParam>>::value));
|
||||
EXPECT_FALSE((fmt::internal::is_string<my_ns::non_string>::value));
|
||||
EXPECT_TRUE((fmt::internal::is_string<FakeQt::QString>::value));
|
||||
fmt::internal::is_string<derived_from_string_view<TypeParam>>::value);
|
||||
using string_view = fmt::internal::std_string_view<TypeParam>;
|
||||
EXPECT_TRUE(std::is_empty<string_view>::value !=
|
||||
fmt::internal::is_string<string_view>::value);
|
||||
EXPECT_TRUE(fmt::internal::is_string<my_ns::my_string<TypeParam>>::value);
|
||||
EXPECT_FALSE(fmt::internal::is_string<my_ns::non_string>::value);
|
||||
EXPECT_TRUE(fmt::internal::is_string<FakeQt::QString>::value);
|
||||
}
|
||||
|
||||
TEST(CoreTest, Format) {
|
||||
// This should work without including fmt/format.h.
|
||||
#ifdef FMT_FORMAT_H_
|
||||
# error fmt/format.h must not be included in the core test
|
||||
# error fmt/format.h must not be included in the core test
|
||||
#endif
|
||||
EXPECT_EQ(fmt::format("{}", 42), "42");
|
||||
}
|
||||
|
@ -537,7 +677,7 @@ TEST(CoreTest, Format) {
|
|||
TEST(CoreTest, FormatTo) {
|
||||
// This should work without including fmt/format.h.
|
||||
#ifdef FMT_FORMAT_H_
|
||||
# error fmt/format.h must not be included in the core test
|
||||
# error fmt/format.h must not be included in the core test
|
||||
#endif
|
||||
std::string s;
|
||||
fmt::format_to(std::back_inserter(s), "{}", 42);
|
||||
|
@ -551,15 +691,18 @@ TEST(CoreTest, ToStringViewForeignStrings) {
|
|||
EXPECT_EQ(to_string_view(my_string<wchar_t>(L"42")), L"42");
|
||||
EXPECT_EQ(to_string_view(QString(L"42")), L"42");
|
||||
fmt::internal::type type =
|
||||
fmt::internal::get_type<fmt::format_context, my_string<char>>::value;
|
||||
EXPECT_EQ(type, fmt::internal::string_type);
|
||||
fmt::internal::mapped_type_constant<my_string<char>,
|
||||
fmt::format_context>::value;
|
||||
EXPECT_EQ(type, fmt::internal::type::string_type);
|
||||
type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
|
||||
fmt::wformat_context>::value;
|
||||
EXPECT_EQ(type, fmt::internal::type::string_type);
|
||||
type =
|
||||
fmt::internal::get_type<fmt::wformat_context, my_string<wchar_t>>::value;
|
||||
EXPECT_EQ(type, fmt::internal::string_type);
|
||||
type = fmt::internal::get_type<fmt::wformat_context, QString>::value;
|
||||
EXPECT_EQ(type, fmt::internal::string_type);
|
||||
fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
|
||||
EXPECT_EQ(type, fmt::internal::type::string_type);
|
||||
// Does not compile: only wide format contexts are compatible with QString!
|
||||
// type = fmt::internal::get_type<fmt::format_context, QString>::value;
|
||||
// type = fmt::internal::mapped_type_constant<QString,
|
||||
// fmt::format_context>::value;
|
||||
}
|
||||
|
||||
TEST(CoreTest, FormatForeignStrings) {
|
||||
|
@ -572,6 +715,10 @@ TEST(CoreTest, FormatForeignStrings) {
|
|||
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42");
|
||||
}
|
||||
|
||||
struct implicitly_convertible_to_string {
|
||||
operator std::string() const { return "foo"; }
|
||||
};
|
||||
|
||||
struct implicitly_convertible_to_string_view {
|
||||
operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
@ -581,7 +728,7 @@ TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
|
|||
}
|
||||
|
||||
// std::is_constructible is broken in MSVC until version 2015.
|
||||
#if FMT_USE_EXPLICIT && (!FMT_MSC_VER || FMT_MSC_VER >= 1900)
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||
struct explicitly_convertible_to_string_view {
|
||||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
@ -590,6 +737,17 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
|
|||
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
|
||||
}
|
||||
|
||||
# ifdef FMT_USE_STRING_VIEW
|
||||
struct explicitly_convertible_to_std_string_view {
|
||||
explicit operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
|
||||
EXPECT_EQ("foo",
|
||||
fmt::format("{}", explicitly_convertible_to_std_string_view()));
|
||||
}
|
||||
# endif
|
||||
|
||||
struct explicitly_convertible_to_wstring_view {
|
||||
explicit operator fmt::wstring_view() const { return L"foo"; }
|
||||
};
|
||||
|
@ -598,16 +756,15 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
|
|||
EXPECT_EQ(L"foo",
|
||||
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
|
||||
}
|
||||
#endif
|
||||
|
||||
struct explicitly_convertible_to_string_like {
|
||||
template <
|
||||
typename String,
|
||||
typename = typename std::enable_if<
|
||||
std::is_constructible<String, const char*, std::size_t>::value>::type>
|
||||
FMT_EXPLICIT operator String() const { return String("foo", 3u); }
|
||||
struct disabled_rvalue_conversion {
|
||||
operator const char*() const& { return "foo"; }
|
||||
operator const char*() & { return "foo"; }
|
||||
operator const char*() const&& = delete;
|
||||
operator const char*() && = delete;
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
|
||||
TEST(FormatterTest, DisabledRValueConversion) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
|
||||
}
|
||||
#endif
|
||||
|
|
73
externals/fmt/test/cuda-test/CMakeLists.txt
vendored
Normal file
73
externals/fmt/test/cuda-test/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
# We can find some usecases which follow the guide of CMake which uses
|
||||
# `enable_language(CUDA)` instead of `find_package(CUDA)` and let the CMake
|
||||
# built-in functions use NVCC.
|
||||
|
||||
# See: https://cmake.org/cmake/help/latest/module/FindCUDA.html#replacement
|
||||
#
|
||||
# However, this requires CMake version 3.10 or higher and we can't be sure most
|
||||
# of the CUDA projects are using those.
|
||||
#
|
||||
# This test relies on `find_package(CUDA)` in the parent CMake config.
|
||||
|
||||
# These can be updated when NVCC becomes ready for C++ 17 features
|
||||
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
|
||||
set(CMAKE_CUDA_STANDARD 14)
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED 14)
|
||||
|
||||
# In this test, we assume that the user is going to compile CUDA source code
|
||||
# with some libraries (fmt in this case).
|
||||
#
|
||||
# In addition to that, this test invokes both the C++ host compiler and NVCC
|
||||
# by providing another (non-CUDA) C++ source code.
|
||||
if (${CMAKE_VERSION} VERSION_LESS 3.15)
|
||||
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html
|
||||
list(APPEND CUDA_NVCC_FLAGS "-std=c++14")
|
||||
if (MSVC)
|
||||
# This is the solution of pytorch:
|
||||
# https://github.com/pytorch/pytorch/pull/7118
|
||||
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/std:c++14")
|
||||
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/Zc:__cplusplus")
|
||||
# for the reason of this -Xcompiler options, see below.
|
||||
endif ()
|
||||
cuda_add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
|
||||
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
|
||||
if (MSVC)
|
||||
# This part is for (non-CUDA) C++ code. MSVC can define incorrect
|
||||
# `__cplusplus` macro. Fix for the issue is to use additional compiler flag.
|
||||
#
|
||||
# See Also:
|
||||
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
|
||||
# https://github.com/Microsoft/vscode-cpptools/issues/2595
|
||||
target_compile_options(fmt-in-cuda-test PRIVATE /Zc:__cplusplus /permissive-)
|
||||
endif ()
|
||||
else()
|
||||
# now using a "new" way of handling CUDA
|
||||
add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
|
||||
set_target_properties(fmt-in-cuda-test PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
|
||||
if (MSVC)
|
||||
# with MSVC, 'cxx_std_14' will only propagate to the host code (MSVC), but will
|
||||
# not set __cplusplus correctly anyway, while nvcc will ignore it.
|
||||
# If specified for nvcc on the command line as '-std=c++14' nvcc will emit this
|
||||
# message instead:
|
||||
# nvcc warning : The -std=c++14 flag is not supported with the configured host
|
||||
# compiler. Flag will be ignored.
|
||||
set_property(SOURCE cuda-cpp14.cu APPEND PROPERTY
|
||||
COMPILE_OPTIONS -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus)
|
||||
set_property(SOURCE cpp14.cc APPEND PROPERTY
|
||||
COMPILE_OPTIONS /std:c++14 /Zc:__cplusplus)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_target_property(IN_USE_CUDA_STANDARD fmt-in-cuda-test CUDA_STANDARD)
|
||||
message(STATUS "cuda_standard: ${IN_USE_CUDA_STANDARD}")
|
||||
|
||||
get_target_property(IN_USE_CUDA_STANDARD_REQUIRED
|
||||
fmt-in-cuda-test CUDA_STANDARD_REQUIRED)
|
||||
message(STATUS "cuda_standard_required: ${IN_USE_CUDA_STANDARD_REQUIRED}")
|
||||
|
||||
# We don't use PUBLIC or other keyword for reasons explained in the
|
||||
# CUDA_LINK_LIBRARIES_KEYWORD section in
|
||||
# https://cmake.org/cmake/help/latest/module/FindCUDA.html
|
||||
target_link_libraries(fmt-in-cuda-test fmt::fmt)
|
||||
|
11
externals/fmt/test/cuda-test/cpp14.cc
vendored
Normal file
11
externals/fmt/test/cuda-test/cpp14.cc
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
// The purpose of this part is to ensure NVCC's host compiler also supports
|
||||
// the standard version. See 'cuda-cpp14.cu'.
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
|
||||
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for host compiler");
|
||||
|
||||
auto make_message_cpp() -> std::string {
|
||||
return fmt::format("host compiler \t: __cplusplus == {}", __cplusplus);
|
||||
}
|
28
externals/fmt/test/cuda-test/cuda-cpp14.cu
vendored
Normal file
28
externals/fmt/test/cuda-test/cuda-cpp14.cu
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Direct NVCC command line example:
|
||||
//
|
||||
// nvcc ./cuda-cpp14.cu -x cu -I"../include" -l"fmtd" -L"../build/Debug" \
|
||||
// -std=c++14 -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus
|
||||
|
||||
// Ensure that we are using the latest C++ standard for NVCC
|
||||
// The version is C++14
|
||||
//
|
||||
// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-cplusplus-language-support
|
||||
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
|
||||
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for nvcc");
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <cuda.h>
|
||||
#include <iostream>
|
||||
|
||||
extern auto make_message_cpp() -> std::string;
|
||||
extern auto make_message_cuda() -> std::string;
|
||||
|
||||
int main() {
|
||||
std::cout << make_message_cuda() << std::endl;
|
||||
std::cout << make_message_cpp() << std::endl;
|
||||
}
|
||||
|
||||
auto make_message_cuda() -> std::string {
|
||||
return fmt::format("nvcc compiler \t: __cplusplus == {}", __cplusplus);
|
||||
}
|
22
externals/fmt/test/custom-formatter-test.cc
vendored
22
externals/fmt/test/custom-formatter-test.cc
vendored
|
@ -5,6 +5,10 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
|
@ -13,22 +17,22 @@
|
|||
|
||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
||||
// rounded to 0.
|
||||
class custom_arg_formatter :
|
||||
public fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>> {
|
||||
class custom_arg_formatter
|
||||
: public fmt::arg_formatter<fmt::buffer_range<char>> {
|
||||
public:
|
||||
typedef fmt::back_insert_range<fmt::internal::buffer> range;
|
||||
using range = fmt::buffer_range<char>;
|
||||
typedef fmt::arg_formatter<range> base;
|
||||
|
||||
custom_arg_formatter(
|
||||
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
|
||||
: base(ctx, s) {}
|
||||
custom_arg_formatter(fmt::format_context& ctx,
|
||||
fmt::format_parse_context* parse_ctx,
|
||||
fmt::format_specs* s = 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, spec()->precision)) == 0.0)
|
||||
value = 0;
|
||||
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
|
||||
return base::operator()(value);
|
||||
}
|
||||
};
|
||||
|
@ -41,7 +45,7 @@ std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
|||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string custom_format(const char *format_str, const Args & ... 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);
|
||||
}
|
||||
|
|
861
externals/fmt/test/format
vendored
Normal file
861
externals/fmt/test/format
vendored
Normal file
|
@ -0,0 +1,861 @@
|
|||
// Formatting library for C++ - the standard API implementation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_FORMAT_
|
||||
#define FMT_FORMAT_
|
||||
|
||||
#include <cassert>
|
||||
#include <variant>
|
||||
#include "fmt/format.h"
|
||||
|
||||
// This implementation verifies the correctness of the standard API proposed in
|
||||
// P0645 Text Formatting and is optimized for copy-pasting from the paper, not
|
||||
// for efficiency or readability. An efficient implementation should not use
|
||||
// std::variant and should store packed argument type tags separately from
|
||||
// values in basic_format_args for small number of arguments.
|
||||
|
||||
namespace std {
|
||||
template<class T>
|
||||
constexpr bool Integral = is_integral_v<T>;
|
||||
|
||||
template <class O>
|
||||
using iter_difference_t = ptrdiff_t;
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.syn
|
||||
namespace std {
|
||||
// [format.error], class format_error
|
||||
class format_error;
|
||||
|
||||
// [format.formatter], formatter
|
||||
template<class charT> class basic_format_parse_context;
|
||||
using format_parse_context = basic_format_parse_context<char>;
|
||||
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
||||
|
||||
template<class Out, class charT> class basic_format_context;
|
||||
using format_context = basic_format_context<
|
||||
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<char>>, char>;
|
||||
using wformat_context = basic_format_context<
|
||||
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<wchar_t>>, wchar_t>;
|
||||
|
||||
template<class T, class charT = char> struct formatter {
|
||||
formatter() = delete;
|
||||
};
|
||||
|
||||
// [format.arguments], arguments
|
||||
template<class Context> class basic_format_arg;
|
||||
|
||||
template<class Visitor, class Context>
|
||||
/* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
|
||||
|
||||
template<class Context, class... Args> struct format_arg_store; // exposition only
|
||||
|
||||
template<class Context> class basic_format_args;
|
||||
using format_args = basic_format_args<format_context>;
|
||||
using wformat_args = basic_format_args<wformat_context>;
|
||||
|
||||
template<class Out, class charT>
|
||||
using format_args_t = basic_format_args<basic_format_context<Out, charT>>;
|
||||
|
||||
template<class Context = format_context, class... Args>
|
||||
format_arg_store<Context, Args...>
|
||||
make_format_args(const Args&... args);
|
||||
template<class... Args>
|
||||
format_arg_store<wformat_context, Args...>
|
||||
make_wformat_args(const Args&... args);
|
||||
|
||||
// [format.functions], formatting functions
|
||||
template<class... Args>
|
||||
string format(string_view fmt, const Args&... args);
|
||||
template<class... Args>
|
||||
wstring format(wstring_view fmt, const Args&... args);
|
||||
|
||||
string vformat(string_view fmt, format_args args);
|
||||
wstring vformat(wstring_view fmt, wformat_args args);
|
||||
|
||||
template<class Out, class... Args>
|
||||
Out format_to(Out out, string_view fmt, const Args&... args);
|
||||
template<class Out, class... Args>
|
||||
Out format_to(Out out, wstring_view fmt, const Args&... args);
|
||||
|
||||
template<class Out>
|
||||
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
|
||||
template<class Out>
|
||||
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
|
||||
|
||||
template<class Out>
|
||||
struct format_to_n_result {
|
||||
Out out;
|
||||
iter_difference_t<Out> size;
|
||||
};
|
||||
|
||||
template<class Out, class... Args>
|
||||
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
string_view fmt, const Args&... args);
|
||||
template<class Out, class... Args>
|
||||
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
wstring_view fmt, const Args&... args);
|
||||
|
||||
template<class... Args>
|
||||
size_t formatted_size(string_view fmt, const Args&... args);
|
||||
template<class... Args>
|
||||
size_t formatted_size(wstring_view fmt, const Args&... args);
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.error
|
||||
namespace std {
|
||||
class format_error : public runtime_error {
|
||||
public:
|
||||
explicit format_error(const string& what_arg) : runtime_error(what_arg) {}
|
||||
explicit format_error(const char* what_arg) : runtime_error(what_arg) {}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace detail {
|
||||
struct error_handler {
|
||||
// This function is intentionally not constexpr to give a compile-time error.
|
||||
void on_error(const char* message) {
|
||||
throw std::format_error(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.parse_context
|
||||
namespace std {
|
||||
template<class charT>
|
||||
class basic_format_parse_context {
|
||||
public:
|
||||
using char_type = charT;
|
||||
using const_iterator = typename basic_string_view<charT>::const_iterator;
|
||||
using iterator = const_iterator;
|
||||
|
||||
private:
|
||||
iterator begin_; // exposition only
|
||||
iterator end_; // exposition only
|
||||
enum indexing { unknown, manual, automatic }; // exposition only
|
||||
indexing indexing_; // exposition only
|
||||
size_t next_arg_id_; // exposition only
|
||||
size_t num_args_; // exposition only
|
||||
|
||||
public:
|
||||
explicit constexpr basic_format_parse_context(basic_string_view<charT> fmt,
|
||||
size_t num_args = 0) noexcept;
|
||||
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
||||
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
|
||||
|
||||
constexpr const_iterator begin() const noexcept;
|
||||
constexpr const_iterator end() const noexcept;
|
||||
constexpr void advance_to(const_iterator it);
|
||||
|
||||
constexpr size_t next_arg_id();
|
||||
constexpr void check_arg_id(size_t id);
|
||||
|
||||
// Implementation detail:
|
||||
constexpr void check_arg_id(fmt::string_view) {}
|
||||
detail::error_handler error_handler() const { return {}; }
|
||||
void on_error(const char* msg) { error_handler().on_error(msg); }
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<class charT>
|
||||
/* explicit */ constexpr basic_format_parse_context<charT>::
|
||||
basic_format_parse_context(basic_string_view<charT> fmt,
|
||||
size_t num_args) noexcept
|
||||
: begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {}
|
||||
|
||||
template<class charT>
|
||||
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::begin() const noexcept { return begin_; }
|
||||
|
||||
template<class charT>
|
||||
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::end() const noexcept { return end_; }
|
||||
|
||||
template<class charT>
|
||||
constexpr void basic_format_parse_context<charT>::advance_to(typename basic_format_parse_context<charT>::iterator it) { begin_ = it; }
|
||||
|
||||
template<class charT>
|
||||
constexpr size_t basic_format_parse_context<charT>::next_arg_id() {
|
||||
if (indexing_ == manual)
|
||||
throw format_error("manual to automatic indexing");
|
||||
if (indexing_ == unknown)
|
||||
indexing_ = automatic;
|
||||
return next_arg_id_++;
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
constexpr void basic_format_parse_context<charT>::check_arg_id(size_t id) {
|
||||
// clang doesn't support __builtin_is_constant_evaluated yet
|
||||
//if (!(!__builtin_is_constant_evaluated() || id < num_args_))
|
||||
// throw format_error(invalid index is out of range");
|
||||
if (indexing_ == automatic)
|
||||
throw format_error("automatic to manual indexing");
|
||||
if (indexing_ == unknown)
|
||||
indexing_ = manual;
|
||||
}
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.context
|
||||
namespace std {
|
||||
template<class Out, class charT>
|
||||
class basic_format_context {
|
||||
basic_format_args<basic_format_context> args_; // exposition only
|
||||
Out out_; // exposition only
|
||||
|
||||
public:
|
||||
using iterator = Out;
|
||||
using char_type = charT;
|
||||
template<class T> using formatter_type = formatter<T, charT>;
|
||||
|
||||
basic_format_arg<basic_format_context> arg(size_t id) const;
|
||||
|
||||
iterator out();
|
||||
void advance_to(iterator it);
|
||||
|
||||
// Implementation details:
|
||||
using format_arg = basic_format_arg<basic_format_context>;
|
||||
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::internal::locale_ref)
|
||||
: args_(args), out_(out) {}
|
||||
detail::error_handler error_handler() const { return {}; }
|
||||
basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const {
|
||||
return {}; // unused: named arguments are not supported yet
|
||||
}
|
||||
void on_error(const char* msg) { error_handler().on_error(msg); }
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<class O, class charT>
|
||||
basic_format_arg<basic_format_context<O, charT>> basic_format_context<O, charT>::arg(size_t id) const { return args_.get(id); }
|
||||
|
||||
template<class O, class charT>
|
||||
typename basic_format_context<O, charT>::iterator basic_format_context<O, charT>::out() { return out_; }
|
||||
|
||||
template<class O, class charT>
|
||||
void basic_format_context<O, charT>::advance_to(typename basic_format_context<O, charT>::iterator it) { out_ = it; }
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
constexpr bool is_standard_integer_v =
|
||||
std::is_same_v<T, signed char> ||
|
||||
std::is_same_v<T, short int> ||
|
||||
std::is_same_v<T, int> ||
|
||||
std::is_same_v<T, long int> ||
|
||||
std::is_same_v<T, long long int>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_standard_unsigned_integer_v =
|
||||
std::is_same_v<T, unsigned char> ||
|
||||
std::is_same_v<T, unsigned short int> ||
|
||||
std::is_same_v<T, unsigned int> ||
|
||||
std::is_same_v<T, unsigned long int> ||
|
||||
std::is_same_v<T, unsigned long long int>;
|
||||
|
||||
template <typename T, typename Char> struct formatter;
|
||||
}
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.arg
|
||||
namespace std {
|
||||
template<class Context>
|
||||
class basic_format_arg {
|
||||
public:
|
||||
class handle;
|
||||
|
||||
private:
|
||||
using char_type = typename Context::char_type; // exposition only
|
||||
|
||||
variant<monostate, bool, char_type,
|
||||
int, unsigned int, long long int, unsigned long long int,
|
||||
double, long double,
|
||||
const char_type*, basic_string_view<char_type>,
|
||||
const void*, handle> value; // exposition only
|
||||
|
||||
template<typename T,
|
||||
typename = enable_if_t<
|
||||
std::is_same_v<T, bool> ||
|
||||
std::is_same_v<T, char_type> ||
|
||||
(std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) ||
|
||||
detail::is_standard_integer_v<T> ||
|
||||
detail::is_standard_unsigned_integer_v<T> ||
|
||||
sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0
|
||||
>> explicit basic_format_arg(const T& v) noexcept; // exposition only
|
||||
explicit basic_format_arg(float n) noexcept; // exposition only
|
||||
explicit basic_format_arg(double n) noexcept; // exposition only
|
||||
explicit basic_format_arg(long double n) noexcept; // exposition only
|
||||
explicit basic_format_arg(const char_type* s); // exposition only
|
||||
|
||||
template<class traits>
|
||||
explicit basic_format_arg(
|
||||
basic_string_view<char_type, traits> s) noexcept; // exposition only
|
||||
|
||||
template<class traits, class Allocator>
|
||||
explicit basic_format_arg(
|
||||
const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
|
||||
|
||||
explicit basic_format_arg(nullptr_t) noexcept; // exposition only
|
||||
|
||||
template<class T, typename = enable_if_t<is_void_v<T>>>
|
||||
explicit basic_format_arg(const T* p) noexcept; // exposition only
|
||||
|
||||
// Fails due to a bug in clang
|
||||
//template<class Visitor, class Ctx>
|
||||
// friend auto visit_format_arg(Visitor&& vis,
|
||||
// basic_format_arg<Ctx> arg); // exposition only
|
||||
|
||||
friend auto get_value(basic_format_arg arg) {
|
||||
return arg.value;
|
||||
}
|
||||
|
||||
template <typename T, typename Char> friend struct detail::formatter;
|
||||
|
||||
template<class Ctx, class... Args>
|
||||
friend format_arg_store<Ctx, Args...>
|
||||
make_format_args(const Args&... args); // exposition only
|
||||
|
||||
public:
|
||||
basic_format_arg() noexcept;
|
||||
|
||||
explicit operator bool() const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<class Context>
|
||||
basic_format_arg<Context>::basic_format_arg() noexcept {}
|
||||
|
||||
template<class Context>
|
||||
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T& v) noexcept {
|
||||
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char_type>)
|
||||
value = v;
|
||||
else if constexpr (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>)
|
||||
value = static_cast<wchar_t>(v);
|
||||
else if constexpr (detail::is_standard_integer_v<T> && sizeof(T) <= sizeof(int))
|
||||
value = static_cast<int>(v);
|
||||
else if constexpr (detail::is_standard_unsigned_integer_v<T> && sizeof(T) <= sizeof(unsigned))
|
||||
value = static_cast<unsigned>(v);
|
||||
else if constexpr (detail::is_standard_integer_v<T>)
|
||||
value = static_cast<long long int>(v);
|
||||
else if constexpr (detail::is_standard_unsigned_integer_v<T>)
|
||||
value = static_cast<unsigned long long int>(v);
|
||||
else if constexpr (sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0)
|
||||
value = handle(v);
|
||||
}
|
||||
|
||||
template<class Context>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(float n) noexcept
|
||||
: value(static_cast<double>(n)) {}
|
||||
|
||||
template<class Context>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(double n) noexcept
|
||||
: value(n) {}
|
||||
|
||||
template<class Context>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(long double n) noexcept
|
||||
: value(n) {}
|
||||
|
||||
template<class Context>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(const typename basic_format_arg<Context>::char_type* s)
|
||||
: value(s) {
|
||||
assert(s != nullptr);
|
||||
}
|
||||
|
||||
template<class Context>
|
||||
template<class traits>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type, traits> s) noexcept
|
||||
: value(s) {}
|
||||
|
||||
template<class Context>
|
||||
template<class traits, class Allocator>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(
|
||||
const basic_string<char_type, traits, Allocator>& s) noexcept
|
||||
: value(basic_string_view<char_type>(s.data(), s.size())) {}
|
||||
|
||||
|
||||
template<class Context>
|
||||
/* explicit */ basic_format_arg<Context>::basic_format_arg(nullptr_t) noexcept
|
||||
: value(static_cast<const void*>(nullptr)) {}
|
||||
|
||||
template<class Context>
|
||||
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T* p) noexcept
|
||||
: value(p) {}
|
||||
|
||||
template<class Context>
|
||||
/* explicit */ basic_format_arg<Context>::operator bool() const noexcept {
|
||||
return !holds_alternative<monostate>(value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<class Context>
|
||||
class basic_format_arg<Context>::handle {
|
||||
const void* ptr_; // exposition only
|
||||
void (*format_)(basic_format_parse_context<char_type>&,
|
||||
Context&, const void*); // exposition only
|
||||
|
||||
template<class T> explicit handle(const T& val) noexcept; // exposition only
|
||||
|
||||
friend class basic_format_arg<Context>; // exposition only
|
||||
|
||||
public:
|
||||
void format(basic_format_parse_context<char_type>&, Context& ctx) const;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<class Context>
|
||||
template<class T> /* explicit */ basic_format_arg<Context>::handle::handle(const T& val) noexcept
|
||||
: ptr_(&val), format_([](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) {
|
||||
typename Context::template formatter_type<T> f;
|
||||
parse_ctx.advance_to(f.parse(parse_ctx));
|
||||
format_ctx.advance_to(f.format(*static_cast<const T*>(ptr), format_ctx));
|
||||
}) {}
|
||||
|
||||
template<class Context>
|
||||
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const {
|
||||
format_(parse_ctx, format_ctx, ptr_);
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.visit
|
||||
template<class Visitor, class Context>
|
||||
auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg) {
|
||||
return visit(vis, get_value(arg));
|
||||
}
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.store
|
||||
namespace std {
|
||||
template<class Context, class... Args>
|
||||
struct format_arg_store { // exposition only
|
||||
array<basic_format_arg<Context>, sizeof...(Args)> args;
|
||||
};
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.basic_args
|
||||
namespace std {
|
||||
template<class Context>
|
||||
class basic_format_args {
|
||||
size_t size_; // exposition only
|
||||
const basic_format_arg<Context>* data_; // exposition only
|
||||
|
||||
public:
|
||||
basic_format_args() noexcept;
|
||||
|
||||
template<class... Args>
|
||||
basic_format_args(const format_arg_store<Context, Args...>& store) noexcept;
|
||||
|
||||
basic_format_arg<Context> get(size_t i) const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template<class Context>
|
||||
basic_format_args<Context>::basic_format_args() noexcept : size_(0) {}
|
||||
|
||||
template<class Context>
|
||||
template<class... Args>
|
||||
basic_format_args<Context>::basic_format_args(const format_arg_store<Context, Args...>& store) noexcept
|
||||
: size_(sizeof...(Args)), data_(store.args.data()) {}
|
||||
|
||||
template<class Context>
|
||||
basic_format_arg<Context> basic_format_args<Context>::get(size_t i) const noexcept {
|
||||
return i < size_ ? data_[i] : basic_format_arg<Context>();
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
// https://fmt.dev/Text%20Formatting.html#format.make_args
|
||||
template<class Context /*= format_context*/, class... Args>
|
||||
format_arg_store<Context, Args...> make_format_args(const Args&... args) {
|
||||
return {basic_format_arg<Context>(args)...};
|
||||
}
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.make_wargs
|
||||
template<class... Args>
|
||||
format_arg_store<wformat_context, Args...> make_wformat_args(const Args&... args) {
|
||||
return make_format_args<wformat_context>(args...);
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace detail {
|
||||
|
||||
template <typename Range>
|
||||
class arg_formatter
|
||||
: public fmt::internal::arg_formatter_base<Range, error_handler> {
|
||||
private:
|
||||
using char_type = typename Range::value_type;
|
||||
using base = fmt::internal::arg_formatter_base<Range, error_handler>;
|
||||
using format_context = std::basic_format_context<typename base::iterator, char_type>;
|
||||
using parse_context = basic_format_parse_context<char_type>;
|
||||
|
||||
parse_context* parse_ctx_;
|
||||
format_context& ctx_;
|
||||
|
||||
public:
|
||||
typedef Range range;
|
||||
typedef typename base::iterator iterator;
|
||||
typedef typename base::format_specs format_specs;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an argument formatter object.
|
||||
*ctx* is a reference to the formatting context,
|
||||
*spec* contains format specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
|
||||
: base(Range(ctx.out()), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
|
||||
|
||||
using base::operator();
|
||||
|
||||
/** Formats an argument of a user-defined type. */
|
||||
iterator operator()(typename std::basic_format_arg<format_context>::handle handle) {
|
||||
handle.format(*parse_ctx_, ctx_);
|
||||
return this->out();
|
||||
}
|
||||
|
||||
iterator operator()(monostate) {
|
||||
throw format_error("");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
inline fmt::internal::type get_type(basic_format_arg<Context> arg) {
|
||||
return visit_format_arg([&] (auto val) {
|
||||
using char_type = typename Context::char_type;
|
||||
using T = decltype(val);
|
||||
if (std::is_same_v<T, monostate>)
|
||||
return fmt::internal::type::none_type;
|
||||
if (std::is_same_v<T, bool>)
|
||||
return fmt::internal::type::bool_type;
|
||||
if (std::is_same_v<T, char_type>)
|
||||
return fmt::internal::type::char_type;
|
||||
if (std::is_same_v<T, int>)
|
||||
return fmt::internal::type::int_type;
|
||||
if (std::is_same_v<T, unsigned int>)
|
||||
return fmt::internal::type::uint_type;
|
||||
if (std::is_same_v<T, long long int>)
|
||||
return fmt::internal::type::long_long_type;
|
||||
if (std::is_same_v<T, unsigned long long int>)
|
||||
return fmt::internal::type::ulong_long_type;
|
||||
if (std::is_same_v<T, double>)
|
||||
return fmt::internal::type::double_type;
|
||||
if (std::is_same_v<T, long double>)
|
||||
return fmt::internal::type::long_double_type;
|
||||
if (std::is_same_v<T, const char_type*>)
|
||||
return fmt::internal::type::cstring_type;
|
||||
if (std::is_same_v<T, basic_string_view<char_type>>)
|
||||
return fmt::internal::type::string_type;
|
||||
if (std::is_same_v<T, const void*>)
|
||||
return fmt::internal::type::pointer_type;
|
||||
assert(get_value(arg).index() == 12);
|
||||
return fmt::internal::type::custom_type;
|
||||
}, arg);
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
class custom_formatter {
|
||||
private:
|
||||
using parse_context = basic_format_parse_context<typename Context::char_type>;
|
||||
parse_context& parse_ctx_;
|
||||
Context& format_ctx_;
|
||||
|
||||
public:
|
||||
custom_formatter(parse_context& parse_ctx, Context& ctx) : parse_ctx_(parse_ctx), format_ctx_(ctx) {}
|
||||
|
||||
bool operator()(typename basic_format_arg<Context>::handle h) const {
|
||||
h.format(parse_ctx_, format_ctx_);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> bool operator()(T) const { return false; }
|
||||
};
|
||||
|
||||
template <typename ArgFormatter, typename Char, typename Context>
|
||||
struct format_handler : detail::error_handler {
|
||||
typedef typename ArgFormatter::range range;
|
||||
|
||||
format_handler(range r, basic_string_view<Char> str,
|
||||
basic_format_args<Context> format_args,
|
||||
fmt::internal::locale_ref loc)
|
||||
: parse_ctx(str), context(r.begin(), format_args, loc) {}
|
||||
|
||||
void on_text(const Char* begin, const Char* end) {
|
||||
auto size = fmt::internal::to_unsigned(end - begin);
|
||||
auto out = context.out();
|
||||
auto&& it = fmt::internal::reserve(out, size);
|
||||
it = std::copy_n(begin, size, it);
|
||||
context.advance_to(out);
|
||||
}
|
||||
|
||||
void on_arg_id() {
|
||||
arg = context.arg(parse_ctx.next_arg_id());
|
||||
}
|
||||
void on_arg_id(unsigned id) {
|
||||
parse_ctx.check_arg_id(id);
|
||||
arg = context.arg(id);
|
||||
}
|
||||
void on_arg_id(fmt::basic_string_view<Char>) {}
|
||||
|
||||
void on_replacement_field(const Char* p) {
|
||||
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
|
||||
custom_formatter<Context> f(parse_ctx, context);
|
||||
if (!visit_format_arg(f, arg))
|
||||
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
|
||||
}
|
||||
|
||||
const Char* on_format_specs(const Char* begin, const Char* end) {
|
||||
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
|
||||
custom_formatter<Context> f(parse_ctx, context);
|
||||
if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
|
||||
fmt::basic_format_specs<Char> specs;
|
||||
using fmt::internal::specs_handler;
|
||||
using parse_context = basic_format_parse_context<Char>;
|
||||
fmt::internal::specs_checker<specs_handler<parse_context, Context>> handler(
|
||||
specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg));
|
||||
begin = parse_format_specs(begin, end, handler);
|
||||
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
|
||||
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx, &specs), arg));
|
||||
return begin;
|
||||
}
|
||||
|
||||
basic_format_parse_context<Char> parse_ctx;
|
||||
Context context;
|
||||
basic_format_arg<Context> arg;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter {
|
||||
// Parses format specifiers stopping either at the end of the range or at the
|
||||
// terminating '}'.
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
|
||||
namespace internal = fmt::internal;
|
||||
typedef internal::dynamic_specs_handler<ParseContext> handler_type;
|
||||
auto type = internal::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
|
||||
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
type);
|
||||
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
auto type_spec = specs_.type;
|
||||
auto eh = ctx.error_handler();
|
||||
switch (type) {
|
||||
case internal::type::none_type:
|
||||
case internal::type::named_arg_type:
|
||||
FMT_ASSERT(false, "invalid argument type");
|
||||
break;
|
||||
case internal::type::int_type:
|
||||
case internal::type::uint_type:
|
||||
case internal::type::long_long_type:
|
||||
case internal::type::ulong_long_type:
|
||||
case internal::type::bool_type:
|
||||
handle_int_type_spec(type_spec,
|
||||
internal::int_type_checker<decltype(eh)>(eh));
|
||||
break;
|
||||
case internal::type::char_type:
|
||||
handle_char_specs(
|
||||
&specs_, internal::char_specs_checker<decltype(eh)>(type_spec, eh));
|
||||
break;
|
||||
case internal::type::double_type:
|
||||
case internal::type::long_double_type:
|
||||
internal::parse_float_type_spec(specs_, eh);
|
||||
break;
|
||||
case internal::type::cstring_type:
|
||||
internal::handle_cstring_type_spec(
|
||||
type_spec, internal::cstring_type_checker<decltype(eh)>(eh));
|
||||
break;
|
||||
case internal::type::string_type:
|
||||
internal::check_string_type_spec(type_spec, eh);
|
||||
break;
|
||||
case internal::type::pointer_type:
|
||||
internal::check_pointer_type_spec(type_spec, eh);
|
||||
break;
|
||||
case internal::type::custom_type:
|
||||
// Custom format specifiers should be checked in parse functions of
|
||||
// formatter specializations.
|
||||
break;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
|
||||
specs_.width, specs_.width_ref, ctx);
|
||||
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
|
||||
specs_.precision, specs_.precision_ref, ctx);
|
||||
using range_type = fmt::internal::output_range<typename FormatContext::iterator,
|
||||
typename FormatContext::char_type>;
|
||||
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
|
||||
basic_format_arg<FormatContext>(val));
|
||||
}
|
||||
|
||||
private:
|
||||
fmt::internal::dynamic_format_specs<Char> specs_;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// https://fmt.dev/Text%20Formatting.html#format.functions
|
||||
template<class... Args>
|
||||
string format(string_view fmt, const Args&... args) {
|
||||
return vformat(fmt, make_format_args(args...));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
wstring format(wstring_view fmt, const Args&... args) {
|
||||
return vformat(fmt, make_wformat_args(args...));
|
||||
}
|
||||
|
||||
string vformat(string_view fmt, format_args args) {
|
||||
fmt::memory_buffer mbuf;
|
||||
fmt::internal::buffer<char>& buf = mbuf;
|
||||
using range = fmt::buffer_range<char>;
|
||||
detail::format_handler<detail::arg_formatter<range>, char, format_context>
|
||||
h(range(std::back_inserter(buf)), fmt, args, {});
|
||||
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);
|
||||
return to_string(mbuf);
|
||||
}
|
||||
|
||||
wstring vformat(wstring_view fmt, wformat_args args);
|
||||
|
||||
template<class Out, class... Args>
|
||||
Out format_to(Out out, string_view fmt, const Args&... args) {
|
||||
using context = basic_format_context<Out, decltype(fmt)::value_type>;
|
||||
return vformat_to(out, fmt, make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template<class Out, class... Args>
|
||||
Out format_to(Out out, wstring_view fmt, const Args&... args) {
|
||||
using context = basic_format_context<Out, decltype(fmt)::value_type>;
|
||||
return vformat_to(out, fmt, make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template<class Out>
|
||||
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
|
||||
using range = fmt::internal::output_range<Out, char>;
|
||||
detail::format_handler<detail::arg_formatter<range>, char, basic_format_context<Out, char>>
|
||||
h(range(out), fmt, args, {});
|
||||
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);
|
||||
return h.context.out();
|
||||
}
|
||||
|
||||
template<class Out>
|
||||
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
|
||||
|
||||
template<class Out, class... Args>
|
||||
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
string_view fmt, const Args&... args);
|
||||
template<class Out, class... Args>
|
||||
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
wstring_view fmt, const Args&... args);
|
||||
|
||||
template<class... Args>
|
||||
size_t formatted_size(string_view fmt, const Args&... args);
|
||||
template<class... Args>
|
||||
size_t formatted_size(wstring_view fmt, const Args&... args);
|
||||
|
||||
#define charT char
|
||||
|
||||
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
|
||||
|
||||
template<> struct formatter<char, wchar_t>;
|
||||
|
||||
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
|
||||
|
||||
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
|
||||
|
||||
template<size_t N> struct formatter<const charT[N], charT>
|
||||
: detail::formatter<std::basic_string_view<charT>, charT> {};
|
||||
|
||||
template<class traits, class Allocator>
|
||||
struct formatter<basic_string<charT, traits, Allocator>, charT>
|
||||
: detail::formatter<std::basic_string_view<charT>, charT> {};
|
||||
|
||||
template<class traits>
|
||||
struct formatter<basic_string_view<charT, traits>, charT>
|
||||
: detail::formatter<std::basic_string_view<charT>, charT> {};
|
||||
|
||||
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
|
||||
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
|
||||
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
|
||||
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
|
||||
|
||||
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
|
||||
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
|
||||
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
|
||||
template <> struct formatter<long, charT>
|
||||
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
|
||||
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
|
||||
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
|
||||
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
|
||||
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
|
||||
template <> struct formatter<unsigned long, charT>
|
||||
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
|
||||
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
|
||||
|
||||
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
|
||||
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
|
||||
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
|
||||
|
||||
#undef charT
|
||||
|
||||
#define charT wchar_t
|
||||
|
||||
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
|
||||
|
||||
template<> struct formatter<char, wchar_t> : detail::formatter<charT, charT> {};
|
||||
|
||||
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
|
||||
|
||||
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
|
||||
|
||||
template<size_t N> struct formatter<const charT[N], charT>
|
||||
: detail::formatter<std::basic_string_view<charT>, charT> {};
|
||||
|
||||
template<class traits, class Allocator>
|
||||
struct formatter<std::basic_string<charT, traits, Allocator>, charT>
|
||||
: detail::formatter<std::basic_string_view<charT>, charT> {};
|
||||
|
||||
template<class traits>
|
||||
struct formatter<std::basic_string_view<charT, traits>, charT>
|
||||
: detail::formatter<std::basic_string_view<charT>, charT> {};
|
||||
|
||||
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
|
||||
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
|
||||
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
|
||||
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
|
||||
|
||||
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
|
||||
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
|
||||
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
|
||||
template <> struct formatter<long, charT>
|
||||
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
|
||||
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
|
||||
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
|
||||
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
|
||||
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
|
||||
template <> struct formatter<unsigned long, charT>
|
||||
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
|
||||
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
|
||||
|
||||
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
|
||||
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
|
||||
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
|
||||
|
||||
#undef charT
|
||||
|
||||
template<> struct formatter<const wchar_t, char> {
|
||||
formatter() = delete;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FMT_FORMAT_
|
6
externals/fmt/test/format-dyn-args-test.cc
vendored
Normal file
6
externals/fmt/test/format-dyn-args-test.cc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) 2020 Vladimir Solontsov
|
||||
// SPDX-License-Identifier: MIT Licence
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "gtest-extra.h"
|
444
externals/fmt/test/format-impl-test.cc
vendored
444
externals/fmt/test/format-impl-test.cc
vendored
|
@ -10,75 +10,243 @@
|
|||
#include "test-assert.h"
|
||||
|
||||
// Include format.cc instead of format.h to test implementation.
|
||||
#include "../src/format.cc"
|
||||
#include "fmt/color.h"
|
||||
#include "fmt/printf.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "../src/format.cc"
|
||||
#include "fmt/printf.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#if FMT_HAS_CPP_ATTRIBUTE(noreturn)
|
||||
# define FMT_NORETURN [[noreturn]]
|
||||
#else
|
||||
# define FMT_NORETURN
|
||||
#endif
|
||||
|
||||
using fmt::internal::bigint;
|
||||
using fmt::internal::fp;
|
||||
using fmt::internal::max_value;
|
||||
|
||||
template <bool is_iec559>
|
||||
void test_construct_from_double() {
|
||||
static_assert(!std::is_copy_constructible<bigint>::value, "");
|
||||
static_assert(!std::is_copy_assignable<bigint>::value, "");
|
||||
|
||||
TEST(BigIntTest, Construct) {
|
||||
EXPECT_EQ("", fmt::format("{}", bigint()));
|
||||
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
|
||||
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
|
||||
}
|
||||
|
||||
TEST(BigIntTest, Compare) {
|
||||
bigint n1(42);
|
||||
bigint n2(42);
|
||||
EXPECT_EQ(compare(n1, n2), 0);
|
||||
n2 <<= 32;
|
||||
EXPECT_LT(compare(n1, n2), 0);
|
||||
bigint n3(43);
|
||||
EXPECT_LT(compare(n1, n3), 0);
|
||||
EXPECT_GT(compare(n3, n1), 0);
|
||||
bigint n4(42 * 0x100000001);
|
||||
EXPECT_LT(compare(n2, n4), 0);
|
||||
EXPECT_GT(compare(n4, n2), 0);
|
||||
}
|
||||
|
||||
TEST(BigIntTest, AddCompare) {
|
||||
EXPECT_LT(
|
||||
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
|
||||
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
|
||||
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
|
||||
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
|
||||
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
|
||||
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
|
||||
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
|
||||
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
|
||||
bigint(0x300000010)),
|
||||
0);
|
||||
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||
bigint(0x300000000)),
|
||||
0);
|
||||
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||
bigint(0x300000001)),
|
||||
0);
|
||||
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||
bigint(0x300000002)),
|
||||
0);
|
||||
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||
bigint(0x300000003)),
|
||||
0);
|
||||
}
|
||||
|
||||
TEST(BigIntTest, ShiftLeft) {
|
||||
bigint n(0x42);
|
||||
n <<= 0;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
n <<= 1;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
n <<= 25;
|
||||
EXPECT_EQ("108000000", fmt::format("{}", n));
|
||||
}
|
||||
|
||||
TEST(BigIntTest, Multiply) {
|
||||
bigint n(0x42);
|
||||
EXPECT_THROW(n *= 0, assertion_failure);
|
||||
n *= 1;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
n *= 2;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
n *= 0x12345678;
|
||||
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
|
||||
bigint bigmax(max_value<uint32_t>());
|
||||
bigmax *= max_value<uint32_t>();
|
||||
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
|
||||
bigmax.assign(max_value<uint64_t>());
|
||||
bigmax *= max_value<uint64_t>();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
|
||||
}
|
||||
|
||||
TEST(BigIntTest, Accumulator) {
|
||||
fmt::internal::accumulator acc;
|
||||
EXPECT_EQ(acc.lower, 0);
|
||||
EXPECT_EQ(acc.upper, 0);
|
||||
acc.upper = 12;
|
||||
acc.lower = 34;
|
||||
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
|
||||
acc += 56;
|
||||
EXPECT_EQ(acc.lower, 90);
|
||||
acc += fmt::internal::max_value<uint64_t>();
|
||||
EXPECT_EQ(acc.upper, 13);
|
||||
EXPECT_EQ(acc.lower, 89);
|
||||
acc >>= 32;
|
||||
EXPECT_EQ(acc.upper, 0);
|
||||
EXPECT_EQ(acc.lower, 13 * 0x100000000);
|
||||
}
|
||||
|
||||
TEST(BigIntTest, Square) {
|
||||
bigint n0(0);
|
||||
n0.square();
|
||||
EXPECT_EQ("0", fmt::format("{}", n0));
|
||||
bigint n1(0x100);
|
||||
n1.square();
|
||||
EXPECT_EQ("10000", fmt::format("{}", n1));
|
||||
bigint n2(0xfffffffff);
|
||||
n2.square();
|
||||
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
|
||||
bigint n3(max_value<uint64_t>());
|
||||
n3.square();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
|
||||
bigint n4;
|
||||
n4.assign_pow10(10);
|
||||
EXPECT_EQ("2540be400", fmt::format("{}", n4));
|
||||
}
|
||||
|
||||
TEST(BigIntTest, DivModAssignZeroDivisor) {
|
||||
bigint zero(0);
|
||||
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
|
||||
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
|
||||
}
|
||||
|
||||
TEST(BigIntTest, DivModAssignSelf) {
|
||||
bigint n(100);
|
||||
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
|
||||
}
|
||||
|
||||
TEST(BigIntTest, DivModAssignUnaligned) {
|
||||
// (42 << 340) / pow(10, 100):
|
||||
bigint n1(42);
|
||||
n1 <<= 340;
|
||||
bigint n2;
|
||||
n2.assign_pow10(100);
|
||||
int result = n1.divmod_assign(n2);
|
||||
EXPECT_EQ(result, 9406);
|
||||
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
|
||||
fmt::format("{}", n1));
|
||||
}
|
||||
|
||||
TEST(BigIntTest, DivModAssign) {
|
||||
// 100 / 10:
|
||||
bigint n1(100);
|
||||
int result = n1.divmod_assign(bigint(10));
|
||||
EXPECT_EQ(result, 10);
|
||||
EXPECT_EQ("0", fmt::format("{}", n1));
|
||||
// pow(10, 100) / (42 << 320):
|
||||
n1.assign_pow10(100);
|
||||
result = n1.divmod_assign(bigint(42) <<= 320);
|
||||
EXPECT_EQ(result, 111);
|
||||
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
|
||||
fmt::format("{}", n1));
|
||||
// 42 / 100:
|
||||
bigint n2(42);
|
||||
n1.assign_pow10(2);
|
||||
result = n2.divmod_assign(n1);
|
||||
EXPECT_EQ(result, 0);
|
||||
EXPECT_EQ("2a", fmt::format("{}", n2));
|
||||
}
|
||||
|
||||
template <bool is_iec559> void run_double_tests() {
|
||||
fmt::print("warning: double is not IEC559, skipping FP tests\n");
|
||||
}
|
||||
|
||||
template <>
|
||||
void test_construct_from_double<true>() {
|
||||
auto v = fp(1.23);
|
||||
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
|
||||
EXPECT_EQ(v.e, -52);
|
||||
template <> void run_double_tests<true>() {
|
||||
// Construct from double.
|
||||
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, ConstructFromDouble) {
|
||||
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
|
||||
TEST(FPTest, DoubleTests) {
|
||||
run_double_tests<std::numeric_limits<double>::is_iec559>();
|
||||
}
|
||||
|
||||
TEST(FPTest, Normalize) {
|
||||
auto v = fp(0xbeef, 42);
|
||||
v.normalize();
|
||||
EXPECT_EQ(0xbeef000000000000, v.f);
|
||||
EXPECT_EQ(-6, v.e);
|
||||
const auto v = fp(0xbeef, 42);
|
||||
auto normalized = normalize(v);
|
||||
EXPECT_EQ(0xbeef000000000000, normalized.f);
|
||||
EXPECT_EQ(-6, normalized.e);
|
||||
}
|
||||
|
||||
TEST(FPTest, ComputeBoundariesSubnormal) {
|
||||
auto v = fp(0xbeef, 42);
|
||||
fp lower, upper;
|
||||
v.compute_boundaries(lower, upper);
|
||||
EXPECT_EQ(0xbeee800000000000, lower.f);
|
||||
EXPECT_EQ(-6, lower.e);
|
||||
EXPECT_EQ(0xbeef800000000000, upper.f);
|
||||
EXPECT_EQ(-6, upper.e);
|
||||
}
|
||||
|
||||
TEST(FPTest, ComputeBoundaries) {
|
||||
auto v = fp(0x10000000000000, 42);
|
||||
fp lower, upper;
|
||||
v.compute_boundaries(lower, upper);
|
||||
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
|
||||
EXPECT_EQ(31, lower.e);
|
||||
EXPECT_EQ(0x8000000000000400, upper.f);
|
||||
EXPECT_EQ(31, upper.e);
|
||||
}
|
||||
|
||||
TEST(FPTest, Subtract) {
|
||||
auto v = fp(123, 1) - fp(102, 1);
|
||||
EXPECT_EQ(v.f, 21u);
|
||||
EXPECT_EQ(v.e, 1);
|
||||
TEST(FPTest, ComputeFloatBoundaries) {
|
||||
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) {
|
||||
|
@ -98,33 +266,87 @@ TEST(FPTest, GetCachedPower) {
|
|||
EXPECT_LE(exp, fp.e);
|
||||
int dec_exp_step = 8;
|
||||
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
|
||||
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e));
|
||||
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(static_cast<double>(fp.f), fp.e));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) {
|
||||
TEST(FPTest, GetRoundDirection) {
|
||||
using fmt::internal::get_round_direction;
|
||||
using fmt::internal::round_direction;
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10));
|
||||
for (size_t i = 41; i < 60; ++i)
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, i, 10));
|
||||
uint64_t max = max_value<uint64_t>();
|
||||
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
|
||||
// Check that remainder + error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 2));
|
||||
// Check that 2 * (remainder + error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown,
|
||||
get_round_direction(max, max / 2 + 1, max / 2));
|
||||
// Check that remainder - error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, 40, 41));
|
||||
// Check that 2 * (remainder - error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
|
||||
}
|
||||
|
||||
TEST(FPTest, FixedHandler) {
|
||||
struct handler : fmt::internal::fixed_handler {
|
||||
char buffer[10];
|
||||
handler(int prec = 0) : fmt::internal::fixed_handler() {
|
||||
buf = buffer;
|
||||
precision = prec;
|
||||
}
|
||||
};
|
||||
int exp = 0;
|
||||
handler().on_digit('0', 100, 99, 0, exp, false);
|
||||
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
|
||||
assertion_failure);
|
||||
namespace digits = fmt::internal::digits;
|
||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done);
|
||||
// Check that divisor - error doesn't overflow.
|
||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
|
||||
// Check that 2 * error doesn't overflow.
|
||||
uint64_t max = max_value<uint64_t>();
|
||||
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false),
|
||||
digits::error);
|
||||
}
|
||||
|
||||
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
|
||||
fmt::memory_buffer buf;
|
||||
grisu2_format(4.2f, buf, fmt::core_format_specs());
|
||||
format_float(0.42, -1, fmt::internal::float_specs(), buf);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct ValueExtractor: fmt::internal::function<T> {
|
||||
T operator()(T value) {
|
||||
return value;
|
||||
}
|
||||
template <typename T> struct value_extractor {
|
||||
T operator()(T value) { return value; }
|
||||
|
||||
template <typename U>
|
||||
FMT_NORETURN T operator()(U) {
|
||||
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::internal::int128_t) {
|
||||
throw std::runtime_error("invalid type __int128_t");
|
||||
}
|
||||
|
||||
FMT_NORETURN T operator()(fmt::internal::uint128_t) {
|
||||
throw std::runtime_error("invalid type __uint128_t");
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
TEST(FormatTest, ArgConverter) {
|
||||
long long value = std::numeric_limits<long long>::max();
|
||||
long long value = max_value<long long>();
|
||||
auto arg = fmt::internal::make_arg<fmt::format_context>(value);
|
||||
visit(fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
|
||||
arg);
|
||||
EXPECT_EQ(value, visit(ValueExtractor<long long>(), arg));
|
||||
fmt::visit_format_arg(
|
||||
fmt::internal::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) {
|
||||
|
@ -136,11 +358,11 @@ TEST(FormatTest, FormatNegativeNaN) {
|
|||
}
|
||||
|
||||
TEST(FormatTest, StrError) {
|
||||
char *message = FMT_NULL;
|
||||
char* message = nullptr;
|
||||
char buffer[BUFFER_SIZE];
|
||||
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = FMT_NULL, 0),
|
||||
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = nullptr, 0),
|
||||
"invalid buffer");
|
||||
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
|
||||
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0),
|
||||
"invalid buffer");
|
||||
buffer[0] = 'x';
|
||||
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
|
||||
|
@ -151,17 +373,19 @@ TEST(FormatTest, StrError) {
|
|||
int error_code = EDOM;
|
||||
#endif
|
||||
|
||||
int result = fmt::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
|
||||
int result =
|
||||
fmt::internal::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
|
||||
EXPECT_EQ(result, 0);
|
||||
std::size_t message_size = std::strlen(message);
|
||||
EXPECT_GE(BUFFER_SIZE - 1u, message_size);
|
||||
EXPECT_EQ(get_system_error(error_code), message);
|
||||
|
||||
// safe_strerror never uses buffer on MinGW.
|
||||
#ifndef __MINGW32__
|
||||
result = fmt::safe_strerror(error_code, message = buffer, message_size);
|
||||
#if !defined(__MINGW32__) && !defined(__sun)
|
||||
result =
|
||||
fmt::internal::safe_strerror(error_code, message = buffer, message_size);
|
||||
EXPECT_EQ(ERANGE, result);
|
||||
result = fmt::safe_strerror(error_code, message = buffer, 1);
|
||||
result = fmt::internal::safe_strerror(error_code, message = buffer, 1);
|
||||
EXPECT_EQ(buffer, message); // Message should point to buffer.
|
||||
EXPECT_EQ(ERANGE, result);
|
||||
EXPECT_STREQ("", message);
|
||||
|
@ -173,14 +397,14 @@ TEST(FormatTest, FormatErrorCode) {
|
|||
{
|
||||
fmt::memory_buffer buffer;
|
||||
format_to(buffer, "garbage");
|
||||
fmt::format_error_code(buffer, 42, "test");
|
||||
fmt::internal::format_error_code(buffer, 42, "test");
|
||||
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||
}
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
std::string prefix(
|
||||
fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
|
||||
fmt::format_error_code(buffer, 42, prefix);
|
||||
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
|
||||
'x');
|
||||
fmt::internal::format_error_code(buffer, 42, prefix);
|
||||
EXPECT_EQ(msg, to_string(buffer));
|
||||
}
|
||||
int codes[] = {42, -1};
|
||||
|
@ -188,59 +412,49 @@ TEST(FormatTest, FormatErrorCode) {
|
|||
// Test maximum buffer size.
|
||||
msg = fmt::format("error {}", codes[i]);
|
||||
fmt::memory_buffer buffer;
|
||||
std::string prefix(
|
||||
fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
|
||||
fmt::format_error_code(buffer, codes[i], prefix);
|
||||
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
|
||||
fmt::internal::format_error_code(buffer, codes[i], prefix);
|
||||
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
|
||||
std::size_t size = fmt::inline_buffer_size;
|
||||
EXPECT_EQ(size, buffer.size());
|
||||
buffer.resize(0);
|
||||
// Test with a message that doesn't fit into the buffer.
|
||||
prefix += 'x';
|
||||
fmt::format_error_code(buffer, codes[i], prefix);
|
||||
fmt::internal::format_error_code(buffer, codes[i], prefix);
|
||||
EXPECT_EQ(msg, to_string(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FormatTest, CountCodePoints) {
|
||||
EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик")));
|
||||
#ifndef __cpp_char8_t
|
||||
using fmt::char8_t;
|
||||
#endif
|
||||
EXPECT_EQ(
|
||||
4, fmt::internal::count_code_points(
|
||||
fmt::basic_string_view<fmt::internal::char8_type>(
|
||||
reinterpret_cast<const fmt::internal::char8_type*>("ёжик"))));
|
||||
}
|
||||
|
||||
TEST(ColorsTest, Colors) {
|
||||
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");
|
||||
// Tests fmt::internal::count_digits for integer type Int.
|
||||
template <typename Int> void test_count_digits() {
|
||||
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i));
|
||||
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
|
||||
n *= 10;
|
||||
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
|
||||
EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UtilTest, CountDigits) {
|
||||
test_count_digits<uint32_t>();
|
||||
test_count_digits<uint64_t>();
|
||||
}
|
||||
|
||||
TEST(UtilTest, WriteUIntPtr) {
|
||||
fmt::memory_buffer buf;
|
||||
fmt::internal::writer writer(buf);
|
||||
writer.write_pointer(
|
||||
fmt::internal::fallback_uintptr(reinterpret_cast<void*>(0xface)),
|
||||
nullptr);
|
||||
EXPECT_EQ("0xface", to_string(buf));
|
||||
}
|
||||
|
|
1826
externals/fmt/test/format-test.cc
vendored
1826
externals/fmt/test/format-test.cc
vendored
File diff suppressed because it is too large
Load diff
3
externals/fmt/test/fuzzing/.gitignore
vendored
Normal file
3
externals/fmt/test/fuzzing/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# ignore artifacts from the build.sh script
|
||||
build-*/
|
||||
|
38
externals/fmt/test/fuzzing/CMakeLists.txt
vendored
Normal file
38
externals/fmt/test/fuzzing/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright (c) 2019, Paul Dreik
|
||||
# License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
# settings this links in a main. useful for reproducing,
|
||||
# kcov, gdb, afl, valgrind.
|
||||
# (note that libFuzzer can also reproduce, just pass it the files)
|
||||
option(FMT_FUZZ_LINKMAIN "enables the reproduce mode, instead of libFuzzer" On)
|
||||
|
||||
# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for
|
||||
# the fuzz targets, otherwise the cmake configuration step fails.
|
||||
set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
|
||||
|
||||
# Find all fuzzers.
|
||||
set(SOURCES
|
||||
chrono_duration.cpp
|
||||
named_arg.cpp
|
||||
one_arg.cpp
|
||||
sprintf.cpp
|
||||
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)
|
||||
target_sources(${name} PRIVATE main.cpp)
|
||||
endif ()
|
||||
target_link_libraries(${name} PRIVATE fmt)
|
||||
if (FMT_FUZZ_LDFLAGS)
|
||||
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
|
||||
endif ()
|
||||
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
|
||||
endmacro ()
|
||||
|
||||
foreach (X IN ITEMS ${SOURCES})
|
||||
implement_fuzzer(${X})
|
||||
endforeach ()
|
43
externals/fmt/test/fuzzing/README.md
vendored
Normal file
43
externals/fmt/test/fuzzing/README.md
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
# FMT Fuzzer
|
||||
|
||||
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 FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
if(spec.precision>100000) {
|
||||
throw std::runtime_error("fuzz mode - avoiding large precision");
|
||||
}
|
||||
#endif
|
||||
```
|
||||
This macro is the defacto standard for making fuzzing practically possible, 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
|
||||
tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on
|
||||
Windows (using clang>=8) or on Mac, but the script will probably not work out of
|
||||
the box.
|
||||
|
||||
Something along
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
export CXX=clang++
|
||||
export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
|
||||
cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||
cmake --build .
|
||||
```
|
||||
should work to build the fuzzers for all platforms which clang supports.
|
||||
|
||||
Execute a fuzzer with for instance
|
||||
```sh
|
||||
cd build
|
||||
export UBSAN_OPTIONS=halt_on_error=1
|
||||
mkdir out_chrono
|
||||
bin/fuzzer_chrono_duration out_chrono
|
||||
```
|
110
externals/fmt/test/fuzzing/build.sh
vendored
Executable file
110
externals/fmt/test/fuzzing/build.sh
vendored
Executable file
|
@ -0,0 +1,110 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# 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)
|
||||
# - libFuzzer build (you will need clang)
|
||||
# - afl build (you will need afl)
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2019 Paul Dreik
|
||||
#
|
||||
# License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
set -e
|
||||
me=$(basename $0)
|
||||
root=$(readlink -f "$(dirname "$0")/../..")
|
||||
|
||||
|
||||
echo $me: root=$root
|
||||
|
||||
here=$(pwd)
|
||||
|
||||
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
|
||||
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
|
||||
|
||||
#builds the fuzzers as one would do if using afl or just making
|
||||
#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
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL -g" cmake \
|
||||
$CMAKEFLAGSALL \
|
||||
-DFMT_FUZZ_LINKMAIN=On \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
cmake --build $builddir
|
||||
|
||||
#builds the fuzzers as oss-fuzz does
|
||||
builddir=$here/build-fuzzers-ossfuzz
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="clang++" \
|
||||
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \
|
||||
cmake $CMAKEFLAGSALL \
|
||||
-DFMT_FUZZ_LINKMAIN=Off \
|
||||
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||
|
||||
cmake --build $builddir
|
||||
|
||||
|
||||
#builds fuzzers for local fuzzing with libfuzzer with asan+usan
|
||||
builddir=$here/build-fuzzers-libfuzzer
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="clang++" \
|
||||
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \
|
||||
cmake $CMAKEFLAGSALL \
|
||||
-DFMT_FUZZ_LINKMAIN=Off \
|
||||
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||
|
||||
cmake --build $builddir
|
||||
|
||||
#builds fuzzers for local fuzzing with libfuzzer with asan only
|
||||
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
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="clang++" \
|
||||
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \
|
||||
cmake $CMAKEFLAGSALL \
|
||||
-DFMT_FUZZ_LINKMAIN=Off \
|
||||
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
cmake --build $builddir
|
||||
|
||||
|
||||
#builds fuzzers for local fuzzing with afl
|
||||
builddir=$here/build-fuzzers-afl
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="afl-g++" \
|
||||
CXXFLAGS="$CXXFLAGSALL -fsanitize=address,undefined" \
|
||||
cmake $CMAKEFLAGSALL \
|
||||
-DFMT_FUZZ_LINKMAIN=On
|
||||
|
||||
cmake --build $builddir
|
||||
|
||||
|
||||
echo $me: all good
|
||||
|
152
externals/fmt/test/fuzzing/chrono_duration.cpp
vendored
Normal file
152
externals/fmt/test/fuzzing/chrono_duration.cpp
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
// 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, std::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, std::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;
|
||||
}
|
67
externals/fmt/test/fuzzing/fuzzer_common.h
vendored
Normal file
67
externals/fmt/test/fuzzing/fuzzer_common.h
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
#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
|
21
externals/fmt/test/fuzzing/main.cpp
vendored
Normal file
21
externals/fmt/test/fuzzing/main.cpp
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::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<std::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());
|
||||
}
|
||||
}
|
128
externals/fmt/test/fuzzing/named_arg.cpp
vendored
Normal file
128
externals/fmt/test/fuzzing/named_arg.cpp
vendored
Normal file
|
@ -0,0 +1,128 @@
|
|||
// 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, std::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);
|
||||
std::memcpy(argnamebuffer.data(), Data, argsize);
|
||||
auto argname = fmt::string_view(argnamebuffer.data(), argsize);
|
||||
#else
|
||||
auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize);
|
||||
#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, std::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;
|
||||
}
|
131
externals/fmt/test/fuzzing/one_arg.cpp
vendored
Normal file
131
externals/fmt/test/fuzzing/one_arg.cpp
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
// 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, std::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, std::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, std::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
Normal file
116
externals/fmt/test/fuzzing/sprintf.cpp
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
// 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, std::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, std::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;
|
||||
}
|
112
externals/fmt/test/fuzzing/two_args.cpp
vendored
Normal file
112
externals/fmt/test/fuzzing/two_args.cpp
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
// 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, std::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, std::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;
|
||||
}
|
2910
externals/fmt/test/gmock-gtest-all.cc
vendored
2910
externals/fmt/test/gmock-gtest-all.cc
vendored
File diff suppressed because it is too large
Load diff
75
externals/fmt/test/grisu-test.cc
vendored
Normal file
75
externals/fmt/test/grisu-test.cc
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
// 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));
|
||||
}
|
228
externals/fmt/test/gtest-extra-test.cc
vendored
228
externals/fmt/test/gtest-extra-test.cc
vendored
|
@ -7,23 +7,22 @@
|
|||
|
||||
#include "gtest-extra.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <gtest/gtest-spi.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
# include <crtdbg.h> // for _CrtSetReportMode
|
||||
#endif // _WIN32
|
||||
# include <crtdbg.h> // for _CrtSetReportMode
|
||||
#endif // _WIN32
|
||||
|
||||
#include "util.h"
|
||||
|
||||
using testing::internal::scoped_ptr;
|
||||
|
||||
namespace {
|
||||
|
||||
// This is used to suppress coverity warnings about untrusted values.
|
||||
std::string sanitize(const std::string &s) {
|
||||
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));
|
||||
|
@ -53,11 +52,9 @@ int SingleEvaluationTest::b_;
|
|||
|
||||
void do_nothing() {}
|
||||
|
||||
void throw_exception() {
|
||||
throw std::runtime_error("test");
|
||||
}
|
||||
FMT_NORETURN void throw_exception() { throw std::runtime_error("test"); }
|
||||
|
||||
void throw_system_error() {
|
||||
FMT_NORETURN void throw_system_error() {
|
||||
throw fmt::system_error(EDOM, "test");
|
||||
}
|
||||
|
||||
|
@ -72,43 +69,42 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_THROW_MSG) {
|
|||
// Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument
|
||||
// exactly once.
|
||||
TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++), "01234");
|
||||
EXPECT_EQ(s_ + 1, p_);
|
||||
}
|
||||
|
||||
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
|
||||
// exactly once.
|
||||
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_WRITE(stdout, std::printf("test"), p_++), "01234");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++),
|
||||
"01234");
|
||||
EXPECT_EQ(s_ + 1, p_);
|
||||
}
|
||||
|
||||
// Tests that assertion arguments are evaluated exactly once.
|
||||
TEST_F(SingleEvaluationTest, ExceptionTests) {
|
||||
// successful EXPECT_THROW_MSG
|
||||
EXPECT_THROW_MSG({ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
}, std::exception, (b_++, "test"));
|
||||
EXPECT_THROW_MSG(
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
},
|
||||
std::exception, (b_++, "test"));
|
||||
EXPECT_EQ(1, a_);
|
||||
EXPECT_EQ(1, b_);
|
||||
|
||||
// failed EXPECT_THROW_MSG, throws different type
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
}, std::logic_error, (b_++, "test")), "throws a different type");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
},
|
||||
std::logic_error, (b_++, "test")),
|
||||
"throws a different type");
|
||||
EXPECT_EQ(2, a_);
|
||||
EXPECT_EQ(2, b_);
|
||||
|
||||
// failed EXPECT_THROW_MSG, throws an exception with different message
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
}, std::exception, (b_++, "other")),
|
||||
"throws an exception with a different message");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
},
|
||||
std::exception, (b_++, "other")),
|
||||
"throws an exception with a different message");
|
||||
EXPECT_EQ(3, a_);
|
||||
EXPECT_EQ(3, b_);
|
||||
|
||||
|
@ -121,56 +117,95 @@ TEST_F(SingleEvaluationTest, ExceptionTests) {
|
|||
|
||||
TEST_F(SingleEvaluationTest, SystemErrorTests) {
|
||||
// successful EXPECT_SYSTEM_ERROR
|
||||
EXPECT_SYSTEM_ERROR({ // NOLINT
|
||||
a_++;
|
||||
throw_system_error();
|
||||
}, EDOM, (b_++, "test"));
|
||||
EXPECT_SYSTEM_ERROR(
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
throw_system_error();
|
||||
},
|
||||
EDOM, (b_++, "test"));
|
||||
EXPECT_EQ(1, a_);
|
||||
EXPECT_EQ(1, b_);
|
||||
|
||||
// failed EXPECT_SYSTEM_ERROR, throws different type
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR({ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
}, EDOM, (b_++, "test")), "throws a different type");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
throw_exception();
|
||||
},
|
||||
EDOM, (b_++, "test")),
|
||||
"throws a different type");
|
||||
EXPECT_EQ(2, a_);
|
||||
EXPECT_EQ(2, b_);
|
||||
|
||||
// failed EXPECT_SYSTEM_ERROR, throws an exception with different message
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR({ // NOLINT
|
||||
a_++;
|
||||
throw_system_error();
|
||||
}, EDOM, (b_++, "other")),
|
||||
"throws an exception with a different message");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
throw_system_error();
|
||||
},
|
||||
EDOM, (b_++, "other")),
|
||||
"throws an exception with a different message");
|
||||
EXPECT_EQ(3, a_);
|
||||
EXPECT_EQ(3, b_);
|
||||
|
||||
// failed EXPECT_SYSTEM_ERROR, throws nothing
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")), "throws nothing");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")),
|
||||
"throws nothing");
|
||||
EXPECT_EQ(4, a_);
|
||||
EXPECT_EQ(4, b_);
|
||||
}
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
|
||||
// exactly once.
|
||||
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
|
||||
"01234");
|
||||
EXPECT_EQ(s_ + 1, p_);
|
||||
}
|
||||
|
||||
// Tests that assertion arguments are evaluated exactly once.
|
||||
TEST_F(SingleEvaluationTest, WriteTests) {
|
||||
// successful EXPECT_WRITE
|
||||
EXPECT_WRITE(stdout, { // NOLINT
|
||||
a_++;
|
||||
std::printf("test");
|
||||
}, (b_++, "test"));
|
||||
EXPECT_WRITE(stdout,
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
std::printf("test");
|
||||
},
|
||||
(b_++, "test"));
|
||||
EXPECT_EQ(1, a_);
|
||||
EXPECT_EQ(1, b_);
|
||||
|
||||
// failed EXPECT_WRITE
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, { // NOLINT
|
||||
a_++;
|
||||
std::printf("test");
|
||||
}, (b_++, "other")), "Actual: test");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout,
|
||||
{ // NOLINT
|
||||
a_++;
|
||||
std::printf("test");
|
||||
},
|
||||
(b_++, "other")),
|
||||
"Actual: test");
|
||||
EXPECT_EQ(2, a_);
|
||||
EXPECT_EQ(2, b_);
|
||||
}
|
||||
|
||||
// Tests EXPECT_WRITE.
|
||||
TEST(ExpectTest, EXPECT_WRITE) {
|
||||
EXPECT_WRITE(stdout, do_nothing(), "");
|
||||
EXPECT_WRITE(stdout, std::printf("test"), "test");
|
||||
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
|
||||
"Expected: this\n"
|
||||
" Actual: that");
|
||||
}
|
||||
|
||||
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
||||
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
|
||||
<< "expected failure",
|
||||
"expected failure");
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
// Tests that the compiler will not complain about unreachable code in the
|
||||
// EXPECT_THROW_MSG macro.
|
||||
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
|
||||
|
@ -179,8 +214,8 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
|
|||
EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), "");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(throw 1, runtime_error, ""), "");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
|
||||
throw runtime_error("a"), runtime_error, "b"), "");
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_THROW_MSG(throw runtime_error("a"), runtime_error, "b"), "");
|
||||
}
|
||||
|
||||
// Tests that the compiler will not complain about unreachable code in the
|
||||
|
@ -190,8 +225,9 @@ TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
|||
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(throw 1, EDOM, ""), "");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
|
||||
throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"), "");
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"),
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
||||
|
@ -263,23 +299,13 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
|
|||
format_system_error(EDOM, "test")));
|
||||
}
|
||||
|
||||
// Tests EXPECT_WRITE.
|
||||
TEST(ExpectTest, EXPECT_WRITE) {
|
||||
EXPECT_WRITE(stdout, do_nothing(), "");
|
||||
EXPECT_WRITE(stdout, std::printf("test"), "test");
|
||||
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_WRITE(stdout, std::printf("that"), "this"),
|
||||
"Expected: this\n"
|
||||
" Actual: that");
|
||||
}
|
||||
|
||||
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
|
||||
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
|
||||
<< "unexpected failure";
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_THROW_MSG(throw_exception(), std::exception, "other")
|
||||
<< "expected failure", "expected failure");
|
||||
<< "expected failure",
|
||||
"expected failure");
|
||||
}
|
||||
|
||||
TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
|
||||
|
@ -287,15 +313,8 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
|
|||
<< "unexpected failure";
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other")
|
||||
<< "expected failure", "expected failure");
|
||||
}
|
||||
|
||||
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
||||
EXPECT_WRITE(stdout, std::printf("test"), "test")
|
||||
<< "unexpected failure";
|
||||
EXPECT_NONFATAL_FAILURE(
|
||||
EXPECT_WRITE(stdout, std::printf("test"), "other")
|
||||
<< "expected failure", "expected failure");
|
||||
<< "expected failure",
|
||||
"expected failure");
|
||||
}
|
||||
|
||||
TEST(UtilTest, FormatSystemError) {
|
||||
|
@ -304,7 +323,7 @@ TEST(UtilTest, FormatSystemError) {
|
|||
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
|
||||
}
|
||||
|
||||
#if FMT_USE_FILE_DESCRIPTORS
|
||||
#if FMT_USE_FCNTL
|
||||
|
||||
using fmt::buffered_file;
|
||||
using fmt::error_code;
|
||||
|
@ -340,10 +359,10 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
|
|||
// Put a character in a file buffer.
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
FMT_POSIX(close(write_fd));
|
||||
scoped_ptr<OutputRedirect> redir{FMT_NULL};
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
|
||||
EBADF, "cannot flush stream");
|
||||
redir.reset(FMT_NULL);
|
||||
std::unique_ptr<OutputRedirect> redir{nullptr};
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EBADF,
|
||||
"cannot flush stream");
|
||||
redir.reset(nullptr);
|
||||
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
||||
}
|
||||
|
||||
|
@ -352,9 +371,10 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
|
|||
int fd = (f.fileno)();
|
||||
file copy = file::dup(fd);
|
||||
FMT_POSIX(close(fd));
|
||||
scoped_ptr<OutputRedirect> redir{FMT_NULL};
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
|
||||
EBADF, fmt::format("cannot duplicate file descriptor {}", fd));
|
||||
std::unique_ptr<OutputRedirect> redir{nullptr};
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||
redir.reset(new OutputRedirect(f.get())), EBADF,
|
||||
fmt::format("cannot duplicate file descriptor {}", fd));
|
||||
copy.dup2(fd); // "undo" close or dtor will fail
|
||||
}
|
||||
|
||||
|
@ -383,8 +403,8 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
|||
// Put a character in a file buffer.
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
FMT_POSIX(close(write_fd));
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(),
|
||||
EBADF, "cannot flush stream");
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(), EBADF,
|
||||
"cannot flush stream");
|
||||
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
||||
}
|
||||
|
||||
|
@ -394,18 +414,20 @@ TEST(OutputRedirectTest, ErrorInDtor) {
|
|||
int write_fd = write_end.descriptor();
|
||||
file write_copy = write_end.dup(write_fd);
|
||||
buffered_file f = write_end.fdopen("w");
|
||||
scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
|
||||
std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
|
||||
// Put a character in a file buffer.
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
EXPECT_WRITE(stderr, {
|
||||
// The close function must be called inside EXPECT_WRITE, otherwise
|
||||
// the system may recycle closed file descriptor when redirecting the
|
||||
// output in EXPECT_STDERR and the second close will break output
|
||||
// redirection.
|
||||
FMT_POSIX(close(write_fd));
|
||||
SUPPRESS_ASSERT(redir.reset(FMT_NULL));
|
||||
}, format_system_error(EBADF, "cannot flush stream"));
|
||||
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
||||
EXPECT_WRITE(stderr,
|
||||
{
|
||||
// The close function must be called inside EXPECT_WRITE,
|
||||
// otherwise the system may recycle closed file descriptor when
|
||||
// redirecting the output in EXPECT_STDERR and the second close
|
||||
// will break output redirection.
|
||||
FMT_POSIX(close(write_fd));
|
||||
SUPPRESS_ASSERT(redir.reset(nullptr));
|
||||
},
|
||||
format_system_error(EBADF, "cannot flush stream"));
|
||||
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
||||
}
|
||||
|
||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||
|
|
25
externals/fmt/test/gtest-extra.cc
vendored
25
externals/fmt/test/gtest-extra.cc
vendored
|
@ -7,30 +7,28 @@
|
|||
|
||||
#include "gtest-extra.h"
|
||||
|
||||
#if FMT_USE_FILE_DESCRIPTORS
|
||||
#if FMT_USE_FCNTL
|
||||
|
||||
using fmt::file;
|
||||
|
||||
void OutputRedirect::flush() {
|
||||
#if EOF != -1
|
||||
# error "FMT_RETRY assumes return value of -1 indicating failure"
|
||||
#endif
|
||||
# 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");
|
||||
if (result != 0) throw fmt::system_error(errno, "cannot flush stream");
|
||||
}
|
||||
|
||||
void OutputRedirect::restore() {
|
||||
if (original_.descriptor() == -1)
|
||||
return; // Already restored.
|
||||
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) {
|
||||
OutputRedirect::OutputRedirect(FILE* f) : file_(f) {
|
||||
flush();
|
||||
int fd = FMT_POSIX(fileno(f));
|
||||
// Create a file object referring to the original file.
|
||||
|
@ -45,7 +43,7 @@ OutputRedirect::OutputRedirect(FILE *f) : file_(f) {
|
|||
OutputRedirect::~OutputRedirect() FMT_NOEXCEPT {
|
||||
try {
|
||||
restore();
|
||||
} catch (const std::exception &e) {
|
||||
} catch (const std::exception& e) {
|
||||
std::fputs(e.what(), stderr);
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +54,7 @@ std::string OutputRedirect::restore_and_read() {
|
|||
|
||||
// Read everything from the pipe.
|
||||
std::string content;
|
||||
if (read_end_.descriptor() == -1)
|
||||
return content; // Already read.
|
||||
if (read_end_.descriptor() == -1) return content; // Already read.
|
||||
enum { BUFFER_SIZE = 4096 };
|
||||
char buffer[BUFFER_SIZE];
|
||||
std::size_t count = 0;
|
||||
|
@ -69,7 +66,7 @@ std::string OutputRedirect::restore_and_read() {
|
|||
return content;
|
||||
}
|
||||
|
||||
std::string read(file &f, std::size_t count) {
|
||||
std::string read(file& f, std::size_t count) {
|
||||
std::string buffer(count, '\0');
|
||||
std::size_t n = 0, offset = 0;
|
||||
do {
|
||||
|
@ -81,7 +78,7 @@ std::string read(file &f, std::size_t count) {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
std::string format_system_error(int error_code, fmt::string_view message) {
|
||||
fmt::memory_buffer out;
|
||||
|
|
165
externals/fmt/test/gtest-extra.h
vendored
165
externals/fmt/test/gtest-extra.h
vendored
|
@ -10,70 +10,59 @@
|
|||
|
||||
#include <string>
|
||||
#include "gmock.h"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#ifndef FMT_USE_FILE_DESCRIPTORS
|
||||
# define FMT_USE_FILE_DESCRIPTORS 0
|
||||
#endif
|
||||
|
||||
#if FMT_USE_FILE_DESCRIPTORS
|
||||
# include "fmt/posix.h"
|
||||
#endif
|
||||
#include "fmt/os.h"
|
||||
|
||||
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||
std::string gtest_expected_message = expected_message; \
|
||||
bool gtest_caught_expected = false; \
|
||||
try { \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
} \
|
||||
catch (expected_exception const& e) { \
|
||||
if (gtest_expected_message != e.what()) { \
|
||||
gtest_ar \
|
||||
<< #statement " throws an exception with a different message.\n" \
|
||||
<< "Expected: " << gtest_expected_message << "\n" \
|
||||
<< " Actual: " << e.what(); \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
gtest_caught_expected = true; \
|
||||
} \
|
||||
catch (...) { \
|
||||
gtest_ar << \
|
||||
"Expected: " #statement " throws an exception of type " \
|
||||
#expected_exception ".\n Actual: it throws a different type."; \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
if (!gtest_caught_expected) { \
|
||||
gtest_ar << \
|
||||
"Expected: " #statement " throws an exception of type " \
|
||||
#expected_exception ".\n Actual: it throws nothing."; \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
} else \
|
||||
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
|
||||
fail(gtest_ar.failure_message())
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||
std::string gtest_expected_message = expected_message; \
|
||||
bool gtest_caught_expected = false; \
|
||||
try { \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
} catch (expected_exception const& e) { \
|
||||
if (gtest_expected_message != e.what()) { \
|
||||
gtest_ar << #statement \
|
||||
" throws an exception with a different message.\n" \
|
||||
<< "Expected: " << gtest_expected_message << "\n" \
|
||||
<< " Actual: " << e.what(); \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
gtest_caught_expected = true; \
|
||||
} catch (...) { \
|
||||
gtest_ar << "Expected: " #statement \
|
||||
" throws an exception of type " #expected_exception \
|
||||
".\n Actual: it throws a different type."; \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
if (!gtest_caught_expected) { \
|
||||
gtest_ar << "Expected: " #statement \
|
||||
" throws an exception of type " #expected_exception \
|
||||
".\n Actual: it throws nothing."; \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
} else \
|
||||
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \
|
||||
: fail(gtest_ar.failure_message())
|
||||
|
||||
// Tests that the statement throws the expected exception and the exception's
|
||||
// what() method returns expected message.
|
||||
#define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \
|
||||
FMT_TEST_THROW_(statement, expected_exception, \
|
||||
expected_message, GTEST_NONFATAL_FAILURE_)
|
||||
FMT_TEST_THROW_(statement, expected_exception, expected_message, \
|
||||
GTEST_NONFATAL_FAILURE_)
|
||||
|
||||
std::string format_system_error(int error_code, fmt::string_view message);
|
||||
|
||||
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
|
||||
EXPECT_THROW_MSG(statement, fmt::system_error, \
|
||||
format_system_error(error_code, message))
|
||||
EXPECT_THROW_MSG(statement, fmt::system_error, \
|
||||
format_system_error(error_code, message))
|
||||
|
||||
#if FMT_USE_FILE_DESCRIPTORS
|
||||
#if FMT_USE_FCNTL
|
||||
|
||||
// Captures file output by redirecting it to a pipe.
|
||||
// The output it can handle is limited by the pipe capacity.
|
||||
class OutputRedirect {
|
||||
private:
|
||||
FILE *file_;
|
||||
FILE* file_;
|
||||
fmt::file original_; // Original file passed to redirector.
|
||||
fmt::file read_end_; // Read end of the pipe where the output is redirected.
|
||||
|
||||
|
@ -83,7 +72,7 @@ class OutputRedirect {
|
|||
void restore();
|
||||
|
||||
public:
|
||||
explicit OutputRedirect(FILE *file);
|
||||
explicit OutputRedirect(FILE* file);
|
||||
~OutputRedirect() FMT_NOEXCEPT;
|
||||
|
||||
// Restores the original file, reads output from the pipe into a string
|
||||
|
@ -91,29 +80,28 @@ class OutputRedirect {
|
|||
std::string restore_and_read();
|
||||
};
|
||||
|
||||
#define FMT_TEST_WRITE_(statement, expected_output, file, fail) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||
std::string gtest_expected_output = expected_output; \
|
||||
OutputRedirect gtest_redir(file); \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
std::string gtest_output = gtest_redir.restore_and_read(); \
|
||||
if (gtest_output != gtest_expected_output) { \
|
||||
gtest_ar \
|
||||
<< #statement " produces different output.\n" \
|
||||
<< "Expected: " << gtest_expected_output << "\n" \
|
||||
<< " Actual: " << gtest_output; \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
} else \
|
||||
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
|
||||
fail(gtest_ar.failure_message())
|
||||
# define FMT_TEST_WRITE_(statement, expected_output, file, fail) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||
std::string gtest_expected_output = expected_output; \
|
||||
OutputRedirect gtest_redir(file); \
|
||||
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||
std::string gtest_output = gtest_redir.restore_and_read(); \
|
||||
if (gtest_output != gtest_expected_output) { \
|
||||
gtest_ar << #statement " produces different output.\n" \
|
||||
<< "Expected: " << gtest_expected_output << "\n" \
|
||||
<< " Actual: " << gtest_output; \
|
||||
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
|
||||
} \
|
||||
} else \
|
||||
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \
|
||||
: fail(gtest_ar.failure_message())
|
||||
|
||||
// Tests that the statement writes the expected output to file.
|
||||
#define EXPECT_WRITE(file, statement, expected_output) \
|
||||
# define EXPECT_WRITE(file, statement, expected_output) \
|
||||
FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# ifdef _MSC_VER
|
||||
|
||||
// Suppresses Windows assertions on invalid file descriptors, making
|
||||
// POSIX functions return proper error codes instead of crashing on Windows.
|
||||
|
@ -122,40 +110,45 @@ class SuppressAssert {
|
|||
_invalid_parameter_handler original_handler_;
|
||||
int original_report_mode_;
|
||||
|
||||
static void handle_invalid_parameter(const wchar_t *,
|
||||
const wchar_t *, const wchar_t *, unsigned , uintptr_t) {}
|
||||
static void handle_invalid_parameter(const wchar_t*, const wchar_t*,
|
||||
const wchar_t*, unsigned, uintptr_t) {}
|
||||
|
||||
public:
|
||||
SuppressAssert()
|
||||
: original_handler_(_set_invalid_parameter_handler(handle_invalid_parameter)),
|
||||
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {
|
||||
}
|
||||
: original_handler_(
|
||||
_set_invalid_parameter_handler(handle_invalid_parameter)),
|
||||
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
|
||||
~SuppressAssert() {
|
||||
_set_invalid_parameter_handler(original_handler_);
|
||||
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
|
||||
}
|
||||
};
|
||||
|
||||
# define SUPPRESS_ASSERT(statement) { SuppressAssert sa; statement; }
|
||||
#else
|
||||
# define SUPPRESS_ASSERT(statement) statement
|
||||
#endif // _MSC_VER
|
||||
# define SUPPRESS_ASSERT(statement) \
|
||||
{ \
|
||||
SuppressAssert sa; \
|
||||
statement; \
|
||||
}
|
||||
# else
|
||||
# define SUPPRESS_ASSERT(statement) statement
|
||||
# endif // _MSC_VER
|
||||
|
||||
#define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
|
||||
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
|
||||
# define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
|
||||
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
|
||||
|
||||
// Attempts to read count characters from a file.
|
||||
std::string read(fmt::file &f, std::size_t count);
|
||||
std::string read(fmt::file& f, std::size_t count);
|
||||
|
||||
#define EXPECT_READ(file, expected_content) \
|
||||
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
|
||||
# define EXPECT_READ(file, expected_content) \
|
||||
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
|
||||
|
||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||
#else
|
||||
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
template <typename Mock>
|
||||
struct ScopedMock : testing::StrictMock<Mock> {
|
||||
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
|
||||
ScopedMock() { Mock::instance = this; }
|
||||
~ScopedMock() { Mock::instance = FMT_NULL; }
|
||||
~ScopedMock() { Mock::instance = nullptr; }
|
||||
};
|
||||
|
||||
#endif // FMT_GTEST_EXTRA_H_
|
||||
|
|
63
externals/fmt/test/locale-test.cc
vendored
63
externals/fmt/test/locale-test.cc
vendored
|
@ -8,27 +8,82 @@
|
|||
#include "fmt/locale.h"
|
||||
#include "gmock.h"
|
||||
|
||||
template <typename Char>
|
||||
struct numpunct : std::numpunct<Char> {
|
||||
using fmt::internal::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, "{:n}", 1.23));
|
||||
}
|
||||
|
||||
TEST(LocaleTest, Format) {
|
||||
std::locale loc(std::locale(), new numpunct<char>());
|
||||
EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));
|
||||
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:n}", 1234567));
|
||||
EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567));
|
||||
fmt::format_arg_store<fmt::format_context, int> as{1234567};
|
||||
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as)));
|
||||
std::string s;
|
||||
fmt::format_to(std::back_inserter(s), loc, "{:n}", 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, "{:n}", 1234567));
|
||||
|
||||
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
|
||||
EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:n}", 12345678));
|
||||
|
||||
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, "{:n}", max_value<uint32_t>()));
|
||||
}
|
||||
|
||||
TEST(LocaleTest, WFormat) {
|
||||
std::locale loc(std::locale(), new numpunct<wchar_t>());
|
||||
EXPECT_EQ(L"1,234,567", fmt::format(std::locale(), L"{:n}", 1234567));
|
||||
EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:n}", 1234567));
|
||||
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567));
|
||||
fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
|
||||
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:n}", fmt::wformat_args(as)));
|
||||
EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:n}", 1234567));
|
||||
|
||||
std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>());
|
||||
EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:n}", 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"{:n}", 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"{:n}", max_value<uint32_t>()));
|
||||
}
|
||||
|
||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
|
34
externals/fmt/test/mock-allocator.h
vendored
34
externals/fmt/test/mock-allocator.h
vendored
|
@ -8,53 +8,51 @@
|
|||
#ifndef FMT_MOCK_ALLOCATOR_H_
|
||||
#define FMT_MOCK_ALLOCATOR_H_
|
||||
|
||||
#include "gmock.h"
|
||||
#include "fmt/format.h"
|
||||
#include "gmock.h"
|
||||
|
||||
template <typename T>
|
||||
class mock_allocator {
|
||||
template <typename T> class mock_allocator {
|
||||
public:
|
||||
mock_allocator() {}
|
||||
mock_allocator(const mock_allocator &) {}
|
||||
mock_allocator(const mock_allocator&) {}
|
||||
typedef T value_type;
|
||||
MOCK_METHOD1_T(allocate, T* (std::size_t n));
|
||||
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
|
||||
MOCK_METHOD1_T(allocate, T*(std::size_t n));
|
||||
MOCK_METHOD2_T(deallocate, void(T* p, std::size_t n));
|
||||
};
|
||||
|
||||
template <typename Allocator>
|
||||
class allocator_ref {
|
||||
template <typename Allocator> class allocator_ref {
|
||||
private:
|
||||
Allocator *alloc_;
|
||||
Allocator* alloc_;
|
||||
|
||||
void move(allocator_ref &other) {
|
||||
void move(allocator_ref& other) {
|
||||
alloc_ = other.alloc_;
|
||||
other.alloc_ = FMT_NULL;
|
||||
other.alloc_ = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef typename Allocator::value_type value_type;
|
||||
|
||||
explicit allocator_ref(Allocator *alloc = FMT_NULL) : alloc_(alloc) {}
|
||||
explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {}
|
||||
|
||||
allocator_ref(const allocator_ref &other) : alloc_(other.alloc_) {}
|
||||
allocator_ref(allocator_ref &&other) { move(other); }
|
||||
allocator_ref(const allocator_ref& other) : alloc_(other.alloc_) {}
|
||||
allocator_ref(allocator_ref&& other) { move(other); }
|
||||
|
||||
allocator_ref& operator=(allocator_ref &&other) {
|
||||
allocator_ref& operator=(allocator_ref&& other) {
|
||||
assert(this != &other);
|
||||
move(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_ref& operator=(const allocator_ref &other) {
|
||||
allocator_ref& operator=(const allocator_ref& other) {
|
||||
alloc_ = other.alloc_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
Allocator *get() const { return alloc_; }
|
||||
Allocator* get() const { return alloc_; }
|
||||
|
||||
value_type* allocate(std::size_t n) {
|
||||
return fmt::internal::allocate(*alloc_, n);
|
||||
return std::allocator_traits<Allocator>::allocate(*alloc_, n);
|
||||
}
|
||||
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Formatting library for C++ - tests of the C++ interface to POSIX functions
|
||||
// Formatting library for C++ - tests of the OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
|
@ -7,20 +7,138 @@
|
|||
|
||||
#include <cstdlib> // std::exit
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "fmt/posix.h"
|
||||
#include "fmt/os.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
using fmt::buffered_file;
|
||||
using fmt::error_code;
|
||||
using fmt::file;
|
||||
|
||||
using testing::internal::scoped_ptr;
|
||||
#ifdef _WIN32
|
||||
|
||||
# include <windows.h>
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8) {
|
||||
std::string s = "ёжик";
|
||||
fmt::internal::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8EmptyString) {
|
||||
std::string s = "";
|
||||
fmt::internal::utf16_to_utf8 u(L"");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
template <typename Converter, typename Char>
|
||||
void check_utf_conversion_error(
|
||||
const char* message,
|
||||
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
||||
fmt::system_error error(0, "");
|
||||
try {
|
||||
(Converter)(str);
|
||||
} catch (const fmt::system_error& e) {
|
||||
error = e;
|
||||
}
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
|
||||
EXPECT_EQ(fmt::to_string(out), error.what());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8Error) {
|
||||
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>(
|
||||
"cannot convert string from UTF-16 to UTF-8");
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8Convert) {
|
||||
fmt::internal::utf16_to_utf8 u;
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
||||
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
|
||||
}
|
||||
|
||||
TEST(UtilTest, FormatWindowsError) {
|
||||
LPWSTR message = 0;
|
||||
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
0, ERROR_FILE_EXISTS,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
||||
fmt::internal::utf16_to_utf8 utf8_message(message);
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
|
||||
"test");
|
||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
actual_message.resize(0);
|
||||
auto max_size = fmt::internal::max_value<size_t>();
|
||||
fmt::internal::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) {
|
||||
LPWSTR message = 0;
|
||||
// this error code is not available on all Windows platforms and
|
||||
// Windows SDKs, so do not fail the test if the error string cannot
|
||||
// be retrieved.
|
||||
const int provisioning_not_allowed =
|
||||
0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
0, static_cast<DWORD>(provisioning_not_allowed),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
|
||||
return;
|
||||
}
|
||||
fmt::internal::utf16_to_utf8 utf8_message(message);
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::internal::format_windows_error(actual_message, provisioning_not_allowed,
|
||||
"test");
|
||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, WindowsError) {
|
||||
fmt::system_error error(0, "");
|
||||
try {
|
||||
throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error");
|
||||
} catch (const fmt::system_error& e) {
|
||||
error = e;
|
||||
}
|
||||
fmt::memory_buffer message;
|
||||
fmt::internal::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
|
||||
EXPECT_EQ(to_string(message), error.what());
|
||||
EXPECT_EQ(ERROR_FILE_EXISTS, error.error_code());
|
||||
}
|
||||
|
||||
TEST(UtilTest, ReportWindowsError) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
|
||||
out.push_back('\n');
|
||||
EXPECT_WRITE(stderr,
|
||||
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
|
||||
fmt::to_string(out));
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
|
||||
using fmt::file;
|
||||
|
||||
// Checks if the file is open by reading one character from it.
|
||||
static bool isopen(int fd) {
|
||||
|
@ -45,9 +163,9 @@ static file open_file() {
|
|||
}
|
||||
|
||||
// Attempts to write a string to a file.
|
||||
static void write(file &f, fmt::string_view s) {
|
||||
static void write(file& f, fmt::string_view s) {
|
||||
std::size_t num_chars_left = s.size();
|
||||
const char *ptr = s.data();
|
||||
const char* ptr = s.data();
|
||||
do {
|
||||
std::size_t count = f.write(ptr, num_chars_left);
|
||||
ptr += count;
|
||||
|
@ -59,26 +177,26 @@ static void write(file &f, fmt::string_view s) {
|
|||
|
||||
TEST(BufferedFileTest, DefaultCtor) {
|
||||
buffered_file f;
|
||||
EXPECT_TRUE(f.get() == FMT_NULL);
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveCtor) {
|
||||
buffered_file bf = open_buffered_file();
|
||||
FILE *fp = bf.get();
|
||||
EXPECT_TRUE(fp != FMT_NULL);
|
||||
FILE* fp = bf.get();
|
||||
EXPECT_TRUE(fp != nullptr);
|
||||
buffered_file bf2(std::move(bf));
|
||||
EXPECT_EQ(fp, bf2.get());
|
||||
EXPECT_TRUE(bf.get() == FMT_NULL);
|
||||
EXPECT_TRUE(bf.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveAssignment) {
|
||||
buffered_file bf = open_buffered_file();
|
||||
FILE *fp = bf.get();
|
||||
EXPECT_TRUE(fp != FMT_NULL);
|
||||
FILE* fp = bf.get();
|
||||
EXPECT_TRUE(fp != nullptr);
|
||||
buffered_file bf2;
|
||||
bf2 = std::move(bf);
|
||||
EXPECT_EQ(fp, bf2.get());
|
||||
EXPECT_TRUE(bf.get() == FMT_NULL);
|
||||
EXPECT_TRUE(bf.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||
|
@ -90,13 +208,13 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
|||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
||||
FILE *fp = FMT_NULL;
|
||||
FILE* fp = nullptr;
|
||||
buffered_file f(open_buffered_file(&fp));
|
||||
EXPECT_EQ(fp, f.get());
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
||||
FILE *fp = FMT_NULL;
|
||||
FILE* fp = nullptr;
|
||||
buffered_file f;
|
||||
f = open_buffered_file(&fp);
|
||||
EXPECT_EQ(fp, f.get());
|
||||
|
@ -119,22 +237,24 @@ TEST(BufferedFileTest, CloseFileInDtor) {
|
|||
}
|
||||
|
||||
TEST(BufferedFileTest, CloseErrorInDtor) {
|
||||
scoped_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
|
||||
EXPECT_WRITE(stderr, {
|
||||
// The close function must be called inside EXPECT_WRITE, otherwise
|
||||
// the system may recycle closed file descriptor when redirecting the
|
||||
// output in EXPECT_STDERR and the second close will break output
|
||||
// redirection.
|
||||
FMT_POSIX(close(f->fileno()));
|
||||
SUPPRESS_ASSERT(f.reset(FMT_NULL));
|
||||
}, format_system_error(EBADF, "cannot close file") + "\n");
|
||||
std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
|
||||
EXPECT_WRITE(stderr,
|
||||
{
|
||||
// The close function must be called inside EXPECT_WRITE,
|
||||
// otherwise the system may recycle closed file descriptor when
|
||||
// redirecting the output in EXPECT_STDERR and the second close
|
||||
// will break output redirection.
|
||||
FMT_POSIX(close(f->fileno()));
|
||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||
},
|
||||
format_system_error(EBADF, "cannot close file") + "\n");
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, Close) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int fd = f.fileno();
|
||||
f.close();
|
||||
EXPECT_TRUE(f.get() == FMT_NULL);
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
EXPECT_TRUE(isclosed(fd));
|
||||
}
|
||||
|
||||
|
@ -142,22 +262,24 @@ TEST(BufferedFileTest, CloseError) {
|
|||
buffered_file f = open_buffered_file();
|
||||
FMT_POSIX(close(f.fileno()));
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||
EXPECT_TRUE(f.get() == FMT_NULL);
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, Fileno) {
|
||||
buffered_file f;
|
||||
#ifndef __COVERITY__
|
||||
# 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
|
||||
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);
|
||||
file copy = file::dup(f.fileno());
|
||||
|
@ -170,7 +292,7 @@ TEST(FileTest, DefaultCtor) {
|
|||
}
|
||||
|
||||
TEST(FileTest, OpenBufferedFileInCtor) {
|
||||
FILE *fp = safe_fopen("test-file", "w");
|
||||
FILE* fp = safe_fopen("test-file", "w");
|
||||
std::fputs(FILE_CONTENT, fp);
|
||||
std::fclose(fp);
|
||||
file f("test-file", file::RDONLY);
|
||||
|
@ -178,8 +300,8 @@ TEST(FileTest, OpenBufferedFileInCtor) {
|
|||
}
|
||||
|
||||
TEST(FileTest, OpenBufferedFileError) {
|
||||
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY),
|
||||
ENOENT, "cannot open file nonexistent");
|
||||
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
|
||||
"cannot open file nonexistent");
|
||||
}
|
||||
|
||||
TEST(FileTest, MoveCtor) {
|
||||
|
@ -209,7 +331,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
|
|||
EXPECT_TRUE(isclosed(old_fd));
|
||||
}
|
||||
|
||||
static file OpenBufferedFile(int &fd) {
|
||||
static file OpenBufferedFile(int& fd) {
|
||||
file f = open_file();
|
||||
fd = f.descriptor();
|
||||
return f;
|
||||
|
@ -246,15 +368,17 @@ TEST(FileTest, CloseFileInDtor) {
|
|||
}
|
||||
|
||||
TEST(FileTest, CloseErrorInDtor) {
|
||||
scoped_ptr<file> f(new file(open_file()));
|
||||
EXPECT_WRITE(stderr, {
|
||||
// The close function must be called inside EXPECT_WRITE, otherwise
|
||||
// the system may recycle closed file descriptor when redirecting the
|
||||
// output in EXPECT_STDERR and the second close will break output
|
||||
// redirection.
|
||||
FMT_POSIX(close(f->descriptor()));
|
||||
SUPPRESS_ASSERT(f.reset(FMT_NULL));
|
||||
}, format_system_error(EBADF, "cannot close file") + "\n");
|
||||
std::unique_ptr<file> f(new file(open_file()));
|
||||
EXPECT_WRITE(stderr,
|
||||
{
|
||||
// The close function must be called inside EXPECT_WRITE,
|
||||
// otherwise the system may recycle closed file descriptor when
|
||||
// redirecting the output in EXPECT_STDERR and the second close
|
||||
// will break output redirection.
|
||||
FMT_POSIX(close(f->descriptor()));
|
||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||
},
|
||||
format_system_error(EBADF, "cannot close file") + "\n");
|
||||
}
|
||||
|
||||
TEST(FileTest, Close) {
|
||||
|
@ -307,13 +431,13 @@ TEST(FileTest, Dup) {
|
|||
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
|
||||
}
|
||||
|
||||
#ifndef __COVERITY__
|
||||
# ifndef __COVERITY__
|
||||
TEST(FileTest, DupError) {
|
||||
int value = -1;
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value),
|
||||
EBADF, "cannot duplicate file descriptor -1");
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
|
||||
"cannot duplicate file descriptor -1");
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
TEST(FileTest, Dup2) {
|
||||
file f = open_file();
|
||||
|
@ -325,8 +449,9 @@ TEST(FileTest, Dup2) {
|
|||
|
||||
TEST(FileTest, Dup2Error) {
|
||||
file f = open_file();
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
|
||||
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||
f.dup2(-1), EBADF,
|
||||
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
||||
}
|
||||
|
||||
TEST(FileTest, Dup2NoExcept) {
|
||||
|
@ -362,17 +487,12 @@ TEST(FileTest, Fdopen) {
|
|||
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
|
||||
}
|
||||
|
||||
TEST(FileTest, FdopenError) {
|
||||
file f;
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
|
||||
}
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
# ifdef FMT_LOCALE
|
||||
TEST(LocaleTest, Strtod) {
|
||||
fmt::Locale locale;
|
||||
fmt::locale loc;
|
||||
const char *start = "4.2", *ptr = start;
|
||||
EXPECT_EQ(4.2, locale.strtod(ptr));
|
||||
EXPECT_EQ(4.2, loc.strtod(ptr));
|
||||
EXPECT_EQ(start + 3, ptr);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
#endif // FMT_USE_FCNTL
|
190
externals/fmt/test/ostream-test.cc
vendored
190
externals/fmt/test/ostream-test.cc
vendored
|
@ -6,6 +6,21 @@
|
|||
// For the license information refer to format.h.
|
||||
|
||||
#define FMT_STRING_ALIAS 1
|
||||
#include "fmt/format.h"
|
||||
|
||||
struct test {};
|
||||
|
||||
// Test that there is no issues with specializations when fmt/ostream.h is
|
||||
// included after fmt/format.h.
|
||||
namespace fmt {
|
||||
template <> struct formatter<test> : formatter<int> {
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const test&, FormatContext& ctx) {
|
||||
return formatter<int>::format(42, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
||||
#include "fmt/ostream.h"
|
||||
|
||||
#include <sstream>
|
||||
|
@ -16,51 +31,56 @@
|
|||
using fmt::format;
|
||||
using fmt::format_error;
|
||||
|
||||
static std::ostream &operator<<(std::ostream &os, const Date &d) {
|
||||
static std::ostream& operator<<(std::ostream& os, const Date& d) {
|
||||
os << d.year() << '-' << d.month() << '-' << d.day();
|
||||
return os;
|
||||
}
|
||||
|
||||
static std::wostream &operator<<(std::wostream &os, const Date &d) {
|
||||
static std::wostream& operator<<(std::wostream& os, const Date& d) {
|
||||
os << d.year() << L'-' << d.month() << L'-' << d.day();
|
||||
return os;
|
||||
}
|
||||
|
||||
enum TestEnum {};
|
||||
static std::ostream &operator<<(std::ostream &os, TestEnum) {
|
||||
return os << "TestEnum";
|
||||
// Make sure that overloaded comma operators do no harm to is_streamable.
|
||||
struct type_with_comma_op {};
|
||||
template <typename T> void operator,(type_with_comma_op, const T&);
|
||||
template <typename T> type_with_comma_op operator<<(T&, const Date&);
|
||||
|
||||
enum streamable_enum {};
|
||||
static std::ostream& operator<<(std::ostream& os, streamable_enum) {
|
||||
return os << "streamable_enum";
|
||||
}
|
||||
|
||||
static std::wostream &operator<<(std::wostream &os, TestEnum) {
|
||||
return os << L"TestEnum";
|
||||
static std::wostream& operator<<(std::wostream& os, streamable_enum) {
|
||||
return os << L"streamable_enum";
|
||||
}
|
||||
|
||||
enum TestEnum2 {A};
|
||||
enum unstreamable_enum {};
|
||||
|
||||
TEST(OStreamTest, Enum) {
|
||||
EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value));
|
||||
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
|
||||
EXPECT_EQ("0", fmt::format("{}", A));
|
||||
EXPECT_FALSE((fmt::convert_to_int<TestEnum, wchar_t>::value));
|
||||
EXPECT_EQ(L"TestEnum", fmt::format(L"{}", TestEnum()));
|
||||
EXPECT_EQ(L"0", fmt::format(L"{}", A));
|
||||
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_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()));
|
||||
}
|
||||
|
||||
typedef fmt::back_insert_range<fmt::internal::buffer> range;
|
||||
using range = fmt::buffer_range<char>;
|
||||
|
||||
struct test_arg_formatter: fmt::arg_formatter<range> {
|
||||
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
|
||||
: fmt::arg_formatter<range>(ctx, &s) {}
|
||||
struct test_arg_formatter : fmt::arg_formatter<range> {
|
||||
fmt::format_parse_context parse_ctx;
|
||||
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
|
||||
: fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {}
|
||||
};
|
||||
|
||||
TEST(OStreamTest, CustomArg) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::internal::buffer &base = buffer;
|
||||
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
|
||||
fmt::internal::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);
|
||||
visit(af, fmt::internal::make_arg<fmt::format_context>(TestEnum()));
|
||||
EXPECT_EQ("TestEnum", std::string(buffer.data(), buffer.size()));
|
||||
fmt::visit_format_arg(
|
||||
af, fmt::internal::make_arg<fmt::format_context>(streamable_enum()));
|
||||
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
|
||||
}
|
||||
|
||||
TEST(OStreamTest, Format) {
|
||||
|
@ -75,20 +95,22 @@ TEST(OStreamTest, Format) {
|
|||
TEST(OStreamTest, FormatSpecs) {
|
||||
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
|
||||
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
|
||||
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
#if FMT_NUMERIC_ALIGN
|
||||
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
|
||||
"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_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")));
|
||||
|
@ -96,7 +118,7 @@ TEST(OStreamTest, FormatSpecs) {
|
|||
}
|
||||
|
||||
struct EmptyTest {};
|
||||
static std::ostream &operator<<(std::ostream &os, EmptyTest) {
|
||||
static std::ostream& operator<<(std::ostream& os, EmptyTest) {
|
||||
return os << "";
|
||||
}
|
||||
|
||||
|
@ -116,37 +138,36 @@ TEST(OStreamTest, Print) {
|
|||
TEST(OStreamTest, WriteToOStream) {
|
||||
std::ostringstream os;
|
||||
fmt::memory_buffer buffer;
|
||||
const char *foo = "foo";
|
||||
const char* foo = "foo";
|
||||
buffer.append(foo, foo + std::strlen(foo));
|
||||
fmt::internal::write(os, buffer);
|
||||
EXPECT_EQ("foo", os.str());
|
||||
}
|
||||
|
||||
TEST(OStreamTest, WriteToOStreamMaxSize) {
|
||||
std::size_t max_size = std::numeric_limits<std::size_t>::max();
|
||||
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
|
||||
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
|
||||
return;
|
||||
std::size_t max_size = fmt::internal::max_value<std::size_t>();
|
||||
std::streamsize max_streamsize = fmt::internal::max_value<std::streamsize>();
|
||||
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return;
|
||||
|
||||
struct test_buffer : fmt::internal::buffer {
|
||||
struct test_buffer : fmt::internal::buffer<char> {
|
||||
explicit test_buffer(std::size_t size) { resize(size); }
|
||||
void grow(std::size_t) {}
|
||||
} buffer(max_size);
|
||||
|
||||
struct mock_streambuf : std::streambuf {
|
||||
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
|
||||
std::streamsize xsputn(const char *s, std::streamsize n) {
|
||||
const void *v = s;
|
||||
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
|
||||
std::streamsize xsputn(const char* s, std::streamsize n) {
|
||||
const void* v = s;
|
||||
return xsputn(v, n);
|
||||
}
|
||||
} streambuf;
|
||||
|
||||
struct test_ostream : std::ostream {
|
||||
explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {}
|
||||
explicit test_ostream(mock_streambuf& buffer) : std::ostream(&buffer) {}
|
||||
} os(streambuf);
|
||||
|
||||
testing::InSequence sequence;
|
||||
const char *data = FMT_NULL;
|
||||
const char* data = nullptr;
|
||||
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
|
||||
ustreamsize size = max_size;
|
||||
do {
|
||||
|
@ -166,18 +187,40 @@ TEST(OStreamTest, Join) {
|
|||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
TEST(OStreamTest, ConstexprString) {
|
||||
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
|
||||
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
||||
EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace fmt_test {
|
||||
struct ABC {};
|
||||
|
||||
template <typename Output> Output &operator<<(Output &out, ABC) {
|
||||
template <typename Output> Output& operator<<(Output& out, ABC) {
|
||||
out << "ABC";
|
||||
return out;
|
||||
}
|
||||
} // namespace fmt_test
|
||||
} // namespace fmt_test
|
||||
|
||||
template <typename T> struct TestTemplate {};
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, TestTemplate<T>) {
|
||||
return os << 1;
|
||||
}
|
||||
|
||||
namespace fmt {
|
||||
template <typename T> struct formatter<TestTemplate<T>> : formatter<int> {
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(TestTemplate<T>, FormatContext& ctx) {
|
||||
return formatter<int>::format(2, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407
|
||||
TEST(OStreamTest, Template) {
|
||||
EXPECT_EQ("2", fmt::format("{}", TestTemplate<int>()));
|
||||
}
|
||||
|
||||
TEST(FormatTest, FormatToN) {
|
||||
char buffer[4];
|
||||
|
@ -191,6 +234,7 @@ TEST(FormatTest, FormatToN) {
|
|||
EXPECT_EQ(buffer + 3, result.out);
|
||||
EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
TEST(FormatTest, UDL) {
|
||||
|
@ -198,3 +242,51 @@ TEST(FormatTest, UDL) {
|
|||
EXPECT_EQ("{}"_format("test"), "test");
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T> struct convertible {
|
||||
T value;
|
||||
explicit convertible(const T& val) : value(val) {}
|
||||
operator T() const { return value; }
|
||||
};
|
||||
|
||||
TEST(OStreamTest, DisableBuiltinOStreamOperators) {
|
||||
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")));
|
||||
}
|
||||
|
||||
struct explicitly_convertible_to_string_like {
|
||||
template <typename String,
|
||||
typename = typename std::enable_if<std::is_constructible<
|
||||
String, const char*, std::size_t>::value>::type>
|
||||
explicit operator String() const {
|
||||
return String("foo", 3u);
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
explicitly_convertible_to_string_like) {
|
||||
return os << "bar";
|
||||
}
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
|
||||
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
||||
}
|
||||
|
||||
#ifdef FMT_USE_STRING_VIEW
|
||||
struct explicitly_convertible_to_std_string_view {
|
||||
explicit operator fmt::internal::std_string_view<char>() const {
|
||||
return {"foo", 3u};
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
explicitly_convertible_to_std_string_view) {
|
||||
return os << "bar";
|
||||
}
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
|
||||
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
||||
}
|
||||
|
||||
#endif // FMT_USE_STRING_VIEW
|
||||
|
|
343
externals/fmt/test/posix-mock-test.cc
vendored
343
externals/fmt/test/posix-mock-test.cc
vendored
|
@ -6,21 +6,24 @@
|
|||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#ifdef _MSC_VER
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "posix-mock.h"
|
||||
#include "../src/posix.cc"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <climits>
|
||||
#include <memory>
|
||||
|
||||
#include "../src/os.cc"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
# undef max
|
||||
# undef ERROR
|
||||
# include <io.h>
|
||||
# undef max
|
||||
# undef ERROR
|
||||
#endif
|
||||
|
||||
#include "gmock.h"
|
||||
|
@ -29,12 +32,10 @@
|
|||
|
||||
using fmt::buffered_file;
|
||||
using fmt::error_code;
|
||||
using fmt::file;
|
||||
|
||||
using testing::internal::scoped_ptr;
|
||||
using testing::_;
|
||||
using testing::StrEq;
|
||||
using testing::Return;
|
||||
using testing::StrEq;
|
||||
|
||||
namespace {
|
||||
int open_count;
|
||||
|
@ -52,25 +53,25 @@ std::size_t read_nbyte;
|
|||
std::size_t write_nbyte;
|
||||
bool sysconf_error;
|
||||
|
||||
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
|
||||
}
|
||||
enum { NONE, MAX_SIZE, ERROR } fstat_sim;
|
||||
} // namespace
|
||||
|
||||
#define EMULATE_EINTR(func, error_result) \
|
||||
if (func##_count != 0) { \
|
||||
if (func##_count++ != 3) { \
|
||||
errno = EINTR; \
|
||||
return error_result; \
|
||||
} \
|
||||
if (func##_count != 0) { \
|
||||
if (func##_count++ != 3) { \
|
||||
errno = EINTR; \
|
||||
return error_result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
int test::open(const char *path, int oflag, int mode) {
|
||||
int test::open(const char* path, int oflag, int mode) {
|
||||
EMULATE_EINTR(open, -1);
|
||||
return ::open(path, oflag, mode);
|
||||
}
|
||||
#else
|
||||
errno_t test::sopen_s(
|
||||
int* pfh, const char *filename, int oflag, int shflag, int pmode) {
|
||||
errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
|
||||
int pmode) {
|
||||
EMULATE_EINTR(open, EINTR);
|
||||
return _sopen_s(pfh, filename, oflag, shflag, pmode);
|
||||
}
|
||||
|
@ -80,8 +81,7 @@ errno_t test::sopen_s(
|
|||
|
||||
long test::sysconf(int name) {
|
||||
long result = ::sysconf(name);
|
||||
if (!sysconf_error)
|
||||
return result;
|
||||
if (!sysconf_error) return result;
|
||||
// Simulate an error.
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -89,10 +89,9 @@ long test::sysconf(int name) {
|
|||
|
||||
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);
|
||||
if (fstat_sim == MAX_SIZE)
|
||||
buf->st_size = max_file_size();
|
||||
if (fstat_sim == MAX_SIZE) buf->st_size = max_file_size();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -132,18 +131,18 @@ int test::dup2(int fildes, int fildes2) {
|
|||
return ::FMT_POSIX(dup2(fildes, fildes2));
|
||||
}
|
||||
|
||||
FILE *test::fdopen(int fildes, const char *mode) {
|
||||
EMULATE_EINTR(fdopen, FMT_NULL);
|
||||
FILE* test::fdopen(int fildes, const char* mode) {
|
||||
EMULATE_EINTR(fdopen, nullptr);
|
||||
return ::FMT_POSIX(fdopen(fildes, mode));
|
||||
}
|
||||
|
||||
test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) {
|
||||
test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) {
|
||||
read_nbyte = nbyte;
|
||||
EMULATE_EINTR(read, -1);
|
||||
return ::FMT_POSIX(read(fildes, buf, nbyte));
|
||||
}
|
||||
|
||||
test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) {
|
||||
test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) {
|
||||
write_nbyte = nbyte;
|
||||
EMULATE_EINTR(write, -1);
|
||||
return ::FMT_POSIX(write(fildes, buf, nbyte));
|
||||
|
@ -155,23 +154,23 @@ int test::pipe(int fildes[2]) {
|
|||
return ::pipe(fildes);
|
||||
}
|
||||
#else
|
||||
int test::pipe(int *pfds, unsigned psize, int textmode) {
|
||||
int test::pipe(int* pfds, unsigned psize, int textmode) {
|
||||
EMULATE_EINTR(pipe, -1);
|
||||
return _pipe(pfds, psize, textmode);
|
||||
}
|
||||
#endif
|
||||
|
||||
FILE *test::fopen(const char *filename, const char *mode) {
|
||||
EMULATE_EINTR(fopen, FMT_NULL);
|
||||
FILE* test::fopen(const char* filename, const char* mode) {
|
||||
EMULATE_EINTR(fopen, nullptr);
|
||||
return ::fopen(filename, mode);
|
||||
}
|
||||
|
||||
int test::fclose(FILE *stream) {
|
||||
int test::fclose(FILE* stream) {
|
||||
EMULATE_EINTR(fclose, EOF);
|
||||
return ::fclose(stream);
|
||||
}
|
||||
|
||||
int (test::fileno)(FILE *stream) {
|
||||
int(test::fileno)(FILE* stream) {
|
||||
EMULATE_EINTR(fileno, -1);
|
||||
#ifdef fileno
|
||||
return FMT_POSIX(fileno(stream));
|
||||
|
@ -181,18 +180,18 @@ int (test::fileno)(FILE *stream) {
|
|||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
# define EXPECT_RETRY(statement, func, message) \
|
||||
func##_count = 1; \
|
||||
statement; \
|
||||
EXPECT_EQ(4, func##_count); \
|
||||
# define EXPECT_RETRY(statement, func, message) \
|
||||
func##_count = 1; \
|
||||
statement; \
|
||||
EXPECT_EQ(4, func##_count); \
|
||||
func##_count = 0;
|
||||
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
|
||||
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
|
||||
#else
|
||||
# define EXPECT_RETRY(statement, func, message) \
|
||||
func##_count = 1; \
|
||||
# define EXPECT_RETRY(statement, func, message) \
|
||||
func##_count = 1; \
|
||||
EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
|
||||
func##_count = 0;
|
||||
# define EXPECT_EQ_POSIX(expected, actual)
|
||||
# define EXPECT_EQ_POSIX(expected, actual)
|
||||
#endif
|
||||
|
||||
static void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||
|
@ -200,42 +199,48 @@ static void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
|||
f.print("{}", content);
|
||||
}
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
using fmt::file;
|
||||
|
||||
TEST(UtilTest, GetPageSize) {
|
||||
#ifdef _WIN32
|
||||
# ifdef _WIN32
|
||||
SYSTEM_INFO si = {};
|
||||
GetSystemInfo(&si);
|
||||
EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
|
||||
#else
|
||||
# else
|
||||
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
|
||||
sysconf_error = true;
|
||||
EXPECT_SYSTEM_ERROR(
|
||||
fmt::getpagesize(), EINVAL, "cannot get memory page size");
|
||||
EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
|
||||
"cannot get memory page size");
|
||||
sysconf_error = false;
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(FileTest, OpenRetry) {
|
||||
write_file("test", "there must be something here");
|
||||
scoped_ptr<file> f{FMT_NULL};
|
||||
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)),
|
||||
open, "cannot open file test");
|
||||
#ifndef _WIN32
|
||||
write_file("temp", "there must be something here");
|
||||
std::unique_ptr<file> f{nullptr};
|
||||
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
|
||||
"cannot open file temp");
|
||||
# ifndef _WIN32
|
||||
char c = 0;
|
||||
f->read(&c, 1);
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(FileTest, CloseNoRetryInDtor) {
|
||||
file read_end, write_end;
|
||||
file::pipe(read_end, write_end);
|
||||
scoped_ptr<file> f(new file(std::move(read_end)));
|
||||
std::unique_ptr<file> f(new file(std::move(read_end)));
|
||||
int saved_close_count = 0;
|
||||
EXPECT_WRITE(stderr, {
|
||||
close_count = 1;
|
||||
f.reset(FMT_NULL);
|
||||
saved_close_count = close_count;
|
||||
close_count = 0;
|
||||
}, format_system_error(EINTR, "cannot close file") + "\n");
|
||||
EXPECT_WRITE(
|
||||
stderr,
|
||||
{
|
||||
close_count = 1;
|
||||
f.reset(nullptr);
|
||||
saved_close_count = close_count;
|
||||
close_count = 0;
|
||||
},
|
||||
format_system_error(EINTR, "cannot close file") + "\n");
|
||||
EXPECT_EQ(2, saved_close_count);
|
||||
}
|
||||
|
||||
|
@ -250,26 +255,26 @@ TEST(FileTest, CloseNoRetry) {
|
|||
|
||||
TEST(FileTest, Size) {
|
||||
std::string content = "top secret, destroy before reading";
|
||||
write_file("test", content);
|
||||
file f("test", file::RDONLY);
|
||||
write_file("temp", content);
|
||||
file f("temp", file::RDONLY);
|
||||
EXPECT_GE(f.size(), 0);
|
||||
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
||||
#ifdef _WIN32
|
||||
# ifdef _WIN32
|
||||
fmt::memory_buffer message;
|
||||
fmt::internal::format_windows_error(
|
||||
message, ERROR_ACCESS_DENIED, "cannot get file size");
|
||||
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
|
||||
"cannot get file size");
|
||||
fstat_sim = ERROR;
|
||||
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
|
||||
fstat_sim = NONE;
|
||||
#else
|
||||
# else
|
||||
f.close();
|
||||
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(FileTest, MaxSize) {
|
||||
write_file("test", "");
|
||||
file f("test", file::RDONLY);
|
||||
write_file("temp", "");
|
||||
file f("temp", file::RDONLY);
|
||||
fstat_sim = MAX_SIZE;
|
||||
EXPECT_GE(f.size(), 0);
|
||||
EXPECT_EQ(max_file_size(), f.size());
|
||||
|
@ -284,8 +289,8 @@ TEST(FileTest, ReadRetry) {
|
|||
write_end.close();
|
||||
char buffer[SIZE];
|
||||
std::size_t count = 0;
|
||||
EXPECT_RETRY(count = read_end.read(buffer, SIZE),
|
||||
read, "cannot read from file");
|
||||
EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
|
||||
"cannot read from file");
|
||||
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
|
||||
}
|
||||
|
||||
|
@ -294,26 +299,25 @@ TEST(FileTest, WriteRetry) {
|
|||
file::pipe(read_end, write_end);
|
||||
enum { SIZE = 4 };
|
||||
std::size_t count = 0;
|
||||
EXPECT_RETRY(count = write_end.write("test", SIZE),
|
||||
write, "cannot write to file");
|
||||
EXPECT_RETRY(count = write_end.write("test", SIZE), write,
|
||||
"cannot write to file");
|
||||
write_end.close();
|
||||
#ifndef _WIN32
|
||||
# ifndef _WIN32
|
||||
EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
|
||||
char buffer[SIZE + 1];
|
||||
read_end.read(buffer, SIZE);
|
||||
buffer[SIZE] = '\0';
|
||||
EXPECT_STREQ("test", buffer);
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef _WIN32
|
||||
TEST(FileTest, ConvertReadCount) {
|
||||
file read_end, write_end;
|
||||
file::pipe(read_end, write_end);
|
||||
char c;
|
||||
std::size_t size = UINT_MAX;
|
||||
if (sizeof(unsigned) != sizeof(std::size_t))
|
||||
++size;
|
||||
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
|
||||
read_count = 1;
|
||||
read_nbyte = 0;
|
||||
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
|
||||
|
@ -326,20 +330,20 @@ TEST(FileTest, ConvertWriteCount) {
|
|||
file::pipe(read_end, write_end);
|
||||
char c;
|
||||
std::size_t size = UINT_MAX;
|
||||
if (sizeof(unsigned) != sizeof(std::size_t))
|
||||
++size;
|
||||
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
|
||||
write_count = 1;
|
||||
write_nbyte = 0;
|
||||
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
|
||||
write_count = 0;
|
||||
EXPECT_EQ(UINT_MAX, write_nbyte);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
TEST(FileTest, DupNoRetry) {
|
||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||
dup_count = 1;
|
||||
EXPECT_SYSTEM_ERROR(file::dup(stdout_fd), EINTR,
|
||||
EXPECT_SYSTEM_ERROR(
|
||||
file::dup(stdout_fd), EINTR,
|
||||
fmt::format("cannot duplicate file descriptor {}", stdout_fd));
|
||||
dup_count = 0;
|
||||
}
|
||||
|
@ -348,8 +352,8 @@ TEST(FileTest, Dup2Retry) {
|
|||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
||||
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
|
||||
fmt::format("cannot duplicate file descriptor {} to {}",
|
||||
f1.descriptor(), f2.descriptor()));
|
||||
fmt::format("cannot duplicate file descriptor {} to {}",
|
||||
f1.descriptor(), f2.descriptor()));
|
||||
}
|
||||
|
||||
TEST(FileTest, Dup2NoExceptRetry) {
|
||||
|
@ -358,19 +362,19 @@ TEST(FileTest, Dup2NoExceptRetry) {
|
|||
error_code ec;
|
||||
dup2_count = 1;
|
||||
f1.dup2(f2.descriptor(), ec);
|
||||
#ifndef _WIN32
|
||||
# ifndef _WIN32
|
||||
EXPECT_EQ(4, dup2_count);
|
||||
#else
|
||||
# else
|
||||
EXPECT_EQ(EINTR, ec.get());
|
||||
#endif
|
||||
# endif
|
||||
dup2_count = 0;
|
||||
}
|
||||
|
||||
TEST(FileTest, PipeNoRetry) {
|
||||
file read_end, write_end;
|
||||
pipe_count = 1;
|
||||
EXPECT_SYSTEM_ERROR(
|
||||
file::pipe(read_end, write_end), EINTR, "cannot create pipe");
|
||||
EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
|
||||
"cannot create pipe");
|
||||
pipe_count = 0;
|
||||
}
|
||||
|
||||
|
@ -378,34 +382,37 @@ TEST(FileTest, FdopenNoRetry) {
|
|||
file read_end, write_end;
|
||||
file::pipe(read_end, write_end);
|
||||
fdopen_count = 1;
|
||||
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
|
||||
EINTR, "cannot associate stream with file descriptor");
|
||||
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR,
|
||||
"cannot associate stream with file descriptor");
|
||||
fdopen_count = 0;
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, OpenRetry) {
|
||||
write_file("test", "there must be something here");
|
||||
scoped_ptr<buffered_file> f{FMT_NULL};
|
||||
EXPECT_RETRY(f.reset(new buffered_file("test", "r")),
|
||||
fopen, "cannot open file test");
|
||||
#ifndef _WIN32
|
||||
write_file("temp", "there must be something here");
|
||||
std::unique_ptr<buffered_file> f{nullptr};
|
||||
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
|
||||
"cannot open file temp");
|
||||
# ifndef _WIN32
|
||||
char c = 0;
|
||||
if (fread(&c, 1, 1, f->get()) < 1)
|
||||
throw fmt::system_error(errno, "fread failed");
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
||||
file read_end, write_end;
|
||||
file::pipe(read_end, write_end);
|
||||
scoped_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
||||
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
||||
int saved_fclose_count = 0;
|
||||
EXPECT_WRITE(stderr, {
|
||||
fclose_count = 1;
|
||||
f.reset(FMT_NULL);
|
||||
saved_fclose_count = fclose_count;
|
||||
fclose_count = 0;
|
||||
}, format_system_error(EINTR, "cannot close file") + "\n");
|
||||
EXPECT_WRITE(
|
||||
stderr,
|
||||
{
|
||||
fclose_count = 1;
|
||||
f.reset(nullptr);
|
||||
saved_fclose_count = fclose_count;
|
||||
fclose_count = 0;
|
||||
},
|
||||
format_system_error(EINTR, "cannot close file") + "\n");
|
||||
EXPECT_EQ(2, saved_fclose_count);
|
||||
}
|
||||
|
||||
|
@ -428,111 +435,123 @@ TEST(BufferedFileTest, FilenoNoRetry) {
|
|||
EXPECT_EQ(2, fileno_count);
|
||||
fileno_count = 0;
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
struct TestMock {
|
||||
static TestMock *instance;
|
||||
} *TestMock::instance;
|
||||
struct test_mock {
|
||||
static test_mock* instance;
|
||||
} * test_mock::instance;
|
||||
|
||||
TEST(ScopedMock, Scope) {
|
||||
{
|
||||
ScopedMock<TestMock> mock;
|
||||
EXPECT_EQ(&mock, TestMock::instance);
|
||||
TestMock © = mock;
|
||||
ScopedMock<test_mock> mock;
|
||||
EXPECT_EQ(&mock, test_mock::instance);
|
||||
test_mock& copy = mock;
|
||||
static_cast<void>(copy);
|
||||
}
|
||||
EXPECT_EQ(FMT_NULL, TestMock::instance);
|
||||
EXPECT_EQ(nullptr, test_mock::instance);
|
||||
}
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
|
||||
typedef fmt::Locale::Type LocaleType;
|
||||
typedef fmt::locale::type locale_type;
|
||||
|
||||
struct LocaleMock {
|
||||
static LocaleMock *instance;
|
||||
MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale,
|
||||
LocaleType base));
|
||||
MOCK_METHOD1(freelocale, void (LocaleType locale));
|
||||
struct locale_mock {
|
||||
static locale_mock* instance;
|
||||
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
|
||||
locale_type base));
|
||||
MOCK_METHOD1(freelocale, void(locale_type locale));
|
||||
|
||||
MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr,
|
||||
LocaleType locale));
|
||||
} *LocaleMock::instance;
|
||||
MOCK_METHOD3(strtod_l,
|
||||
double(const char* nptr, char** endptr, locale_type locale));
|
||||
} * locale_mock::instance;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4273)
|
||||
# ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4273)
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Winconsistent-dllimport"
|
||||
# endif
|
||||
|
||||
_locale_t _create_locale(int category, const char *locale) {
|
||||
return LocaleMock::instance->newlocale(category, locale, 0);
|
||||
_locale_t _create_locale(int category, const char* locale) {
|
||||
return locale_mock::instance->newlocale(category, locale, 0);
|
||||
}
|
||||
|
||||
void _free_locale(_locale_t locale) {
|
||||
LocaleMock::instance->freelocale(locale);
|
||||
locale_mock::instance->freelocale(locale);
|
||||
}
|
||||
|
||||
double _strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
|
||||
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
||||
return locale_mock::instance->strtod_l(nptr, endptr, locale);
|
||||
}
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# endif
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
|
||||
#define FMT_LOCALE_THROW __THROW
|
||||
#else
|
||||
#define FMT_LOCALE_THROW
|
||||
#endif
|
||||
# if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
|
||||
# define FMT_LOCALE_THROW __THROW
|
||||
# else
|
||||
# define FMT_LOCALE_THROW
|
||||
# endif
|
||||
|
||||
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) FMT_LOCALE_THROW {
|
||||
return LocaleMock::instance->newlocale(category_mask, locale, base);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
|
||||
# if defined(__APPLE__) || \
|
||||
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
|
||||
typedef int FreeLocaleResult;
|
||||
#else
|
||||
# else
|
||||
typedef void FreeLocaleResult;
|
||||
#endif
|
||||
# endif
|
||||
|
||||
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
|
||||
LocaleMock::instance->freelocale(locale);
|
||||
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
|
||||
locale_mock::instance->freelocale(locale);
|
||||
return FreeLocaleResult();
|
||||
}
|
||||
|
||||
double strtod_l(const char *nptr, char **endptr, LocaleType locale) FMT_LOCALE_THROW {
|
||||
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
|
||||
double strtod_l(const char* nptr, char** endptr,
|
||||
locale_type locale) FMT_LOCALE_THROW {
|
||||
return locale_mock::instance->strtod_l(nptr, endptr, locale);
|
||||
}
|
||||
|
||||
#undef FMT_LOCALE_THROW
|
||||
# undef FMT_LOCALE_THROW
|
||||
|
||||
# ifndef _WIN32
|
||||
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
|
||||
return locale_mock::instance->newlocale(category_mask, locale, base);
|
||||
}
|
||||
|
||||
TEST(LocaleTest, LocaleMock) {
|
||||
ScopedMock<LocaleMock> mock;
|
||||
LocaleType locale = reinterpret_cast<LocaleType>(11);
|
||||
ScopedMock<locale_mock> mock;
|
||||
locale_type locale = reinterpret_cast<locale_type>(11);
|
||||
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
|
||||
newlocale(222, "foo", locale);
|
||||
FMT_SYSTEM(newlocale(222, "foo", locale));
|
||||
}
|
||||
# endif
|
||||
|
||||
TEST(LocaleTest, Locale) {
|
||||
#ifndef LC_NUMERIC_MASK
|
||||
# ifndef LC_NUMERIC_MASK
|
||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||
#endif
|
||||
ScopedMock<LocaleMock> mock;
|
||||
LocaleType impl = reinterpret_cast<LocaleType>(42);
|
||||
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), FMT_NULL))
|
||||
# endif
|
||||
ScopedMock<locale_mock> mock;
|
||||
locale_type impl = reinterpret_cast<locale_type>(42);
|
||||
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
|
||||
.WillOnce(Return(impl));
|
||||
EXPECT_CALL(mock, freelocale(impl));
|
||||
fmt::Locale locale;
|
||||
EXPECT_EQ(impl, locale.get());
|
||||
fmt::locale loc;
|
||||
EXPECT_EQ(impl, loc.get());
|
||||
}
|
||||
|
||||
TEST(LocaleTest, Strtod) {
|
||||
ScopedMock<LocaleMock> mock;
|
||||
ScopedMock<locale_mock> mock;
|
||||
EXPECT_CALL(mock, newlocale(_, _, _))
|
||||
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
|
||||
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
|
||||
EXPECT_CALL(mock, freelocale(_));
|
||||
fmt::Locale locale;
|
||||
const char *str = "4.2";
|
||||
fmt::locale loc;
|
||||
const char* str = "4.2";
|
||||
char end = 'x';
|
||||
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
|
||||
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
|
||||
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
|
||||
EXPECT_EQ(777, locale.strtod(str));
|
||||
EXPECT_EQ(777, loc.strtod(str));
|
||||
EXPECT_EQ(&end, str);
|
||||
}
|
||||
|
||||
|
|
36
externals/fmt/test/posix-mock.h
vendored
36
externals/fmt/test/posix-mock.h
vendored
|
@ -9,13 +9,17 @@
|
|||
#define FMT_POSIX_TEST_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#ifdef __APPLE__
|
||||
# include <xlocale.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/param.h> // for FreeBSD version
|
||||
# include <sys/types.h> // for ssize_t
|
||||
# include <sys/param.h> // for FreeBSD version
|
||||
# include <sys/types.h> // for ssize_t
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
@ -28,13 +32,13 @@ namespace test {
|
|||
// Size type for read and write.
|
||||
typedef size_t size_t;
|
||||
typedef ssize_t ssize_t;
|
||||
int open(const char *path, int oflag, int mode);
|
||||
int fstat(int fd, struct stat *buf);
|
||||
int open(const char* path, int oflag, int mode);
|
||||
int fstat(int fd, struct stat* buf);
|
||||
#else
|
||||
typedef unsigned size_t;
|
||||
typedef int ssize_t;
|
||||
errno_t sopen_s(
|
||||
int* pfh, const char *filename, int oflag, int shflag, int pmode);
|
||||
errno_t sopen_s(int* pfh, const char* filename, int oflag, int shflag,
|
||||
int pmode);
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -48,20 +52,24 @@ int close(int fildes);
|
|||
int dup(int fildes);
|
||||
int dup2(int fildes, int fildes2);
|
||||
|
||||
FILE *fdopen(int fildes, const char *mode);
|
||||
FILE* fdopen(int fildes, const char* mode);
|
||||
|
||||
ssize_t read(int fildes, void *buf, size_t nbyte);
|
||||
ssize_t write(int fildes, const void *buf, size_t nbyte);
|
||||
ssize_t read(int fildes, void* buf, size_t nbyte);
|
||||
ssize_t write(int fildes, const void* buf, size_t nbyte);
|
||||
|
||||
#ifndef _WIN32
|
||||
int pipe(int fildes[2]);
|
||||
#else
|
||||
int pipe(int *pfds, unsigned psize, int textmode);
|
||||
int pipe(int* pfds, unsigned psize, int textmode);
|
||||
#endif
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int fclose(FILE *stream);
|
||||
int (fileno)(FILE *stream);
|
||||
FILE* fopen(const char* filename, const char* mode);
|
||||
int fclose(FILE* stream);
|
||||
int(fileno)(FILE* stream);
|
||||
|
||||
#if defined(FMT_LOCALE) && !defined(_WIN32)
|
||||
locale_t newlocale(int category_mask, const char* locale, locale_t base);
|
||||
#endif
|
||||
} // namespace test
|
||||
|
||||
#define FMT_SYSTEM(call) test::call
|
||||
|
|
296
externals/fmt/test/printf-test.cc
vendored
296
externals/fmt/test/printf-test.cc
vendored
|
@ -5,17 +5,19 @@
|
|||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/printf.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
|
||||
#include "fmt/core.h"
|
||||
#include "fmt/printf.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
using fmt::format;
|
||||
using fmt::format_error;
|
||||
using fmt::internal::max_value;
|
||||
|
||||
const unsigned BIG_NUM = INT_MAX + 1u;
|
||||
|
||||
|
@ -35,17 +37,17 @@ static std::wstring make_positional(fmt::wstring_view format) {
|
|||
// A wrapper around fmt::sprintf to workaround bogus warnings about invalid
|
||||
// format strings in MSVC.
|
||||
template <typename... Args>
|
||||
std::string test_sprintf(fmt::string_view format, const Args &... args) {
|
||||
std::string test_sprintf(fmt::string_view format, const Args&... args) {
|
||||
return fmt::sprintf(format, args...);
|
||||
}
|
||||
template <typename... Args>
|
||||
std::wstring test_sprintf(fmt::wstring_view format, const Args &... args) {
|
||||
std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) {
|
||||
return fmt::sprintf(format, args...);
|
||||
}
|
||||
|
||||
#define EXPECT_PRINTF(expected_output, format, arg) \
|
||||
#define EXPECT_PRINTF(expected_output, format, arg) \
|
||||
EXPECT_EQ(expected_output, test_sprintf(format, arg)) \
|
||||
<< "format: " << format; \
|
||||
<< "format: " << format; \
|
||||
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
|
||||
|
||||
TEST(PrintfTest, NoArgs) {
|
||||
|
@ -69,11 +71,10 @@ TEST(PrintfTest, Escape) {
|
|||
TEST(PrintfTest, PositionalArgs) {
|
||||
EXPECT_EQ("42", test_sprintf("%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));
|
||||
EXPECT_EQ("before 42 after", test_sprintf("before %1$d after", 42));
|
||||
EXPECT_EQ("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42));
|
||||
EXPECT_EQ("42 is the answer",
|
||||
test_sprintf("%2$d is the %1$s", "answer", 42));
|
||||
EXPECT_EQ("42 is the answer", test_sprintf("%2$d is the %1$s", "answer", 42));
|
||||
EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
|
||||
}
|
||||
|
||||
|
@ -82,46 +83,46 @@ TEST(PrintfTest, AutomaticArgIndexing) {
|
|||
}
|
||||
|
||||
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), format_error,
|
||||
"number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), format_error,
|
||||
"number is too big");
|
||||
}
|
||||
|
||||
TEST(PrintfTest, SwitchArgIndexing) {
|
||||
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2),
|
||||
format_error, "cannot switch from manual to automatic argument indexing");
|
||||
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2),
|
||||
format_error, "cannot switch from manual to automatic argument indexing");
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
|
||||
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2),
|
||||
format_error, "cannot switch from automatic to manual argument indexing");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2),
|
||||
format_error, "cannot switch from automatic to manual argument indexing");
|
||||
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), format_error,
|
||||
"number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
|
||||
// Indexing errors override width errors.
|
||||
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),
|
||||
format_error, "number is too big");
|
||||
format_error, "number is too big");
|
||||
}
|
||||
|
||||
TEST(PrintfTest, InvalidArgIndex) {
|
||||
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
|
||||
"argument index out of range");
|
||||
"argument index out of range");
|
||||
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
|
||||
"argument index out of range");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42),
|
||||
format_error, "argument index out of range");
|
||||
"argument index out of range");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
|
||||
"argument index out of range");
|
||||
|
||||
EXPECT_THROW_MSG(test_sprintf("%2$", 42),
|
||||
format_error, "argument index out of range");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
|
||||
"argument index out of range");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
|
||||
"number is too big");
|
||||
}
|
||||
|
||||
TEST(PrintfTest, DefaultAlignRight) {
|
||||
|
@ -175,8 +176,8 @@ TEST(PrintfTest, HashFlag) {
|
|||
|
||||
EXPECT_PRINTF("0x42", "%#x", 0x42);
|
||||
EXPECT_PRINTF("0X42", "%#X", 0x42);
|
||||
EXPECT_PRINTF(
|
||||
fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x", -0x42);
|
||||
EXPECT_PRINTF(fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x",
|
||||
-0x42);
|
||||
EXPECT_PRINTF("0", "%#x", 0);
|
||||
|
||||
EXPECT_PRINTF("0x0042", "%#06x", 0x42);
|
||||
|
@ -208,23 +209,23 @@ TEST(PrintfTest, Width) {
|
|||
|
||||
// Width cannot be specified twice.
|
||||
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, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), format_error,
|
||||
"number is too big");
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), format_error,
|
||||
"number is too big");
|
||||
}
|
||||
|
||||
TEST(PrintfTest, DynamicWidth) {
|
||||
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,
|
||||
"width is not integer");
|
||||
"width is not integer");
|
||||
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
|
||||
"argument index out of range");
|
||||
"argument index out of range");
|
||||
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
|
||||
"number is too big");
|
||||
"number is too big");
|
||||
}
|
||||
|
||||
TEST(PrintfTest, IntPrecision) {
|
||||
|
@ -253,8 +254,7 @@ TEST(PrintfTest, FloatPrecision) {
|
|||
safe_sprintf(buffer, "%.3e", 1234.5678);
|
||||
EXPECT_PRINTF(buffer, "%.3e", 1234.5678);
|
||||
EXPECT_PRINTF("1234.568", "%.3f", 1234.5678);
|
||||
safe_sprintf(buffer, "%.3g", 1234.5678);
|
||||
EXPECT_PRINTF(buffer, "%.3g", 1234.5678);
|
||||
EXPECT_PRINTF("1.23e+03", "%.3g", 1234.5678);
|
||||
safe_sprintf(buffer, "%.3a", 1234.5678);
|
||||
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
|
||||
}
|
||||
|
@ -267,24 +267,22 @@ TEST(PrintfTest, DynamicPrecision) {
|
|||
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
|
||||
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
|
||||
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 index out of range");
|
||||
"argument index out of range");
|
||||
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)) {
|
||||
long long prec = static_cast<long long>(INT_MIN) - 1;
|
||||
EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error,
|
||||
"number is too big");
|
||||
}
|
||||
"number is too big");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct make_signed { typedef T type; };
|
||||
template <typename T> struct make_signed { typedef T type; };
|
||||
|
||||
#define SPECIALIZE_MAKE_SIGNED(T, S) \
|
||||
template <> \
|
||||
struct make_signed<T> { typedef S type; }
|
||||
template <> struct make_signed<T> { typedef S type; }
|
||||
|
||||
SPECIALIZE_MAKE_SIGNED(char, signed char);
|
||||
SPECIALIZE_MAKE_SIGNED(unsigned char, signed char);
|
||||
|
@ -295,24 +293,23 @@ SPECIALIZE_MAKE_SIGNED(unsigned long long, long long);
|
|||
|
||||
// Test length format specifier ``length_spec``.
|
||||
template <typename T, typename U>
|
||||
void TestLength(const char *length_spec, U value) {
|
||||
void TestLength(const char* length_spec, U value) {
|
||||
long long signed_value = 0;
|
||||
unsigned long long unsigned_value = 0;
|
||||
// Apply integer promotion to the argument.
|
||||
using std::numeric_limits;
|
||||
unsigned long long max = numeric_limits<U>::max();
|
||||
unsigned long long max = max_value<U>();
|
||||
using fmt::internal::const_check;
|
||||
if (const_check(max <= static_cast<unsigned>(numeric_limits<int>::max()))) {
|
||||
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
|
||||
signed_value = static_cast<int>(value);
|
||||
unsigned_value = static_cast<unsigned>(value);
|
||||
} else if (const_check(max <= numeric_limits<unsigned>::max())) {
|
||||
unsigned_value = static_cast<unsigned long long>(value);
|
||||
} else if (const_check(max <= max_value<unsigned>())) {
|
||||
signed_value = static_cast<unsigned>(value);
|
||||
unsigned_value = static_cast<unsigned>(value);
|
||||
unsigned_value = static_cast<unsigned long long>(value);
|
||||
}
|
||||
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
|
||||
signed_value = static_cast<long long>(value);
|
||||
unsigned_value =
|
||||
static_cast<typename std::make_unsigned<unsigned>::type>(value);
|
||||
unsigned_value = static_cast<unsigned long long>(
|
||||
static_cast<typename std::make_unsigned<unsigned>::type>(value));
|
||||
} else {
|
||||
signed_value = static_cast<typename make_signed<T>::type>(value);
|
||||
unsigned_value = static_cast<typename std::make_unsigned<T>::type>(value);
|
||||
|
@ -335,27 +332,28 @@ void TestLength(const char *length_spec, U value) {
|
|||
EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TestLength(const char *length_spec) {
|
||||
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
|
||||
template <typename T> void TestLength(const char* length_spec) {
|
||||
T min = std::numeric_limits<T>::min(), max = max_value<T>();
|
||||
TestLength<T>(length_spec, 42);
|
||||
TestLength<T>(length_spec, -42);
|
||||
TestLength<T>(length_spec, min);
|
||||
TestLength<T>(length_spec, max);
|
||||
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
|
||||
unsigned long long long_long_max = std::numeric_limits<long long>::max();
|
||||
long long long_long_min = std::numeric_limits<long long>::min();
|
||||
if (static_cast<long long>(min) > long_long_min)
|
||||
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
|
||||
unsigned long long long_long_max = max_value<long long>();
|
||||
if (static_cast<unsigned long long>(max) < long_long_max)
|
||||
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
|
||||
TestLength<T>(length_spec, std::numeric_limits<short>::min());
|
||||
TestLength<T>(length_spec, std::numeric_limits<unsigned short>::max());
|
||||
TestLength<T>(length_spec, max_value<unsigned short>());
|
||||
TestLength<T>(length_spec, std::numeric_limits<int>::min());
|
||||
TestLength<T>(length_spec, std::numeric_limits<int>::max());
|
||||
TestLength<T>(length_spec, max_value<int>());
|
||||
TestLength<T>(length_spec, std::numeric_limits<unsigned>::min());
|
||||
TestLength<T>(length_spec, std::numeric_limits<unsigned>::max());
|
||||
TestLength<T>(length_spec, max_value<unsigned>());
|
||||
TestLength<T>(length_spec, std::numeric_limits<long long>::min());
|
||||
TestLength<T>(length_spec, std::numeric_limits<long long>::max());
|
||||
TestLength<T>(length_spec, max_value<long long>());
|
||||
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::min());
|
||||
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::max());
|
||||
TestLength<T>(length_spec, max_value<unsigned long long>());
|
||||
}
|
||||
|
||||
TEST(PrintfTest, Length) {
|
||||
|
@ -371,9 +369,9 @@ TEST(PrintfTest, Length) {
|
|||
TestLength<intmax_t>("j");
|
||||
TestLength<std::size_t>("z");
|
||||
TestLength<std::ptrdiff_t>("t");
|
||||
long double max = std::numeric_limits<long double>::max();
|
||||
EXPECT_PRINTF(fmt::format("{}", max), "%g", max);
|
||||
EXPECT_PRINTF(fmt::format("{}", max), "%Lg", max);
|
||||
long double max = max_value<long double>();
|
||||
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
|
||||
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
|
||||
}
|
||||
|
||||
TEST(PrintfTest, Bool) {
|
||||
|
@ -394,7 +392,7 @@ TEST(PrintfTest, Int) {
|
|||
TEST(PrintfTest, long_long) {
|
||||
// fmt::printf allows passing long long arguments to %d without length
|
||||
// specifiers.
|
||||
long long max = std::numeric_limits<long long>::max();
|
||||
long long max = max_value<long long>();
|
||||
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
||||
}
|
||||
|
||||
|
@ -411,6 +409,9 @@ TEST(PrintfTest, Float) {
|
|||
EXPECT_PRINTF(buffer, "%E", 392.65);
|
||||
EXPECT_PRINTF("392.65", "%g", 392.65);
|
||||
EXPECT_PRINTF("392.65", "%G", 392.65);
|
||||
EXPECT_PRINTF("392", "%g", 392.0);
|
||||
EXPECT_PRINTF("392", "%G", 392.0);
|
||||
EXPECT_PRINTF("4.56e-07", "%g", 0.000000456);
|
||||
safe_sprintf(buffer, "%a", -392.65);
|
||||
EXPECT_EQ(buffer, format("{:a}", -392.65));
|
||||
safe_sprintf(buffer, "%A", -392.65);
|
||||
|
@ -428,44 +429,44 @@ TEST(PrintfTest, Inf) {
|
|||
|
||||
TEST(PrintfTest, Char) {
|
||||
EXPECT_PRINTF("x", "%c", 'x');
|
||||
int max = std::numeric_limits<int>::max();
|
||||
int max = max_value<int>();
|
||||
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
||||
//EXPECT_PRINTF("x", "%lc", L'x');
|
||||
// EXPECT_PRINTF("x", "%lc", L'x');
|
||||
EXPECT_PRINTF(L"x", L"%c", L'x');
|
||||
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
|
||||
}
|
||||
|
||||
TEST(PrintfTest, String) {
|
||||
EXPECT_PRINTF("abc", "%s", "abc");
|
||||
const char *null_str = FMT_NULL;
|
||||
const char* null_str = nullptr;
|
||||
EXPECT_PRINTF("(null)", "%s", null_str);
|
||||
EXPECT_PRINTF(" (null)", "%10s", null_str);
|
||||
EXPECT_PRINTF(L"abc", L"%s", L"abc");
|
||||
const wchar_t *null_wstr = FMT_NULL;
|
||||
const wchar_t* null_wstr = nullptr;
|
||||
EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
|
||||
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
||||
}
|
||||
|
||||
TEST(PrintfTest, Pointer) {
|
||||
int n;
|
||||
void *p = &n;
|
||||
void* p = &n;
|
||||
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
|
||||
p = FMT_NULL;
|
||||
p = nullptr;
|
||||
EXPECT_PRINTF("(nil)", "%p", p);
|
||||
EXPECT_PRINTF(" (nil)", "%10p", p);
|
||||
const char *s = "test";
|
||||
const char* s = "test";
|
||||
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
|
||||
const char *null_str = FMT_NULL;
|
||||
const char* null_str = nullptr;
|
||||
EXPECT_PRINTF("(nil)", "%p", null_str);
|
||||
|
||||
p = &n;
|
||||
EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p);
|
||||
p = FMT_NULL;
|
||||
p = nullptr;
|
||||
EXPECT_PRINTF(L"(nil)", L"%p", p);
|
||||
EXPECT_PRINTF(L" (nil)", L"%10p", p);
|
||||
const wchar_t *w = L"test";
|
||||
const wchar_t* w = L"test";
|
||||
EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w);
|
||||
const wchar_t *null_wstr = FMT_NULL;
|
||||
const wchar_t* null_wstr = nullptr;
|
||||
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
|
||||
}
|
||||
|
||||
|
@ -473,16 +474,18 @@ TEST(PrintfTest, Location) {
|
|||
// TODO: test %n
|
||||
}
|
||||
|
||||
enum E { A = 42 };
|
||||
enum test_enum { answer = 42 };
|
||||
|
||||
TEST(PrintfTest, Enum) {
|
||||
EXPECT_PRINTF("42", "%d", A);
|
||||
EXPECT_PRINTF("42", "%d", answer);
|
||||
volatile test_enum volatile_enum = answer;
|
||||
EXPECT_PRINTF("42", "%d", volatile_enum);
|
||||
}
|
||||
|
||||
#if FMT_USE_FILE_DESCRIPTORS
|
||||
#if FMT_USE_FCNTL
|
||||
TEST(PrintfTest, Examples) {
|
||||
const char *weekday = "Thursday";
|
||||
const char *month = "August";
|
||||
const char* weekday = "Thursday";
|
||||
const char* month = "August";
|
||||
int day = 21;
|
||||
EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day),
|
||||
"Thursday, 21 August");
|
||||
|
@ -496,12 +499,12 @@ TEST(PrintfTest, PrintfError) {
|
|||
}
|
||||
#endif
|
||||
|
||||
TEST(PrintfTest, WideString) {
|
||||
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
|
||||
}
|
||||
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
|
||||
|
||||
TEST(PrintfTest, PrintfCustom) {
|
||||
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
|
||||
// The test is disabled for now because it requires decoupling
|
||||
// fallback_formatter::format from format_context.
|
||||
//EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
|
||||
}
|
||||
|
||||
TEST(PrintfTest, OStream) {
|
||||
|
@ -520,7 +523,7 @@ TEST(PrintfTest, VPrintf) {
|
|||
EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42");
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
template <typename... Args>
|
||||
void check_format_string_regression(fmt::string_view s, const Args&... args) {
|
||||
fmt::sprintf(s, args...);
|
||||
}
|
||||
|
@ -529,41 +532,90 @@ TEST(PrintfTest, CheckFormatStringRegression) {
|
|||
check_format_string_regression("%c%s", 'x', "");
|
||||
}
|
||||
|
||||
TEST(PrintfTest, FixedLargeExponent) {
|
||||
EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21));
|
||||
}
|
||||
|
||||
TEST(PrintfTest, VSPrintfMakeArgsExample) {
|
||||
fmt::format_arg_store<fmt::printf_context, int, const char *> as{
|
||||
42, "something"};
|
||||
fmt::format_arg_store<fmt::printf_context, int, const char*> as{42,
|
||||
"something"};
|
||||
fmt::basic_format_args<fmt::printf_context> args(as);
|
||||
EXPECT_EQ(
|
||||
"[42] something happened", fmt::vsprintf("[%d] %s happened", args));
|
||||
EXPECT_EQ("[42] something happened", fmt::vsprintf("[%d] %s happened", args));
|
||||
auto as2 = fmt::make_printf_args(42, "something");
|
||||
fmt::basic_format_args<fmt::printf_context> args2(as2);
|
||||
EXPECT_EQ(
|
||||
"[42] something happened", 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",
|
||||
fmt::vsprintf(
|
||||
"[%d] %s happened", fmt::make_printf_args(42, "something")));
|
||||
EXPECT_EQ("[42] something happened",
|
||||
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",
|
||||
fmt::vsprintf("[%d] %s happened",
|
||||
{fmt::make_printf_args(42, "something")}));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PrintfTest, VSPrintfMakeWArgsExample) {
|
||||
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t *> as{
|
||||
42, L"something"};
|
||||
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{
|
||||
42, L"something"};
|
||||
fmt::basic_format_args<fmt::wprintf_context> args(as);
|
||||
EXPECT_EQ(
|
||||
L"[42] something happened",
|
||||
fmt::vsprintf(L"[%d] %s happened", args));
|
||||
auto as2 = fmt::make_wprintf_args(42, L"something");
|
||||
EXPECT_EQ(L"[42] something happened",
|
||||
fmt::vsprintf(L"[%d] %s happened", args));
|
||||
auto as2 = fmt::make_wprintf_args(42, L"something");
|
||||
fmt::basic_format_args<fmt::wprintf_context> args2(as2);
|
||||
EXPECT_EQ(
|
||||
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened", args2));
|
||||
EXPECT_EQ(L"[42] something happened",
|
||||
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",
|
||||
fmt::vsprintf(
|
||||
L"[%d] %s happened", fmt::make_wprintf_args(42, L"something")));
|
||||
EXPECT_EQ(L"[42] something happened",
|
||||
fmt::vsprintf(L"[%d] %s happened",
|
||||
{fmt::make_wprintf_args(42, L"something")}));
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t;
|
||||
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
|
||||
|
||||
// A custom printf argument formatter that doesn't print `-` for floating-point
|
||||
// values rounded to 0.
|
||||
class custom_printf_arg_formatter : public formatter_t {
|
||||
public:
|
||||
using formatter_t::iterator;
|
||||
|
||||
custom_printf_arg_formatter(formatter_t::iterator iter,
|
||||
formatter_t::format_specs& specs, context_t& ctx)
|
||||
: formatter_t(iter, specs, ctx) {}
|
||||
|
||||
using formatter_t::operator();
|
||||
|
||||
#if FMT_MSC_VER > 0 && FMT_MSC_VER <= 1804
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
iterator operator()(T value){
|
||||
#else
|
||||
iterator operator()(double value) {
|
||||
#endif
|
||||
// Comparing a float to 0.0 is safe.
|
||||
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
|
||||
return formatter_t::operator()(value);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
typedef fmt::basic_format_args<context_t> format_args_t;
|
||||
|
||||
std::string custom_vformat(fmt::string_view format_str, format_args_t args) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::vprintf<custom_printf_arg_formatter>(buffer, 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_printf_args(args...);
|
||||
return custom_vformat(format_str, {va});
|
||||
}
|
||||
|
||||
TEST(PrintfTest, CustomFormat) {
|
||||
EXPECT_EQ("0.00", custom_format("%.2f", -.00001));
|
||||
EXPECT_EQ("0.00", custom_format("%.2f", .00001));
|
||||
EXPECT_EQ("1.00", custom_format("%.2f", 1.00001));
|
||||
EXPECT_EQ("-1.00", custom_format("%.2f", -1.00001));
|
||||
}
|
||||
|
|
93
externals/fmt/test/ranges-test.cc
vendored
93
externals/fmt/test/ranges-test.cc
vendored
|
@ -9,17 +9,18 @@
|
|||
// All Rights Reserved
|
||||
// {fmt} support for ranges, containers and types tuple interface.
|
||||
|
||||
/// Check if 'if constexpr' is supported.
|
||||
#include "fmt/ranges.h"
|
||||
|
||||
#include "gtest.h"
|
||||
|
||||
// Check if 'if constexpr' is supported.
|
||||
#if (__cplusplus > 201402L) || \
|
||||
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
|
||||
|
||||
#include "fmt/ranges.h"
|
||||
#include "gtest.h"
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string>
|
||||
# include <array>
|
||||
# include <map>
|
||||
# include <string>
|
||||
# include <vector>
|
||||
|
||||
TEST(RangesTest, FormatVector) {
|
||||
std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
|
||||
|
@ -39,21 +40,46 @@ TEST(RangesTest, FormatMap) {
|
|||
}
|
||||
|
||||
TEST(RangesTest, FormatPair) {
|
||||
std::pair<int64_t, float> pa1{42, 3.14159265358979f};
|
||||
EXPECT_EQ("(42, 3.14159)", fmt::format("{}", pa1));
|
||||
std::pair<int64_t, float> pa1{42, 1.5f};
|
||||
EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
|
||||
}
|
||||
|
||||
TEST(RangesTest, FormatTuple) {
|
||||
std::tuple<int64_t, float, std::string, char> tu1{42, 3.14159265358979f,
|
||||
"this is tuple", 'i'};
|
||||
EXPECT_EQ("(42, 3.14159, \"this is tuple\", 'i')", fmt::format("{}", tu1));
|
||||
std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
|
||||
'i'};
|
||||
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", t));
|
||||
EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
|
||||
}
|
||||
|
||||
TEST(RangesTest, JoinTuple) {
|
||||
// Value tuple args
|
||||
std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
|
||||
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) {
|
||||
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
|
||||
EXPECT_EQ("fmt rocks !",
|
||||
fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
|
||||
}
|
||||
|
||||
struct my_struct {
|
||||
int32_t i;
|
||||
std::string str; // can throw
|
||||
template <std::size_t N>
|
||||
decltype(auto) get() const noexcept {
|
||||
template <std::size_t N> decltype(auto) get() const noexcept {
|
||||
if constexpr (N == 0)
|
||||
return i;
|
||||
else if constexpr (N == 1)
|
||||
|
@ -61,8 +87,7 @@ struct my_struct {
|
|||
}
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
decltype(auto) get(const my_struct& s) noexcept {
|
||||
template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept {
|
||||
return s.get<N>();
|
||||
}
|
||||
|
||||
|
@ -71,8 +96,7 @@ namespace std {
|
|||
template <>
|
||||
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
|
||||
|
||||
template <std::size_t N>
|
||||
struct tuple_element<N, my_struct> {
|
||||
template <std::size_t N> struct tuple_element<N, my_struct> {
|
||||
using type = decltype(std::declval<my_struct>().get<N>());
|
||||
};
|
||||
|
||||
|
@ -83,5 +107,36 @@ TEST(RangesTest, FormatStruct) {
|
|||
EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
|
||||
}
|
||||
|
||||
TEST(RangesTest, FormatTo) {
|
||||
char buf[10];
|
||||
auto end = fmt::format_to(buf, "{}", std::vector{1, 2, 3});
|
||||
*end = '\0';
|
||||
EXPECT_STREQ(buf, "{1, 2, 3}");
|
||||
}
|
||||
|
||||
struct path_like {
|
||||
const path_like* begin() const;
|
||||
const path_like* end() const;
|
||||
|
||||
operator std::string() const;
|
||||
};
|
||||
|
||||
TEST(RangesTest, PathLike) {
|
||||
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
|
||||
struct string_like {
|
||||
const char* begin();
|
||||
const char* end();
|
||||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
explicit operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(RangesTest, FormatStringLike) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", string_like()));
|
||||
}
|
||||
#endif // FMT_USE_STRING_VIEW
|
||||
|
|
114
externals/fmt/test/scan-test.cc
vendored
Normal file
114
externals/fmt/test/scan-test.cc
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
// Formatting library for C++ - scanning API test
|
||||
//
|
||||
// Copyright (c) 2019 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <time.h>
|
||||
#include <climits>
|
||||
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "scan.h"
|
||||
|
||||
TEST(ScanTest, ReadText) {
|
||||
fmt::string_view s = "foo";
|
||||
auto end = fmt::scan(s, "foo");
|
||||
EXPECT_EQ(end, s.end());
|
||||
EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input");
|
||||
}
|
||||
|
||||
TEST(ScanTest, ReadInt) {
|
||||
int n = 0;
|
||||
fmt::scan("42", "{}", n);
|
||||
EXPECT_EQ(n, 42);
|
||||
fmt::scan("-42", "{}", n);
|
||||
EXPECT_EQ(n, -42);
|
||||
}
|
||||
|
||||
TEST(ScanTest, ReadLongLong) {
|
||||
long long n = 0;
|
||||
fmt::scan("42", "{}", n);
|
||||
EXPECT_EQ(n, 42);
|
||||
fmt::scan("-42", "{}", n);
|
||||
EXPECT_EQ(n, -42);
|
||||
}
|
||||
|
||||
TEST(ScanTest, ReadUInt) {
|
||||
unsigned n = 0;
|
||||
fmt::scan("42", "{}", n);
|
||||
EXPECT_EQ(n, 42);
|
||||
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
|
||||
"invalid input");
|
||||
}
|
||||
|
||||
TEST(ScanTest, ReadULongLong) {
|
||||
unsigned long long n = 0;
|
||||
fmt::scan("42", "{}", n);
|
||||
EXPECT_EQ(n, 42);
|
||||
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
|
||||
"invalid input");
|
||||
}
|
||||
|
||||
TEST(ScanTest, ReadString) {
|
||||
std::string s;
|
||||
fmt::scan("foo", "{}", s);
|
||||
EXPECT_EQ(s, "foo");
|
||||
}
|
||||
|
||||
TEST(ScanTest, ReadStringView) {
|
||||
fmt::string_view s;
|
||||
fmt::scan("foo", "{}", s);
|
||||
EXPECT_EQ(s, "foo");
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
namespace fmt {
|
||||
template <> struct scanner<tm> {
|
||||
std::string format;
|
||||
|
||||
scan_parse_context::iterator parse(scan_parse_context& ctx) {
|
||||
auto it = ctx.begin();
|
||||
if (it != ctx.end() && *it == ':') ++it;
|
||||
auto end = it;
|
||||
while (end != ctx.end() && *end != '}') ++end;
|
||||
format.reserve(internal::to_unsigned(end - it + 1));
|
||||
format.append(it, end);
|
||||
format.push_back('\0');
|
||||
return end;
|
||||
}
|
||||
|
||||
template <class ScanContext>
|
||||
typename ScanContext::iterator scan(tm& t, ScanContext& ctx) {
|
||||
auto result = strptime(ctx.begin(), format.c_str(), &t);
|
||||
if (!result) throw format_error("failed to parse time");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
||||
TEST(ScanTest, ReadCustom) {
|
||||
const char* input = "Date: 1985-10-25";
|
||||
auto t = tm();
|
||||
fmt::scan(input, "Date: {0:%Y-%m-%d}", t);
|
||||
EXPECT_EQ(t.tm_year, 85);
|
||||
EXPECT_EQ(t.tm_mon, 9);
|
||||
EXPECT_EQ(t.tm_mday, 25);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(ScanTest, InvalidFormat) {
|
||||
EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error,
|
||||
"argument index out of range");
|
||||
EXPECT_THROW_MSG(fmt::scan("", "{"), fmt::format_error,
|
||||
"invalid format string");
|
||||
}
|
||||
|
||||
TEST(ScanTest, Example) {
|
||||
std::string key;
|
||||
int value;
|
||||
fmt::scan("answer = 42", "{} = {}", key, value);
|
||||
EXPECT_EQ(key, "answer");
|
||||
EXPECT_EQ(value, 42);
|
||||
}
|
237
externals/fmt/test/scan.h
vendored
Normal file
237
externals/fmt/test/scan.h
vendored
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Formatting library for C++ - scanning API proof of concept
|
||||
//
|
||||
// Copyright (c) 2019 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename T, typename Char = char> struct scanner {
|
||||
// A deleted default constructor indicates a disabled scanner.
|
||||
scanner() = delete;
|
||||
};
|
||||
|
||||
class scan_parse_context {
|
||||
private:
|
||||
string_view format_;
|
||||
|
||||
public:
|
||||
using iterator = string_view::iterator;
|
||||
|
||||
explicit FMT_CONSTEXPR scan_parse_context(string_view format)
|
||||
: format_(format) {}
|
||||
|
||||
FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
|
||||
FMT_CONSTEXPR iterator end() const { return format_.end(); }
|
||||
|
||||
void advance_to(iterator it) {
|
||||
format_.remove_prefix(internal::to_unsigned(it - begin()));
|
||||
}
|
||||
};
|
||||
|
||||
struct scan_context {
|
||||
private:
|
||||
string_view input_;
|
||||
|
||||
public:
|
||||
using iterator = const char*;
|
||||
|
||||
explicit scan_context(string_view input) : input_(input) {}
|
||||
|
||||
iterator begin() const { return input_.data(); }
|
||||
iterator end() const { return begin() + input_.size(); }
|
||||
|
||||
void advance_to(iterator it) {
|
||||
input_.remove_prefix(internal::to_unsigned(it - begin()));
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
enum class scan_type {
|
||||
none_type,
|
||||
int_type,
|
||||
uint_type,
|
||||
long_long_type,
|
||||
ulong_long_type,
|
||||
string_type,
|
||||
string_view_type,
|
||||
custom_type
|
||||
};
|
||||
|
||||
struct custom_scan_arg {
|
||||
void* value;
|
||||
void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
|
||||
};
|
||||
|
||||
class scan_arg {
|
||||
public:
|
||||
scan_type type;
|
||||
union {
|
||||
int* int_value;
|
||||
unsigned* uint_value;
|
||||
long long* long_long_value;
|
||||
unsigned long long* ulong_long_value;
|
||||
std::string* string;
|
||||
fmt::string_view* string_view;
|
||||
custom_scan_arg custom;
|
||||
// TODO: more types
|
||||
};
|
||||
|
||||
scan_arg() : type(scan_type::none_type) {}
|
||||
scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {}
|
||||
scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {}
|
||||
scan_arg(long long& value)
|
||||
: type(scan_type::long_long_type), long_long_value(&value) {}
|
||||
scan_arg(unsigned long long& value)
|
||||
: type(scan_type::ulong_long_type), ulong_long_value(&value) {}
|
||||
scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {}
|
||||
scan_arg(fmt::string_view& value)
|
||||
: type(scan_type::string_view_type), string_view(&value) {}
|
||||
template <typename T> scan_arg(T& value) : type(scan_type::custom_type) {
|
||||
custom.value = &value;
|
||||
custom.scan = scan_custom_arg<T>;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
|
||||
scan_context& ctx) {
|
||||
scanner<T> s;
|
||||
parse_ctx.advance_to(s.parse(parse_ctx));
|
||||
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
struct scan_args {
|
||||
int size;
|
||||
const internal::scan_arg* data;
|
||||
|
||||
template <size_t N>
|
||||
scan_args(const std::array<internal::scan_arg, N>& store)
|
||||
: size(N), data(store.data()) {
|
||||
static_assert(N < INT_MAX, "too many arguments");
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct scan_handler : error_handler {
|
||||
private:
|
||||
scan_parse_context parse_ctx_;
|
||||
scan_context scan_ctx_;
|
||||
scan_args args_;
|
||||
int next_arg_id_;
|
||||
scan_arg arg_;
|
||||
|
||||
template <typename T = unsigned> T read_uint() {
|
||||
T value = 0;
|
||||
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
||||
while (it != end) {
|
||||
char c = *it++;
|
||||
if (c < '0' || c > '9') on_error("invalid input");
|
||||
// TODO: check overflow
|
||||
value = value * 10 + static_cast<unsigned>(c - '0');
|
||||
}
|
||||
scan_ctx_.advance_to(it);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T = int> T read_int() {
|
||||
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
||||
bool negative = it != end && *it == '-';
|
||||
if (negative) ++it;
|
||||
scan_ctx_.advance_to(it);
|
||||
const auto value = read_uint<typename std::make_unsigned<T>::type>();
|
||||
if (negative) return -static_cast<T>(value);
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
public:
|
||||
scan_handler(string_view format, string_view input, scan_args args)
|
||||
: parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
|
||||
|
||||
const char* pos() const { return scan_ctx_.begin(); }
|
||||
|
||||
void on_text(const char* begin, const char* end) {
|
||||
auto size = to_unsigned(end - begin);
|
||||
auto it = scan_ctx_.begin();
|
||||
if (it + size > scan_ctx_.end() ||
|
||||
!std::equal(begin, end, make_checked(it, size))) {
|
||||
on_error("invalid input");
|
||||
}
|
||||
scan_ctx_.advance_to(it + size);
|
||||
}
|
||||
|
||||
void on_arg_id() { on_arg_id(next_arg_id_++); }
|
||||
void on_arg_id(int id) {
|
||||
if (id >= args_.size) on_error("argument index out of range");
|
||||
arg_ = args_.data[id];
|
||||
}
|
||||
void on_arg_id(string_view) { on_error("invalid format"); }
|
||||
|
||||
void on_replacement_field(const char*) {
|
||||
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
||||
switch (arg_.type) {
|
||||
case scan_type::int_type:
|
||||
*arg_.int_value = read_int();
|
||||
break;
|
||||
case scan_type::uint_type:
|
||||
*arg_.uint_value = read_uint();
|
||||
break;
|
||||
case scan_type::long_long_type:
|
||||
*arg_.long_long_value = read_int<long long>();
|
||||
break;
|
||||
case scan_type::ulong_long_type:
|
||||
*arg_.ulong_long_value = read_uint<unsigned long long>();
|
||||
break;
|
||||
case scan_type::string_type:
|
||||
while (it != end && *it != ' ') arg_.string->push_back(*it++);
|
||||
scan_ctx_.advance_to(it);
|
||||
break;
|
||||
case scan_type::string_view_type: {
|
||||
auto s = it;
|
||||
while (it != end && *it != ' ') ++it;
|
||||
*arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
|
||||
scan_ctx_.advance_to(it);
|
||||
break;
|
||||
}
|
||||
case scan_type::none_type:
|
||||
case scan_type::custom_type:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
const char* on_format_specs(const char* begin, const char*) {
|
||||
if (arg_.type != scan_type::custom_type) return begin;
|
||||
parse_ctx_.advance_to(begin);
|
||||
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
|
||||
return parse_ctx_.begin();
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename... Args>
|
||||
std::array<internal::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
|
||||
return {{args...}};
|
||||
}
|
||||
|
||||
string_view::iterator vscan(string_view input, string_view format_str,
|
||||
scan_args args) {
|
||||
internal::scan_handler h(format_str, input, args);
|
||||
internal::parse_format_string<false>(format_str, h);
|
||||
return input.begin() + (h.pos() - &*input.begin());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
string_view::iterator scan(string_view input, string_view format_str,
|
||||
Args&... args) {
|
||||
return vscan(input, format_str, make_scan_args(args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
156
externals/fmt/test/std-format-test.cc
vendored
Normal file
156
externals/fmt/test/std-format-test.cc
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
#include <format>
|
||||
#include "gtest.h"
|
||||
|
||||
TEST(StdFormatTest, Escaping) {
|
||||
using namespace std;
|
||||
string s = format("{0}-{{", 8); // s == "8-{"
|
||||
EXPECT_EQ(s, "8-{");
|
||||
}
|
||||
|
||||
TEST(StdFormatTest, Indexing) {
|
||||
using namespace std;
|
||||
string s0 = format("{} to {}", "a", "b"); // OK: automatic indexing
|
||||
string s1 = format("{1} to {0}", "a", "b"); // OK: manual indexing
|
||||
EXPECT_EQ(s0, "a to b");
|
||||
EXPECT_EQ(s1, "b to a");
|
||||
// Error: mixing automatic and manual indexing
|
||||
EXPECT_THROW(string s2 = format("{0} to {}", "a", "b"), std::format_error);
|
||||
// Error: mixing automatic and manual indexing
|
||||
EXPECT_THROW(string s3 = format("{} to {1}", "a", "b"), std::format_error);
|
||||
}
|
||||
|
||||
TEST(StdFormatTest, Alignment) {
|
||||
using namespace std;
|
||||
char c = 120;
|
||||
string s0 = format("{:6}", 42); // s0 == " 42"
|
||||
string s1 = format("{:6}", 'x'); // s1 == "x "
|
||||
string s2 = format("{:*<6}", 'x'); // s2 == "x*****"
|
||||
string s3 = format("{:*>6}", 'x'); // s3 == "*****x"
|
||||
string s4 = format("{:*^6}", 'x'); // s4 == "**x***"
|
||||
// Error: '=' with charT and no integer presentation type
|
||||
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
|
||||
string s6 = format("{:6d}", c); // s6 == " 120"
|
||||
string s7 = format("{:6}", true); // s9 == "true "
|
||||
EXPECT_EQ(s0, " 42");
|
||||
EXPECT_EQ(s1, "x ");
|
||||
EXPECT_EQ(s2, "x*****");
|
||||
EXPECT_EQ(s3, "*****x");
|
||||
EXPECT_EQ(s4, "**x***");
|
||||
EXPECT_EQ(s6, " 120");
|
||||
EXPECT_EQ(s7, "true ");
|
||||
}
|
||||
|
||||
TEST(StdFormatTest, Float) {
|
||||
using namespace std;
|
||||
double inf = numeric_limits<double>::infinity();
|
||||
double nan = numeric_limits<double>::quiet_NaN();
|
||||
string s0 = format("{0:} {0:+} {0:-} {0: }", 1); // s0 == "1 +1 1 1"
|
||||
string s1 = format("{0:} {0:+} {0:-} {0: }", -1); // s1 == "-1 -1 -1 -1"
|
||||
string s2 =
|
||||
format("{0:} {0:+} {0:-} {0: }", inf); // s2 == "inf +inf inf inf"
|
||||
string s3 =
|
||||
format("{0:} {0:+} {0:-} {0: }", nan); // s3 == "nan +nan nan nan"
|
||||
EXPECT_EQ(s0, "1 +1 1 1");
|
||||
EXPECT_EQ(s1, "-1 -1 -1 -1");
|
||||
EXPECT_EQ(s2, "inf +inf inf inf");
|
||||
EXPECT_EQ(s3, "nan +nan nan nan");
|
||||
}
|
||||
|
||||
TEST(StdFormatTest, Int) {
|
||||
using namespace std;
|
||||
string s0 = format("{}", 42); // s0 == "42"
|
||||
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
|
||||
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
|
||||
string s3 = format("{:n}", 1234); // s3 == "1234" (depends on the locale)
|
||||
EXPECT_EQ(s0, "42");
|
||||
EXPECT_EQ(s1, "101010 42 52 2a");
|
||||
EXPECT_EQ(s2, "0x2a 0X2A");
|
||||
EXPECT_EQ(s3, "1234");
|
||||
}
|
||||
|
||||
#include <format>
|
||||
|
||||
enum color { red, green, blue };
|
||||
|
||||
const char* color_names[] = {"red", "green", "blue"};
|
||||
|
||||
template <> struct std::formatter<color> : std::formatter<const char*> {
|
||||
auto format(color c, format_context& ctx) {
|
||||
return formatter<const char*>::format(color_names[c], ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct err {};
|
||||
|
||||
TEST(StdFormatTest, Formatter) {
|
||||
std::string s0 = std::format("{}", 42); // OK: library-provided formatter
|
||||
// std::string s1 = std::format("{}", L"foo"); // Ill-formed: disabled
|
||||
// formatter
|
||||
std::string s2 = std::format("{}", red); // OK: user-provided formatter
|
||||
// std::string s3 = std::format("{}", err{}); // Ill-formed: disabled
|
||||
// formatter
|
||||
EXPECT_EQ(s0, "42");
|
||||
EXPECT_EQ(s2, "red");
|
||||
}
|
||||
|
||||
struct S {
|
||||
int value;
|
||||
};
|
||||
|
||||
template <> struct std::formatter<S> {
|
||||
size_t width_arg_id = 0;
|
||||
|
||||
// Parses a width argument id in the format { <digit> }.
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
auto iter = ctx.begin();
|
||||
// auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; };
|
||||
auto get_char = [&]() { return iter != ctx.end() ? *iter : '\0'; };
|
||||
if (get_char() != '{') return iter;
|
||||
++iter;
|
||||
char c = get_char();
|
||||
if (!isdigit(c) || (++iter, get_char()) != '}')
|
||||
throw format_error("invalid format");
|
||||
width_arg_id = c - '0';
|
||||
ctx.check_arg_id(width_arg_id);
|
||||
return ++iter;
|
||||
}
|
||||
|
||||
// Formats S with width given by the argument width_arg_id.
|
||||
auto format(S s, format_context& ctx) {
|
||||
int width = visit_format_arg(
|
||||
[](auto value) -> int {
|
||||
using type = decltype(value);
|
||||
if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
|
||||
throw format_error("width is not integral");
|
||||
// else if (value < 0 || value > numeric_limits<int>::max())
|
||||
else if (fmt::internal::is_negative(value) ||
|
||||
value > numeric_limits<int>::max())
|
||||
throw format_error("invalid width");
|
||||
else
|
||||
return static_cast<int>(value);
|
||||
},
|
||||
ctx.arg(width_arg_id));
|
||||
return format_to(ctx.out(), "{0:{1}}", s.value, width);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(StdFormatTest, Parsing) {
|
||||
std::string s = std::format("{0:{1}}", S{42}, 10); // s == " 42"
|
||||
EXPECT_EQ(s, " 42");
|
||||
}
|
||||
|
||||
#if FMT_USE_INT128
|
||||
template <> struct std::formatter<__int128_t> : std::formatter<long long> {
|
||||
auto format(__int128_t n, format_context& ctx) {
|
||||
// Format as a long long since we only want to check if it is possible to
|
||||
// specialize formatter for __int128_t.
|
||||
return formatter<long long>::format(static_cast<long long>(n), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(StdFormatTest, Int128) {
|
||||
__int128_t n = 42;
|
||||
auto s = std::format("{}", n);
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
#endif // FMT_USE_INT128
|
7
externals/fmt/test/test-assert.h
vendored
7
externals/fmt/test/test-assert.h
vendored
|
@ -13,9 +13,14 @@
|
|||
|
||||
class assertion_failure : public std::logic_error {
|
||||
public:
|
||||
explicit assertion_failure(const char *message) : std::logic_error(message) {}
|
||||
explicit assertion_failure(const char* message) : std::logic_error(message) {}
|
||||
|
||||
private:
|
||||
virtual void avoid_weak_vtable();
|
||||
};
|
||||
|
||||
void assertion_failure::avoid_weak_vtable() {}
|
||||
|
||||
#define FMT_ASSERT(condition, message) \
|
||||
if (!(condition)) throw assertion_failure(message);
|
||||
|
||||
|
|
12
externals/fmt/test/test-main.cc
vendored
12
externals/fmt/test/test-main.cc
vendored
|
@ -9,23 +9,23 @@
|
|||
#include "gtest.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <crtdbg.h>
|
||||
# include <crtdbg.h>
|
||||
#else
|
||||
# define _CrtSetReportFile(a, b)
|
||||
# define _CrtSetReportMode(a, b)
|
||||
# define _CrtSetReportFile(a, b)
|
||||
# define _CrtSetReportMode(a, b)
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
// Don't display any error dialogs. This also suppresses message boxes
|
||||
// on assertion failures in MinGW where _set_error_mode/CrtSetReportMode
|
||||
// doesn't help.
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
|
||||
SEM_NOOPENFILEERRORBOX);
|
||||
SEM_NOOPENFILEERRORBOX);
|
||||
#endif
|
||||
// Disable message boxes on assertion failures.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
|
|
68
externals/fmt/test/time-test.cc
vendored
68
externals/fmt/test/time-test.cc
vendored
|
@ -1,68 +0,0 @@
|
|||
// Formatting library for C++ - time formatting tests
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifdef WIN32
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "gmock.h"
|
||||
#include "fmt/locale.h"
|
||||
#include "fmt/time.h"
|
||||
|
||||
TEST(TimeTest, Format) {
|
||||
std::tm tm = std::tm();
|
||||
tm.tm_year = 116;
|
||||
tm.tm_mon = 3;
|
||||
tm.tm_mday = 25;
|
||||
EXPECT_EQ("The date is 2016-04-25.",
|
||||
fmt::format("The date is {:%Y-%m-%d}.", tm));
|
||||
}
|
||||
|
||||
TEST(TimeTest, GrowBuffer) {
|
||||
std::string s = "{:";
|
||||
for (int i = 0; i < 30; ++i)
|
||||
s += "%c";
|
||||
s += "}\n";
|
||||
std::time_t t = std::time(FMT_NULL);
|
||||
fmt::format(s, *std::localtime(&t));
|
||||
}
|
||||
|
||||
TEST(TimeTest, FormatToEmptyContainer) {
|
||||
std::string s;
|
||||
auto time = std::tm();
|
||||
time.tm_sec = 42;
|
||||
fmt::format_to(std::back_inserter(s), "{:%S}", time);
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
|
||||
TEST(TimeTest, EmptyResult) {
|
||||
EXPECT_EQ("", fmt::format("{}", std::tm()));
|
||||
}
|
||||
|
||||
static bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
|
||||
return lhs.tm_sec == rhs.tm_sec &&
|
||||
lhs.tm_min == rhs.tm_min &&
|
||||
lhs.tm_hour == rhs.tm_hour &&
|
||||
lhs.tm_mday == rhs.tm_mday &&
|
||||
lhs.tm_mon == rhs.tm_mon &&
|
||||
lhs.tm_year == rhs.tm_year &&
|
||||
lhs.tm_wday == rhs.tm_wday &&
|
||||
lhs.tm_yday == rhs.tm_yday &&
|
||||
lhs.tm_isdst == rhs.tm_isdst;
|
||||
}
|
||||
|
||||
TEST(TimeTest, LocalTime) {
|
||||
std::time_t t = std::time(FMT_NULL);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
|
||||
}
|
||||
|
||||
TEST(TimeTest, GMTime) {
|
||||
std::time_t t = std::time(FMT_NULL);
|
||||
std::tm tm = *std::gmtime(&t);
|
||||
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
||||
}
|
15
externals/fmt/test/util.cc
vendored
15
externals/fmt/test/util.cc
vendored
|
@ -8,7 +8,7 @@
|
|||
#include "util.h"
|
||||
#include <cstring>
|
||||
|
||||
void increment(char *s) {
|
||||
void increment(char* s) {
|
||||
for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) {
|
||||
if (s[i] != '9') {
|
||||
++s[i];
|
||||
|
@ -30,15 +30,20 @@ std::string get_system_error(int error_code) {
|
|||
#endif
|
||||
}
|
||||
|
||||
const char *const FILE_CONTENT = "Don't panic!";
|
||||
const char* const FILE_CONTENT = "Don't panic!";
|
||||
|
||||
fmt::buffered_file open_buffered_file(FILE **fp) {
|
||||
fmt::buffered_file open_buffered_file(FILE** fp) {
|
||||
#if FMT_USE_FCNTL
|
||||
fmt::file read_end, write_end;
|
||||
fmt::file::pipe(read_end, write_end);
|
||||
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
||||
write_end.close();
|
||||
fmt::buffered_file f = read_end.fdopen("r");
|
||||
if (fp)
|
||||
*fp = f.get();
|
||||
if (fp) *fp = f.get();
|
||||
#else
|
||||
fmt::buffered_file f("test-file", "w");
|
||||
fputs(FILE_CONTENT, f.get());
|
||||
if (fp) *fp = f.get();
|
||||
#endif
|
||||
return f;
|
||||
}
|
||||
|
|
35
externals/fmt/test/util.h
vendored
35
externals/fmt/test/util.h
vendored
|
@ -9,18 +9,18 @@
|
|||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "fmt/posix.h"
|
||||
#include "fmt/os.h"
|
||||
|
||||
enum {BUFFER_SIZE = 256};
|
||||
enum { BUFFER_SIZE = 256 };
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define FMT_VSNPRINTF vsprintf_s
|
||||
# define FMT_VSNPRINTF vsprintf_s
|
||||
#else
|
||||
# define FMT_VSNPRINTF vsnprintf
|
||||
# define FMT_VSNPRINTF vsnprintf
|
||||
#endif
|
||||
|
||||
template <std::size_t SIZE>
|
||||
void safe_sprintf(char (&buffer)[SIZE], const char *format, ...) {
|
||||
void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) {
|
||||
std::va_list args;
|
||||
va_start(args, format);
|
||||
FMT_VSNPRINTF(buffer, SIZE, format, args);
|
||||
|
@ -28,19 +28,19 @@ void safe_sprintf(char (&buffer)[SIZE], const char *format, ...) {
|
|||
}
|
||||
|
||||
// Increment a number in a string.
|
||||
void increment(char *s);
|
||||
void increment(char* s);
|
||||
|
||||
std::string get_system_error(int error_code);
|
||||
|
||||
extern const char *const FILE_CONTENT;
|
||||
extern const char* const FILE_CONTENT;
|
||||
|
||||
// Opens a buffered file for reading.
|
||||
fmt::buffered_file open_buffered_file(FILE **fp = FMT_NULL);
|
||||
fmt::buffered_file open_buffered_file(FILE** fp = nullptr);
|
||||
|
||||
inline FILE *safe_fopen(const char *filename, const char *mode) {
|
||||
inline FILE* safe_fopen(const char* filename, const char* mode) {
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix MSVC warning about "unsafe" fopen.
|
||||
FILE *f = 0;
|
||||
FILE* f = 0;
|
||||
errno = fopen_s(&f, filename, mode);
|
||||
return f;
|
||||
#else
|
||||
|
@ -48,34 +48,33 @@ inline FILE *safe_fopen(const char *filename, const char *mode) {
|
|||
#endif
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
class BasicTestString {
|
||||
template <typename Char> class BasicTestString {
|
||||
private:
|
||||
std::basic_string<Char> value_;
|
||||
|
||||
static const Char EMPTY[];
|
||||
|
||||
public:
|
||||
explicit BasicTestString(const Char *value = EMPTY) : value_(value) {}
|
||||
explicit BasicTestString(const Char* value = EMPTY) : value_(value) {}
|
||||
|
||||
const std::basic_string<Char> &value() const { return value_; }
|
||||
const std::basic_string<Char>& value() const { return value_; }
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
const Char BasicTestString<Char>::EMPTY[] = {0};
|
||||
template <typename Char> const Char BasicTestString<Char>::EMPTY[] = {0};
|
||||
|
||||
typedef BasicTestString<char> TestString;
|
||||
typedef BasicTestString<wchar_t> TestWString;
|
||||
|
||||
template <typename Char>
|
||||
std::basic_ostream<Char> &operator<<(
|
||||
std::basic_ostream<Char> &os, const BasicTestString<Char> &s) {
|
||||
std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os,
|
||||
const BasicTestString<Char>& s) {
|
||||
os << s.value();
|
||||
return os;
|
||||
}
|
||||
|
||||
class Date {
|
||||
int year_, month_, day_;
|
||||
|
||||
public:
|
||||
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
|
||||
|
||||
|
|
Loading…
Reference in a new issue