externals: Update fmt to 6.2.0

Merge commit 'c9dec5da8cb2893219c069f92728fb3997c7d81e'
This commit is contained in:
MerryMage 2020-04-23 21:03:34 +01:00
commit 591e7667f2
92 changed files with 18603 additions and 9815 deletions

8
externals/fmt/.clang-format vendored Normal file
View 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

View 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.

View File

@ -1,4 +1,5 @@
.vscode/ .vscode/
.vs/
*.iml *.iml
.idea/ .idea/

View File

@ -7,7 +7,6 @@ os: linux
git: git:
depth: 1 depth: 1
env: env:
global: global:
- secure: |- - secure: |-
@ -39,6 +38,26 @@ matrix:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
- g++-6 - 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 # Apple clang on OS X with C++14
- env: BUILD=Debug STANDARD=14 - env: BUILD=Debug STANDARD=14
compiler: clang compiler: clang
@ -46,8 +65,8 @@ matrix:
- env: BUILD=Release STANDARD=14 - env: BUILD=Release STANDARD=14
compiler: clang compiler: clang
os: osx os: osx
# clang 6.0 on Linux with C++14 # clang 6.0 on Linux with C++14 (builds the fuzzers as well)
- env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14 - env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14 ENABLE_FUZZING=1
compiler: clang compiler: clang
addons: addons:
apt: apt:
@ -73,52 +92,6 @@ matrix:
# g++ 4.8 on Linux with C++11 # g++ 4.8 on Linux with C++11
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11 - env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
compiler: gcc 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: before_script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi - if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi

View File

@ -24,24 +24,38 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE) set(${result_var} "${result}" PARENT_SCOPE)
endfunction() 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. # Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set # This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake). # CMAKE_BUILD_TYPE itself (it does so for nmake).
if (NOT CMAKE_BUILD_TYPE) if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc}) "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif () endif ()
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF) option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Options that control generation of various targets. # Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT}) option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT}) option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test 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 # Get version from core.h
file(READ include/fmt/core.h 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}") 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} set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
@ -66,6 +82,14 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14) include(cxx14)
include(CheckCXXCompilerFlag) 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") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
-Wold-style-cast -Wundef -Wold-style-cast -Wundef
@ -74,7 +98,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wnon-virtual-dtor -Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization -Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual -Winvalid-pch -Woverloaded-virtual
-Wconversion -Wconversion -Wswitch-enum
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow) -Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
@ -93,7 +117,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
endif () endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -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) check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING) if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@ -117,17 +142,24 @@ if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"") set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
endif () endif ()
# Set FrameworkPathOverride to get rid of MSB3644 warnings. # 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 " file(WRITE run-msbuild.bat "
${MSBUILD_SETUP} ${MSBUILD_SETUP}
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif () endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists) include(CheckSymbolExists)
if (WIN32) if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN) check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else () else ()
check_symbol_exists(open fcntl.h HAVE_OPEN) check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif () endif ()
function(add_headers VAR) function(add_headers VAR)
@ -139,17 +171,17 @@ function(add_headers VAR)
endfunction() endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
ostream.h printf.h time.h ranges.h) locale.h os.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc) set(FMT_SOURCES src/format.cc src/os.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -157,27 +189,42 @@ if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:include>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} 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 (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
# Fix rpmlint warning: # Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. # unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed) target_link_libraries(fmt -Wl,--as-needed)
endif () endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif () 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-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only) add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) 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 target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -187,8 +234,9 @@ target_include_directories(fmt-header-only INTERFACE
if (FMT_INSTALL) if (FMT_INSTALL)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for cmake files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
@ -199,14 +247,17 @@ if (FMT_INSTALL)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only) set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif () endif ()
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for libraries, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for include files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(FMT_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for pkgconfig (.pc) files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory. # Generate the version, config and target files into the build directory.
write_basic_package_version_file( write_basic_package_version_file(
@ -235,9 +286,12 @@ if (FMT_INSTALL)
# Install the library and headers. # Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} 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 ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()
@ -251,11 +305,16 @@ if (FMT_TEST)
add_subdirectory(test) add_subdirectory(test)
endif () endif ()
# Control fuzzing independent of the unit tests.
if (FMT_FUZZ)
add_subdirectory(test/fuzzing)
endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (MASTER_PROJECT AND EXISTS ${gitignore}) if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore. # Get the list of ignored files from .gitignore.
file (STRINGS ${gitignore} lines) file (STRINGS ${gitignore} lines)
LIST(REMOVE_ITEM lines /doc/html) list(REMOVE_ITEM lines /doc/html)
foreach (line ${lines}) foreach (line ${lines})
string(REPLACE "." "[.]" line "${line}") string(REPLACE "." "[.]" line "${line}")
string(REPLACE "*" ".*" line "${line}") string(REPLACE "*" ".*" line "${line}")

17
externals/fmt/CONTRIBUTING.md vendored Normal file
View 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!

View File

@ -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!

File diff suppressed because it is too large Load Diff

View File

@ -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 The above copyright notice and this permission notice shall be
modification, are permitted provided that the following conditions are met: included in all copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
list of conditions and the following disclaimer. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2. Redistributions in binary form must reproduce the above copyright notice, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
this list of conditions and the following disclaimer in the documentation NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
and/or other materials provided with the distribution. 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 --- Optional exception to the license ---
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE As an exception, if, as a result of your compiling your source code, portions
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR of this Software are embedded into a machine-executable object form of such
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES source code, you may redistribute such embedded portions in such object form
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; without including the above copyright and permission notices.
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -6,90 +6,90 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt :target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://badges.gitter.im/Join%20Chat.svg .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: Join the chat at https://gitter.im/fmtlib/fmt :alt: fmt is continuously fuzzed att oss-fuzz
:target: https://gitter.im/fmtlib/fmt :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++. **{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 Q&A: ask questions on `StackOverflow with the tag fmt <https://stackoverflow.com/questions/tagged/fmt>`_.
Text Formatting <http://fmtlib.net/Text%20Formatting.html>`__.
Released versions are available from the `Releases page
<https://github.com/fmtlib/fmt/releases>`__.
Features 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. positional arguments for localization.
* `Format string syntax <http://fmtlib.net/dev/syntax.html>`_ similar to the one * `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_ of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python. in Python.
* Safe `printf implementation * 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. 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. * Support for user-defined types.
* High speed: performance of the format API is close to that of glibc's `printf * High performance: faster than common standard library implementations of
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the `printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_ and
performance of IOStreams. See `Speed tests`_ and iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++
`Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_. <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 * Small code size both in terms of source code (the minimum configuration
consists of just three header files, ``core.h``, ``format.h`` and consists of just three header files, ``core.h``, ``format.h`` and
``format-inl.h``) and compiled code. See `Compile time and code bloat`_. ``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests * Reliability: the library has an extensive set of `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 * Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow reported at compile time, automatic memory management prevents buffer overflow
errors. errors.
* Ease of use: small self-contained code base, no external dependencies, * 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>`_ <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. consistent output across platforms and support for older compilers.
* Clean warning-free codebase even on high warning levels * Clean warning-free codebase even on high warning levels
(``-Wall -Wextra -pedantic``). (``-Wall -Wextra -pedantic``).
* Support for wide strings. * Support for wide strings.
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro. * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
See the `documentation <http://fmtlib.net/latest/>`_ for more details. See the `documentation <https://fmt.dev/latest/>`_ for more details.
Examples Examples
-------- --------
This prints ``Hello, world!`` to stdout: Print ``Hello, world!`` to ``stdout``:
.. code:: c++ .. code:: c++
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax fmt::print("Hello, {}!", "world"); // Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // uses printf 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++ .. code:: c++
std::string s = fmt::format("{0}{1}{0}", "abra", "cad"); std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "abracadabra" // 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++ .. code:: c++
// test.cc // test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h> #include <fmt/format.h>
std::string s = format(fmt("{2}"), 42); std::string s = format(FMT_STRING("{2}"), 42);
.. code:: .. code::
$ c++ -Iinclude -std=c++14 test.cc $ 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 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 include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message); ErrorHandler::on_error(message);
@ -98,7 +98,7 @@ Format strings can be checked at compile time:
context_.on_error("argument index out of range"); 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>`_): (`godbolt <https://godbolt.org/g/NXmpU4>`_):
.. code:: c++ .. code:: c++
@ -106,10 +106,10 @@ Format strings can be checked at compile time:
fmt::memory_buffer buf; fmt::memory_buffer buf;
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10) format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16) 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 Format objects of user-defined types via a simple `extension API
`extension API <http://fmtlib.net/latest/api.html#formatting-user-defined-types>`_: <https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
.. code:: c++ .. code:: c++
@ -121,11 +121,10 @@ Formatting of user-defined types is supported via a simple
template <> template <>
struct fmt::formatter<date> { struct fmt::formatter<date> {
template <typename ParseContext> constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
template <typename FormatContext> 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); 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}); std::string s = fmt::format("The date is {}", date{2012, 12, 9});
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
You can create your own functions similar to `format Create your own functions similar to `format
<http://fmtlib.net/latest/api.html#format>`_ and <https://fmt.dev/latest/api.html#format>`_ and
`print <http://fmtlib.net/latest/api.html#print>`_ `print <https://fmt.dev/latest/api.html#print>`_
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_): which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++ .. code:: c++
// Prints formatted error message. // 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::print("Error: ");
fmt::vprint(format, args); fmt::vprint(format, args);
} }
template <typename... 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...)); vreport_error(format, fmt::make_format_args(args...));
} }
report_error("file not found: {}", path); report_error("file not found: {}", path);
Note that ``vreport_error`` is not parameterized on argument types which can 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. 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 Benchmarks
---------- ----------
Speed tests 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 Library Method Run Time, s
================= ============= =========== ================= ============= ===========
libc printf 1.35 libc printf 1.04
libc++ std::ostream 3.42 libc++ std::ostream 3.05
fmt 534bff7 fmt::print 1.56 {fmt} 6.1.1 fmt::print 0.75
tinyformat 2.0.1 tfm::printf 3.73 Boost Format 1.67 boost::format 7.24
Boost Format 1.54 boost::format 8.44 Folly Format folly::format 2.23
Folly Format folly::format 2.54
================= ============= =========== ================= ============= ===========
As you can see ``boost::format`` is much slower than the alternative methods; this {fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat The above results were generated by building ``tinyformat_test.cpp`` on macOS
cannot be faster than the IOStreams because it uses them internally. 10.14.6 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
Performance of fmt is close to that of printf, being `faster than printf on integer three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_, or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
but slower on floating-point formatting which dominates this benchmark. 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 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 2.6 29 26
printf+string 16.4 29 26 printf+string 16.4 29 26
IOStreams 31.1 59 55 iostreams 31.1 59 55
fmt 19.0 37 34 {fmt} 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203 Boost Format 91.9 226 203
Folly Format 115.7 101 88 Folly Format 115.7 101 88
============= =============== ==================== ================== ============= =============== ==================== ==================
As you can see, fmt has 60% less overhead in terms of resulting binary code 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 size compared to iostreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads. and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>`` ``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 2.2 33 30
printf+string 16.0 33 30 printf+string 16.0 33 30
IOStreams 28.3 56 52 iostreams 28.3 56 52
fmt 18.2 59 50 {fmt} 18.2 59 50
tinyformat 32.6 88 82
Boost Format 54.1 365 303 Boost Format 54.1 365 303
Folly Format 79.9 445 430 Folly Format 79.9 445 430
============= =============== ==================== ================== ============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared ``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
libraries to compare formatting function overhead only. Boost Format compare formatting function overhead only. Boost Format is a
and tinyformat are header-only libraries so they don't provide any header-only library so it doesn't provide any linkage options.
linkage options.
Running the tests Running the tests
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -429,7 +242,7 @@ Running the tests
Please refer to `Building the library`__ for the instructions on how to build Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests. 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, Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_, `format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
@ -448,6 +261,175 @@ or the bloat test::
$ make 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 FAQ
--- ---
@ -474,11 +456,11 @@ A: use ``std::tuple``:
License License
------- -------
fmt is distributed under the BSD `license {fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_. <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax 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 section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_ documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. For this reason the documentation is 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 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/vitaut>`_) and Jonathan Müller (`foonathan
<https://github.com/foonathan>`_) with contributions from many other people. <https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and 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. since it had some influence on tinyformat.
Some ideas used in the implementation are borrowed from `Loki Some ideas used in the implementation are borrowed from `Loki
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API <http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in <https://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_. `Clang <https://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format 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 Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary `Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary

View File

@ -6,7 +6,7 @@ endif ()
add_custom_target(doc add_custom_target(doc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION} 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/ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION share/doc/fmt OPTIONAL) DESTINATION share/doc/fmt OPTIONAL)

View File

@ -58,7 +58,7 @@
<span class="caret"></span></a> <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
{% for v in versions.split(',') %} {% 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 %} {% endfor %}
</ul> </ul>
</li> </li>
@ -84,7 +84,7 @@
<div class="jumbotron"> <div class="jumbotron">
<div class="tb-container"> <div class="tb-container">
<h1>{fmt}</h1> <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"> <div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %} {% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success" <a class="btn btn-success"

View File

@ -10,12 +10,14 @@ The {fmt} library API consists of the following parts:
facilities and a lightweight subset of formatting functions facilities and a lightweight subset of formatting functions
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time * :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
format string checks, output iterator and user-defined type support 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/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting * :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
All functions and types provided by the library reside in namespace ``fmt`` and All functions and types provided by the library reside in namespace ``fmt`` and
macros have prefix ``FMT_`` or ``fmt``. macros have prefix ``FMT_``.
.. _core-api: .. _core-api:
@ -23,7 +25,8 @@ Core API
======== ========
``fmt/core.h`` defines the core API which provides argument handling facilities ``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>` The following functions use :ref:`format string syntax <syntax>`
similar to that of Python's `str.format similar to that of Python's `str.format
@ -39,24 +42,25 @@ participate in an overload resolution if the latter is not a string.
.. _format: .. _format:
.. doxygenfunction:: format(const S&, const Args&...) .. doxygenfunction:: format(const S&, Args&&...)
.. doxygenfunction:: vformat(const S&, basic_format_args<typename buffer_context<Char>::type>) .. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>)
.. _print: .. _print:
.. doxygenfunction:: print(const S&, const Args&...) .. doxygenfunction:: print(const S&, Args&&...)
.. doxygenfunction:: vprint(string_view, format_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 *, 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&...) .. doxygenfunction:: fmt::make_format_args(const Args&...)
@ -81,6 +85,19 @@ Compatibility
.. doxygentypedef:: fmt::string_view .. doxygentypedef:: fmt::string_view
.. doxygentypedef:: fmt::wstring_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:
Format API Format API
@ -89,12 +106,16 @@ Format API
``fmt/format.h`` defines the full format API providing compile-time format ``fmt/format.h`` defines the full format API providing compile-time format
string checks, output iterator and user-defined type support. 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 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; }; struct point { double x, y; };
namespace fmt {
template <> template <>
struct formatter<point> { struct fmt::formatter<point> {
template <typename ParseContext> // Presentation format: 'f' - fixed, 'e' - exponential.
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } 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> template <typename FormatContext>
auto format(const point &p, FormatContext &ctx) { auto format(const point& p, FormatContext& ctx) {
return format_to(ctx.begin(), "({:.1f}, {:.1f})", p.x, p.y); // 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:: Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2}; point p = {1, 2};
std::string s = fmt::format("{}", p); std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)" // s == "(1.0, 2.0)"
In the example above the ``formatter<point>::parse`` function ignores the You can also reuse existing formatters via inheritance or composition, for
contents of the format string referred to by ``ctx.begin()`` so the object will example::
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::
enum class color {red, green, blue}; 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> { struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>. // parse is inherited from formatter<string_view>.
template <typename FormatContext> template <typename FormatContext>
auto format(color c, FormatContext &ctx) { auto format(color c, FormatContext& ctx) {
string_view name = "unknown"; string_view name = "unknown";
switch (c) { switch (c) {
case color::red: name = "red"; break; 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" fmt::print("{}", a); // prints "B"
} }
This section shows how to define a custom format function for a user-defined .. doxygenclass:: fmt::basic_format_parse_context
type. The next section describes how to get ``fmt`` to use a conventional stream :members:
output ``operator<<`` when one is defined for a user-defined type.
Output iterator support Output Iterator Support
----------------------- -----------------------
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, const Args&...) .. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...) .. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, Args&&...)
.. doxygenstruct:: fmt::format_to_n_result .. doxygenstruct:: fmt::format_to_n_result
:members: :members:
@ -201,6 +245,8 @@ The following user-defined literals are defined in ``fmt/format.h``.
Utilities Utilities
--------- ---------
.. doxygenstruct:: fmt::is_char
.. doxygentypedef:: fmt::char_t .. doxygentypedef:: fmt::char_t
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...) .. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
@ -209,13 +255,17 @@ Utilities
.. doxygenfunction:: fmt::to_wstring(const T&) .. 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 .. doxygenclass:: fmt::basic_memory_buffer
:protected-members: :protected-members:
:members: :members:
System errors System Errors
------------- -------------
fmt does not use ``errno`` to communicate errors to the user, but it may call fmt does not use ``errno`` to communicate errors to the user, but it may call
@ -232,7 +282,7 @@ the value of ``errno`` being preserved by library functions.
.. _formatstrings: .. _formatstrings:
Custom allocators Custom Allocators
----------------- -----------------
The {fmt} library supports custom dynamic memory allocators. The {fmt} library supports custom dynamic memory allocators.
@ -258,7 +308,7 @@ allocator::
template <typename ...Args> template <typename ...Args>
inline custom_string format(custom_allocator alloc, inline custom_string format(custom_allocator alloc,
fmt::string_view format_str, fmt::string_view format_str,
const Args & ... args) { const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_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`` the default allocator. Also floating-point formatting falls back on ``sprintf``
which may do allocations. 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 It is possible to change the way arguments are formatted by providing a
custom argument formatter class:: custom argument formatter class::
using arg_formatter = using arg_formatter = fmt::arg_formatter<fmt::buffer_range<char>>;
fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>>;
// A custom argument formatter that formats negative integers as unsigned // A custom argument formatter that formats negative integers as unsigned
// with the ``x`` format specifier. // with the ``x`` format specifier.
class custom_arg_formatter : public arg_formatter { class custom_arg_formatter : public arg_formatter {
public: public:
custom_arg_formatter(fmt::format_context &ctx, custom_arg_formatter(fmt::format_context& ctx,
fmt::format_specs *spec = nullptr) fmt::format_parse_context* parse_ctx = nullptr,
: arg_formatter(ctx, spec) {} fmt::format_specs* spec = nullptr)
: arg_formatter(ctx, parse_ctx, spec) {}
using arg_formatter::operator(); using arg_formatter::operator();
auto operator()(int value) { 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 (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value); return arg_formatter::operator()(value);
} }
@ -302,7 +352,7 @@ custom argument formatter class::
template <typename ...Args> template <typename ...Args>
inline std::string custom_format( 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...)); return custom_vformat(format_str, fmt::make_format_args(args...));
} }
@ -311,16 +361,41 @@ custom argument formatter class::
.. doxygenclass:: fmt::arg_formatter .. doxygenclass:: fmt::arg_formatter
:members: :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 The library supports `strftime
<http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time
formatting:: formatting::
#include <fmt/time.h> #include <fmt/chrono.h>
std::time_t t = std::time(nullptr); std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date) // 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: .. _ostream-api:
``std::ostream`` support ``std::ostream`` Support
======================== ========================
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
@ -344,7 +419,7 @@ user-defined types that have overloaded ``operator<<``::
public: public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {} date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const date &d) { friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_; 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)); std::string s = fmt::format("The date is {}", date(2012, 12, 9));
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
.. doxygenfunction:: print(std::basic_ostream<fmt::char_t<S>>&, const S&, const Args&...) .. doxygenfunction:: print(std::basic_ostream<Char>&, const S&, Args&&...)
.. _printf-api: .. _printf-api:
``printf`` formatting ``printf`` Formatting
===================== =====================
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality. 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::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&...) .. doxygenfunction:: sprintf(const S&, const Args&...)

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion 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): def pip_install(package, commit=None, **kwargs):
"Install package using pip." "Install package using pip."
@ -74,7 +74,7 @@ def build_docs(version='dev', **kwargs):
GENERATE_MAN = NO GENERATE_MAN = NO
GENERATE_RTF = NO GENERATE_RTF = NO
CASE_SENSE_NAMES = NO CASE_SENSE_NAMES = NO
INPUT = {0}/core.h {0}/format.h {0}/ostream.h \ INPUT = {0}/core.h {0}/format.h {0}/os.h {0}/ostream.h \
{0}/printf.h {0}/time.h {0}/printf.h {0}/time.h
QUIET = YES QUIET = YES
JAVADOC_AUTOBRIEF = YES JAVADOC_AUTOBRIEF = YES
@ -94,7 +94,7 @@ def build_docs(version='dev', **kwargs):
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}" \ "FMT_END_NAMESPACE=}}" \
"FMT_STRING_ALIAS=1" \ "FMT_STRING_ALIAS=1" \
"FMT_ENABLE_IF_T(B, T)=T" "FMT_ENABLE_IF(B)="
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
'''.format(include_dir, doxyxml_dir).encode('UTF-8')) '''.format(include_dir, doxyxml_dir).encode('UTF-8'))
if p.returncode != 0: if p.returncode != 0:

View File

@ -1,17 +1,18 @@
Overview Overview
======== ========
**fmt** (formerly cppformat) is an open-source formatting library. **{fmt}** is an open-source formatting library providing a fast and safe
It can be used as a fast and safe alternative to printf and IOStreams. alternative to C stdio and C++ iostreams.
.. raw:: html .. raw:: html
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading">What users say:</div> <div class="panel-heading">What users say:</div>
<div class="panel-body"> <div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for a long Thanks for creating this library. Its been a hole in C++ for
time. Ive used both boost::format and loki::SPrintf, and neither felt a long time. Ive used both <code>boost::format</code> and
like the right answer. This does. <code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
</div> </div>
</div> </div>
@ -20,12 +21,13 @@ It can be used as a fast and safe alternative to printf and IOStreams.
Format API Format API
---------- ----------
The replacement-based Format API provides a safe alternative to ``printf``, The format API is similar in spirit to the C ``printf`` family of function but
``sprintf`` and friends with comparable or `better performance is safer, simpler and serveral times `faster
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_. <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 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>`_ `str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
in Python: Python:
.. code:: c++ .. 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"); 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 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++ .. code:: c++
@ -72,21 +74,10 @@ an alternative, slightly terser syntax for named arguments:
.. code:: c++ .. code:: c++
using namespace fmt::literals;
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.", fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
"name"_a="World", "number"_a=42); "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:
Safety Safety
@ -106,10 +97,10 @@ string", because the argument ``"forty-two"`` is a string while the format code
.. code:: c++ .. 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 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 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 which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
needed. needed.
Compact binary code Compact Binary Code
------------------- -------------------
The library is designed to produce compact per-call compiled code. For example 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 * decltype
* trailing return types * trailing return types
* deleted functions * deleted functions
* alias templates
These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
compilers use fmt `version 4.x compilers use {fmt} `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be <https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98. maintained and only requires C++98.
The output of all formatting functions is consistent across platforms. In The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the 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:: .. code::
@ -195,9 +187,9 @@ always prints ``inf``.
Ease of Use 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. 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. using the library both in open-source and commercial projects.
.. raw:: html .. raw:: html

View File

@ -76,19 +76,19 @@ The general form of a *standard format specifier* is:
.. productionlist:: sf .. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`] format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
fill: <a character other than '{', '}' or '\0'> fill: <a character other than '{' or '}'>
align: "<" | ">" | "=" | "^" align: "<" | ">" | "^"
sign: "+" | "-" | " " sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}" width: `integer` | "{" `arg_id` "}"
precision: `integer` | "{" `arg_id` "}" precision: `integer` | "{" `arg_id` "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s" type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X" int_type: "b" | "B" | "d" | "o" | "x" | "X"
The *fill* character can be any character other than '{', '}' or '\\0'. The The *fill* character can be any Unicode code point other than ``'{'`` or
presence of a fill character is signaled by the character following it, which ``'}'``. The presence of a fill character is signaled by the character following
must be one of the alignment options. If the second character of *format_spec* it, which must be one of the alignment options. If the second character of
is not a valid alignment option, then it is assumed that both the fill character *format_spec* is not a valid alignment option, then it is assumed that both the
and the alignment option are absent. fill character and the alignment option are absent.
The meaning of the various alignment options is as follows: 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 | | ``'>'`` | Forces the field to be right-aligned within the |
| | available space (this is the default for numbers). | | | 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 | | ``'^'`` | Forces the field to be centered within the available |
| | space. | | | space. |
+---------+----------------------------------------------------------+ +---------+----------------------------------------------------------+
@ -148,15 +143,17 @@ conversions, trailing zeros are not removed from the result.
.. ifconfig:: False .. ifconfig:: False
The ``','`` option signals the use of a comma for a thousands separator. 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. instead.
*width* is a decimal integer defining the minimum field width. If not *width* is a decimal integer defining the minimum field width. If not
specified, then the field width will be determined by the content. specified, then the field width will be determined by the content.
Preceding the *width* field by a zero (``'0'``) character enables Preceding the *width* field by a zero (``'0'``) character enables sign-aware
sign-aware zero-padding for numeric types. This is equivalent to a *fill* zero-padding for numeric types. It forces the padding to be placed after the
character of ``'0'`` with an *alignment* type of ``'='``. 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 The *precision* is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted with 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"`` | | | ``'#'`` option with this type adds the prefix ``"0X"`` |
| | to the output value. | | | to the output value. |
+---------+----------------------------------------------------------+ +---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses | | ``'L'`` | Locale-specific format. This is the same as ``'d'``, |
| | the current locale setting to insert the appropriate | | | except that it uses the current locale setting to insert |
| | number separator characters. | | | the appropriate number separator characters. |
+---------+----------------------------------------------------------+ +---------+----------------------------------------------------------+
| none | The same as ``'d'``. | | 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. | | | notation using the letter 'e' to indicate the exponent. |
+---------+----------------------------------------------------------+ +---------+----------------------------------------------------------+
| ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an | | ``'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 | | ``'f'`` | Fixed point. Displays the number as a fixed-point |
| | number. | | | number. |
@ -264,10 +261,15 @@ The available presentation types for floating-point values are:
| | ``'E'`` if the number gets too large. The | | | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. | | | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+ +---------+----------------------------------------------------------+
| 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 .. ifconfig:: False
@ -303,7 +305,7 @@ The available presentation types for pointers are:
.. _formatexamples: .. _formatexamples:
Format examples Format Examples
=============== ===============
This section contains examples of the format syntax and comparison with This section contains examples of the format syntax and comparison with
@ -318,72 +320,94 @@ following examples.
Accessing arguments by position:: Accessing arguments by position::
format("{0}, {1}, {2}", 'a', 'b', 'c'); fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c" // Result: "a, b, c"
format("{}, {}, {}", 'a', 'b', 'c'); fmt::format("{}, {}, {}", 'a', 'b', 'c');
// Result: "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" // 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" // Result: "abracadabra"
Aligning the text and specifying a width:: Aligning the text and specifying a width::
format("{:<30}", "left aligned"); fmt::format("{:<30}", "left aligned");
// Result: "left aligned " // Result: "left aligned "
format("{:>30}", "right aligned"); fmt::format("{:>30}", "right aligned");
// Result: " right aligned" // Result: " right aligned"
format("{:^30}", "centered"); fmt::format("{:^30}", "centered");
// Result: " centered " // Result: " centered "
format("{:*^30}", "centered"); // use '*' as a fill char fmt::format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********" // Result: "***********centered***********"
Dynamic width:: Dynamic width::
format("{:<{}}", "left aligned", 30); fmt::format("{:<{}}", "left aligned", 30);
// Result: "left aligned " // Result: "left aligned "
Dynamic precision:: Dynamic precision::
format("{:.{}f}", 3.14, 1); fmt::format("{:.{}f}", 3.14, 1);
// Result: "3.1" // Result: "3.1"
Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:: 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" // 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" // 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" // Result: "3.140000; -3.140000"
Replacing ``%x`` and ``%o`` and converting the value to different bases:: 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" // Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
// with 0x or 0 or 0b as prefix: // 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" // Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
Padded hex byte with prefix and always prints both hex characters:: Padded hex byte with prefix and always prints both hex characters::
format("{:#04x}", 0); fmt::format("{:#04x}", 0);
// Result: "0x00" // 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 .. 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:: Nesting arguments and more complex examples::
>>> for align, text in zip('<^>', ['left', 'center', 'right']): >>> 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 9 9 11 1001
10 A 12 1010 10 A 12 1010
11 B 13 1011 11 B 13 1011

View File

@ -2,21 +2,15 @@
Usage Usage
***** *****
To use the fmt library, add :file:`format.h` and :file:`format.cc` from To use the {fmt} library, add :file:`fmt/core.h`, :file:`fmt/format.h`,
a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_ :file:`fmt/format-inl.h`, :file:`src/format.cc` and optionally other headers
or the `Git repository <https://github.com/fmtlib/fmt>`_ to your project. 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>`. 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:
Building the library Building the Library
==================== ====================
The included `CMake build script`__ can be used to build the fmt The included `CMake build script`__ can be used to build the fmt
@ -31,7 +25,7 @@ workflow starts with::
mkdir build # Create a directory to hold the build output. mkdir build # Create a directory to hold the build output.
cd build 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. 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 __ 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 You can add the ``fmt`` library directory into your project and include it in
your ``CMakeLists.txt`` file:: your ``CMakeLists.txt`` file::
@ -79,11 +79,11 @@ You can detect and use an installed version of {fmt} as follows::
find_package(fmt) find_package(fmt)
target_link_libraries(<your-target> fmt::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 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 make doc
This will generate the HTML documentation in ``doc/html``. 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 Android NDK
=========== ===========

File diff suppressed because it is too large Load Diff

View File

@ -12,184 +12,149 @@
FMT_BEGIN_NAMESPACE 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 { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215) antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255) aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212) aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255) azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220) beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196) bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0) black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205) blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255) blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226) blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42) brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135) burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160) cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0) chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30) chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80) coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237) cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220) cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60) crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255) cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139) dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139) dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11) dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169) dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0) dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107) dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139) dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47) dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0) dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204) dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0) dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122) dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143) dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139) dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209) dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211) dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147) deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255) deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105) dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255) dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34) fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240) floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34) forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255) fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220) gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255) ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0) gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32) golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128) gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0) green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47) green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240) honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180) hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92) indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130) indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240) ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140) khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250) lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245) lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0) lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205) lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230) light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128) light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255) light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211) light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144) light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193) light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122) light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170) light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250) light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153) light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222) light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224) light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0) lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50) lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230) linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255) magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0) maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170) medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205) medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211) medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219) medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113) medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238) medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154) medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204) medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133) medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112) midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250) mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225) misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181) moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173) navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128) navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230) old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0) olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35) olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0) orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0) orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214) orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152) pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238) pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147) pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213) papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185) peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63) peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203) pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221) plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230) powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128) purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153) rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0) red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143) rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225) royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19) saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114) salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96) sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87) sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238) sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45) sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192) silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235) sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205) slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144) slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250) snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127) spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180) steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140) tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128) teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216) thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71) tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208) turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238) violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179) wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255) white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245) white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0) yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50) yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color }; // enum class color
enum class terminal_color : uint8_t { enum class terminal_color : uint8_t {
black = 30, black = 30,
@ -208,27 +173,26 @@ enum class terminal_color : uint8_t {
bright_magenta, bright_magenta,
bright_cyan, bright_cyan,
bright_white bright_white
}; // enum class terminal_color };
enum class emphasis : uint8_t { enum class emphasis : uint8_t {
bold = 1, bold = 1,
italic = 1 << 1, italic = 1 << 1,
underline = 1 << 2, underline = 1 << 2,
strikethrough = 1 << 3 strikethrough = 1 << 3
}; // enum class emphasis };
// rgb is a struct for red, green and blue colors. // 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 // Using the name "rgb" makes some editors show the color in a tooltip.
// editor.
struct rgb { struct rgb {
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {} FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_) FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
: r(r_), g(g_), b(b_) {} FMT_CONSTEXPR rgb(uint32_t hex)
FMT_CONSTEXPR_DECL rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {} FMT_CONSTEXPR rgb(color hex)
FMT_CONSTEXPR_DECL rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF),
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {} b(uint32_t(hex) & 0xFF) {}
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
@ -238,19 +202,17 @@ namespace internal {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
: is_rgb(), value{} {} FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT value{} {
: is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
: is_rgb(true), value{} { value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
: is_rgb(), value{} { value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@ -259,21 +221,23 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace internal } // namespace internal
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: 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) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 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; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
@ -282,7 +246,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) 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; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
@ -291,18 +255,18 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator|(text_style lhs,
text_style operator|(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs |= 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) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 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; foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
} }
@ -311,7 +275,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) 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; background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
} }
@ -320,8 +284,8 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator&(text_style lhs,
text_style operator&(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs &= rhs; return lhs &= rhs;
} }
@ -335,32 +299,32 @@ class text_style {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { 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; return foreground_color;
} }
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { 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; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { 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; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(), : set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
} else { } else {
background_color = text_color; background_color = text_color;
set_background_color = true; set_background_color = true;
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
@ -388,19 +352,17 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
namespace internal { namespace internal {
template <typename Char> template <typename Char> struct ansi_color_escape {
struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, 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 // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == internal::data::BACKGROUND_COLOR; bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) if (is_background) value += 10u;
value += 10u;
std::size_t index = 0; std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
@ -422,7 +384,7 @@ struct ansi_color_escape {
buffer[i] = static_cast<Char>(esc[i]); buffer[i] = static_cast<Char>(esc[i]);
} }
rgb color(text_color.value.rgb_color); 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.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
@ -430,19 +392,15 @@ struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {}; uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em); uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
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::italic)) if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
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)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; std::size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) if (!em_codes[i]) continue;
continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]); buffer[index++] = static_cast<Char>('0' + em_codes[i]);
@ -450,12 +408,17 @@ struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); 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]; 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 { char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
@ -465,77 +428,81 @@ private:
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT { internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR); return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
make_background_color(internal::color_type background) FMT_NOEXCEPT { internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR); return ansi_color_escape<Char>(background, internal::data::background_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> 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); std::fputs(chars, stream);
} }
template <> 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); 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> template <typename Char>
inline void reset_color(FILE *stream) FMT_NOEXCEPT { inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
fputs(internal::data::RESET_COLOR, stream); const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
} }
template <> template <typename Char>
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT { void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
fputs(internal::data::WRESET_COLOR, stream); basic_string_view<Char> format_str,
} basic_format_args<buffer_context<Char>> args) {
// 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) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
internal::fputs<Char>( auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
internal::make_emphasis<Char>(ts.get_emphasis()), f); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
internal::fputs<Char>( auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground()), f); internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
internal::fputs<Char>( auto background =
internal::make_background_color<Char>(ts.get_background()), f); internal::make_background_color<Char>(ts.get_background());
} buf.append(background.begin(), background.end());
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
} }
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), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename String, typename... Args> template <typename S, typename... Args,
typename std::enable_if<internal::is_string<String>::value>::type print( FMT_ENABLE_IF(internal::is_string<S>::value)>
std::FILE *f, const text_style &ts, const String &format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args &... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t; using context = buffer_context<char_t<S>>;
typedef typename buffer_context<char_t>::type context_t; format_arg_store<context, Args...> as{args...};
format_arg_store<context_t, Args...> as{args...}; vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str, basic_format_args<context_t>(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), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename String, typename... Args> template <typename S, typename... Args,
typename std::enable_if<internal::is_string<String>::value>::type print( FMT_ENABLE_IF(internal::is_string<S>::value)>
const text_style &ts, const String &format_str, void print(const text_style& ts, const S& format_str, const Args&... args) {
const Args &... args) {
return print(stdout, ts, format_str, 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 FMT_END_NAMESPACE

595
externals/fmt/include/fmt/compile.h vendored Normal file
View 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_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,65 +8,66 @@
#ifndef FMT_LOCALE_H_ #ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_ #define FMT_LOCALE_H_
#include "format.h"
#include <locale> #include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <typename Char> template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf, const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
typedef back_insert_range<basic_buffer<Char> > range; using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
buf, to_string_view(format_str), args, internal::locale_ref(loc)); internal::locale_ref(loc));
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat( std::basic_string<Char> vformat(
const std::locale &loc, basic_string_view<Char> format_str, const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer); 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( inline std::basic_string<Char> vformat(
const std::locale &loc, const S &format_str, const std::locale& loc, const S& format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args); return internal::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<FMT_CHAR(S)> format( inline std::basic_string<Char> format(const std::locale& loc,
const std::locale &loc, const S &format_str, const Args &... args) { const S& format_str, Args&&... args) {
return internal::vformat( return internal::vformat(
loc, to_string_view(format_str), loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...)); internal::make_args_checked<Args...>(format_str, args...));
} }
template <typename String, typename OutputIt, typename... Args> template <typename S, typename OutputIt, typename... Args,
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value, typename Char = enable_if_t<
OutputIt>::type internal::is_output_iterator<OutputIt>::value, char_t<S>>>
vformat_to(OutputIt out, const std::locale &loc, const String &format_str, inline OutputIt vformat_to(
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) { OutputIt out, const std::locale& loc, const S& format_str,
typedef output_range<OutputIt, FMT_CHAR(String)> range; format_args_t<type_identity_t<OutputIt>, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>( 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> template <typename OutputIt, typename S, typename... Args,
inline typename std::enable_if< FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value && internal::is_string<S>::value)>
internal::is_output_iterator<OutputIt>::value, OutputIt>::type inline OutputIt format_to(OutputIt out, const std::locale& loc,
format_to(OutputIt out, const std::locale &loc, const S &format_str, const S& format_str, Args&&... args) {
const Args &... args) {
internal::check_format_string<Args...>(format_str); 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...}; format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as)); basic_format_args<context>(as));

392
externals/fmt/include/fmt/os.h vendored Normal file
View 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_

View File

@ -8,22 +8,21 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include "format.h"
#include <ostream> #include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <class Char> template <class Char> class formatbuf : public std::basic_streambuf<Char> {
class formatbuf : public std::basic_streambuf<Char> {
private: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; using int_type = typename std::basic_streambuf<Char>::int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; using traits_type = typename std::basic_streambuf<Char>::traits_type;
basic_buffer<Char> &buffer_; buffer<Char>& buffer_;
public: public:
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {} formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@ -39,33 +38,36 @@ class formatbuf : public std::basic_streambuf<Char> {
return ch; 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); buffer_.append(s, s + count);
return count; return count;
} }
}; };
template <typename Char> template <typename Char> struct test_stream : std::basic_ostream<Char> {
struct test_stream : std::basic_ostream<Char> {
private: private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>. // 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). // Checks if T has a user-defined operator<< (e.g. not a member of
template <typename T, typename Char> // std::ostream).
class is_streamable { template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static decltype( static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
internal::declval<test_stream<Char>&>() << std::declval<U>()),
<< internal::declval<U>(), std::true_type()) test(int); void_t<>>::value>
test(int);
template <typename> template <typename> static std::false_type test(...);
static std::false_type test(...);
typedef decltype(test<T>(0)) result; using result = decltype(test<T>(0));
public: public:
static const bool value = result::value; static const bool value = result::value;
@ -73,65 +75,54 @@ class is_streamable {
// Write the content of buf to os. // Write the content of buf to os.
template <typename Char> template <typename Char>
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) { void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char *data = buf.data(); const Char* buf_data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
UnsignedStreamSize size = buf.size(); unsigned_streamsize size = buf.size();
UnsignedStreamSize max_size = unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do { do {
UnsignedStreamSize n = size <= max_size ? size : max_size; unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n)); os.write(buf_data, static_cast<std::streamsize>(n));
data += n; buf_data += n;
size -= n; size -= n;
} while (size != 0); } while (size != 0);
} }
template <typename Char, typename T> template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) { void format_value(buffer<Char>& buf, const T& value,
internal::formatbuf<Char> format_buf(buffer); locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_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.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; 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<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
typename std::enable_if<
internal::is_streamable<T, Char>::value &&
!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, Char> { : formatter<basic_string_view<Char>, Char> {
template <typename Context> 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; 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()); basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx); return formatter<basic_string_view<Char>, Char>::format(str, ctx);
} }
}; };
} // namespace internal
template <typename Char> template <typename Char>
inline void vprint(std::basic_ostream<Char> &os, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_string_view<Char> format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
} }
/** /**
\rst \rst
Prints formatted data to the stream *os*. 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"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args> template <typename S, typename... Args,
inline typename std::enable_if<internal::is_string<S>::value>::type typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str, void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
const Args & ... args) { vprint(os, to_string_view(format_str),
internal::checked_args<S, Args...> ca(format_str, args...); internal::make_args_checked<Args...>(format_str, args...));
vprint(os, to_string_view(format_str), *ca);
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -1,324 +1,2 @@
// A C++ interface to POSIX functions. #include "os.h"
// #warning "fmt/posix.h is deprecated; use fmt/os.h instead"
// 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_

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Formatting library for C++ - the core API // Formatting library for C++ - experimental range support
// //
// Copyright (c) 2012 - present, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.
@ -12,20 +12,21 @@
#ifndef FMT_RANGES_H_ #ifndef FMT_RANGES_H_
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include "format.h" #include <initializer_list>
#include <type_traits> #include <type_traits>
#include "format.h"
// output only up to N items from the range. // output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> template <typename Char> struct formatting_base {
struct formatting_base {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
}; };
@ -33,7 +34,8 @@ struct formatting_base {
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = 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 prefix;
Char delimiter; Char delimiter;
Char postfix; Char postfix;
@ -55,87 +57,75 @@ struct formatting_tuple : formatting_base<Char> {
namespace internal { namespace internal {
template <typename RangeT, typename OutputIterator> 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) for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it; *out++ = *it;
return out;
} }
template <typename OutputIterator> template <typename OutputIterator>
void copy(const char *str, OutputIterator out) { OutputIterator copy(const char* str, OutputIterator out) {
const char *p_curr = str; while (*str) *out++ = *str++;
while (*p_curr) { return out;
*out++ = *p_curr++;
}
} }
template <typename OutputIterator> template <typename OutputIterator>
void copy(char ch, OutputIterator out) { OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
return out;
} }
/// Return true value if T has std::string interface, like std::string_view. /// Return true value if T has std::string interface, like std::string_view.
template <typename T> template <typename T> class is_like_std_string {
class is_like_std_string {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(p->find('a'), p->length(), p->data(), int()); -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> template <typename> static void check(...);
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = 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> template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> template <typename... Ts> struct conditional_helper {};
struct conditional_helper {};
template <typename T, typename _ = void> template <typename T, typename _ = void> struct is_range_ : std::false_type {};
struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T> template <typename T>
struct is_range_<T, typename std::conditional< struct is_range_<
false, T, conditional_t<false,
conditional_helper<decltype(internal::declval<T>().begin()), conditional_helper<decltype(std::declval<T>().begin()),
decltype(internal::declval<T>().end())>, decltype(std::declval<T>().end())>,
void>::type> : std::true_type {}; void>> : std::true_type {};
#endif #endif
/// tuple_size and tuple_element check. /// tuple_size and tuple_element check.
template <typename T> template <typename T> class is_tuple_like_ {
class is_tuple_like_ {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
decltype(std::tuple_size<U>::value, template <typename> static void check(...);
internal::declval<typename std::tuple_element<0, U>::type>(), int());
template <typename>
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = 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 // Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
using index_sequence = std::index_sequence<N...>;
template <std::size_t N> template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>; using make_index_sequence = std::make_index_sequence<N>;
#else #else
template <typename T, T... N> template <typename T, T... N> struct integer_sequence {
struct integer_sequence { using value_type = T;
typedef T value_type;
static FMT_CONSTEXPR std::size_t size() { static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
return sizeof...(N);
}
}; };
template <std::size_t... N> template <std::size_t... N>
@ -151,7 +141,7 @@ using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> 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 std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; 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> template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
get_indexes(T const &) { return {}; } T const&) {
return {};
}
template <class Tuple, class F> template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
void for_each(Tuple &&tup, F &&f) {
const auto indexes = get_indexes(tup); const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
template<typename Arg> template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, typename std::decay<Arg>::type>::value)>
typename std::enable_if< FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " {}" : "{}"; return add_space ? " {}" : "{}";
} }
template<typename Arg> template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, typename std::decay<Arg>::type>::value)>
typename std::enable_if< FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
@ -186,61 +175,58 @@ 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*) { 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) { 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) { 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 } // namespace internal
template <typename T> template <typename T> struct is_tuple_like {
struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = 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> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> { private:
private:
// C++11 generic lambda for format() // C++11 generic lambda for format()
template <typename FormatContext> template <typename FormatContext> struct format_each {
struct format_each { template <typename T> void operator()(const T& v) {
template <typename T>
void operator()(const T& v) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
internal::copy(formatting.delimiter, out); out = internal::copy(formatting.delimiter, out);
} }
format_to(out, out = format_to(out,
internal::format_str_quoted( internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v), (formatting.add_delimiter_spaces && i > 0), v),
v); v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
std::size_t& i; 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; formatting_tuple<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext = format_context> 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(); auto out = ctx.out();
std::size_t i = 0; std::size_t i = 0;
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
@ -255,54 +241,147 @@ public:
} }
}; };
template <typename T> template <typename T, typename Char> struct is_range {
struct is_range {
static FMT_CONSTEXPR_DECL const bool value = 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> template <typename RangeT, typename Char>
struct formatter<RangeT, 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; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(const RangeT& values,
const RangeT &values, FormatContext &ctx) { FormatContext& ctx) {
auto out = ctx.out(); auto out = internal::copy(formatting.prefix, ctx.out());
internal::copy(formatting.prefix, out);
std::size_t i = 0; std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) { for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) *out++ = ' ';
*out++ = ' '; out = internal::copy(formatting.delimiter, out);
}
internal::copy(formatting.delimiter, out);
} }
format_to(out, out = format_to(out,
internal::format_str_quoted( internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it), (formatting.add_delimiter_spaces && i > 0), *it),
*it); *it);
if (++i > formatting.range_length_limit) { if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>"); out = format_to(out, " ... <other elements>");
break; break;
} }
} }
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) *out++ = ' ';
*out++ = ' '; return internal::copy(formatting.postfix, out);
}
internal::copy(formatting.postfix, out);
return ctx.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 FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@ -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_

View File

@ -8,51 +8,168 @@
#include "fmt/format-inl.h" #include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>; namespace internal {
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
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; template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char. // 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::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( template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args); 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);
template FMT_API std::string internal::vformat<char>( template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>); string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to( 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( template FMT_API int internal::snprintf_float(double, int,
double, internal::buffer &, core_format_specs); internal::float_specs,
template FMT_API void internal::sprintf_format( internal::buffer<char>&);
long double, internal::buffer &, core_format_specs); 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. // 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::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( template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t *, 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 std::wstring internal::vformat<wchar_t>( template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>); wstring_view, basic_format_args<wformat_context>);

316
externals/fmt/src/os.cc vendored Normal file
View 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

View File

@ -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

File diff suppressed because it is too large Load Diff

19
externals/fmt/support/Vagrantfile vendored Normal file
View 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

View File

@ -23,14 +23,17 @@ else:
# Add MSBuild 14.0 to PATH as described in # Add MSBuild 14.0 to PATH as described in
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc. # 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 os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
if image == 'Visual Studio 2013': if image == 'Visual Studio 2019':
generator = 'Visual Studio 12 2013' generator = 'Visual Studio 16 2019'
elif image == 'Visual Studio 2015': if platform == 'x64':
generator = 'Visual Studio 14 2015' cmake_command.extend(['-A', 'x64'])
elif image == 'Visual Studio 2017': else:
generator = 'Visual Studio 15 2017' if image == 'Visual Studio 2015':
if platform == 'x64': generator = 'Visual Studio 14 2015'
generator += ' Win64' elif image == 'Visual Studio 2017':
generator = 'Visual Studio 15 2017'
if platform == 'x64':
generator += ' Win64'
cmake_command.append('-G' + generator) cmake_command.append('-G' + generator)
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4'] build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
test_command = ['ctest', '-C', config] test_command = ['ctest', '-C', config]

View File

@ -4,20 +4,27 @@ configuration:
clone_depth: 1 clone_depth: 1
image:
- Visual Studio 2015
- Visual Studio 2019
- Visual Studio 2017
platform: platform:
- Win32 - Win32
- x64 - x64
image:
- Visual Studio 2013
- Visual Studio 2015
- Visual Studio 2017
environment: environment:
CTEST_OUTPUT_ON_FAILURE: 1 CTEST_OUTPUT_ON_FAILURE: 1
MSVC_DEFAULT_OPTIONS: ON MSVC_DEFAULT_OPTIONS: ON
BUILD: msvc BUILD: msvc
matrix:
exclude:
- image: Visual Studio 2015
platform: Win32
- image: Visual Studio 2019
platform: Win32
before_build: before_build:
- mkdir build - mkdir build
- cd build - cd build

View File

@ -9,10 +9,12 @@ buildscript {
// //
// https://developer.android.com/studio/releases/gradle-plugin // https://developer.android.com/studio/releases/gradle-plugin
// //
// Notice that 3.1.3 here is the version of [Android Gradle Plugin] // Notice that 3.3.0 here is the version of [Android Gradle Plugin]
// Accroding to URL above you will need Gradle 4.4 or higher // 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 { repositories {
@ -43,8 +45,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 21 // Android 5.0+ minSdkVersion 21 // Android 5.0+
targetSdkVersion 25 // Follow Compile SDK targetSdkVersion 25 // Follow Compile SDK
versionCode 20 // Follow release count versionCode 21 // Follow release count
versionName "5.2.1" // Follow Official version versionName "5.3.0" // Follow Official version
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {

View File

@ -48,43 +48,6 @@ endif ()
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG}) 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 if user-defined literals are available
check_cxx_source_compiles(" check_cxx_source_compiles("
void operator\"\" _udl(long double); void operator\"\" _udl(long double);
@ -94,4 +57,14 @@ if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF) set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif () 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 ) set(CMAKE_REQUIRED_FLAGS )

View File

@ -1,11 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@ prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: fmt Name: fmt
Description: A modern formatting library Description: A modern formatting library
Version: @FMT_VERSION@ Version: @FMT_VERSION@
Libs: -L${libdir} -lfmt Libs: -L${libdir} -l@FMT_LIB_NAME@
Cflags: -I${includedir} Cflags: -I${includedir}

View File

@ -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 %*

View File

@ -5,6 +5,9 @@
Usage: Usage:
manage.py release [<branch>] manage.py release [<branch>]
manage.py site 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 from __future__ import print_function
@ -134,7 +137,7 @@ def update_site(env):
if not os.path.exists(contents): if not os.path.exists(contents):
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents) os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
# Fix issues in reference.rst/api.rst. # 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) pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
with rewrite(os.path.join(target_doc_dir, filename)) as b: with rewrite(os.path.join(target_doc_dir, filename)) as b:
b.data = b.data.replace('std::ostream &', 'std::ostream&') 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('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned') b.data = b.data.replace('unsigned int', 'unsigned')
b.data = b.data.replace('operator""_', 'operator"" _') 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. # Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst') index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b: with rewrite(index) as b:

View File

@ -1,2 +1,2 @@
If you are not redirected automatically, follow the 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/>`_.

View File

@ -2,15 +2,15 @@
{% block extrahead %} {% block extrahead %}
<meta charset="UTF-8"> <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"> <script type="text/javascript">
window.location.href = "http://fmtlib.net/latest/" window.location.href = "https://fmt.dev/latest/"
</script> </script>
<title>Page Redirection</title> <title>Page Redirection</title>
{% endblock %} {% endblock %}
{% block document %} {% 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 %} {% endblock %}
{% block footer %} {% block footer %}

View File

@ -83,19 +83,25 @@ install_dir = os.path.join(fmt_dir, "_install")
build_dir = os.path.join(fmt_dir, "_build") build_dir = os.path.join(fmt_dir, "_build")
test_build_dir = os.path.join(fmt_dir, "_build_test") test_build_dir = os.path.join(fmt_dir, "_build_test")
# Configure library. # Configure the library.
makedirs_if_not_exist(build_dir) makedirs_if_not_exist(build_dir)
cmake_flags = [ cmake_flags = [
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build, '-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
'-DCMAKE_CXX_STANDARD=' + standard '-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] + 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. # Build the library.
check_call(['make', '-j4'], cwd=build_dir) check_call(['cmake', '--build','.'], cwd=build_dir)
# Test library. # Test the library.
env = os.environ.copy() env = os.environ.copy()
env['CTEST_OUTPUT_ON_FAILURE'] = '1' env['CTEST_OUTPUT_ON_FAILURE'] = '1'
if call(['make', 'test'], env=env, cwd=build_dir): 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()) print(f.read())
sys.exit(-1) sys.exit(-1)
# Install library. # Install the library.
check_call(['make', 'install'], cwd=build_dir) check_call(['make', 'install'], cwd=build_dir)
# Test installation. # Test installation.

View File

@ -17,13 +17,18 @@ else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0) target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif () endif ()
if (NOT SUPPORTS_VARIADIC_TEMPLATES OR NOT SUPPORTS_INITIALIZER_LIST) target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
# Workaround a bug in implementation of variadic templates in MSVC11.
if (MSVC) if (MSVC)
# Workaround a bug in implementation of variadic templates in MSVC11.
target_compile_definitions(gmock PUBLIC _VARIADIC_MAX=10) 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 () endif ()
# GTest doesn't detect <tuple> with clang. # GTest doesn't detect <tuple> with clang.
@ -33,15 +38,13 @@ endif ()
# Silence MSVC tr1 deprecation warning in gmock. # Silence MSVC tr1 deprecation warning in gmock.
target_compile_definitions(gmock target_compile_definitions(gmock
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=0) PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1)
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Build the actual library tests # Build the actual library tests
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc) set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
add_library(test-main STATIC ${TEST_MAIN_SRC}) add_library(test-main STATIC ${TEST_MAIN_SRC})
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_include_directories(test-main SYSTEM PUBLIC gtest gmock) target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
target_link_libraries(test-main gmock fmt) target_link_libraries(test-main gmock fmt)
@ -74,44 +77,53 @@ function(add_fmt_test name)
target_link_libraries(${name} test-main) target_link_libraries(${name} test-main)
# Define if certain C++ features can be used. # Define if certain C++ features can be used.
target_compile_definitions(${name} PRIVATE
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
FMT_USE_ENUM_BASE=$<BOOL:${SUPPORTS_ENUM_BASE}>)
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
if (FMT_WERROR)
target_compile_options(${name} PRIVATE ${WERROR_FLAG})
endif ()
target_include_directories(${name} SYSTEM PUBLIC gtest gmock) target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
add_test(NAME ${name} COMMAND ${name}) add_test(NAME ${name} COMMAND ${name})
endfunction() endfunction()
add_fmt_test(assert-test) add_fmt_test(assert-test)
add_fmt_test(chrono-test) add_fmt_test(chrono-test)
add_fmt_test(color-test)
add_fmt_test(core-test) add_fmt_test(core-test)
add_fmt_test(grisu-test)
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
add_fmt_test(gtest-extra-test) add_fmt_test(gtest-extra-test)
add_fmt_test(format-test mock-allocator.h) add_fmt_test(format-test mock-allocator.h)
if (MSVC)
target_compile_options(format-test PRIVATE /bigobj)
endif ()
if (NOT (MSVC AND BUILD_SHARED_LIBS)) if (NOT (MSVC AND BUILD_SHARED_LIBS))
add_fmt_test(format-impl-test) add_fmt_test(format-impl-test)
endif () endif ()
add_fmt_test(locale-test) add_fmt_test(locale-test)
add_fmt_test(ostream-test) add_fmt_test(ostream-test)
add_fmt_test(compile-test)
add_fmt_test(printf-test) add_fmt_test(printf-test)
add_fmt_test(time-test)
add_fmt_test(custom-formatter-test) add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-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 add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC}) posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories( target_include_directories(
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include) posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock) target_link_libraries(posix-mock-test gmock)
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock) target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
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_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(posix-test) add_fmt_test(os-test)
endif () endif ()
add_fmt_executable(header-only-test add_fmt_executable(header-only-test
@ -126,41 +138,41 @@ else ()
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1) target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
endif () 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}") message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
# Test that the library compiles without windows.h. # MSVC fails to compile GMock when C++17 is enabled.
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (FMT_HAS_VARIANT AND NOT MSVC)
add_library(no-windows-h-test ../src/format.cc) add_fmt_test(std-format-test)
target_include_directories( set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
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)
endif () 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 --build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-test" "${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
"${CMAKE_CURRENT_BINARY_DIR}/compile-test" "${CMAKE_CURRENT_BINARY_DIR}/compile-error-test"
--build-generator ${CMAKE_GENERATOR} --build-generator ${CMAKE_GENERATOR}
--build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options --build-options
@ -202,3 +214,23 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif () endif ()
# Activate optional CUDA tests if CUDA is found. For version selection see
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
if (FMT_CUDA_TEST)
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()
endif ()

View File

@ -8,15 +8,21 @@
#include "fmt/core.h" #include "fmt/core.h"
#include "gtest.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) { TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED( #if GTEST_HAS_DEATH_TEST
FMT_ASSERT(false, "don't panic!"), "don't panic!"); 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);
} }

View File

@ -5,6 +5,10 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#ifdef WIN32
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "gtest-extra.h" #include "gtest-extra.h"
@ -34,21 +38,71 @@ std::tm make_second(int s) {
return time; return time;
} }
std::string format_tm(const std::tm &time, const char *spec, std::string format_tm(const std::tm& time, const char* spec,
const std::locale &loc) { const std::locale& loc) {
auto &facet = std::use_facet<std::time_put<char>>(loc); auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os; std::ostringstream os;
os.imbue(loc); os.imbue(loc);
facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec)); facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec));
return os.str(); return os.str();
} }
#define EXPECT_TIME(spec, time, duration) { \ TEST(TimeTest, Format) {
std::locale loc("ja_JP.utf8"); \ std::tm tm = std::tm();
EXPECT_EQ(format_tm(time, spec, loc), \ 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)); \ fmt::format(loc, "{:" spec "}", duration)); \
} }
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
TEST(ChronoTest, FormatDefault) { TEST(ChronoTest, FormatDefault) {
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42as", EXPECT_EQ("42as",
@ -83,12 +137,54 @@ TEST(ChronoTest, FormatDefault) {
fmt::format("{}", std::chrono::duration<int, std::exa>(42))); fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42))); EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
EXPECT_EQ("42[15]s", EXPECT_EQ(
fmt::format("{}", "42[15]s",
std::chrono::duration<int, std::ratio<15, 1>>(42))); fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ("42[15/4]s", EXPECT_EQ(
fmt::format("{}", "42[15/4]s",
std::chrono::duration<int, std::ratio<15, 4>>(42))); 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) { TEST(ChronoTest, Align) {
@ -103,7 +199,8 @@ TEST(ChronoTest, Align) {
fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345))); fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("~~03:25:45~~", EXPECT_EQ("~~03:25:45~~",
fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345))); 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) { TEST(ChronoTest, FormatSpecs) {
@ -131,6 +228,8 @@ TEST(ChronoTest, FormatSpecs) {
fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25", fmt::format("{:%R}", 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("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) { 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("{:%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("{:%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, EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error,
"invalid format"); "invalid format");
EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error, EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error,
@ -160,13 +257,14 @@ TEST(ChronoTest, InvalidSpecs) {
} }
TEST(ChronoTest, Locale) { TEST(ChronoTest, Locale) {
const char *loc_name = "ja_JP.utf8"; const char* loc_name = "ja_JP.utf8";
bool has_locale = false; bool has_locale = false;
std::locale loc; std::locale loc;
try { try {
loc = std::locale(loc_name); loc = std::locale(loc_name);
has_locale = true; has_locale = true;
} catch (const std::runtime_error &) {} } catch (const std::runtime_error&) {
}
if (!has_locale) { if (!has_locale) {
fmt::print("{} locale is missing.\n", loc_name); fmt::print("{} locale is missing.\n", loc_name);
return; return;
@ -183,3 +281,106 @@ TEST(ChronoTest, Locale) {
EXPECT_TIME("%r", time, sec); EXPECT_TIME("%r", time, sec);
EXPECT_TIME("%p", 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
View 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
View 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), "");
}

View File

@ -11,17 +11,16 @@
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <memory>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <memory>
#include "test-assert.h"
#include "gmock.h" #include "gmock.h"
#include "test-assert.h"
// Check if fmt/core.h compiles with windows.h included before it. // Check if fmt/core.h compiles with windows.h included before it.
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif #endif
#include "fmt/core.h" #include "fmt/core.h"
@ -30,9 +29,9 @@
#undef max #undef max
using fmt::basic_format_arg; using fmt::basic_format_arg;
using fmt::internal::basic_buffer;
using fmt::internal::value;
using fmt::string_view; using fmt::string_view;
using fmt::internal::buffer;
using fmt::internal::value;
using testing::_; using testing::_;
using testing::StrictMock; using testing::StrictMock;
@ -42,24 +41,23 @@ namespace {
struct test_struct {}; struct test_struct {};
template <typename Context, typename T> 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); return fmt::internal::make_arg<Context>(value);
} }
} // namespace } // namespace
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> template <typename Char> struct formatter<test_struct, Char> {
struct formatter<test_struct, Char> {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return 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()) { -> decltype(ctx.out()) {
const Char *test = "test"; const Char* test = "test";
return std::copy_n(test, std::strlen(test), ctx.out()); 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 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(BufferTest, Noncopyable) { TEST(BufferTest, Noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char> >::value); EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
#if !FMT_MSC_VER # if !FMT_MSC_VER
// std::is_copy_assignable is broken in MSVC2013. // std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_copy_assignable<basic_buffer<char> >::value); EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
#endif # endif
} }
TEST(BufferTest, Nonmoveable) { TEST(BufferTest, Nonmoveable) {
EXPECT_FALSE(std::is_move_constructible<basic_buffer<char> >::value); EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
#if !FMT_MSC_VER # if !FMT_MSC_VER
// std::is_move_assignable is broken in MSVC2013. // std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value); EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
#endif # endif
} }
#endif #endif
// A test buffer with a dummy grow method. // A test buffer with a dummy grow method.
template <typename T> template <typename T> struct test_buffer : buffer<T> {
struct test_buffer : basic_buffer<T> { void grow(std::size_t capacity) { this->set(nullptr, capacity); }
void grow(std::size_t capacity) { this->set(FMT_NULL, capacity); }
}; };
template <typename T> template <typename T> struct mock_buffer : buffer<T> {
struct mock_buffer : basic_buffer<T> { MOCK_METHOD1(do_grow, void(std::size_t capacity));
MOCK_METHOD1(do_grow, void (std::size_t capacity));
void grow(std::size_t capacity) { void grow(std::size_t capacity) {
this->set(this->data(), capacity); this->set(this->data(), capacity);
@ -99,14 +95,14 @@ struct mock_buffer : basic_buffer<T> {
} }
mock_buffer() {} mock_buffer() {}
mock_buffer(T *data) { this->set(data, 0); } mock_buffer(T* data) { this->set(data, 0); }
mock_buffer(T *data, std::size_t capacity) { this->set(data, capacity); } mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); }
}; };
TEST(BufferTest, Ctor) { TEST(BufferTest, Ctor) {
{ {
mock_buffer<int> buffer; 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.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity()); EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
} }
@ -130,13 +126,18 @@ TEST(BufferTest, Ctor) {
struct dying_buffer : test_buffer<int> { struct dying_buffer : test_buffer<int> {
MOCK_METHOD0(die, void()); MOCK_METHOD0(die, void());
~dying_buffer() { die(); } ~dying_buffer() { die(); }
private:
virtual void avoid_weak_vtable();
}; };
void dying_buffer::avoid_weak_vtable() {}
TEST(BufferTest, VirtualDtor) { TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer; 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()); EXPECT_CALL(*mock_buffer, die());
basic_buffer<int> *buffer = mock_buffer; buffer<int>* buffer = mock_buffer;
delete buffer; delete buffer;
} }
@ -147,7 +148,7 @@ TEST(BufferTest, Access) {
EXPECT_EQ(11, buffer[0]); EXPECT_EQ(11, buffer[0]);
buffer[3] = 42; buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3)); 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]); EXPECT_EQ(42, const_buffer[3]);
} }
@ -182,7 +183,7 @@ TEST(BufferTest, Clear) {
TEST(BufferTest, Append) { TEST(BufferTest, Append) {
char data[15]; char data[15];
mock_buffer<char> buffer(data, 10); mock_buffer<char> buffer(data, 10);
const char *test = "test"; const char* test = "test";
buffer.append(test, test + 5); buffer.append(test, test + 5);
EXPECT_STREQ(test, &buffer[0]); EXPECT_STREQ(test, &buffer[0]);
EXPECT_EQ(5u, buffer.size()); EXPECT_EQ(5u, buffer.size());
@ -197,7 +198,7 @@ TEST(BufferTest, Append) {
TEST(BufferTest, AppendAllocatesEnoughStorage) { TEST(BufferTest, AppendAllocatesEnoughStorage) {
char data[19]; char data[19];
mock_buffer<char> buffer(data, 10); mock_buffer<char> buffer(data, 10);
const char *test = "abcdefgh"; const char* test = "abcdefgh";
buffer.resize(10); buffer.resize(10);
EXPECT_CALL(buffer, do_grow(19)); EXPECT_CALL(buffer, do_grow(19));
buffer.append(test, test + 9); buffer.append(test, test + 9);
@ -211,35 +212,31 @@ TEST(ArgTest, FormatArgs) {
struct custom_context { struct custom_context {
typedef char char_type; typedef char char_type;
template <typename T> template <typename T> struct formatter_type {
struct formatter_type { template <typename ParseContext>
struct type { auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
template <typename ParseContext> return ctx.begin();
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { }
return ctx.begin();
}
const char *format(const T &, custom_context& ctx) { const char* format(const T&, custom_context& ctx) {
ctx.called = true; ctx.called = true;
return FMT_NULL; return nullptr;
} }
};
}; };
bool called; bool called;
fmt::format_parse_context ctx;
fmt::format_parse_context parse_context() { fmt::format_parse_context& parse_context() { return ctx; }
return fmt::format_parse_context(""); void advance_to(const char*) {}
}
void advance_to(const char *) {}
}; };
TEST(ArgTest, MakeValueWithCustomContext) { TEST(ArgTest, MakeValueWithCustomContext) {
test_struct t; test_struct t;
fmt::internal::value<custom_context> arg = fmt::internal::value<custom_context> arg(
fmt::internal::make_value<custom_context>(t); fmt::internal::arg_mapper<custom_context>().map(t));
custom_context ctx = {false}; custom_context ctx = {false, fmt::format_parse_context("")};
arg.custom.format(&t, ctx); arg.custom.format(&t, ctx.parse_context(), ctx);
EXPECT_TRUE(ctx.called); EXPECT_TRUE(ctx.called);
} }
@ -249,40 +246,35 @@ template <typename Char>
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) { bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
return lhs.value == rhs.value; return lhs.value == rhs.value;
} }
} } // namespace internal
FMT_END_NAMESPACE FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable // Use a unique result type to make sure that there are no undesirable
// conversions. // conversions.
struct test_result {}; struct test_result {};
template <typename T> template <typename T> struct mock_visitor {
struct mock_visitor { template <typename U> struct result { typedef test_result type; };
template <typename U>
struct result { typedef test_result type; };
mock_visitor() { mock_visitor() {
ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result())); ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
} }
MOCK_METHOD1_T(visit, test_result (T value)); MOCK_METHOD1_T(visit, test_result(T value));
MOCK_METHOD0_T(unexpected, void ()); MOCK_METHOD0_T(unexpected, void());
test_result operator()(T value) { return visit(value); } test_result operator()(T value) { return visit(value); }
template <typename U> template <typename U> test_result operator()(U) {
test_result operator()(U) {
unexpected(); unexpected();
return test_result(); return test_result();
} }
}; };
template <typename T> template <typename T> struct visit_type { typedef T Type; };
struct visit_type { typedef T Type; };
#define VISIT_TYPE(Type_, visit_type_) \ #define VISIT_TYPE(Type_, visit_type_) \
template <> \ template <> struct visit_type<Type_> { typedef visit_type_ Type; }
struct visit_type<Type_> { typedef visit_type_ Type; }
VISIT_TYPE(signed char, int); VISIT_TYPE(signed char, int);
VISIT_TYPE(unsigned char, unsigned); VISIT_TYPE(unsigned char, unsigned);
@ -297,30 +289,30 @@ VISIT_TYPE(long, long long);
VISIT_TYPE(unsigned long, unsigned long long); VISIT_TYPE(unsigned long, unsigned long long);
#endif #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) { \ #define CHECK_ARG(value, typename_) \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \ { \
EXPECT_CALL(visitor, visit(expected)); \ typedef decltype(value) value_type; \
typedef std::back_insert_iterator<basic_buffer<Char>> iterator; \ typename_ visit_type<value_type>::Type expected = value; \
fmt::visit(visitor, \ CHECK_ARG_(char, expected, value) \
make_arg<fmt::basic_format_context<iterator, Char>>(value)); \ CHECK_ARG_(wchar_t, expected, value) \
} }
#define CHECK_ARG(value, typename_) { \ template <typename T> class NumericArgTest : public testing::Test {};
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> typedef ::testing::Types<bool, signed char, unsigned char, signed,
class NumericArgTest : public testing::Test {}; unsigned short, int, unsigned, long, unsigned long,
long long, unsigned long long, float, double,
typedef ::testing::Types< long double>
bool, signed char, unsigned char, signed, unsigned short, Types;
int, unsigned, long, unsigned long, long long, unsigned long long,
float, double, long double> Types;
TYPED_TEST_CASE(NumericArgTest, Types); TYPED_TEST_CASE(NumericArgTest, Types);
template <typename T> template <typename T>
@ -330,7 +322,7 @@ typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
template <typename T> template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type typename std::enable_if<std::is_floating_point<T>::value, T>::type
test_value() { test_value() {
return static_cast<T>(4.2); return static_cast<T>(4.2);
} }
@ -348,8 +340,8 @@ TEST(ArgTest, CharArg) {
TEST(ArgTest, StringArg) { TEST(ArgTest, StringArg) {
char str_data[] = "test"; char str_data[] = "test";
char *str = str_data; char* str = str_data;
const char *cstr = str; const char* cstr = str;
CHECK_ARG_(char, cstr, str); CHECK_ARG_(char, cstr, str);
string_view sref(str); string_view sref(str);
@ -358,8 +350,8 @@ TEST(ArgTest, StringArg) {
TEST(ArgTest, WStringArg) { TEST(ArgTest, WStringArg) {
wchar_t str_data[] = L"test"; wchar_t str_data[] = L"test";
wchar_t *str = str_data; wchar_t* str = str_data;
const wchar_t *cstr = str; const wchar_t* cstr = str;
fmt::wstring_view sref(str); fmt::wstring_view sref(str);
CHECK_ARG_(wchar_t, cstr, str); CHECK_ARG_(wchar_t, cstr, str);
@ -369,8 +361,8 @@ TEST(ArgTest, WStringArg) {
} }
TEST(ArgTest, PointerArg) { TEST(ArgTest, PointerArg) {
void *p = FMT_NULL; void* p = nullptr;
const void *cp = FMT_NULL; const void* cp = nullptr;
CHECK_ARG_(char, cp, p); CHECK_ARG_(char, cp, p);
CHECK_ARG_(wchar_t, cp, p); CHECK_ARG_(wchar_t, cp, p);
CHECK_ARG(cp, ); CHECK_ARG(cp, );
@ -379,14 +371,15 @@ TEST(ArgTest, PointerArg) {
struct check_custom { struct check_custom {
test_result operator()( test_result operator()(
fmt::basic_format_arg<fmt::format_context>::handle h) const { 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]; 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) {} void grow(std::size_t) {}
} buffer; } buffer;
fmt::internal::basic_buffer<char> &base = buffer; fmt::internal::buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); fmt::format_parse_context parse_ctx("");
h.format(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())); EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
return test_result(); return test_result();
} }
@ -395,30 +388,135 @@ struct check_custom {
TEST(ArgTest, CustomArg) { TEST(ArgTest, CustomArg) {
test_struct test; test_struct test;
typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle> typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
visitor; visitor;
testing::StrictMock<visitor> v; testing::StrictMock<visitor> v;
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom())); 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) { TEST(ArgTest, VisitInvalidArg) {
testing::StrictMock< mock_visitor<fmt::monostate> > visitor; testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
EXPECT_CALL(visitor, visit(_)); EXPECT_CALL(visitor, visit(_));
fmt::basic_format_arg<fmt::format_context> arg; 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(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"; char str[100] = "some string";
EXPECT_EQ(std::strlen(str), string_view(str).size()); EXPECT_EQ(std::strlen(str), string_view(str).size());
EXPECT_LT(std::strlen(str), sizeof(str)); EXPECT_LT(std::strlen(str), sizeof(str));
} }
// Check string_view's comparison operator. // Check string_view's comparison operator.
template <template <typename> class Op> template <template <typename> class Op> void check_op() {
void check_op() { const char* inputs[] = {"foo", "fop", "fo"};
const char *inputs[] = {"foo", "fop", "fo"};
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs); std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (std::size_t i = 0; i < num_inputs; ++i) { for (std::size_t i = 0; i < num_inputs; ++i) {
for (std::size_t j = 0; j < num_inputs; ++j) { for (std::size_t j = 0; j < num_inputs; ++j) {
@ -442,63 +540,105 @@ TEST(StringViewTest, Compare) {
check_op<std::greater_equal>(); 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) { FMT_BEGIN_NAMESPACE
EXPECT_FALSE((fmt::convert_to_int<char, char>::value)); template <> struct formatter<enabled_formatter> {
EXPECT_FALSE((fmt::convert_to_int<const char *, char>::value)); auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
EXPECT_TRUE((fmt::convert_to_int<basic_enum, char>::value)); 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) { struct convertible_to_c_string {
EXPECT_TRUE((fmt::convert_to_int<enum_with_underlying_type, char>::value)); 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 { namespace my_ns {
template <typename Char> template <typename Char> class my_string {
class my_string {
public: public:
my_string(const Char *s) : s_(s) {} my_string(const Char* s) : s_(s) {}
const Char * data() const FMT_NOEXCEPT { return s_.data(); } const Char* data() const FMT_NOEXCEPT { return s_.data(); }
std::size_t length() const FMT_NOEXCEPT { return s_.size(); } std::size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); } operator const Char*() const { return s_.c_str(); }
private: private:
std::basic_string<Char> s_; std::basic_string<Char> s_;
}; };
template <typename Char> template <typename Char>
inline fmt::basic_string_view<Char> inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
to_string_view(const my_string<Char> &s) FMT_NOEXCEPT { FMT_NOEXCEPT {
return { s.data(), s.length() }; return {s.data(), s.length()};
} }
struct non_string {}; struct non_string {};
} } // namespace my_ns
namespace FakeQt { namespace FakeQt {
class QString { class QString {
public: public:
QString(const wchar_t *s) : s_(std::make_shared<std::wstring>(s)) {} QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
const wchar_t *utf16() const FMT_NOEXCEPT { return s_->data(); } const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); } 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: private:
std::shared_ptr<std::wstring> s_; std::shared_ptr<std::wstring> s_;
}; };
inline fmt::basic_string_view<wchar_t> to_string_view( inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
const QString &s) FMT_NOEXCEPT { FMT_NOEXCEPT {
return {s.utf16(), return {s.utf16(), static_cast<std::size_t>(s.size())};
static_cast<std::size_t>(s.size())};
}
} }
} // namespace FakeQt
template <typename T> template <typename T> class IsStringTest : public testing::Test {};
class IsStringTest : public testing::Test {};
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes; typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
TYPED_TEST_CASE(IsStringTest, StringCharTypes); TYPED_TEST_CASE(IsStringTest, StringCharTypes);
@ -506,30 +646,30 @@ TYPED_TEST_CASE(IsStringTest, StringCharTypes);
namespace { namespace {
template <typename Char> template <typename Char>
struct derived_from_string_view : fmt::basic_string_view<Char> {}; struct derived_from_string_view : fmt::basic_string_view<Char> {};
} } // namespace
TYPED_TEST(IsStringTest, IsString) { TYPED_TEST(IsStringTest, IsString) {
EXPECT_TRUE((fmt::internal::is_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<const TypeParam*>::value);
EXPECT_TRUE((fmt::internal::is_string<TypeParam[2]>::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<const TypeParam[2]>::value);
EXPECT_TRUE((fmt::internal::is_string<std::basic_string<TypeParam>>::value)); EXPECT_TRUE(fmt::internal::is_string<std::basic_string<TypeParam>>::value);
EXPECT_TRUE( EXPECT_TRUE(
(fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value)); fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value);
EXPECT_TRUE( EXPECT_TRUE(
(fmt::internal::is_string<derived_from_string_view<TypeParam>>::value)); fmt::internal::is_string<derived_from_string_view<TypeParam>>::value);
#ifdef FMT_STRING_VIEW using string_view = fmt::internal::std_string_view<TypeParam>;
EXPECT_TRUE((fmt::internal::is_string<FMT_STRING_VIEW<TypeParam>>::value)); EXPECT_TRUE(std::is_empty<string_view>::value !=
#endif fmt::internal::is_string<string_view>::value);
EXPECT_TRUE((fmt::internal::is_string<my_ns::my_string<TypeParam>>::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_FALSE(fmt::internal::is_string<my_ns::non_string>::value);
EXPECT_TRUE((fmt::internal::is_string<FakeQt::QString>::value)); EXPECT_TRUE(fmt::internal::is_string<FakeQt::QString>::value);
} }
TEST(CoreTest, Format) { TEST(CoreTest, Format) {
// This should work without including fmt/format.h. // This should work without including fmt/format.h.
#ifdef 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 #endif
EXPECT_EQ(fmt::format("{}", 42), "42"); EXPECT_EQ(fmt::format("{}", 42), "42");
} }
@ -537,7 +677,7 @@ TEST(CoreTest, Format) {
TEST(CoreTest, FormatTo) { TEST(CoreTest, FormatTo) {
// This should work without including fmt/format.h. // This should work without including fmt/format.h.
#ifdef 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 #endif
std::string s; std::string s;
fmt::format_to(std::back_inserter(s), "{}", 42); 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(my_string<wchar_t>(L"42")), L"42");
EXPECT_EQ(to_string_view(QString(L"42")), L"42"); EXPECT_EQ(to_string_view(QString(L"42")), L"42");
fmt::internal::type type = fmt::internal::type type =
fmt::internal::get_type<fmt::format_context, my_string<char>>::value; fmt::internal::mapped_type_constant<my_string<char>,
EXPECT_EQ(type, fmt::internal::string_type); 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 = type =
fmt::internal::get_type<fmt::wformat_context, my_string<wchar_t>>::value; fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::string_type); EXPECT_EQ(type, fmt::internal::type::string_type);
type = fmt::internal::get_type<fmt::wformat_context, QString>::value;
EXPECT_EQ(type, fmt::internal::string_type);
// Does not compile: only wide format contexts are compatible with QString! // 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) { TEST(CoreTest, FormatForeignStrings) {
@ -572,6 +715,10 @@ TEST(CoreTest, FormatForeignStrings) {
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42"); 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 { struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; } operator fmt::string_view() const { return "foo"; }
}; };
@ -581,7 +728,7 @@ TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
} }
// std::is_constructible is broken in MSVC until version 2015. // 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 { struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; } 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())); 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 { struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; } explicit operator fmt::wstring_view() const { return L"foo"; }
}; };
@ -598,16 +756,15 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo", EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view())); fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
} }
#endif
struct explicitly_convertible_to_string_like { struct disabled_rvalue_conversion {
template < operator const char*() const& { return "foo"; }
typename String, operator const char*() & { return "foo"; }
typename = typename std::enable_if< operator const char*() const&& = delete;
std::is_constructible<String, const char*, std::size_t>::value>::type> operator const char*() && = delete;
FMT_EXPLICIT operator String() const { return String("foo", 3u); }
}; };
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) { TEST(FormatterTest, DisabledRValueConversion) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like())); EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
} }
#endif

View 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
View 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);
}

View 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);
}

View File

@ -5,6 +5,10 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/format.h" #include "fmt/format.h"
#include "gtest-extra.h" #include "gtest-extra.h"
@ -13,22 +17,22 @@
// A custom argument formatter that doesn't print `-` for floating-point values // A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0. // rounded to 0.
class custom_arg_formatter : class custom_arg_formatter
public fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>> { : public fmt::arg_formatter<fmt::buffer_range<char>> {
public: public:
typedef fmt::back_insert_range<fmt::internal::buffer> range; using range = fmt::buffer_range<char>;
typedef fmt::arg_formatter<range> base; typedef fmt::arg_formatter<range> base;
custom_arg_formatter( custom_arg_formatter(fmt::format_context& ctx,
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL) fmt::format_parse_context* parse_ctx,
: base(ctx, s) {} fmt::format_specs* s = nullptr)
: base(ctx, parse_ctx, s) {}
using base::operator(); using base::operator();
iterator operator()(double value) { iterator operator()(double value) {
// Comparing a float to 0.0 is safe. // Comparing a float to 0.0 is safe.
if (round(value * pow(10, spec()->precision)) == 0.0) if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
value = 0;
return base::operator()(value); return base::operator()(value);
} }
}; };
@ -41,7 +45,7 @@ std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
} }
template <typename... 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...); auto va = fmt::make_format_args(args...);
return custom_vformat(format_str, va); return custom_vformat(format_str, va);
} }

861
externals/fmt/test/format vendored Normal file
View 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_

View File

@ -0,0 +1,6 @@
// Copyright (c) 2020 Vladimir Solontsov
// SPDX-License-Identifier: MIT Licence
#include <fmt/core.h>
#include "gtest-extra.h"

View File

@ -10,75 +10,243 @@
#include "test-assert.h" #include "test-assert.h"
// Include format.cc instead of format.h to test implementation. // 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 <algorithm>
#include <cstring> #include <cstring>
#include "../src/format.cc"
#include "fmt/printf.h"
#include "gmock.h" #include "gmock.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
#undef min
#undef max #undef max
#if FMT_HAS_CPP_ATTRIBUTE(noreturn) using fmt::internal::bigint;
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
using fmt::internal::fp; using fmt::internal::fp;
using fmt::internal::max_value;
template <bool is_iec559> static_assert(!std::is_copy_constructible<bigint>::value, "");
void test_construct_from_double() { 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"); fmt::print("warning: double is not IEC559, skipping FP tests\n");
} }
template <> template <> void run_double_tests<true>() {
void test_construct_from_double<true>() { // Construct from double.
auto v = fp(1.23); EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -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(FPTest, DoubleTests) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>(); run_double_tests<std::numeric_limits<double>::is_iec559>();
} }
TEST(FPTest, Normalize) { TEST(FPTest, Normalize) {
auto v = fp(0xbeef, 42); const auto v = fp(0xbeef, 42);
v.normalize(); auto normalized = normalize(v);
EXPECT_EQ(0xbeef000000000000, v.f); EXPECT_EQ(0xbeef000000000000, normalized.f);
EXPECT_EQ(-6, v.e); EXPECT_EQ(-6, normalized.e);
} }
TEST(FPTest, ComputeBoundariesSubnormal) { TEST(FPTest, ComputeFloatBoundaries) {
auto v = fp(0xbeef, 42); struct {
fp lower, upper; double x, lower, upper;
v.compute_boundaries(lower, upper); } tests[] = {
EXPECT_EQ(0xbeee800000000000, lower.f); // regular
EXPECT_EQ(-6, lower.e); {1.5f, 1.4999999403953552, 1.5000000596046448},
EXPECT_EQ(0xbeef800000000000, upper.f); // boundary
EXPECT_EQ(-6, upper.e); {1.0f, 0.9999999701976776, 1.0000000596046448},
} // min normal
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
TEST(FPTest, ComputeBoundaries) { // max subnormal
auto v = fp(0x10000000000000, 42); {1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
fp lower, upper; // min subnormal
v.compute_boundaries(lower, upper); {1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
EXPECT_EQ(0x7ffffffffffffe00, lower.f); };
EXPECT_EQ(31, lower.e); for (auto test : tests) {
EXPECT_EQ(0x8000000000000400, upper.f); fp vlower = normalize(fp(test.lower));
EXPECT_EQ(31, upper.e); fp vupper = normalize(fp(test.upper));
} vlower.f >>= vupper.e - vlower.e;
vlower.e = vupper.e;
TEST(FPTest, Subtract) { fp value;
auto v = fp(123, 1) - fp(102, 1); auto b = value.assign_float_with_boundaries(test.x);
EXPECT_EQ(v.f, 21u); EXPECT_EQ(vlower.f, b.lower);
EXPECT_EQ(v.e, 1); EXPECT_EQ(vupper.f, b.upper);
}
} }
TEST(FPTest, Multiply) { TEST(FPTest, Multiply) {
@ -98,33 +266,87 @@ TEST(FPTest, GetCachedPower) {
EXPECT_LE(exp, fp.e); EXPECT_LE(exp, fp.e);
int dec_exp_step = 8; int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10)); 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; 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> template <typename T> struct value_extractor {
struct ValueExtractor: fmt::internal::function<T> { T operator()(T value) { return value; }
T operator()(T value) {
return value;
}
template <typename U> template <typename U> FMT_NORETURN T operator()(U) {
FMT_NORETURN T operator()(U) {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name())); 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) { 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); auto arg = fmt::internal::make_arg<fmt::format_context>(value);
visit(fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'), fmt::visit_format_arg(
arg); fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
EXPECT_EQ(value, visit(ValueExtractor<long long>(), arg)); arg);
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
} }
TEST(FormatTest, FormatNegativeNaN) { TEST(FormatTest, FormatNegativeNaN) {
@ -136,11 +358,11 @@ TEST(FormatTest, FormatNegativeNaN) {
} }
TEST(FormatTest, StrError) { TEST(FormatTest, StrError) {
char *message = FMT_NULL; char* message = nullptr;
char buffer[BUFFER_SIZE]; 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"); "invalid buffer");
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0), EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer"); "invalid buffer");
buffer[0] = 'x'; buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__) #if defined(_GNU_SOURCE) && !defined(__COVERITY__)
@ -151,17 +373,19 @@ TEST(FormatTest, StrError) {
int error_code = EDOM; int error_code = EDOM;
#endif #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); EXPECT_EQ(result, 0);
std::size_t message_size = std::strlen(message); std::size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size); EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(get_system_error(error_code), message); EXPECT_EQ(get_system_error(error_code), message);
// safe_strerror never uses buffer on MinGW. // safe_strerror never uses buffer on MinGW.
#ifndef __MINGW32__ #if !defined(__MINGW32__) && !defined(__sun)
result = fmt::safe_strerror(error_code, message = buffer, message_size); result =
fmt::internal::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result); 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(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result); EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message); EXPECT_STREQ("", message);
@ -173,14 +397,14 @@ TEST(FormatTest, FormatErrorCode) {
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
format_to(buffer, "garbage"); 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)); EXPECT_EQ("test: " + msg, to_string(buffer));
} }
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
std::string prefix( std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x'); 'x');
fmt::format_error_code(buffer, 42, prefix); fmt::internal::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer)); EXPECT_EQ(msg, to_string(buffer));
} }
int codes[] = {42, -1}; int codes[] = {42, -1};
@ -188,59 +412,49 @@ TEST(FormatTest, FormatErrorCode) {
// Test maximum buffer size. // Test maximum buffer size.
msg = fmt::format("error {}", codes[i]); msg = fmt::format("error {}", codes[i]);
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
std::string prefix( std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::inline_buffer_size - msg.size() - sep.size(), 'x'); fmt::internal::format_error_code(buffer, codes[i], prefix);
fmt::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer)); EXPECT_EQ(prefix + sep + msg, to_string(buffer));
std::size_t size = fmt::inline_buffer_size; std::size_t size = fmt::inline_buffer_size;
EXPECT_EQ(size, buffer.size()); EXPECT_EQ(size, buffer.size());
buffer.resize(0); buffer.resize(0);
// Test with a message that doesn't fit into the buffer. // Test with a message that doesn't fit into the buffer.
prefix += 'x'; 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)); EXPECT_EQ(msg, to_string(buffer));
} }
} }
TEST(FormatTest, CountCodePoints) { 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) { // Tests fmt::internal::count_digits for integer type Int.
EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), template <typename Int> void test_count_digits() {
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i));
EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue), "blue"), for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
"\x1b[38;2;000;000;255mblue\x1b[0m"); n *= 10;
EXPECT_WRITE( EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
stdout, EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
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"); TEST(UtilTest, CountDigits) {
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::italic, "italic"), test_count_digits<uint32_t>();
"\x1b[3mitalic\x1b[0m"); test_count_digits<uint64_t>();
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::underline, "underline"), }
"\x1b[4munderline\x1b[0m");
EXPECT_WRITE(stdout, TEST(UtilTest, WriteUIntPtr) {
fmt::print(fmt::emphasis::strikethrough, "strikethrough"), fmt::memory_buffer buf;
"\x1b[9mstrikethrough\x1b[0m"); fmt::internal::writer writer(buf);
EXPECT_WRITE( writer.write_pointer(
stdout, fmt::internal::fallback_uintptr(reinterpret_cast<void*>(0xface)),
fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), nullptr);
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); EXPECT_EQ("0xface", to_string(buf));
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");
} }

File diff suppressed because it is too large Load Diff

3
externals/fmt/test/fuzzing/.gitignore vendored Normal file
View File

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

View 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
View 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
View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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;
}

File diff suppressed because it is too large Load Diff

75
externals/fmt/test/grisu-test.cc vendored Normal file
View 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));
}

View File

@ -7,23 +7,22 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#include <cstring>
#include <algorithm>
#include <stdexcept>
#include <gtest/gtest-spi.h> #include <gtest/gtest-spi.h>
#include <algorithm>
#include <cstring>
#include <memory>
#include <stdexcept>
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
# include <crtdbg.h> // for _CrtSetReportMode # include <crtdbg.h> // for _CrtSetReportMode
#endif // _WIN32 #endif // _WIN32
#include "util.h" #include "util.h"
using testing::internal::scoped_ptr;
namespace { namespace {
// This is used to suppress coverity warnings about untrusted values. // 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; std::string result;
for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i)
result.push_back(static_cast<char>(*i & 0xff)); result.push_back(static_cast<char>(*i & 0xff));
@ -53,11 +52,9 @@ int SingleEvaluationTest::b_;
void do_nothing() {} void do_nothing() {}
void throw_exception() { FMT_NORETURN void throw_exception() { throw std::runtime_error("test"); }
throw std::runtime_error("test");
}
void throw_system_error() { FMT_NORETURN void throw_system_error() {
throw fmt::system_error(EDOM, "test"); 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 // Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument
// exactly once. // exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) { TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++),
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++), "01234"); "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_EQ(s_ + 1, p_); EXPECT_EQ(s_ + 1, p_);
} }
// Tests that assertion arguments are evaluated exactly once. // Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) { TEST_F(SingleEvaluationTest, ExceptionTests) {
// successful EXPECT_THROW_MSG // successful EXPECT_THROW_MSG
EXPECT_THROW_MSG({ // NOLINT EXPECT_THROW_MSG(
a_++; { // NOLINT
throw_exception(); a_++;
}, std::exception, (b_++, "test")); throw_exception();
},
std::exception, (b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_THROW_MSG, throws different type // failed EXPECT_THROW_MSG, throws different type
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
a_++; { // NOLINT
throw_exception(); a_++;
}, std::logic_error, (b_++, "test")), "throws a different type"); throw_exception();
},
std::logic_error, (b_++, "test")),
"throws a different type");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); EXPECT_EQ(2, b_);
// failed EXPECT_THROW_MSG, throws an exception with different message // failed EXPECT_THROW_MSG, throws an exception with different message
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
a_++; { // NOLINT
throw_exception(); a_++;
}, std::exception, (b_++, "other")), throw_exception();
"throws an exception with a different message"); },
std::exception, (b_++, "other")),
"throws an exception with a different message");
EXPECT_EQ(3, a_); EXPECT_EQ(3, a_);
EXPECT_EQ(3, b_); EXPECT_EQ(3, b_);
@ -121,56 +117,95 @@ TEST_F(SingleEvaluationTest, ExceptionTests) {
TEST_F(SingleEvaluationTest, SystemErrorTests) { TEST_F(SingleEvaluationTest, SystemErrorTests) {
// successful EXPECT_SYSTEM_ERROR // successful EXPECT_SYSTEM_ERROR
EXPECT_SYSTEM_ERROR({ // NOLINT EXPECT_SYSTEM_ERROR(
a_++; { // NOLINT
throw_system_error(); a_++;
}, EDOM, (b_++, "test")); throw_system_error();
},
EDOM, (b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_SYSTEM_ERROR, throws different type // failed EXPECT_SYSTEM_ERROR, throws different type
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
a_++; { // NOLINT
throw_exception(); a_++;
}, EDOM, (b_++, "test")), "throws a different type"); throw_exception();
},
EDOM, (b_++, "test")),
"throws a different type");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); EXPECT_EQ(2, b_);
// failed EXPECT_SYSTEM_ERROR, throws an exception with different message // failed EXPECT_SYSTEM_ERROR, throws an exception with different message
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
a_++; { // NOLINT
throw_system_error(); a_++;
}, EDOM, (b_++, "other")), throw_system_error();
"throws an exception with a different message"); },
EDOM, (b_++, "other")),
"throws an exception with a different message");
EXPECT_EQ(3, a_); EXPECT_EQ(3, a_);
EXPECT_EQ(3, b_); EXPECT_EQ(3, b_);
// failed EXPECT_SYSTEM_ERROR, throws nothing // failed EXPECT_SYSTEM_ERROR, throws nothing
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")),
EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")), "throws nothing"); "throws nothing");
EXPECT_EQ(4, a_); EXPECT_EQ(4, a_);
EXPECT_EQ(4, b_); 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. // Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) { TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE // successful EXPECT_WRITE
EXPECT_WRITE(stdout, { // NOLINT EXPECT_WRITE(stdout,
a_++; { // NOLINT
std::printf("test"); a_++;
}, (b_++, "test")); std::printf("test");
},
(b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_WRITE // failed EXPECT_WRITE
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, { // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout,
a_++; { // NOLINT
std::printf("test"); a_++;
}, (b_++, "other")), "Actual: test"); std::printf("test");
},
(b_++, "other")),
"Actual: test");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); 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 // Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro. // EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) { TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
@ -179,8 +214,8 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, ""); EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, 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 1, runtime_error, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG( EXPECT_NONFATAL_FAILURE(
throw runtime_error("a"), runtime_error, "b"), ""); EXPECT_THROW_MSG(throw runtime_error("a"), runtime_error, "b"), "");
} }
// Tests that the compiler will not complain about unreachable code in the // 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_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test");
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR( EXPECT_NONFATAL_FAILURE(
throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"), ""); EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"),
"");
} }
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) { TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
@ -263,23 +299,13 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
format_system_error(EDOM, "test"))); 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) { TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
EXPECT_THROW_MSG(throw_exception(), std::exception, "test") EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
<< "unexpected failure"; << "unexpected failure";
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(
EXPECT_THROW_MSG(throw_exception(), std::exception, "other") EXPECT_THROW_MSG(throw_exception(), std::exception, "other")
<< "expected failure", "expected failure"); << "expected failure",
"expected failure");
} }
TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) { TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
@ -287,15 +313,8 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
<< "unexpected failure"; << "unexpected failure";
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other") EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other")
<< "expected failure", "expected failure"); << "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");
} }
TEST(UtilTest, FormatSystemError) { TEST(UtilTest, FormatSystemError) {
@ -304,7 +323,7 @@ TEST(UtilTest, FormatSystemError) {
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message")); 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::buffered_file;
using fmt::error_code; using fmt::error_code;
@ -340,10 +359,10 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
scoped_ptr<OutputRedirect> redir{FMT_NULL}; std::unique_ptr<OutputRedirect> redir{nullptr};
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EBADF,
EBADF, "cannot flush stream"); "cannot flush stream");
redir.reset(FMT_NULL); redir.reset(nullptr);
write_copy.dup2(write_fd); // "undo" close or dtor will fail write_copy.dup2(write_fd); // "undo" close or dtor will fail
} }
@ -352,9 +371,10 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
int fd = (f.fileno)(); int fd = (f.fileno)();
file copy = file::dup(fd); file copy = file::dup(fd);
FMT_POSIX(close(fd)); FMT_POSIX(close(fd));
scoped_ptr<OutputRedirect> redir{FMT_NULL}; std::unique_ptr<OutputRedirect> redir{nullptr};
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(
EBADF, fmt::format("cannot duplicate file descriptor {}", fd)); redir.reset(new OutputRedirect(f.get())), EBADF,
fmt::format("cannot duplicate file descriptor {}", fd));
copy.dup2(fd); // "undo" close or dtor will fail copy.dup2(fd); // "undo" close or dtor will fail
} }
@ -383,8 +403,8 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(), EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(), EBADF,
EBADF, "cannot flush stream"); "cannot flush stream");
write_copy.dup2(write_fd); // "undo" close or dtor will fail write_copy.dup2(write_fd); // "undo" close or dtor will fail
} }
@ -394,18 +414,20 @@ TEST(OutputRedirectTest, ErrorInDtor) {
int write_fd = write_end.descriptor(); int write_fd = write_end.descriptor();
file write_copy = write_end.dup(write_fd); file write_copy = write_end.dup(write_fd);
buffered_file f = write_end.fdopen("w"); buffered_file f = write_end.fdopen("w");
scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get())); std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
// The close function must be called inside EXPECT_WRITE, otherwise {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE,
// output in EXPECT_STDERR and the second close will break output // otherwise the system may recycle closed file descriptor when
// redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(write_fd)); // will break output redirection.
SUPPRESS_ASSERT(redir.reset(FMT_NULL)); FMT_POSIX(close(write_fd));
}, format_system_error(EBADF, "cannot flush stream")); SUPPRESS_ASSERT(redir.reset(nullptr));
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail },
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 #endif // FMT_USE_FILE_DESCRIPTORS

View File

@ -7,30 +7,28 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FCNTL
using fmt::file; using fmt::file;
void OutputRedirect::flush() { void OutputRedirect::flush() {
#if EOF != -1 # if EOF != -1
# error "FMT_RETRY assumes return value of -1 indicating failure" # error "FMT_RETRY assumes return value of -1 indicating failure"
#endif # endif
int result = 0; int result = 0;
FMT_RETRY(result, fflush(file_)); FMT_RETRY(result, fflush(file_));
if (result != 0) if (result != 0) throw fmt::system_error(errno, "cannot flush stream");
throw fmt::system_error(errno, "cannot flush stream");
} }
void OutputRedirect::restore() { void OutputRedirect::restore() {
if (original_.descriptor() == -1) if (original_.descriptor() == -1) return; // Already restored.
return; // Already restored.
flush(); flush();
// Restore the original file. // Restore the original file.
original_.dup2(FMT_POSIX(fileno(file_))); original_.dup2(FMT_POSIX(fileno(file_)));
original_.close(); original_.close();
} }
OutputRedirect::OutputRedirect(FILE *f) : file_(f) { OutputRedirect::OutputRedirect(FILE* f) : file_(f) {
flush(); flush();
int fd = FMT_POSIX(fileno(f)); int fd = FMT_POSIX(fileno(f));
// Create a file object referring to the original file. // Create a file object referring to the original file.
@ -45,7 +43,7 @@ OutputRedirect::OutputRedirect(FILE *f) : file_(f) {
OutputRedirect::~OutputRedirect() FMT_NOEXCEPT { OutputRedirect::~OutputRedirect() FMT_NOEXCEPT {
try { try {
restore(); restore();
} catch (const std::exception &e) { } catch (const std::exception& e) {
std::fputs(e.what(), stderr); std::fputs(e.what(), stderr);
} }
} }
@ -56,8 +54,7 @@ std::string OutputRedirect::restore_and_read() {
// Read everything from the pipe. // Read everything from the pipe.
std::string content; std::string content;
if (read_end_.descriptor() == -1) if (read_end_.descriptor() == -1) return content; // Already read.
return content; // Already read.
enum { BUFFER_SIZE = 4096 }; enum { BUFFER_SIZE = 4096 };
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
std::size_t count = 0; std::size_t count = 0;
@ -69,7 +66,7 @@ std::string OutputRedirect::restore_and_read() {
return content; 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::string buffer(count, '\0');
std::size_t n = 0, offset = 0; std::size_t n = 0, offset = 0;
do { do {
@ -81,7 +78,7 @@ std::string read(file &f, std::size_t count) {
return buffer; return buffer;
} }
#endif // FMT_USE_FILE_DESCRIPTORS #endif // FMT_USE_FCNTL
std::string format_system_error(int error_code, fmt::string_view message) { std::string format_system_error(int error_code, fmt::string_view message) {
fmt::memory_buffer out; fmt::memory_buffer out;

View File

@ -10,70 +10,59 @@
#include <string> #include <string>
#include "gmock.h" #include "gmock.h"
#include "fmt/os.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
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
std::string gtest_expected_message = expected_message; \ std::string gtest_expected_message = expected_message; \
bool gtest_caught_expected = false; \ bool gtest_caught_expected = false; \
try { \ try { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} \ } catch (expected_exception const& e) { \
catch (expected_exception const& e) { \ if (gtest_expected_message != e.what()) { \
if (gtest_expected_message != e.what()) { \ gtest_ar << #statement \
gtest_ar \ " throws an exception with a different message.\n" \
<< #statement " throws an exception with a different message.\n" \ << "Expected: " << gtest_expected_message << "\n" \
<< "Expected: " << gtest_expected_message << "\n" \ << " Actual: " << e.what(); \
<< " Actual: " << e.what(); \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } \
} \ gtest_caught_expected = true; \
gtest_caught_expected = true; \ } catch (...) { \
} \ gtest_ar << "Expected: " #statement \
catch (...) { \ " throws an exception of type " #expected_exception \
gtest_ar << \ ".\n Actual: it throws a different type."; \
"Expected: " #statement " throws an exception of type " \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
#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 \
if (!gtest_caught_expected) { \ " throws an exception of type " #expected_exception \
gtest_ar << \ ".\n Actual: it throws nothing."; \
"Expected: " #statement " throws an exception of type " \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
#expected_exception ".\n Actual: it throws nothing."; \ } \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } else \
} \ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \
} else \ : fail(gtest_ar.failure_message())
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
fail(gtest_ar.failure_message())
// Tests that the statement throws the expected exception and the exception's // Tests that the statement throws the expected exception and the exception's
// what() method returns expected message. // what() method returns expected message.
#define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \ #define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \
FMT_TEST_THROW_(statement, expected_exception, \ FMT_TEST_THROW_(statement, expected_exception, expected_message, \
expected_message, GTEST_NONFATAL_FAILURE_) GTEST_NONFATAL_FAILURE_)
std::string format_system_error(int error_code, fmt::string_view message); std::string format_system_error(int error_code, fmt::string_view message);
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \ #define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
EXPECT_THROW_MSG(statement, fmt::system_error, \ EXPECT_THROW_MSG(statement, fmt::system_error, \
format_system_error(error_code, message)) format_system_error(error_code, message))
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FCNTL
// Captures file output by redirecting it to a pipe. // Captures file output by redirecting it to a pipe.
// The output it can handle is limited by the pipe capacity. // The output it can handle is limited by the pipe capacity.
class OutputRedirect { class OutputRedirect {
private: private:
FILE *file_; FILE* file_;
fmt::file original_; // Original file passed to redirector. fmt::file original_; // Original file passed to redirector.
fmt::file read_end_; // Read end of the pipe where the output is redirected. fmt::file read_end_; // Read end of the pipe where the output is redirected.
@ -83,7 +72,7 @@ class OutputRedirect {
void restore(); void restore();
public: public:
explicit OutputRedirect(FILE *file); explicit OutputRedirect(FILE* file);
~OutputRedirect() FMT_NOEXCEPT; ~OutputRedirect() FMT_NOEXCEPT;
// Restores the original file, reads output from the pipe into a string // Restores the original file, reads output from the pipe into a string
@ -91,29 +80,28 @@ class OutputRedirect {
std::string restore_and_read(); std::string restore_and_read();
}; };
#define FMT_TEST_WRITE_(statement, expected_output, file, fail) \ # define FMT_TEST_WRITE_(statement, expected_output, file, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
std::string gtest_expected_output = expected_output; \ std::string gtest_expected_output = expected_output; \
OutputRedirect gtest_redir(file); \ OutputRedirect gtest_redir(file); \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
std::string gtest_output = gtest_redir.restore_and_read(); \ std::string gtest_output = gtest_redir.restore_and_read(); \
if (gtest_output != gtest_expected_output) { \ if (gtest_output != gtest_expected_output) { \
gtest_ar \ gtest_ar << #statement " produces different output.\n" \
<< #statement " produces different output.\n" \ << "Expected: " << gtest_expected_output << "\n" \
<< "Expected: " << gtest_expected_output << "\n" \ << " Actual: " << gtest_output; \
<< " Actual: " << gtest_output; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } \
} \ } else \
} else \ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ : fail(gtest_ar.failure_message())
fail(gtest_ar.failure_message())
// Tests that the statement writes the expected output to file. // 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_) FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_)
#ifdef _MSC_VER # ifdef _MSC_VER
// Suppresses Windows assertions on invalid file descriptors, making // Suppresses Windows assertions on invalid file descriptors, making
// POSIX functions return proper error codes instead of crashing on Windows. // POSIX functions return proper error codes instead of crashing on Windows.
@ -122,40 +110,45 @@ class SuppressAssert {
_invalid_parameter_handler original_handler_; _invalid_parameter_handler original_handler_;
int original_report_mode_; int original_report_mode_;
static void handle_invalid_parameter(const wchar_t *, static void handle_invalid_parameter(const wchar_t*, const wchar_t*,
const wchar_t *, const wchar_t *, unsigned , uintptr_t) {} const wchar_t*, unsigned, uintptr_t) {}
public: public:
SuppressAssert() SuppressAssert()
: original_handler_(_set_invalid_parameter_handler(handle_invalid_parameter)), : original_handler_(
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) { _set_invalid_parameter_handler(handle_invalid_parameter)),
} original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
~SuppressAssert() { ~SuppressAssert() {
_set_invalid_parameter_handler(original_handler_); _set_invalid_parameter_handler(original_handler_);
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_); _CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
} }
}; };
# define SUPPRESS_ASSERT(statement) { SuppressAssert sa; statement; } # define SUPPRESS_ASSERT(statement) \
#else { \
# define SUPPRESS_ASSERT(statement) statement SuppressAssert sa; \
#endif // _MSC_VER statement; \
}
# else
# define SUPPRESS_ASSERT(statement) statement
# endif // _MSC_VER
#define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \ # define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message) EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// Attempts to read count characters from a file. // 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) \ # define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(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> template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; } ScopedMock() { Mock::instance = this; }
~ScopedMock() { Mock::instance = FMT_NULL; } ~ScopedMock() { Mock::instance = nullptr; }
}; };
#endif // FMT_GTEST_EXTRA_H_ #endif // FMT_GTEST_EXTRA_H_

View File

@ -8,27 +8,82 @@
#include "fmt/locale.h" #include "fmt/locale.h"
#include "gmock.h" #include "gmock.h"
template <typename Char> using fmt::internal::max_value;
struct numpunct : std::numpunct<Char> {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> {
protected: 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 '~'; } 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) { TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>()); 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)); EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567));
fmt::format_arg_store<fmt::format_context, int> as{1234567}; fmt::format_arg_store<fmt::format_context, int> as{1234567};
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as))); EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as)));
std::string s; std::string s;
fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567); fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567);
EXPECT_EQ("1~234~567", s); 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) { TEST(LocaleTest, WFormat) {
std::locale loc(std::locale(), new numpunct<wchar_t>()); 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)); EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567));
fmt::format_arg_store<fmt::wformat_context, int> as{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"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

View File

@ -8,53 +8,51 @@
#ifndef FMT_MOCK_ALLOCATOR_H_ #ifndef FMT_MOCK_ALLOCATOR_H_
#define FMT_MOCK_ALLOCATOR_H_ #define FMT_MOCK_ALLOCATOR_H_
#include "gmock.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "gmock.h"
template <typename T> template <typename T> class mock_allocator {
class mock_allocator {
public: public:
mock_allocator() {} mock_allocator() {}
mock_allocator(const mock_allocator &) {} mock_allocator(const mock_allocator&) {}
typedef T value_type; typedef T value_type;
MOCK_METHOD1_T(allocate, T* (std::size_t n)); MOCK_METHOD1_T(allocate, T*(std::size_t n));
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n)); MOCK_METHOD2_T(deallocate, void(T* p, std::size_t n));
}; };
template <typename Allocator> template <typename Allocator> class allocator_ref {
class allocator_ref {
private: private:
Allocator *alloc_; Allocator* alloc_;
void move(allocator_ref &other) { void move(allocator_ref& other) {
alloc_ = other.alloc_; alloc_ = other.alloc_;
other.alloc_ = FMT_NULL; other.alloc_ = nullptr;
} }
public: public:
typedef typename Allocator::value_type value_type; 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(const allocator_ref& other) : alloc_(other.alloc_) {}
allocator_ref(allocator_ref &&other) { move(other); } allocator_ref(allocator_ref&& other) { move(other); }
allocator_ref& operator=(allocator_ref &&other) { allocator_ref& operator=(allocator_ref&& other) {
assert(this != &other); assert(this != &other);
move(other); move(other);
return *this; return *this;
} }
allocator_ref& operator=(const allocator_ref &other) { allocator_ref& operator=(const allocator_ref& other) {
alloc_ = other.alloc_; alloc_ = other.alloc_;
return *this; return *this;
} }
public: public:
Allocator *get() const { return alloc_; } Allocator* get() const { return alloc_; }
value_type* allocate(std::size_t n) { 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); } void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
}; };

View File

@ -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 // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.
@ -7,20 +7,138 @@
#include <cstdlib> // std::exit #include <cstdlib> // std::exit
#include <cstring> #include <cstring>
#include <memory>
#include "fmt/posix.h" #include "fmt/os.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
#ifdef fileno #ifdef fileno
# undef fileno # undef fileno
#endif #endif
using fmt::buffered_file; using fmt::buffered_file;
using fmt::error_code; 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. // Checks if the file is open by reading one character from it.
static bool isopen(int fd) { static bool isopen(int fd) {
@ -45,9 +163,9 @@ static file open_file() {
} }
// Attempts to write a string to a file. // Attempts to write a string to a file.
static void write(file &f, fmt::string_view s) { static void write(file& f, fmt::string_view s) {
std::size_t num_chars_left = s.size(); std::size_t num_chars_left = s.size();
const char *ptr = s.data(); const char* ptr = s.data();
do { do {
std::size_t count = f.write(ptr, num_chars_left); std::size_t count = f.write(ptr, num_chars_left);
ptr += count; ptr += count;
@ -59,26 +177,26 @@ static void write(file &f, fmt::string_view s) {
TEST(BufferedFileTest, DefaultCtor) { TEST(BufferedFileTest, DefaultCtor) {
buffered_file f; buffered_file f;
EXPECT_TRUE(f.get() == FMT_NULL); EXPECT_TRUE(f.get() == nullptr);
} }
TEST(BufferedFileTest, MoveCtor) { TEST(BufferedFileTest, MoveCtor) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE* fp = bf.get();
EXPECT_TRUE(fp != FMT_NULL); EXPECT_TRUE(fp != nullptr);
buffered_file bf2(std::move(bf)); buffered_file bf2(std::move(bf));
EXPECT_EQ(fp, bf2.get()); EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == FMT_NULL); EXPECT_TRUE(bf.get() == nullptr);
} }
TEST(BufferedFileTest, MoveAssignment) { TEST(BufferedFileTest, MoveAssignment) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE* fp = bf.get();
EXPECT_TRUE(fp != FMT_NULL); EXPECT_TRUE(fp != nullptr);
buffered_file bf2; buffered_file bf2;
bf2 = std::move(bf); bf2 = std::move(bf);
EXPECT_EQ(fp, bf2.get()); EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == FMT_NULL); EXPECT_TRUE(bf.get() == nullptr);
} }
TEST(BufferedFileTest, MoveAssignmentClosesFile) { TEST(BufferedFileTest, MoveAssignmentClosesFile) {
@ -90,13 +208,13 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
} }
TEST(BufferedFileTest, MoveFromTemporaryInCtor) { TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = FMT_NULL; FILE* fp = nullptr;
buffered_file f(open_buffered_file(&fp)); buffered_file f(open_buffered_file(&fp));
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
} }
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) { TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
FILE *fp = FMT_NULL; FILE* fp = nullptr;
buffered_file f; buffered_file f;
f = open_buffered_file(&fp); f = open_buffered_file(&fp);
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
@ -119,22 +237,24 @@ TEST(BufferedFileTest, CloseFileInDtor) {
} }
TEST(BufferedFileTest, CloseErrorInDtor) { TEST(BufferedFileTest, CloseErrorInDtor) {
scoped_ptr<buffered_file> f(new buffered_file(open_buffered_file())); std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
// The close function must be called inside EXPECT_WRITE, otherwise {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE,
// output in EXPECT_STDERR and the second close will break output // otherwise the system may recycle closed file descriptor when
// redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(f->fileno())); // will break output redirection.
SUPPRESS_ASSERT(f.reset(FMT_NULL)); FMT_POSIX(close(f->fileno()));
}, format_system_error(EBADF, "cannot close file") + "\n"); SUPPRESS_ASSERT(f.reset(nullptr));
},
format_system_error(EBADF, "cannot close file") + "\n");
} }
TEST(BufferedFileTest, Close) { TEST(BufferedFileTest, Close) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = f.fileno(); int fd = f.fileno();
f.close(); f.close();
EXPECT_TRUE(f.get() == FMT_NULL); EXPECT_TRUE(f.get() == nullptr);
EXPECT_TRUE(isclosed(fd)); EXPECT_TRUE(isclosed(fd));
} }
@ -142,22 +262,24 @@ TEST(BufferedFileTest, CloseError) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
FMT_POSIX(close(f.fileno())); FMT_POSIX(close(f.fileno()));
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_TRUE(f.get() == FMT_NULL); EXPECT_TRUE(f.get() == nullptr);
} }
TEST(BufferedFileTest, Fileno) { TEST(BufferedFileTest, Fileno) {
buffered_file f; buffered_file f;
#ifndef __COVERITY__ # ifndef __COVERITY__
// fileno on a null FILE pointer either crashes or returns an error. // fileno on a null FILE pointer either crashes or returns an error.
// Disable Coverity because this is intentional. // Disable Coverity because this is intentional.
EXPECT_DEATH_IF_SUPPORTED({ EXPECT_DEATH_IF_SUPPORTED(
try { {
f.fileno(); try {
} catch (const fmt::system_error&) { f.fileno();
std::exit(1); } catch (const fmt::system_error&) {
} std::exit(1);
}, ""); }
#endif },
"");
# endif
f = open_buffered_file(); f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1); EXPECT_TRUE(f.fileno() != -1);
file copy = file::dup(f.fileno()); file copy = file::dup(f.fileno());
@ -170,7 +292,7 @@ TEST(FileTest, DefaultCtor) {
} }
TEST(FileTest, OpenBufferedFileInCtor) { TEST(FileTest, OpenBufferedFileInCtor) {
FILE *fp = safe_fopen("test-file", "w"); FILE* fp = safe_fopen("test-file", "w");
std::fputs(FILE_CONTENT, fp); std::fputs(FILE_CONTENT, fp);
std::fclose(fp); std::fclose(fp);
file f("test-file", file::RDONLY); file f("test-file", file::RDONLY);
@ -178,8 +300,8 @@ TEST(FileTest, OpenBufferedFileInCtor) {
} }
TEST(FileTest, OpenBufferedFileError) { TEST(FileTest, OpenBufferedFileError) {
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
ENOENT, "cannot open file nonexistent"); "cannot open file nonexistent");
} }
TEST(FileTest, MoveCtor) { TEST(FileTest, MoveCtor) {
@ -209,7 +331,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
} }
static file OpenBufferedFile(int &fd) { static file OpenBufferedFile(int& fd) {
file f = open_file(); file f = open_file();
fd = f.descriptor(); fd = f.descriptor();
return f; return f;
@ -246,15 +368,17 @@ TEST(FileTest, CloseFileInDtor) {
} }
TEST(FileTest, CloseErrorInDtor) { TEST(FileTest, CloseErrorInDtor) {
scoped_ptr<file> f(new file(open_file())); std::unique_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
// The close function must be called inside EXPECT_WRITE, otherwise {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE,
// output in EXPECT_STDERR and the second close will break output // otherwise the system may recycle closed file descriptor when
// redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(f->descriptor())); // will break output redirection.
SUPPRESS_ASSERT(f.reset(FMT_NULL)); FMT_POSIX(close(f->descriptor()));
}, format_system_error(EBADF, "cannot close file") + "\n"); SUPPRESS_ASSERT(f.reset(nullptr));
},
format_system_error(EBADF, "cannot close file") + "\n");
} }
TEST(FileTest, Close) { TEST(FileTest, Close) {
@ -307,13 +431,13 @@ TEST(FileTest, Dup) {
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT))); EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
} }
#ifndef __COVERITY__ # ifndef __COVERITY__
TEST(FileTest, DupError) { TEST(FileTest, DupError) {
int value = -1; int value = -1;
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
EBADF, "cannot duplicate file descriptor -1"); "cannot duplicate file descriptor -1");
} }
#endif # endif
TEST(FileTest, Dup2) { TEST(FileTest, Dup2) {
file f = open_file(); file f = open_file();
@ -325,8 +449,9 @@ TEST(FileTest, Dup2) {
TEST(FileTest, Dup2Error) { TEST(FileTest, Dup2Error) {
file f = open_file(); file f = open_file();
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF, EXPECT_SYSTEM_ERROR_NOASSERT(
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor())); f.dup2(-1), EBADF,
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
} }
TEST(FileTest, Dup2NoExcept) { TEST(FileTest, Dup2NoExcept) {
@ -362,17 +487,12 @@ TEST(FileTest, Fdopen) {
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get()))); EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
} }
TEST(FileTest, FdopenError) { # ifdef FMT_LOCALE
file f;
EXPECT_SYSTEM_ERROR_NOASSERT(
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
}
#ifdef FMT_LOCALE
TEST(LocaleTest, Strtod) { TEST(LocaleTest, Strtod) {
fmt::Locale locale; fmt::locale loc;
const char *start = "4.2", *ptr = start; 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); EXPECT_EQ(start + 3, ptr);
} }
#endif # endif
#endif // FMT_USE_FCNTL

View File

@ -6,6 +6,21 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#define FMT_STRING_ALIAS 1 #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 "fmt/ostream.h"
#include <sstream> #include <sstream>
@ -16,51 +31,56 @@
using fmt::format; using fmt::format;
using fmt::format_error; 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(); os << d.year() << '-' << d.month() << '-' << d.day();
return os; 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(); os << d.year() << L'-' << d.month() << L'-' << d.day();
return os; return os;
} }
enum TestEnum {}; // Make sure that overloaded comma operators do no harm to is_streamable.
static std::ostream &operator<<(std::ostream &os, TestEnum) { struct type_with_comma_op {};
return os << "TestEnum"; 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) { static std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"TestEnum"; return os << L"streamable_enum";
} }
enum TestEnum2 {A}; enum unstreamable_enum {};
TEST(OStreamTest, Enum) { TEST(OStreamTest, Enum) {
EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value)); EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum())); EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
EXPECT_EQ("0", fmt::format("{}", A)); EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
EXPECT_FALSE((fmt::convert_to_int<TestEnum, wchar_t>::value)); EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
EXPECT_EQ(L"TestEnum", fmt::format(L"{}", TestEnum()));
EXPECT_EQ(L"0", fmt::format(L"{}", A));
} }
typedef fmt::back_insert_range<fmt::internal::buffer> range; using range = fmt::buffer_range<char>;
struct test_arg_formatter: fmt::arg_formatter<range> { struct test_arg_formatter : fmt::arg_formatter<range> {
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) fmt::format_parse_context parse_ctx;
: fmt::arg_formatter<range>(ctx, &s) {} test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
: fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {}
}; };
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::internal::buffer &base = buffer; fmt::internal::buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
fmt::format_specs spec; fmt::format_specs spec;
test_arg_formatter af(ctx, spec); test_arg_formatter af(ctx, spec);
visit(af, fmt::internal::make_arg<fmt::format_context>(TestEnum())); fmt::visit_format_arg(
EXPECT_EQ("TestEnum", std::string(buffer.data(), buffer.size())); af, fmt::internal::make_arg<fmt::format_context>(streamable_enum()));
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
} }
TEST(OStreamTest, Format) { TEST(OStreamTest, Format) {
@ -75,20 +95,22 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) { TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def"))); EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def"))); EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), #if FMT_NUMERIC_ALIGN
format_error, "format specifier requires numeric argument"); 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_EQ("def**", format("{0:*<5}", TestString("def"))); EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()), EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()), EXPECT_THROW_MSG(format("{0:-}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()), EXPECT_THROW_MSG(format("{0: }", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()), EXPECT_THROW_MSG(format("{0:#}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()), EXPECT_THROW_MSG(format("{0:05}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_EQ("test ", format("{0:13}", TestString("test"))); EXPECT_EQ("test ", format("{0:13}", TestString("test")));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13)); EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
EXPECT_EQ("te", format("{0:.2}", TestString("test"))); EXPECT_EQ("te", format("{0:.2}", TestString("test")));
@ -96,7 +118,7 @@ TEST(OStreamTest, FormatSpecs) {
} }
struct EmptyTest {}; struct EmptyTest {};
static std::ostream &operator<<(std::ostream &os, EmptyTest) { static std::ostream& operator<<(std::ostream& os, EmptyTest) {
return os << ""; return os << "";
} }
@ -116,37 +138,36 @@ TEST(OStreamTest, Print) {
TEST(OStreamTest, WriteToOStream) { TEST(OStreamTest, WriteToOStream) {
std::ostringstream os; std::ostringstream os;
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
const char *foo = "foo"; const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo)); buffer.append(foo, foo + std::strlen(foo));
fmt::internal::write(os, buffer); fmt::internal::write(os, buffer);
EXPECT_EQ("foo", os.str()); EXPECT_EQ("foo", os.str());
} }
TEST(OStreamTest, WriteToOStreamMaxSize) { TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max(); std::size_t max_size = fmt::internal::max_value<std::size_t>();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max(); std::streamsize max_streamsize = fmt::internal::max_value<std::streamsize>();
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return;
return;
struct test_buffer : fmt::internal::buffer { struct test_buffer : fmt::internal::buffer<char> {
explicit test_buffer(std::size_t size) { resize(size); } explicit test_buffer(std::size_t size) { resize(size); }
void grow(std::size_t) {} void grow(std::size_t) {}
} buffer(max_size); } buffer(max_size);
struct mock_streambuf : std::streambuf { struct mock_streambuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n)); MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
std::streamsize xsputn(const char *s, std::streamsize n) { std::streamsize xsputn(const char* s, std::streamsize n) {
const void *v = s; const void* v = s;
return xsputn(v, n); return xsputn(v, n);
} }
} streambuf; } streambuf;
struct test_ostream : std::ostream { struct test_ostream : std::ostream {
explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {} explicit test_ostream(mock_streambuf& buffer) : std::ostream(&buffer) {}
} os(streambuf); } os(streambuf);
testing::InSequence sequence; testing::InSequence sequence;
const char *data = FMT_NULL; const char* data = nullptr;
typedef std::make_unsigned<std::streamsize>::type ustreamsize; typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize size = max_size; ustreamsize size = max_size;
do { do {
@ -166,18 +187,40 @@ TEST(OStreamTest, Join) {
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
TEST(OStreamTest, ConstexprString) { 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 #endif
namespace fmt_test { namespace fmt_test {
struct ABC {}; struct ABC {};
template <typename Output> Output &operator<<(Output &out, ABC) { template <typename Output> Output& operator<<(Output& out, ABC) {
out << "ABC"; out << "ABC";
return out; 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) { TEST(FormatTest, FormatToN) {
char buffer[4]; char buffer[4];
@ -191,6 +234,7 @@ TEST(FormatTest, FormatToN) {
EXPECT_EQ(buffer + 3, result.out); EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("xABx", fmt::string_view(buffer, 4)); EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
} }
#endif
#if FMT_USE_USER_DEFINED_LITERALS #if FMT_USE_USER_DEFINED_LITERALS
TEST(FormatTest, UDL) { TEST(FormatTest, UDL) {
@ -198,3 +242,51 @@ TEST(FormatTest, UDL) {
EXPECT_EQ("{}"_format("test"), "test"); EXPECT_EQ("{}"_format("test"), "test");
} }
#endif #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

View File

@ -6,21 +6,24 @@
// For the license information refer to format.h. // For the license information refer to format.h.
// Disable bogus MSVC warnings. // Disable bogus MSVC warnings.
#ifdef _MSC_VER #ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#include "posix-mock.h" #include "posix-mock.h"
#include "../src/posix.cc"
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <climits> #include <climits>
#include <memory>
#include "../src/os.cc"
#ifdef _WIN32 #ifdef _WIN32
# include <io.h> # include <io.h>
# undef max # undef max
# undef ERROR # undef ERROR
#endif #endif
#include "gmock.h" #include "gmock.h"
@ -29,12 +32,10 @@
using fmt::buffered_file; using fmt::buffered_file;
using fmt::error_code; using fmt::error_code;
using fmt::file;
using testing::internal::scoped_ptr;
using testing::_; using testing::_;
using testing::StrEq;
using testing::Return; using testing::Return;
using testing::StrEq;
namespace { namespace {
int open_count; int open_count;
@ -52,25 +53,25 @@ std::size_t read_nbyte;
std::size_t write_nbyte; std::size_t write_nbyte;
bool sysconf_error; 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) \ #define EMULATE_EINTR(func, error_result) \
if (func##_count != 0) { \ if (func##_count != 0) { \
if (func##_count++ != 3) { \ if (func##_count++ != 3) { \
errno = EINTR; \ errno = EINTR; \
return error_result; \ return error_result; \
} \ } \
} }
#ifndef _MSC_VER #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); EMULATE_EINTR(open, -1);
return ::open(path, oflag, mode); return ::open(path, oflag, mode);
} }
#else #else
errno_t test::sopen_s( errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
int* pfh, const char *filename, int oflag, int shflag, int pmode) { int pmode) {
EMULATE_EINTR(open, EINTR); EMULATE_EINTR(open, EINTR);
return _sopen_s(pfh, filename, oflag, shflag, pmode); return _sopen_s(pfh, filename, oflag, shflag, pmode);
} }
@ -80,8 +81,7 @@ errno_t test::sopen_s(
long test::sysconf(int name) { long test::sysconf(int name) {
long result = ::sysconf(name); long result = ::sysconf(name);
if (!sysconf_error) if (!sysconf_error) return result;
return result;
// Simulate an error. // Simulate an error.
errno = EINVAL; errno = EINVAL;
return -1; return -1;
@ -89,10 +89,9 @@ long test::sysconf(int name) {
static off_t max_file_size() { return std::numeric_limits<off_t>::max(); } static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
int test::fstat(int fd, struct stat *buf) { int test::fstat(int fd, struct stat* buf) {
int result = ::fstat(fd, buf); int result = ::fstat(fd, buf);
if (fstat_sim == MAX_SIZE) if (fstat_sim == MAX_SIZE) buf->st_size = max_file_size();
buf->st_size = max_file_size();
return result; return result;
} }
@ -132,18 +131,18 @@ int test::dup2(int fildes, int fildes2) {
return ::FMT_POSIX(dup2(fildes, fildes2)); return ::FMT_POSIX(dup2(fildes, fildes2));
} }
FILE *test::fdopen(int fildes, const char *mode) { FILE* test::fdopen(int fildes, const char* mode) {
EMULATE_EINTR(fdopen, FMT_NULL); EMULATE_EINTR(fdopen, nullptr);
return ::FMT_POSIX(fdopen(fildes, mode)); 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; read_nbyte = nbyte;
EMULATE_EINTR(read, -1); EMULATE_EINTR(read, -1);
return ::FMT_POSIX(read(fildes, buf, nbyte)); 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; write_nbyte = nbyte;
EMULATE_EINTR(write, -1); EMULATE_EINTR(write, -1);
return ::FMT_POSIX(write(fildes, buf, nbyte)); return ::FMT_POSIX(write(fildes, buf, nbyte));
@ -155,23 +154,23 @@ int test::pipe(int fildes[2]) {
return ::pipe(fildes); return ::pipe(fildes);
} }
#else #else
int test::pipe(int *pfds, unsigned psize, int textmode) { int test::pipe(int* pfds, unsigned psize, int textmode) {
EMULATE_EINTR(pipe, -1); EMULATE_EINTR(pipe, -1);
return _pipe(pfds, psize, textmode); return _pipe(pfds, psize, textmode);
} }
#endif #endif
FILE *test::fopen(const char *filename, const char *mode) { FILE* test::fopen(const char* filename, const char* mode) {
EMULATE_EINTR(fopen, FMT_NULL); EMULATE_EINTR(fopen, nullptr);
return ::fopen(filename, mode); return ::fopen(filename, mode);
} }
int test::fclose(FILE *stream) { int test::fclose(FILE* stream) {
EMULATE_EINTR(fclose, EOF); EMULATE_EINTR(fclose, EOF);
return ::fclose(stream); return ::fclose(stream);
} }
int (test::fileno)(FILE *stream) { int(test::fileno)(FILE* stream) {
EMULATE_EINTR(fileno, -1); EMULATE_EINTR(fileno, -1);
#ifdef fileno #ifdef fileno
return FMT_POSIX(fileno(stream)); return FMT_POSIX(fileno(stream));
@ -181,18 +180,18 @@ int (test::fileno)(FILE *stream) {
} }
#ifndef _WIN32 #ifndef _WIN32
# define EXPECT_RETRY(statement, func, message) \ # define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \ func##_count = 1; \
statement; \ statement; \
EXPECT_EQ(4, func##_count); \ EXPECT_EQ(4, func##_count); \
func##_count = 0; func##_count = 0;
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual) # define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
#else #else
# define EXPECT_RETRY(statement, func, message) \ # define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \ func##_count = 1; \
EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
func##_count = 0; func##_count = 0;
# define EXPECT_EQ_POSIX(expected, actual) # define EXPECT_EQ_POSIX(expected, actual)
#endif #endif
static void write_file(fmt::cstring_view filename, fmt::string_view content) { 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); f.print("{}", content);
} }
#if FMT_USE_FCNTL
using fmt::file;
TEST(UtilTest, GetPageSize) { TEST(UtilTest, GetPageSize) {
#ifdef _WIN32 # ifdef _WIN32
SYSTEM_INFO si = {}; SYSTEM_INFO si = {};
GetSystemInfo(&si); GetSystemInfo(&si);
EXPECT_EQ(si.dwPageSize, fmt::getpagesize()); EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
#else # else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize()); EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true; sysconf_error = true;
EXPECT_SYSTEM_ERROR( EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
fmt::getpagesize(), EINVAL, "cannot get memory page size"); "cannot get memory page size");
sysconf_error = false; sysconf_error = false;
#endif # endif
} }
TEST(FileTest, OpenRetry) { TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here"); write_file("temp", "there must be something here");
scoped_ptr<file> f{FMT_NULL}; std::unique_ptr<file> f{nullptr};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
open, "cannot open file test"); "cannot open file temp");
#ifndef _WIN32 # ifndef _WIN32
char c = 0; char c = 0;
f->read(&c, 1); f->read(&c, 1);
#endif # endif
} }
TEST(FileTest, CloseNoRetryInDtor) { TEST(FileTest, CloseNoRetryInDtor) {
file read_end, write_end; file read_end, write_end;
file::pipe(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; int saved_close_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(
close_count = 1; stderr,
f.reset(FMT_NULL); {
saved_close_count = close_count; close_count = 1;
close_count = 0; f.reset(nullptr);
}, format_system_error(EINTR, "cannot close file") + "\n"); saved_close_count = close_count;
close_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_close_count); EXPECT_EQ(2, saved_close_count);
} }
@ -250,26 +255,26 @@ TEST(FileTest, CloseNoRetry) {
TEST(FileTest, Size) { TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading"; std::string content = "top secret, destroy before reading";
write_file("test", content); write_file("temp", content);
file f("test", file::RDONLY); file f("temp", file::RDONLY);
EXPECT_GE(f.size(), 0); EXPECT_GE(f.size(), 0);
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size())); EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
#ifdef _WIN32 # ifdef _WIN32
fmt::memory_buffer message; fmt::memory_buffer message;
fmt::internal::format_windows_error( fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
message, ERROR_ACCESS_DENIED, "cannot get file size"); "cannot get file size");
fstat_sim = ERROR; fstat_sim = ERROR;
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message)); EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
fstat_sim = NONE; fstat_sim = NONE;
#else # else
f.close(); f.close();
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes"); EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
#endif # endif
} }
TEST(FileTest, MaxSize) { TEST(FileTest, MaxSize) {
write_file("test", ""); write_file("temp", "");
file f("test", file::RDONLY); file f("temp", file::RDONLY);
fstat_sim = MAX_SIZE; fstat_sim = MAX_SIZE;
EXPECT_GE(f.size(), 0); EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size()); EXPECT_EQ(max_file_size(), f.size());
@ -284,8 +289,8 @@ TEST(FileTest, ReadRetry) {
write_end.close(); write_end.close();
char buffer[SIZE]; char buffer[SIZE];
std::size_t count = 0; std::size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE), EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
read, "cannot read from file"); "cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
} }
@ -294,26 +299,25 @@ TEST(FileTest, WriteRetry) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
enum { SIZE = 4 }; enum { SIZE = 4 };
std::size_t count = 0; std::size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE), EXPECT_RETRY(count = write_end.write("test", SIZE), write,
write, "cannot write to file"); "cannot write to file");
write_end.close(); write_end.close();
#ifndef _WIN32 # ifndef _WIN32
EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
char buffer[SIZE + 1]; char buffer[SIZE + 1];
read_end.read(buffer, SIZE); read_end.read(buffer, SIZE);
buffer[SIZE] = '\0'; buffer[SIZE] = '\0';
EXPECT_STREQ("test", buffer); EXPECT_STREQ("test", buffer);
#endif # endif
} }
#ifdef _WIN32 # ifdef _WIN32
TEST(FileTest, ConvertReadCount) { TEST(FileTest, ConvertReadCount) {
file read_end, write_end; file read_end, write_end;
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
++size;
read_count = 1; read_count = 1;
read_nbyte = 0; read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), fmt::system_error); EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
@ -326,20 +330,20 @@ TEST(FileTest, ConvertWriteCount) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
++size;
write_count = 1; write_count = 1;
write_nbyte = 0; write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), fmt::system_error); EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
write_count = 0; write_count = 0;
EXPECT_EQ(UINT_MAX, write_nbyte); EXPECT_EQ(UINT_MAX, write_nbyte);
} }
#endif # endif
TEST(FileTest, DupNoRetry) { TEST(FileTest, DupNoRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
dup_count = 1; 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)); fmt::format("cannot duplicate file descriptor {}", stdout_fd));
dup_count = 0; dup_count = 0;
} }
@ -348,8 +352,8 @@ TEST(FileTest, Dup2Retry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd); file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
fmt::format("cannot duplicate file descriptor {} to {}", fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor())); f1.descriptor(), f2.descriptor()));
} }
TEST(FileTest, Dup2NoExceptRetry) { TEST(FileTest, Dup2NoExceptRetry) {
@ -358,19 +362,19 @@ TEST(FileTest, Dup2NoExceptRetry) {
error_code ec; error_code ec;
dup2_count = 1; dup2_count = 1;
f1.dup2(f2.descriptor(), ec); f1.dup2(f2.descriptor(), ec);
#ifndef _WIN32 # ifndef _WIN32
EXPECT_EQ(4, dup2_count); EXPECT_EQ(4, dup2_count);
#else # else
EXPECT_EQ(EINTR, ec.get()); EXPECT_EQ(EINTR, ec.get());
#endif # endif
dup2_count = 0; dup2_count = 0;
} }
TEST(FileTest, PipeNoRetry) { TEST(FileTest, PipeNoRetry) {
file read_end, write_end; file read_end, write_end;
pipe_count = 1; pipe_count = 1;
EXPECT_SYSTEM_ERROR( EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
file::pipe(read_end, write_end), EINTR, "cannot create pipe"); "cannot create pipe");
pipe_count = 0; pipe_count = 0;
} }
@ -378,34 +382,37 @@ TEST(FileTest, FdopenNoRetry) {
file read_end, write_end; file read_end, write_end;
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
fdopen_count = 1; fdopen_count = 1;
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR,
EINTR, "cannot associate stream with file descriptor"); "cannot associate stream with file descriptor");
fdopen_count = 0; fdopen_count = 0;
} }
TEST(BufferedFileTest, OpenRetry) { TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here"); write_file("temp", "there must be something here");
scoped_ptr<buffered_file> f{FMT_NULL}; std::unique_ptr<buffered_file> f{nullptr};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
fopen, "cannot open file test"); "cannot open file temp");
#ifndef _WIN32 # ifndef _WIN32
char c = 0; char c = 0;
if (fread(&c, 1, 1, f->get()) < 1) if (fread(&c, 1, 1, f->get()) < 1)
throw fmt::system_error(errno, "fread failed"); throw fmt::system_error(errno, "fread failed");
#endif # endif
} }
TEST(BufferedFileTest, CloseNoRetryInDtor) { TEST(BufferedFileTest, CloseNoRetryInDtor) {
file read_end, write_end; file read_end, write_end;
file::pipe(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; int saved_fclose_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(
fclose_count = 1; stderr,
f.reset(FMT_NULL); {
saved_fclose_count = fclose_count; fclose_count = 1;
fclose_count = 0; f.reset(nullptr);
}, format_system_error(EINTR, "cannot close file") + "\n"); saved_fclose_count = fclose_count;
fclose_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_fclose_count); EXPECT_EQ(2, saved_fclose_count);
} }
@ -428,111 +435,123 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count); EXPECT_EQ(2, fileno_count);
fileno_count = 0; fileno_count = 0;
} }
#endif // FMT_USE_FCNTL
struct TestMock { struct test_mock {
static TestMock *instance; static test_mock* instance;
} *TestMock::instance; } * test_mock::instance;
TEST(ScopedMock, Scope) { TEST(ScopedMock, Scope) {
{ {
ScopedMock<TestMock> mock; ScopedMock<test_mock> mock;
EXPECT_EQ(&mock, TestMock::instance); EXPECT_EQ(&mock, test_mock::instance);
TestMock &copy = mock; test_mock& copy = mock;
static_cast<void>(copy); static_cast<void>(copy);
} }
EXPECT_EQ(FMT_NULL, TestMock::instance); EXPECT_EQ(nullptr, test_mock::instance);
} }
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
typedef fmt::Locale::Type LocaleType; typedef fmt::locale::type locale_type;
struct LocaleMock { struct locale_mock {
static LocaleMock *instance; static locale_mock* instance;
MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale, MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
LocaleType base)); locale_type base));
MOCK_METHOD1(freelocale, void (LocaleType locale)); MOCK_METHOD1(freelocale, void(locale_type locale));
MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr, MOCK_METHOD3(strtod_l,
LocaleType locale)); double(const char* nptr, char** endptr, locale_type locale));
} *LocaleMock::instance; } * locale_mock::instance;
#ifdef _MSC_VER # ifdef _MSC_VER
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4273) # 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) { _locale_t _create_locale(int category, const char* locale) {
return LocaleMock::instance->newlocale(category, locale, 0); return locale_mock::instance->newlocale(category, locale, 0);
} }
void _free_locale(_locale_t locale) { 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) { double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale); return locale_mock::instance->strtod_l(nptr, endptr, locale);
} }
# pragma warning(pop) # ifdef __clang__
#endif # pragma clang diagnostic pop
# endif
# pragma warning(pop)
# endif
#if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408 # if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
#define FMT_LOCALE_THROW __THROW # define FMT_LOCALE_THROW __THROW
#else # else
#define FMT_LOCALE_THROW # define FMT_LOCALE_THROW
#endif # endif
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) FMT_LOCALE_THROW { # if defined(__APPLE__) || \
return LocaleMock::instance->newlocale(category_mask, locale, base); (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
}
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult; typedef int FreeLocaleResult;
#else # else
typedef void FreeLocaleResult; typedef void FreeLocaleResult;
#endif # endif
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW { FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
LocaleMock::instance->freelocale(locale); locale_mock::instance->freelocale(locale);
return FreeLocaleResult(); return FreeLocaleResult();
} }
double strtod_l(const char *nptr, char **endptr, LocaleType locale) FMT_LOCALE_THROW { double strtod_l(const char* nptr, char** endptr,
return LocaleMock::instance->strtod_l(nptr, endptr, locale); 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) { TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock; ScopedMock<locale_mock> mock;
LocaleType locale = reinterpret_cast<LocaleType>(11); locale_type locale = reinterpret_cast<locale_type>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale)); EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale); FMT_SYSTEM(newlocale(222, "foo", locale));
} }
# endif
TEST(LocaleTest, Locale) { TEST(LocaleTest, Locale) {
#ifndef LC_NUMERIC_MASK # ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { LC_NUMERIC_MASK = LC_NUMERIC };
#endif # endif
ScopedMock<LocaleMock> mock; ScopedMock<locale_mock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42); locale_type impl = reinterpret_cast<locale_type>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), FMT_NULL)) EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
.WillOnce(Return(impl)); .WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl)); EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale; fmt::locale loc;
EXPECT_EQ(impl, locale.get()); EXPECT_EQ(impl, loc.get());
} }
TEST(LocaleTest, Strtod) { TEST(LocaleTest, Strtod) {
ScopedMock<LocaleMock> mock; ScopedMock<locale_mock> mock;
EXPECT_CALL(mock, newlocale(_, _, _)) EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42))); .WillOnce(Return(reinterpret_cast<locale_type>(42)));
EXPECT_CALL(mock, freelocale(_)); EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale; fmt::locale loc;
const char *str = "4.2"; const char* str = "4.2";
char end = 'x'; 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))); .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); EXPECT_EQ(&end, str);
} }

View File

@ -9,13 +9,17 @@
#define FMT_POSIX_TEST_H #define FMT_POSIX_TEST_H
#include <errno.h> #include <errno.h>
#include <locale.h>
#include <stdio.h> #include <stdio.h>
#ifdef __APPLE__
# include <xlocale.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#else #else
# include <sys/param.h> // for FreeBSD version # include <sys/param.h> // for FreeBSD version
# include <sys/types.h> // for ssize_t # include <sys/types.h> // for ssize_t
#endif #endif
#ifndef _MSC_VER #ifndef _MSC_VER
@ -28,13 +32,13 @@ namespace test {
// Size type for read and write. // Size type for read and write.
typedef size_t size_t; typedef size_t size_t;
typedef ssize_t ssize_t; typedef ssize_t ssize_t;
int open(const char *path, int oflag, int mode); int open(const char* path, int oflag, int mode);
int fstat(int fd, struct stat *buf); int fstat(int fd, struct stat* buf);
#else #else
typedef unsigned size_t; typedef unsigned size_t;
typedef int ssize_t; typedef int ssize_t;
errno_t sopen_s( errno_t sopen_s(int* pfh, const char* filename, int oflag, int shflag,
int* pfh, const char *filename, int oflag, int shflag, int pmode); int pmode);
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
@ -48,20 +52,24 @@ int close(int fildes);
int dup(int fildes); int dup(int fildes);
int dup2(int fildes, int fildes2); 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 read(int fildes, void* buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte); ssize_t write(int fildes, const void* buf, size_t nbyte);
#ifndef _WIN32 #ifndef _WIN32
int pipe(int fildes[2]); int pipe(int fildes[2]);
#else #else
int pipe(int *pfds, unsigned psize, int textmode); int pipe(int* pfds, unsigned psize, int textmode);
#endif #endif
FILE *fopen(const char *filename, const char *mode); FILE* fopen(const char* filename, const char* mode);
int fclose(FILE *stream); int fclose(FILE* stream);
int (fileno)(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 } // namespace test
#define FMT_SYSTEM(call) test::call #define FMT_SYSTEM(call) test::call

View File

@ -5,17 +5,19 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#include "fmt/printf.h"
#include <cctype> #include <cctype>
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include "fmt/core.h" #include "fmt/core.h"
#include "fmt/printf.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
using fmt::format; using fmt::format;
using fmt::format_error; using fmt::format_error;
using fmt::internal::max_value;
const unsigned BIG_NUM = INT_MAX + 1u; 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 // A wrapper around fmt::sprintf to workaround bogus warnings about invalid
// format strings in MSVC. // format strings in MSVC.
template <typename... Args> 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...); return fmt::sprintf(format, args...);
} }
template <typename... Args> template <typename... Args>
std::wstring test_sprintf(fmt::wstring_view format, const Args &... args) { std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) {
return fmt::sprintf(format, 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)) \ EXPECT_EQ(expected_output, test_sprintf(format, arg)) \
<< "format: " << format; \ << "format: " << format; \
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg)) EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
TEST(PrintfTest, NoArgs) { TEST(PrintfTest, NoArgs) {
@ -69,11 +71,10 @@ TEST(PrintfTest, Escape) {
TEST(PrintfTest, PositionalArgs) { TEST(PrintfTest, PositionalArgs) {
EXPECT_EQ("42", test_sprintf("%1$d", 42)); EXPECT_EQ("42", test_sprintf("%1$d", 42));
EXPECT_EQ("before 42", test_sprintf("before %1$d", 42)); EXPECT_EQ("before 42", test_sprintf("before %1$d", 42));
EXPECT_EQ("42 after", test_sprintf("%1$d after",42)); EXPECT_EQ("42 after", test_sprintf("%1$d after", 42));
EXPECT_EQ("before 42 after", test_sprintf("before %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("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42));
EXPECT_EQ("42 is the answer", EXPECT_EQ("42 is the answer", test_sprintf("%2$d is the %1$s", "answer", 42));
test_sprintf("%2$d is the %1$s", "answer", 42));
EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad")); EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
} }
@ -82,46 +83,46 @@ TEST(PrintfTest, AutomaticArgIndexing) {
} }
TEST(PrintfTest, NumberIsTooBigInArgIndex) { TEST(PrintfTest, NumberIsTooBigInArgIndex) {
EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), format_error,
format_error, "number is too big"); "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), format_error,
format_error, "number is too big"); "number is too big");
} }
TEST(PrintfTest, SwitchArgIndexing) { TEST(PrintfTest, SwitchArgIndexing) {
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
format_error, "cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
format_error, "cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error,
format_error, "cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), format_error,
format_error, "number is too big"); "number is too big");
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error,
format_error, "cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
// Indexing errors override width errors. // Indexing errors override width errors.
EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
} }
TEST(PrintfTest, InvalidArgIndex) { TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error, 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, EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
format_error, "argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
format_error, "argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
format_error, "number is too big"); "number is too big");
} }
TEST(PrintfTest, DefaultAlignRight) { TEST(PrintfTest, DefaultAlignRight) {
@ -175,8 +176,8 @@ TEST(PrintfTest, HashFlag) {
EXPECT_PRINTF("0x42", "%#x", 0x42); EXPECT_PRINTF("0x42", "%#x", 0x42);
EXPECT_PRINTF("0X42", "%#X", 0x42); EXPECT_PRINTF("0X42", "%#X", 0x42);
EXPECT_PRINTF( EXPECT_PRINTF(fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x",
fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x", -0x42); -0x42);
EXPECT_PRINTF("0", "%#x", 0); EXPECT_PRINTF("0", "%#x", 0);
EXPECT_PRINTF("0x0042", "%#06x", 0x42); EXPECT_PRINTF("0x0042", "%#06x", 0x42);
@ -208,23 +209,23 @@ TEST(PrintfTest, Width) {
// Width cannot be specified twice. // Width cannot be specified twice.
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
"invalid type specifier"); "invalid type specifier");
EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), format_error,
format_error, "number is too big"); "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), format_error,
format_error, "number is too big"); "number is too big");
} }
TEST(PrintfTest, DynamicWidth) { TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42)); EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42));
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42)); EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
"width is not integer"); "width is not integer");
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, 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, EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
} }
TEST(PrintfTest, IntPrecision) { TEST(PrintfTest, IntPrecision) {
@ -253,8 +254,7 @@ TEST(PrintfTest, FloatPrecision) {
safe_sprintf(buffer, "%.3e", 1234.5678); safe_sprintf(buffer, "%.3e", 1234.5678);
EXPECT_PRINTF(buffer, "%.3e", 1234.5678); EXPECT_PRINTF(buffer, "%.3e", 1234.5678);
EXPECT_PRINTF("1234.568", "%.3f", 1234.5678); EXPECT_PRINTF("1234.568", "%.3f", 1234.5678);
safe_sprintf(buffer, "%.3g", 1234.5678); EXPECT_PRINTF("1.23e+03", "%.3g", 1234.5678);
EXPECT_PRINTF(buffer, "%.3g", 1234.5678);
safe_sprintf(buffer, "%.3a", 1234.5678); safe_sprintf(buffer, "%.3a", 1234.5678);
EXPECT_PRINTF(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("00042", test_sprintf("%.*d", 5, 42));
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42)); EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, 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, EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
if (sizeof(long long) != sizeof(int)) { if (sizeof(long long) != sizeof(int)) {
long long prec = static_cast<long long>(INT_MIN) - 1; long long prec = static_cast<long long>(INT_MIN) - 1;
EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error,
"number is too big"); "number is too big");
} }
} }
template <typename T> template <typename T> struct make_signed { typedef T type; };
struct make_signed { typedef T type; };
#define SPECIALIZE_MAKE_SIGNED(T, S) \ #define SPECIALIZE_MAKE_SIGNED(T, S) \
template <> \ template <> struct make_signed<T> { typedef S type; }
struct make_signed<T> { typedef S type; }
SPECIALIZE_MAKE_SIGNED(char, signed char); SPECIALIZE_MAKE_SIGNED(char, signed char);
SPECIALIZE_MAKE_SIGNED(unsigned 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``. // Test length format specifier ``length_spec``.
template <typename T, typename U> template <typename T, typename U>
void TestLength(const char *length_spec, U value) { void TestLength(const char* length_spec, U value) {
long long signed_value = 0; long long signed_value = 0;
unsigned long long unsigned_value = 0; unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument. // Apply integer promotion to the argument.
using std::numeric_limits; unsigned long long max = max_value<U>();
unsigned long long max = numeric_limits<U>::max();
using fmt::internal::const_check; 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); signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned>(value); unsigned_value = static_cast<unsigned long long>(value);
} else if (const_check(max <= numeric_limits<unsigned>::max())) { } else if (const_check(max <= max_value<unsigned>())) {
signed_value = static_cast<unsigned>(value); 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)) { if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
signed_value = static_cast<long long>(value); signed_value = static_cast<long long>(value);
unsigned_value = unsigned_value = static_cast<unsigned long long>(
static_cast<typename std::make_unsigned<unsigned>::type>(value); static_cast<typename std::make_unsigned<unsigned>::type>(value));
} else { } else {
signed_value = static_cast<typename make_signed<T>::type>(value); signed_value = static_cast<typename make_signed<T>::type>(value);
unsigned_value = static_cast<typename std::make_unsigned<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); EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value);
} }
template <typename T> template <typename T> void TestLength(const char* length_spec) {
void TestLength(const char *length_spec) { T min = std::numeric_limits<T>::min(), max = max_value<T>();
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
TestLength<T>(length_spec, 42); TestLength<T>(length_spec, 42);
TestLength<T>(length_spec, -42); TestLength<T>(length_spec, -42);
TestLength<T>(length_spec, min); TestLength<T>(length_spec, min);
TestLength<T>(length_spec, max); TestLength<T>(length_spec, max);
TestLength<T>(length_spec, static_cast<long long>(min) - 1); long long long_long_min = std::numeric_limits<long long>::min();
unsigned long long long_long_max = std::numeric_limits<long long>::max(); 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) if (static_cast<unsigned long long>(max) < long_long_max)
TestLength<T>(length_spec, static_cast<long long>(max) + 1); 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<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>::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>::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>::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>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::max()); TestLength<T>(length_spec, max_value<unsigned long long>());
} }
TEST(PrintfTest, Length) { TEST(PrintfTest, Length) {
@ -371,9 +369,9 @@ TEST(PrintfTest, Length) {
TestLength<intmax_t>("j"); TestLength<intmax_t>("j");
TestLength<std::size_t>("z"); TestLength<std::size_t>("z");
TestLength<std::ptrdiff_t>("t"); TestLength<std::ptrdiff_t>("t");
long double max = std::numeric_limits<long double>::max(); long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{}", max), "%g", max); EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
EXPECT_PRINTF(fmt::format("{}", max), "%Lg", max); EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
} }
TEST(PrintfTest, Bool) { TEST(PrintfTest, Bool) {
@ -394,7 +392,7 @@ TEST(PrintfTest, Int) {
TEST(PrintfTest, long_long) { TEST(PrintfTest, long_long) {
// fmt::printf allows passing long long arguments to %d without length // fmt::printf allows passing long long arguments to %d without length
// specifiers. // specifiers.
long long max = std::numeric_limits<long long>::max(); long long max = max_value<long long>();
EXPECT_PRINTF(fmt::format("{}", max), "%d", max); EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
} }
@ -411,6 +409,9 @@ TEST(PrintfTest, Float) {
EXPECT_PRINTF(buffer, "%E", 392.65); EXPECT_PRINTF(buffer, "%E", 392.65);
EXPECT_PRINTF("392.65", "%g", 392.65); EXPECT_PRINTF("392.65", "%g", 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); safe_sprintf(buffer, "%a", -392.65);
EXPECT_EQ(buffer, format("{:a}", -392.65)); EXPECT_EQ(buffer, format("{:a}", -392.65));
safe_sprintf(buffer, "%A", -392.65); safe_sprintf(buffer, "%A", -392.65);
@ -428,44 +429,44 @@ TEST(PrintfTest, Inf) {
TEST(PrintfTest, Char) { TEST(PrintfTest, Char) {
EXPECT_PRINTF("x", "%c", 'x'); 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(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(L"x", L"%c", L'x');
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max); EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
} }
TEST(PrintfTest, String) { TEST(PrintfTest, String) {
EXPECT_PRINTF("abc", "%s", "abc"); 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)", "%s", null_str);
EXPECT_PRINTF(" (null)", "%10s", null_str); EXPECT_PRINTF(" (null)", "%10s", null_str);
EXPECT_PRINTF(L"abc", L"%s", L"abc"); 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"%s", null_wstr);
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
} }
TEST(PrintfTest, Pointer) { TEST(PrintfTest, Pointer) {
int n; int n;
void *p = &n; void* p = &n;
EXPECT_PRINTF(fmt::format("{}", p), "%p", p); EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
p = FMT_NULL; p = nullptr;
EXPECT_PRINTF("(nil)", "%p", p); EXPECT_PRINTF("(nil)", "%p", p);
EXPECT_PRINTF(" (nil)", "%10p", p); EXPECT_PRINTF(" (nil)", "%10p", p);
const char *s = "test"; const char* s = "test";
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s); 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); EXPECT_PRINTF("(nil)", "%p", null_str);
p = &n; p = &n;
EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p); 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"%p", p);
EXPECT_PRINTF(L" (nil)", L"%10p", 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); 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); EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
} }
@ -473,16 +474,18 @@ TEST(PrintfTest, Location) {
// TODO: test %n // TODO: test %n
} }
enum E { A = 42 }; enum test_enum { answer = 42 };
TEST(PrintfTest, Enum) { 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) { TEST(PrintfTest, Examples) {
const char *weekday = "Thursday"; const char* weekday = "Thursday";
const char *month = "August"; const char* month = "August";
int day = 21; int day = 21;
EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day), EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day),
"Thursday, 21 August"); "Thursday, 21 August");
@ -496,12 +499,12 @@ TEST(PrintfTest, PrintfError) {
} }
#endif #endif
TEST(PrintfTest, WideString) { TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}
TEST(PrintfTest, PrintfCustom) { 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) { TEST(PrintfTest, OStream) {
@ -520,7 +523,7 @@ TEST(PrintfTest, VPrintf) {
EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42"); 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) { void check_format_string_regression(fmt::string_view s, const Args&... args) {
fmt::sprintf(s, args...); fmt::sprintf(s, args...);
} }
@ -529,41 +532,90 @@ TEST(PrintfTest, CheckFormatStringRegression) {
check_format_string_regression("%c%s", 'x', ""); check_format_string_regression("%c%s", 'x', "");
} }
TEST(PrintfTest, FixedLargeExponent) {
EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21));
}
TEST(PrintfTest, VSPrintfMakeArgsExample) { TEST(PrintfTest, VSPrintfMakeArgsExample) {
fmt::format_arg_store<fmt::printf_context, int, const char *> as{ fmt::format_arg_store<fmt::printf_context, int, const char*> as{42,
42, "something"}; "something"};
fmt::basic_format_args<fmt::printf_context> args(as); fmt::basic_format_args<fmt::printf_context> args(as);
EXPECT_EQ( EXPECT_EQ("[42] something happened", fmt::vsprintf("[%d] %s happened", args));
"[42] something happened", fmt::vsprintf("[%d] %s happened", args));
auto as2 = fmt::make_printf_args(42, "something"); auto as2 = fmt::make_printf_args(42, "something");
fmt::basic_format_args<fmt::printf_context> args2(as2); fmt::basic_format_args<fmt::printf_context> args2(as2);
EXPECT_EQ( EXPECT_EQ("[42] something happened",
"[42] something happened", fmt::vsprintf("[%d] %s happened", args2)); fmt::vsprintf("[%d] %s happened", args2));
//the older gcc versions can't cast the return value // The older gcc versions can't cast the return value.
#if !defined(__GNUC__) || (__GNUC__ > 4) #if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ( EXPECT_EQ("[42] something happened",
"[42] something happened", fmt::vsprintf("[%d] %s happened",
fmt::vsprintf( {fmt::make_printf_args(42, "something")}));
"[%d] %s happened", fmt::make_printf_args(42, "something")));
#endif #endif
} }
TEST(PrintfTest, VSPrintfMakeWArgsExample) { TEST(PrintfTest, VSPrintfMakeWArgsExample) {
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t *> as{ fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{
42, L"something"}; 42, L"something"};
fmt::basic_format_args<fmt::wprintf_context> args(as); fmt::basic_format_args<fmt::wprintf_context> args(as);
EXPECT_EQ( EXPECT_EQ(L"[42] something happened",
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened", args));
fmt::vsprintf(L"[%d] %s happened", args)); auto as2 = fmt::make_wprintf_args(42, L"something");
auto as2 = fmt::make_wprintf_args(42, L"something");
fmt::basic_format_args<fmt::wprintf_context> args2(as2); fmt::basic_format_args<fmt::wprintf_context> args2(as2);
EXPECT_EQ( EXPECT_EQ(L"[42] something happened",
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened", args2)); fmt::vsprintf(L"[%d] %s happened", args2));
// the older gcc versions can't cast the return value // the older gcc versions can't cast the return value
#if !defined(__GNUC__) || (__GNUC__ > 4) #if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ( EXPECT_EQ(L"[42] something happened",
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened",
fmt::vsprintf( {fmt::make_wprintf_args(42, L"something")}));
L"[%d] %s happened", fmt::make_wprintf_args(42, L"something")));
#endif #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));
}

View File

@ -9,17 +9,18 @@
// All Rights Reserved // All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface. // {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) || \ #if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
#include "fmt/ranges.h" # include <array>
#include "gtest.h" # include <map>
# include <string>
#include <vector> # include <vector>
#include <array>
#include <map>
#include <string>
TEST(RangesTest, FormatVector) { TEST(RangesTest, FormatVector) {
std::vector<int32_t> iv{1, 2, 3, 5, 7, 11}; std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
@ -39,21 +40,46 @@ TEST(RangesTest, FormatMap) {
} }
TEST(RangesTest, FormatPair) { TEST(RangesTest, FormatPair) {
std::pair<int64_t, float> pa1{42, 3.14159265358979f}; std::pair<int64_t, float> pa1{42, 1.5f};
EXPECT_EQ("(42, 3.14159)", fmt::format("{}", pa1)); EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
} }
TEST(RangesTest, FormatTuple) { TEST(RangesTest, FormatTuple) {
std::tuple<int64_t, float, std::string, char> tu1{42, 3.14159265358979f, std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
"this is tuple", 'i'}; 'i'};
EXPECT_EQ("(42, 3.14159, \"this is tuple\", 'i')", fmt::format("{}", tu1)); 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 { struct my_struct {
int32_t i; int32_t i;
std::string str; // can throw std::string str; // can throw
template <std::size_t N> template <std::size_t N> decltype(auto) get() const noexcept {
decltype(auto) get() const noexcept {
if constexpr (N == 0) if constexpr (N == 0)
return i; return i;
else if constexpr (N == 1) else if constexpr (N == 1)
@ -61,8 +87,7 @@ struct my_struct {
} }
}; };
template <std::size_t N> template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept {
decltype(auto) get(const my_struct& s) noexcept {
return s.get<N>(); return s.get<N>();
} }
@ -71,8 +96,7 @@ namespace std {
template <> template <>
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {}; struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
template <std::size_t N> template <std::size_t N> struct tuple_element<N, my_struct> {
struct tuple_element<N, my_struct> {
using type = decltype(std::declval<my_struct>().get<N>()); using type = decltype(std::declval<my_struct>().get<N>());
}; };
@ -83,5 +107,36 @@ TEST(RangesTest, FormatStruct) {
EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst)); 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 > #endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
// 201402L && _MSC_VER >= 1910) // 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
View 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
View 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
View 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

View File

@ -13,9 +13,14 @@
class assertion_failure : public std::logic_error { class assertion_failure : public std::logic_error {
public: 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) \ #define FMT_ASSERT(condition, message) \
if (!(condition)) throw assertion_failure(message); if (!(condition)) throw assertion_failure(message);

View File

@ -9,23 +9,23 @@
#include "gtest.h" #include "gtest.h"
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
# include <crtdbg.h> # include <crtdbg.h>
#else #else
# define _CrtSetReportFile(a, b) # define _CrtSetReportFile(a, b)
# define _CrtSetReportMode(a, b) # define _CrtSetReportMode(a, b)
#endif #endif
int main(int argc, char **argv) { int main(int argc, char** argv) {
#ifdef _WIN32 #ifdef _WIN32
// Don't display any error dialogs. This also suppresses message boxes // Don't display any error dialogs. This also suppresses message boxes
// on assertion failures in MinGW where _set_error_mode/CrtSetReportMode // on assertion failures in MinGW where _set_error_mode/CrtSetReportMode
// doesn't help. // doesn't help.
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX); SEM_NOOPENFILEERRORBOX);
#endif #endif
// Disable message boxes on assertion failures. // Disable message boxes on assertion failures.
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);

View File

@ -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)));
}

View File

@ -8,7 +8,7 @@
#include "util.h" #include "util.h"
#include <cstring> #include <cstring>
void increment(char *s) { void increment(char* s) {
for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) { for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) {
if (s[i] != '9') { if (s[i] != '9') {
++s[i]; ++s[i];
@ -30,15 +30,20 @@ std::string get_system_error(int error_code) {
#endif #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 read_end, write_end;
fmt::file::pipe(read_end, write_end); fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT)); write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close(); write_end.close();
fmt::buffered_file f = read_end.fdopen("r"); fmt::buffered_file f = read_end.fdopen("r");
if (fp) if (fp) *fp = f.get();
*fp = f.get(); #else
fmt::buffered_file f("test-file", "w");
fputs(FILE_CONTENT, f.get());
if (fp) *fp = f.get();
#endif
return f; return f;
} }

View File

@ -9,18 +9,18 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include "fmt/posix.h" #include "fmt/os.h"
enum {BUFFER_SIZE = 256}; enum { BUFFER_SIZE = 256 };
#ifdef _MSC_VER #ifdef _MSC_VER
# define FMT_VSNPRINTF vsprintf_s # define FMT_VSNPRINTF vsprintf_s
#else #else
# define FMT_VSNPRINTF vsnprintf # define FMT_VSNPRINTF vsnprintf
#endif #endif
template <std::size_t SIZE> 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; std::va_list args;
va_start(args, format); va_start(args, format);
FMT_VSNPRINTF(buffer, SIZE, format, args); 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. // Increment a number in a string.
void increment(char *s); void increment(char* s);
std::string get_system_error(int error_code); 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. // 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__) #if defined(_WIN32) && !defined(__MINGW32__)
// Fix MSVC warning about "unsafe" fopen. // Fix MSVC warning about "unsafe" fopen.
FILE *f = 0; FILE* f = 0;
errno = fopen_s(&f, filename, mode); errno = fopen_s(&f, filename, mode);
return f; return f;
#else #else
@ -48,34 +48,33 @@ inline FILE *safe_fopen(const char *filename, const char *mode) {
#endif #endif
} }
template <typename Char> template <typename Char> class BasicTestString {
class BasicTestString {
private: private:
std::basic_string<Char> value_; std::basic_string<Char> value_;
static const Char EMPTY[]; static const Char EMPTY[];
public: 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> template <typename Char> const Char BasicTestString<Char>::EMPTY[] = {0};
const Char BasicTestString<Char>::EMPTY[] = {0};
typedef BasicTestString<char> TestString; typedef BasicTestString<char> TestString;
typedef BasicTestString<wchar_t> TestWString; typedef BasicTestString<wchar_t> TestWString;
template <typename Char> template <typename Char>
std::basic_ostream<Char> &operator<<( std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os,
std::basic_ostream<Char> &os, const BasicTestString<Char> &s) { const BasicTestString<Char>& s) {
os << s.value(); os << s.value();
return os; return os;
} }
class Date { class Date {
int year_, month_, day_; int year_, month_, day_;
public: public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {} Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}