externals: Update fmt to 5.2.1
Merge commit '5f7df9a182cdca4d7338289c2e52eddeec9c223d' into HEAD
This commit is contained in:
commit
783fc707fa
81 changed files with 12563 additions and 9605 deletions
3
externals/fmt/.github/pull_request_template
vendored
3
externals/fmt/.github/pull_request_template
vendored
|
@ -1,3 +0,0 @@
|
||||||
<!---
|
|
||||||
Please make sure you've followed the guidelines outlined in the CONTRIBUTING.rst file.
|
|
||||||
--->
|
|
14
externals/fmt/.gitignore
vendored
14
externals/fmt/.gitignore
vendored
|
@ -1,5 +1,17 @@
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
.externalNativeBuild/
|
||||||
|
.gradle/
|
||||||
|
gradle/
|
||||||
|
gradlew*
|
||||||
|
local.properties
|
||||||
|
build/
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
/_CPack_Packages
|
/_CPack_Packages
|
||||||
|
/CMakeScripts
|
||||||
/doc/doxyxml
|
/doc/doxyxml
|
||||||
/doc/html
|
/doc/html
|
||||||
virtualenv
|
virtualenv
|
||||||
|
@ -8,6 +20,7 @@ virtualenv
|
||||||
*~
|
*~
|
||||||
*.a
|
*.a
|
||||||
*.so*
|
*.so*
|
||||||
|
*.xcodeproj
|
||||||
*.zip
|
*.zip
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
CPack*.cmake
|
CPack*.cmake
|
||||||
|
@ -15,5 +28,6 @@ fmt-*.cmake
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
FMT.build
|
||||||
Makefile
|
Makefile
|
||||||
run-msbuild.bat
|
run-msbuild.bat
|
||||||
|
|
132
externals/fmt/.travis.yml
vendored
132
externals/fmt/.travis.yml
vendored
|
@ -1,27 +1,129 @@
|
||||||
language: cpp
|
language: cpp
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: required # the doc target uses sudo to install dependencies
|
sudo: false
|
||||||
|
|
||||||
|
os: linux
|
||||||
|
|
||||||
|
git:
|
||||||
|
depth: 1
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: |-
|
- secure: |-
|
||||||
Gsnp9ERFnXt+diCfc7Vb72g+7HDn1MCHvw4zfUDdoBh9bxxFlLQRlzZZfwWhzni57lflrt
|
a1eovNn4uol9won7ghr67eD3/59oeESN+G9bWE+ecI1V6yRseG9whniGhIpC/YfMW/Qz5I
|
||||||
0QHXafu+oBVOJuNv6WauV3+ZyuWIQRmNGjZFNLvZsXHK/dyad2vGQBPvEkb+8l/aCyTpbr
|
5sxSmFjaw9bxCISNwUIrL1O5x2AmRYTnFcXk4dFsUvlZg+WeF/aKyBYCNRM8C2ndbBmtAO
|
||||||
6pxmyzLHSn1ZR7OX5rfPvwM3tOyZ3H0=
|
o1F2EwFbiso0EmtzhAPs19ujiVxkLn4=
|
||||||
matrix:
|
|
||||||
- BUILD=Doc
|
|
||||||
- BUILD=Debug STANDARD=0x
|
|
||||||
- BUILD=Release STANDARD=98
|
|
||||||
- BUILD=Release STANDARD=0x
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
include:
|
||||||
- os: osx
|
# Documentation
|
||||||
env: BUILD=Doc
|
- env: BUILD=Doc
|
||||||
|
sudo: required
|
||||||
|
# g++ 6 on Linux with C++14
|
||||||
|
- env: COMPILER=g++-6 BUILD=Debug STANDARD=14
|
||||||
|
compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
update: true
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-6
|
||||||
|
- env: COMPILER=g++-6 BUILD=Release STANDARD=14
|
||||||
|
compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
update: true
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-6
|
||||||
|
# Apple clang on OS X with C++14
|
||||||
|
- env: BUILD=Debug STANDARD=14
|
||||||
|
compiler: clang
|
||||||
|
os: osx
|
||||||
|
- env: BUILD=Release STANDARD=14
|
||||||
|
compiler: clang
|
||||||
|
os: osx
|
||||||
|
# clang 6.0 on Linux with C++14
|
||||||
|
- env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14
|
||||||
|
compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
update: true
|
||||||
|
packages:
|
||||||
|
- clang-6.0
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- llvm-toolchain-trusty
|
||||||
|
- llvm-toolchain-trusty-6.0
|
||||||
|
# clang 4.0 on Linux with C++14
|
||||||
|
- env: COMPILER=clang++-4.0 BUILD=Debug STANDARD=11
|
||||||
|
compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
update: true
|
||||||
|
packages:
|
||||||
|
- clang-4.0
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- llvm-toolchain-trusty
|
||||||
|
- llvm-toolchain-trusty-4.0
|
||||||
|
# g++ 4.8 on Linux with C++11
|
||||||
|
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
|
||||||
|
compiler: gcc
|
||||||
|
# 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
|
||||||
|
# android:
|
||||||
|
# addons:
|
||||||
|
# apt:
|
||||||
|
# update: true
|
||||||
|
# components:
|
||||||
|
# - tools
|
||||||
|
# - platform-tools
|
||||||
|
# - android-21
|
||||||
|
# - sys-img-armeabi-v7a-android-21
|
||||||
|
# env:
|
||||||
|
# - ANDROID=true
|
||||||
|
# before_install:
|
||||||
|
# - git submodule update --init --recursive
|
||||||
|
# - sudo apt-get install wget unzip tree
|
||||||
|
# install:
|
||||||
|
# # Accept SDK Licenses + Install NDK
|
||||||
|
# - yes | sdkmanager --update > /dev/null 2>&1
|
||||||
|
# - sdkmanager ndk-bundle > /dev/null 2>&1
|
||||||
|
# # Download Gradle 4.3.1
|
||||||
|
# - wget https://services.gradle.org/distributions/gradle-4.3.1-bin.zip
|
||||||
|
# - mkdir -p gradle
|
||||||
|
# - unzip -q -d ./gradle gradle-4.3.1-bin.zip
|
||||||
|
# - export GRADLE=${TRAVIS_BUILD_DIR}/gradle/gradle-4.3.1/bin/gradle
|
||||||
|
# before_script:
|
||||||
|
# - bash $GRADLE --version
|
||||||
|
# - cd ./support
|
||||||
|
# script:
|
||||||
|
# - bash $GRADLE clean assemble
|
||||||
|
# after_success:
|
||||||
|
# - cd ${TRAVIS_BUILD_DIR}
|
||||||
|
# - tree ./libs
|
||||||
|
allow_failures:
|
||||||
|
# Errors
|
||||||
|
- env: COMPILER=g++-4.4 BUILD=Debug STANDARD=11
|
||||||
|
compiler: gcc
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi
|
||||||
|
- if [[ "${BUILD}" != "Doc" ]]; then ${CXX} --version; fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- support/travis-build.py
|
- support/travis-build.py
|
||||||
|
|
184
externals/fmt/CMakeLists.txt
vendored
184
externals/fmt/CMakeLists.txt
vendored
|
@ -1,20 +1,18 @@
|
||||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8.12)
|
# Use newer policies if available, up to most recent tested version of CMake.
|
||||||
|
if(${CMAKE_VERSION} VERSION_LESS 3.11)
|
||||||
if (POLICY CMP0048) # Version variables
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
cmake_policy(SET CMP0048 OLD)
|
else()
|
||||||
endif ()
|
cmake_policy(VERSION 3.11)
|
||||||
|
endif()
|
||||||
if (POLICY CMP0063) # Visibility
|
|
||||||
cmake_policy(SET CMP0063 OLD)
|
|
||||||
endif (POLICY CMP0063)
|
|
||||||
|
|
||||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||||
# or if it is the master project.
|
# or if it is the master project.
|
||||||
set(MASTER_PROJECT OFF)
|
set(MASTER_PROJECT OFF)
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
set(MASTER_PROJECT ON)
|
set(MASTER_PROJECT ON)
|
||||||
|
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Joins arguments and places the results in ${result_var}.
|
# Joins arguments and places the results in ${result_var}.
|
||||||
|
@ -36,19 +34,19 @@ if (NOT CMAKE_BUILD_TYPE)
|
||||||
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)
|
||||||
|
|
||||||
# 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_USE_CPP11 "Enable the addition of C++11 compiler flags." ON)
|
|
||||||
|
|
||||||
project(FMT)
|
project(FMT)
|
||||||
|
|
||||||
# Starting with cmake 3.0 VERSION is part of the project command.
|
# Get version from core.h
|
||||||
file(READ fmt/format.h format_h)
|
file(READ include/fmt/core.h core_h)
|
||||||
if (NOT format_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
|
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
|
||||||
message(FATAL_ERROR "Cannot get FMT_VERSION from format.h.")
|
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.")
|
||||||
endif ()
|
endif ()
|
||||||
# Use math to skip leading zeros if any.
|
# Use math to skip leading zeros if any.
|
||||||
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||||
|
@ -65,10 +63,59 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
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")
|
||||||
|
|
||||||
include(cxx11)
|
include(cxx14)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wshadow -pedantic)
|
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
||||||
|
-Wold-style-cast -Wfloat-equal -Wlogical-op -Wundef
|
||||||
|
-Wredundant-decls -Wshadow -Wwrite-strings -Wpointer-arith
|
||||||
|
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
||||||
|
-Wcast-align -Wnon-virtual-dtor
|
||||||
|
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||||
|
-Winvalid-pch -Woverloaded-virtual
|
||||||
|
-Wno-ctor-dtor-privacy -Wno-dangling-else -Wno-float-equal
|
||||||
|
-Wno-format-nonliteral -Wno-sign-conversion -Wno-shadow)
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept)
|
||||||
|
endif ()
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
||||||
|
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
||||||
|
-Wvector-operation-performance -Wsized-deallocation)
|
||||||
|
endif ()
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
||||||
|
-Wnull-dereference -Wduplicated-cond)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(WERROR_FLAG -Werror)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS -Weverything -Wpedantic
|
||||||
|
-Wno-weak-vtables -Wno-padded -Wno-gnu-statement-expression
|
||||||
|
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
|
||||||
|
-Wno-global-constructors -Wno-disabled-macro-expansion
|
||||||
|
-Wno-switch-enum -Wno-documentation-unknown-command
|
||||||
|
-Wno-unused-member-function
|
||||||
|
-Wno-format-nonliteral -Wno-missing-noreturn -Wno-undefined-func-template
|
||||||
|
-Wno-shadow -Wno-sign-conversion -Wno-used-but-marked-unused
|
||||||
|
-Wno-covered-switch-default -Wno-missing-prototypes
|
||||||
|
-Wno-missing-variable-declarations -Wno-double-promotion)
|
||||||
|
|
||||||
|
set(WERROR_FLAG -Werror)
|
||||||
|
|
||||||
|
check_cxx_compiler_flag(-Wno-zero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
||||||
|
if (HAS_NULLPTR_WARNING)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||||
|
-Wno-zero-as-null-pointer-constant)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS /W3)
|
||||||
|
set(WERROR_FLAG /WX)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||||
|
@ -94,7 +141,106 @@ else ()
|
||||||
check_symbol_exists(open fcntl.h HAVE_OPEN)
|
check_symbol_exists(open fcntl.h HAVE_OPEN)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(fmt)
|
function(add_headers VAR)
|
||||||
|
set(headers ${${VAR}})
|
||||||
|
foreach (header ${ARGN})
|
||||||
|
set(headers ${headers} include/fmt/${header})
|
||||||
|
endforeach()
|
||||||
|
set(${VAR} ${headers} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Define the fmt library, its includes and the needed defines.
|
||||||
|
add_headers(FMT_HEADERS color.h core.h format.h format-inl.h ostream.h printf.h
|
||||||
|
time.h ranges.h)
|
||||||
|
set(FMT_SOURCES src/format.cc)
|
||||||
|
if (HAVE_OPEN)
|
||||||
|
add_headers(FMT_HEADERS posix.h)
|
||||||
|
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
||||||
|
add_library(fmt::fmt ALIAS fmt)
|
||||||
|
|
||||||
|
if (FMT_WERROR)
|
||||||
|
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
|
||||||
|
endif ()
|
||||||
|
if (FMT_PEDANTIC)
|
||||||
|
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_include_directories(fmt PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
|
set_target_properties(fmt PROPERTIES
|
||||||
|
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
||||||
|
DEBUG_POSTFIX d)
|
||||||
|
|
||||||
|
if (BUILD_SHARED_LIBS)
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
# Fix rpmlint warning:
|
||||||
|
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
|
||||||
|
target_link_libraries(fmt -Wl,--as-needed)
|
||||||
|
endif ()
|
||||||
|
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_library(fmt-header-only INTERFACE)
|
||||||
|
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
||||||
|
|
||||||
|
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
||||||
|
|
||||||
|
target_include_directories(fmt-header-only INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
|
# Install targets.
|
||||||
|
if (FMT_INSTALL)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||||
|
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||||
|
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
||||||
|
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
||||||
|
set(targets_export_name fmt-targets)
|
||||||
|
|
||||||
|
set (INSTALL_TARGETS fmt)
|
||||||
|
if (TARGET fmt-header-only)
|
||||||
|
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||||
|
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||||
|
|
||||||
|
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
||||||
|
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||||
|
|
||||||
|
# Generate the version, config and target files into the build directory.
|
||||||
|
write_basic_package_version_file(
|
||||||
|
${version_config}
|
||||||
|
VERSION ${FMT_VERSION}
|
||||||
|
COMPATIBILITY AnyNewerVersion)
|
||||||
|
configure_package_config_file(
|
||||||
|
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
|
||||||
|
${project_config}
|
||||||
|
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
# Use a namespace because CMake provides better diagnostics for namespaced
|
||||||
|
# imported targets.
|
||||||
|
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
||||||
|
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
||||||
|
|
||||||
|
# Install version, config and target files.
|
||||||
|
install(
|
||||||
|
FILES ${project_config} ${version_config}
|
||||||
|
DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
||||||
|
NAMESPACE fmt::)
|
||||||
|
|
||||||
|
# Install the library and headers.
|
||||||
|
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
||||||
|
DESTINATION ${FMT_LIB_DIR})
|
||||||
|
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (FMT_DOC)
|
if (FMT_DOC)
|
||||||
add_subdirectory(doc)
|
add_subdirectory(doc)
|
||||||
|
|
3
externals/fmt/CONTRIBUTING.rst
vendored
3
externals/fmt/CONTRIBUTING.rst
vendored
|
@ -6,6 +6,7 @@ All C++ code must adhere to `Google C++ Style Guide
|
||||||
exceptions:
|
exceptions:
|
||||||
|
|
||||||
* Exceptions are permitted
|
* Exceptions are permitted
|
||||||
* snake_case should be used instead of UpperCamelCase for function names
|
* snake_case should be used instead of UpperCamelCase for function and type
|
||||||
|
names
|
||||||
|
|
||||||
Thanks for contributing!
|
Thanks for contributing!
|
||||||
|
|
831
externals/fmt/ChangeLog.rst
vendored
831
externals/fmt/ChangeLog.rst
vendored
|
@ -1,34 +1,648 @@
|
||||||
|
5.2.1 - 2018-09-21
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Fixed ``visit`` lookup issues on gcc 7 & 8
|
||||||
|
(`#870 <https://github.com/fmtlib/fmt/pull/870>`_).
|
||||||
|
Thanks `@medithe <https://github.com/medithe>`_.
|
||||||
|
|
||||||
|
* Fixed linkage errors on older gcc.
|
||||||
|
|
||||||
|
* Prevented ``fmt/range.h`` from specializing ``fmt::basic_string_view``
|
||||||
|
(`#865 <https://github.com/fmtlib/fmt/issues/865>`_,
|
||||||
|
`#868 <https://github.com/fmtlib/fmt/pull/868>`_).
|
||||||
|
Thanks `@hhggit (dual) <https://github.com/hhggit>`_.
|
||||||
|
|
||||||
|
* Improved error message when formatting unknown types
|
||||||
|
(`#872 <https://github.com/fmtlib/fmt/pull/872>`_).
|
||||||
|
Thanks `@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
|
||||||
|
|
||||||
|
* Disabled templated user-defined literals when compiled under nvcc
|
||||||
|
(`#875 <https://github.com/fmtlib/fmt/pull/875>`_).
|
||||||
|
Thanks `@CandyGumdrop (Candy Gumdrop) <https://github.com/CandyGumdrop>`_,
|
||||||
|
|
||||||
|
* Fixed ``format_to`` formatting to ``wmemory_buffer``
|
||||||
|
(`#874 <https://github.com/fmtlib/fmt/issues/874>`_).
|
||||||
|
|
||||||
|
5.2.0 - 2018-09-13
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Optimized format string parsing and argument processing which resulted in up
|
||||||
|
to 5x speed up on long format strings and significant performance boost on
|
||||||
|
various benchmarks. For example, version 5.2 is 2.22x faster than 5.1 on
|
||||||
|
decimal integer formatting with ``format_to`` (macOS, clang-902.0.39.2):
|
||||||
|
|
||||||
|
================== ======= =======
|
||||||
|
Method Time, s Speedup
|
||||||
|
================== ======= =======
|
||||||
|
fmt::format 5.1 0.58
|
||||||
|
fmt::format 5.2 0.35 1.66x
|
||||||
|
fmt::format_to 5.1 0.51
|
||||||
|
fmt::format_to 5.2 0.23 2.22x
|
||||||
|
sprintf 0.71
|
||||||
|
std::to_string 1.01
|
||||||
|
std::stringstream 1.73
|
||||||
|
================== ======= =======
|
||||||
|
|
||||||
|
* Changed the ``fmt`` macro from opt-out to opt-in to prevent name collisions.
|
||||||
|
To enable it define the ``FMT_STRING_ALIAS`` macro to 1 before including
|
||||||
|
``fmt/format.h``:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#define FMT_STRING_ALIAS 1
|
||||||
|
#include <fmt/format.h>
|
||||||
|
std::string answer = format(fmt("{}"), 42);
|
||||||
|
|
||||||
|
* Added compile-time format string checks to ``format_to`` overload that takes
|
||||||
|
``fmt::memory_buffer`` (`#783 <https://github.com/fmtlib/fmt/issues/783>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
// Compile-time error: invalid type specifier.
|
||||||
|
fmt::format_to(buf, fmt("{:d}"), "foo");
|
||||||
|
|
||||||
|
* Moved experimental color support to ``fmt/color.h`` and enabled the
|
||||||
|
new API by default. The old API can be enabled by defining the
|
||||||
|
``FMT_DEPRECATED_COLORS`` macro.
|
||||||
|
|
||||||
|
* Added formatting support for types explicitly convertible to
|
||||||
|
``fmt::string_view``:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
struct foo {
|
||||||
|
explicit operator fmt::string_view() const { return "foo"; }
|
||||||
|
};
|
||||||
|
auto s = format("{}", foo());
|
||||||
|
|
||||||
|
In particular, this makes formatting function work with
|
||||||
|
``folly::StringPiece``.
|
||||||
|
|
||||||
|
* Implemented preliminary support for ``char*_t`` by replacing the ``format``
|
||||||
|
function overloads with a single function template parameterized on the string
|
||||||
|
type.
|
||||||
|
|
||||||
|
* Added support for dynamic argument lists
|
||||||
|
(`#814 <https://github.com/fmtlib/fmt/issues/814>`_,
|
||||||
|
`#819 <https://github.com/fmtlib/fmt/pull/819>`_).
|
||||||
|
Thanks `@MikePopoloski (Michael Popoloski)
|
||||||
|
<https://github.com/MikePopoloski>`_.
|
||||||
|
|
||||||
|
* Reduced executable size overhead for embedded targets using newlib nano by
|
||||||
|
making locale dependency optional
|
||||||
|
(`#839 <https://github.com/fmtlib/fmt/pull/839>`_).
|
||||||
|
Thanks `@teajay-fr (Thomas Benard) <https://github.com/teajay-fr>`_.
|
||||||
|
|
||||||
|
* Keep ``noexcept`` specifier when exceptions are disabled
|
||||||
|
(`#801 <https://github.com/fmtlib/fmt/issues/801>`_,
|
||||||
|
`#810 <https://github.com/fmtlib/fmt/pull/810>`_).
|
||||||
|
Thanks `@qis (Alexej Harm) <https://github.com/qis>`_.
|
||||||
|
|
||||||
|
* Fixed formatting of user-defined types providing ``operator<<`` with
|
||||||
|
``format_to_n``
|
||||||
|
(`#806 <https://github.com/fmtlib/fmt/pull/806>`_).
|
||||||
|
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
|
||||||
|
|
||||||
|
* Fixed dynamic linkage of new symbols
|
||||||
|
(`#808 <https://github.com/fmtlib/fmt/issues/808>`_).
|
||||||
|
|
||||||
|
* Fixed global initialization issue
|
||||||
|
(`#807 <https://github.com/fmtlib/fmt/issues/807>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
// This works on compilers with constexpr support.
|
||||||
|
static const std::string answer = fmt::format("{}", 42);
|
||||||
|
|
||||||
|
* Fixed various compiler warnings and errors
|
||||||
|
(`#804 <https://github.com/fmtlib/fmt/pull/804>`_,
|
||||||
|
`#809 <https://github.com/fmtlib/fmt/issues/809>`_,
|
||||||
|
`#811 <https://github.com/fmtlib/fmt/pull/811>`_,
|
||||||
|
`#822 <https://github.com/fmtlib/fmt/issues/822>`_,
|
||||||
|
`#827 <https://github.com/fmtlib/fmt/pull/827>`_,
|
||||||
|
`#830 <https://github.com/fmtlib/fmt/issues/830>`_,
|
||||||
|
`#838 <https://github.com/fmtlib/fmt/pull/838>`_,
|
||||||
|
`#843 <https://github.com/fmtlib/fmt/issues/843>`_,
|
||||||
|
`#844 <https://github.com/fmtlib/fmt/pull/844>`_,
|
||||||
|
`#851 <https://github.com/fmtlib/fmt/issues/851>`_,
|
||||||
|
`#852 <https://github.com/fmtlib/fmt/pull/852>`_,
|
||||||
|
`#854 <https://github.com/fmtlib/fmt/pull/854>`_).
|
||||||
|
Thanks `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_,
|
||||||
|
`@medithe <https://github.com/medithe>`_, and
|
||||||
|
`@eliasdaler (Elias Daler) <https://github.com/eliasdaler>`_.
|
||||||
|
|
||||||
|
5.1.0 - 2018-07-05
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Added experimental support for RGB color output enabled with
|
||||||
|
the ``FMT_EXTENDED_COLORS`` macro:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#define FMT_EXTENDED_COLORS
|
||||||
|
#define FMT_HEADER_ONLY // or compile fmt with FMT_EXTENDED_COLORS defined
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
fmt::print(fmt::color::steel_blue, "Some beautiful text");
|
||||||
|
|
||||||
|
The old API (the ``print_colored`` and ``vprint_colored`` functions and the
|
||||||
|
``color`` enum) is now deprecated.
|
||||||
|
(`#762 <https://github.com/fmtlib/fmt/issues/762>`_
|
||||||
|
`#767 <https://github.com/fmtlib/fmt/pull/767>`_).
|
||||||
|
thanks `@remotion (remo) <https://github.com/remotion>`_.
|
||||||
|
|
||||||
|
* Added quotes to strings in ranges and tuples
|
||||||
|
(`#766 <https://github.com/fmtlib/fmt/pull/766>`_).
|
||||||
|
Thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
|
||||||
|
|
||||||
|
* Made ``format_to`` work with ``basic_memory_buffer``
|
||||||
|
(`#776 <https://github.com/fmtlib/fmt/issues/776>`_).
|
||||||
|
|
||||||
|
* Added ``vformat_to_n`` and ``wchar_t`` overload of ``format_to_n``
|
||||||
|
(`#764 <https://github.com/fmtlib/fmt/issues/764>`_,
|
||||||
|
`#769 <https://github.com/fmtlib/fmt/issues/769>`_).
|
||||||
|
|
||||||
|
* Made ``is_range`` and ``is_tuple_like`` part of public (experimental) API
|
||||||
|
to allow specialization for user-defined types
|
||||||
|
(`#751 <https://github.com/fmtlib/fmt/issues/751>`_,
|
||||||
|
`#759 <https://github.com/fmtlib/fmt/pull/759>`_).
|
||||||
|
Thanks `@drrlvn (Dror Levin) <https://github.com/drrlvn>`_.
|
||||||
|
|
||||||
|
* Added more compilers to continuous integration and increased ``FMT_PEDANTIC``
|
||||||
|
warning levels
|
||||||
|
(`#736 <https://github.com/fmtlib/fmt/pull/736>`_).
|
||||||
|
Thanks `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_.
|
||||||
|
|
||||||
|
* Fixed compilation with MSVC 2013.
|
||||||
|
|
||||||
|
* Fixed handling of user-defined types in ``format_to``
|
||||||
|
(`#793 <https://github.com/fmtlib/fmt/issues/793>`_).
|
||||||
|
|
||||||
|
* Forced linking of inline ``vformat`` functions into the library
|
||||||
|
(`#795 <https://github.com/fmtlib/fmt/issues/795>`_).
|
||||||
|
|
||||||
|
* Fixed incorrect call to on_align in ``'{:}='``
|
||||||
|
(`#750 <https://github.com/fmtlib/fmt/issues/750>`_).
|
||||||
|
|
||||||
|
* Fixed floating-point formatting to a non-back_insert_iterator with sign &
|
||||||
|
numeric alignment specified
|
||||||
|
(`#756 <https://github.com/fmtlib/fmt/issues/756>`_).
|
||||||
|
|
||||||
|
* Fixed formatting to an array with ``format_to_n``
|
||||||
|
(`#778 <https://github.com/fmtlib/fmt/issues/778>`_).
|
||||||
|
|
||||||
|
* Fixed formatting of more than 15 named arguments
|
||||||
|
(`#754 <https://github.com/fmtlib/fmt/issues/754>`_).
|
||||||
|
|
||||||
|
* Fixed handling of compile-time strings when including ``fmt/ostream.h``.
|
||||||
|
(`#768 <https://github.com/fmtlib/fmt/issues/768>`_).
|
||||||
|
|
||||||
|
* Fixed various compiler warnings and errors
|
||||||
|
(`#742 <https://github.com/fmtlib/fmt/issues/742>`_,
|
||||||
|
`#748 <https://github.com/fmtlib/fmt/issues/748>`_,
|
||||||
|
`#752 <https://github.com/fmtlib/fmt/issues/752>`_,
|
||||||
|
`#770 <https://github.com/fmtlib/fmt/issues/770>`_,
|
||||||
|
`#775 <https://github.com/fmtlib/fmt/pull/775>`_,
|
||||||
|
`#779 <https://github.com/fmtlib/fmt/issues/779>`_,
|
||||||
|
`#780 <https://github.com/fmtlib/fmt/pull/780>`_,
|
||||||
|
`#790 <https://github.com/fmtlib/fmt/pull/790>`_,
|
||||||
|
`#792 <https://github.com/fmtlib/fmt/pull/792>`_,
|
||||||
|
`#800 <https://github.com/fmtlib/fmt/pull/800>`_).
|
||||||
|
Thanks `@Remotion (Remo) <https://github.com/Remotion>`_,
|
||||||
|
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
|
||||||
|
`@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
|
||||||
|
`@Dark-Passenger (Dhruv Paranjape) <https://github.com/Dark-Passenger>`_, and
|
||||||
|
`@0x8000-0000 (Sign Bit) <https://github.com/0x8000-0000>`_.
|
||||||
|
|
||||||
|
5.0.0 - 2018-05-21
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Added a requirement for partial C++11 support, most importantly variadic
|
||||||
|
templates and type traits, and dropped ``FMT_VARIADIC_*`` emulation macros.
|
||||||
|
Variadic templates are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013).
|
||||||
|
For older compilers use {fmt} `version 4.x
|
||||||
|
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
|
||||||
|
maintained and works with C++98 compilers.
|
||||||
|
|
||||||
|
* Renamed symbols to follow standard C++ naming conventions and proposed a subset
|
||||||
|
of the library for standardization in `P0645R2 Text Formatting
|
||||||
|
<https://wg21.link/P0645>`_.
|
||||||
|
|
||||||
|
* Implemented ``constexpr`` parsing of format strings and `compile-time format
|
||||||
|
string checks
|
||||||
|
<http://fmtlib.net/dev/api.html#compile-time-format-string-checks>`_. For
|
||||||
|
example
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
std::string s = format(fmt("{:d}"), "foo");
|
||||||
|
|
||||||
|
gives a compile-time error because ``d`` is an invalid specifier for strings
|
||||||
|
(`godbolt <https://godbolt.org/g/rnCy9Q>`__)::
|
||||||
|
|
||||||
|
...
|
||||||
|
<source>:4:19: note: in instantiation of function template specialization 'fmt::v5::format<S, char [4]>' requested here
|
||||||
|
std::string s = format(fmt("{:d}"), "foo");
|
||||||
|
^
|
||||||
|
format.h:1337:13: note: non-constexpr function 'on_error' cannot be used in a constant expression
|
||||||
|
handler.on_error("invalid type specifier");
|
||||||
|
|
||||||
|
Compile-time checks require relaxed ``constexpr`` (C++14 feature) support. If
|
||||||
|
the latter is not available, checks will be performed at runtime.
|
||||||
|
|
||||||
|
* Separated format string parsing and formatting in the extension API to enable
|
||||||
|
compile-time format string processing. For example
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
struct Answer {};
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
template <>
|
||||||
|
struct formatter<Answer> {
|
||||||
|
constexpr auto parse(parse_context& ctx) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
spec = *it;
|
||||||
|
if (spec != 'd' && spec != 's')
|
||||||
|
throw format_error("invalid specifier");
|
||||||
|
return ++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(Answer, FormatContext& ctx) {
|
||||||
|
return spec == 's' ?
|
||||||
|
format_to(ctx.begin(), "{}", "fourty-two") :
|
||||||
|
format_to(ctx.begin(), "{}", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
char spec = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string s = format(fmt("{:x}"), Answer());
|
||||||
|
|
||||||
|
gives a compile-time error due to invalid format specifier (`godbolt
|
||||||
|
<https://godbolt.org/g/2jQ1Dv>`__)::
|
||||||
|
|
||||||
|
...
|
||||||
|
<source>:12:45: error: expression '<throw-expression>' is not a constant expression
|
||||||
|
throw format_error("invalid specifier");
|
||||||
|
|
||||||
|
* Added `iterator support
|
||||||
|
<http://fmtlib.net/dev/api.html#output-iterator-support>`_:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||||
|
|
||||||
|
* Added the `format_to_n
|
||||||
|
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
|
||||||
|
function that restricts the output to the specified number of characters
|
||||||
|
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
char out[4];
|
||||||
|
fmt::format_to_n(out, sizeof(out), "{}", 12345);
|
||||||
|
// out == "1234" (without terminating '\0')
|
||||||
|
|
||||||
|
* Added the `formatted_size
|
||||||
|
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
|
||||||
|
function for computing the output size:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
auto size = fmt::formatted_size("{}", 12345); // size == 5
|
||||||
|
|
||||||
|
* Improved compile times by reducing dependencies on standard headers and
|
||||||
|
providing a lightweight `core API <http://fmtlib.net/dev/api.html#core-api>`_:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
fmt::print("The answer is {}.", 42);
|
||||||
|
|
||||||
|
See `Compile time and code bloat
|
||||||
|
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_.
|
||||||
|
|
||||||
|
* Added the `make_format_args
|
||||||
|
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_
|
||||||
|
function for capturing formatting arguments:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
// Prints formatted error message.
|
||||||
|
void vreport_error(const char *format, fmt::format_args args) {
|
||||||
|
fmt::print("Error: ");
|
||||||
|
fmt::vprint(format, args);
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
void report_error(const char *format, const Args & ... args) {
|
||||||
|
vreport_error(format, fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
* Added the ``make_printf_args`` function for capturing ``printf`` arguments
|
||||||
|
(`#687 <https://github.com/fmtlib/fmt/issues/687>`_,
|
||||||
|
`#694 <https://github.com/fmtlib/fmt/pull/694>`_).
|
||||||
|
Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_.
|
||||||
|
|
||||||
|
* Added prefix ``v`` to non-variadic functions taking ``format_args`` to
|
||||||
|
distinguish them from variadic ones:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
std::string vformat(string_view format_str, format_args args);
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string format(string_view format_str, const Args & ... args);
|
||||||
|
|
||||||
|
* Added experimental support for formatting ranges, containers and tuple-like
|
||||||
|
types in ``fmt/ranges.h`` (`#735 <https://github.com/fmtlib/fmt/pull/735>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
|
std::vector<int> v = {1, 2, 3};
|
||||||
|
fmt::print("{}", v); // prints {1, 2, 3}
|
||||||
|
|
||||||
|
Thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
|
||||||
|
|
||||||
|
* Implemented ``wchar_t`` date and time formatting
|
||||||
|
(`#712 <https://github.com/fmtlib/fmt/pull/712>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/time.h>
|
||||||
|
|
||||||
|
std::time_t t = std::time(nullptr);
|
||||||
|
auto s = fmt::format(L"The date is {:%Y-%m-%d}.", *std::localtime(&t));
|
||||||
|
|
||||||
|
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
|
||||||
|
|
||||||
|
* Provided more wide string overloads
|
||||||
|
(`#724 <https://github.com/fmtlib/fmt/pull/724>`_).
|
||||||
|
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
|
||||||
|
|
||||||
|
* Switched from a custom null-terminated string view class to ``string_view``
|
||||||
|
in the format API and provided ``fmt::string_view`` which implements a subset
|
||||||
|
of ``std::string_view`` API for pre-C++17 systems.
|
||||||
|
|
||||||
|
* Added support for ``std::experimental::string_view``
|
||||||
|
(`#607 <https://github.com/fmtlib/fmt/pull/607>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <experimental/string_view>
|
||||||
|
|
||||||
|
fmt::print("{}", std::experimental::string_view("foo"));
|
||||||
|
|
||||||
|
Thanks `@virgiliofornazin (Virgilio Alexandre Fornazin)
|
||||||
|
<https://github.com/virgiliofornazin>`__.
|
||||||
|
|
||||||
|
* Allowed mixing named and automatic arguments:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::format("{} {two}", 1, fmt::arg("two", 2));
|
||||||
|
|
||||||
|
* Removed the write API in favor of the `format API
|
||||||
|
<http://fmtlib.net/dev/api.html#format-api>`_ with compile-time handling of
|
||||||
|
format strings.
|
||||||
|
|
||||||
|
* Disallowed formatting of multibyte strings into a wide character target
|
||||||
|
(`#606 <https://github.com/fmtlib/fmt/pull/606>`_).
|
||||||
|
|
||||||
|
* Improved documentation
|
||||||
|
(`#515 <https://github.com/fmtlib/fmt/pull/515>`_,
|
||||||
|
`#614 <https://github.com/fmtlib/fmt/issues/614>`_,
|
||||||
|
`#617 <https://github.com/fmtlib/fmt/pull/617>`_,
|
||||||
|
`#661 <https://github.com/fmtlib/fmt/pull/661>`_,
|
||||||
|
`#680 <https://github.com/fmtlib/fmt/pull/680>`_).
|
||||||
|
Thanks `@ibell (Ian Bell) <https://github.com/ibell>`_,
|
||||||
|
`@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_, and
|
||||||
|
`@johnthagen <https://github.com/johnthagen>`_.
|
||||||
|
|
||||||
|
* Implemented more efficient handling of large number of format arguments.
|
||||||
|
|
||||||
|
* Introduced an inline namespace for symbol versioning.
|
||||||
|
|
||||||
|
* Added debug postfix ``d`` to the ``fmt`` library name
|
||||||
|
(`#636 <https://github.com/fmtlib/fmt/issues/636>`_).
|
||||||
|
|
||||||
|
* Removed unnecessary ``fmt/`` prefix in includes
|
||||||
|
(`#397 <https://github.com/fmtlib/fmt/pull/397>`_).
|
||||||
|
Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_.
|
||||||
|
|
||||||
|
* Moved ``fmt/*.h`` to ``include/fmt/*.h`` to prevent irrelevant files and
|
||||||
|
directories appearing on the include search paths when fmt is used as a
|
||||||
|
subproject and moved source files to the ``src`` directory.
|
||||||
|
|
||||||
|
* Added qmake project file ``support/fmt.pro``
|
||||||
|
(`#641 <https://github.com/fmtlib/fmt/pull/641>`_).
|
||||||
|
Thanks `@cowo78 (Giuseppe Corbelli) <https://github.com/cowo78>`_.
|
||||||
|
|
||||||
|
* Added Gradle build file ``support/build.gradle``
|
||||||
|
(`#649 <https://github.com/fmtlib/fmt/pull/649>`_).
|
||||||
|
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_.
|
||||||
|
|
||||||
|
* Removed ``FMT_CPPFORMAT`` CMake option.
|
||||||
|
|
||||||
|
* Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc
|
||||||
|
(`#616 <https://github.com/fmtlib/fmt/pull/616>`_).
|
||||||
|
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
|
||||||
|
|
||||||
|
* Fixed handling of nested braces in ``fmt::join``
|
||||||
|
(`#638 <https://github.com/fmtlib/fmt/issues/638>`_).
|
||||||
|
|
||||||
|
* Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5
|
||||||
|
(`#497 <https://github.com/fmtlib/fmt/pull/497>`_).
|
||||||
|
Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_.
|
||||||
|
|
||||||
|
* Added a missing ``inline`` in the header-only mode
|
||||||
|
(`#626 <https://github.com/fmtlib/fmt/pull/626>`_).
|
||||||
|
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
|
||||||
|
|
||||||
|
* Fixed various compiler warnings
|
||||||
|
(`#640 <https://github.com/fmtlib/fmt/pull/640>`_,
|
||||||
|
`#656 <https://github.com/fmtlib/fmt/pull/656>`_,
|
||||||
|
`#679 <https://github.com/fmtlib/fmt/pull/679>`_,
|
||||||
|
`#681 <https://github.com/fmtlib/fmt/pull/681>`_,
|
||||||
|
`#705 <https://github.com/fmtlib/fmt/pull/705>`__,
|
||||||
|
`#715 <https://github.com/fmtlib/fmt/issues/715>`_,
|
||||||
|
`#717 <https://github.com/fmtlib/fmt/pull/717>`_,
|
||||||
|
`#720 <https://github.com/fmtlib/fmt/pull/720>`_,
|
||||||
|
`#723 <https://github.com/fmtlib/fmt/pull/723>`_,
|
||||||
|
`#726 <https://github.com/fmtlib/fmt/pull/726>`_,
|
||||||
|
`#730 <https://github.com/fmtlib/fmt/pull/730>`_,
|
||||||
|
`#739 <https://github.com/fmtlib/fmt/pull/739>`_).
|
||||||
|
Thanks `@peterbell10 <https://github.com/peterbell10>`_,
|
||||||
|
`@LarsGullik <https://github.com/LarsGullik>`_,
|
||||||
|
`@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
|
||||||
|
`@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_,
|
||||||
|
`@christianparpart (Christian Parpart) <https://github.com/christianparpart>`_,
|
||||||
|
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
|
||||||
|
and `@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
|
* Worked around an MSVC bug and fixed several warnings
|
||||||
|
(`#653 <https://github.com/fmtlib/fmt/pull/653>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
|
* Worked around GCC bug 67371
|
||||||
|
(`#682 <https://github.com/fmtlib/fmt/issues/682>`_).
|
||||||
|
|
||||||
|
* Fixed compilation with ``-fno-exceptions``
|
||||||
|
(`#655 <https://github.com/fmtlib/fmt/pull/655>`_).
|
||||||
|
Thanks `@chenxiaolong (Andrew Gunnerson) <https://github.com/chenxiaolong>`_.
|
||||||
|
|
||||||
|
* Made ``constexpr remove_prefix`` gcc version check tighter
|
||||||
|
(`#648 <https://github.com/fmtlib/fmt/issues/648>`_).
|
||||||
|
|
||||||
|
* Renamed internal type enum constants to prevent collision with poorly written
|
||||||
|
C libraries (`#644 <https://github.com/fmtlib/fmt/issues/644>`_).
|
||||||
|
|
||||||
|
* Added detection of ``wostream operator<<``
|
||||||
|
(`#650 <https://github.com/fmtlib/fmt/issues/650>`_).
|
||||||
|
|
||||||
|
* Fixed compilation on OpenBSD
|
||||||
|
(`#660 <https://github.com/fmtlib/fmt/pull/660>`_).
|
||||||
|
Thanks `@hubslave <https://github.com/hubslave>`_.
|
||||||
|
|
||||||
|
* Fixed compilation on FreeBSD 12
|
||||||
|
(`#732 <https://github.com/fmtlib/fmt/pull/732>`_).
|
||||||
|
Thanks `@dankm <https://github.com/dankm>`_.
|
||||||
|
|
||||||
|
* Fixed compilation when there is a mismatch between ``-std`` options between
|
||||||
|
the library and user code
|
||||||
|
(`#664 <https://github.com/fmtlib/fmt/issues/664>`_).
|
||||||
|
|
||||||
|
* Fixed compilation with GCC 7 and ``-std=c++11``
|
||||||
|
(`#734 <https://github.com/fmtlib/fmt/issues/734>`_).
|
||||||
|
|
||||||
|
* Improved generated binary code on GCC 7 and older
|
||||||
|
(`#668 <https://github.com/fmtlib/fmt/issues/668>`_).
|
||||||
|
|
||||||
|
* Fixed handling of numeric alignment with no width
|
||||||
|
(`#675 <https://github.com/fmtlib/fmt/issues/675>`_).
|
||||||
|
|
||||||
|
* Fixed handling of empty strings in UTF8/16 converters
|
||||||
|
(`#676 <https://github.com/fmtlib/fmt/pull/676>`_).
|
||||||
|
Thanks `@vgalka-sl (Vasili Galka) <https://github.com/vgalka-sl>`_.
|
||||||
|
|
||||||
|
* Fixed formatting of an empty ``string_view``
|
||||||
|
(`#689 <https://github.com/fmtlib/fmt/issues/689>`_).
|
||||||
|
|
||||||
|
* Fixed detection of ``string_view`` on libc++
|
||||||
|
(`#686 <https://github.com/fmtlib/fmt/issues/686>`_).
|
||||||
|
|
||||||
|
* Fixed DLL issues (`#696 <https://github.com/fmtlib/fmt/pull/696>`_).
|
||||||
|
Thanks `@sebkoenig <https://github.com/sebkoenig>`_.
|
||||||
|
|
||||||
|
* Fixed compile checks for mixing narrow and wide strings
|
||||||
|
(`#690 <https://github.com/fmtlib/fmt/issues/690>`_).
|
||||||
|
|
||||||
|
* Disabled unsafe implicit conversion to ``std::string``
|
||||||
|
(`#729 <https://github.com/fmtlib/fmt/issues/729>`_).
|
||||||
|
|
||||||
|
* Fixed handling of reused format specs (as in ``fmt::join``) for pointers
|
||||||
|
(`#725 <https://github.com/fmtlib/fmt/pull/725>`_).
|
||||||
|
Thanks `@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
|
* Fixed installation of ``fmt/ranges.h``
|
||||||
|
(`#738 <https://github.com/fmtlib/fmt/pull/738>`_).
|
||||||
|
Thanks `@sv1990 <https://github.com/sv1990>`_.
|
||||||
|
|
||||||
4.1.0 - 2017-12-20
|
4.1.0 - 2017-12-20
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()`` (`#559 <https://github.com/fmtlib/fmt/pull/559>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()``
|
||||||
|
(`#559 <https://github.com/fmtlib/fmt/pull/559>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
* Added support for C++17 ``std::string_view`` (`#571 <https://github.com/fmtlib/fmt/pull/571>`_ and `#578 <https://github.com/fmtlib/fmt/pull/578>`_). Thanks `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_ and `@mwinterb <https://github.com/mwinterb>`_.
|
* Added support for C++17 ``std::string_view``
|
||||||
|
(`#571 <https://github.com/fmtlib/fmt/pull/571>`_ and
|
||||||
|
`#578 <https://github.com/fmtlib/fmt/pull/578>`_).
|
||||||
|
Thanks `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_ and
|
||||||
|
`@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
* Enabled stream exceptions to catch errors (`#581 <https://github.com/fmtlib/fmt/issues/581>`_). Thanks `@crusader-mike <https://github.com/crusader-mike>`_.
|
* Enabled stream exceptions to catch errors
|
||||||
|
(`#581 <https://github.com/fmtlib/fmt/issues/581>`_).
|
||||||
|
Thanks `@crusader-mike <https://github.com/crusader-mike>`_.
|
||||||
|
|
||||||
* Allowed formatting of class hierarchies with ``fmt::format_arg()`` (`#547 <https://github.com/fmtlib/fmt/pull/547>`_). Thanks `@rollbear (Björn Fahller) <https://github.com/rollbear>`_.
|
* Allowed formatting of class hierarchies with ``fmt::format_arg()``
|
||||||
|
(`#547 <https://github.com/fmtlib/fmt/pull/547>`_).
|
||||||
|
Thanks `@rollbear (Björn Fahller) <https://github.com/rollbear>`_.
|
||||||
|
|
||||||
* Removed limitations on character types
|
* Removed limitations on character types
|
||||||
(`#563 <https://github.com/fmtlib/fmt/pull/563>`_).
|
(`#563 <https://github.com/fmtlib/fmt/pull/563>`_).
|
||||||
Thanks `@Yelnats321 (Elnar Dakeshov) <https://github.com/Yelnats321>`_.
|
Thanks `@Yelnats321 (Elnar Dakeshov) <https://github.com/Yelnats321>`_.
|
||||||
|
|
||||||
* Conditionally enabled use of ``std::allocator_traits`` (`#583 <https://github.com/fmtlib/fmt/pull/583>`_). Thanks `@mwinterb <https://github.com/mwinterb>`_.
|
* Conditionally enabled use of ``std::allocator_traits``
|
||||||
|
(`#583 <https://github.com/fmtlib/fmt/pull/583>`_).
|
||||||
|
Thanks `@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
* Added support for ``const`` variadic member function emulation with ``FMT_VARIADIC_CONST`` (`#591 <https://github.com/fmtlib/fmt/pull/591>`_). Thanks `@ludekvodicka (Ludek Vodicka) <https://github.com/ludekvodicka>`_.
|
* Added support for ``const`` variadic member function emulation with
|
||||||
|
``FMT_VARIADIC_CONST`` (`#591 <https://github.com/fmtlib/fmt/pull/591>`_).
|
||||||
|
Thanks `@ludekvodicka (Ludek Vodicka) <https://github.com/ludekvodicka>`_.
|
||||||
|
|
||||||
* Various bugfixes: bad overflow check, unsupported implicit type conversion when determining formatting function, test segfaults (`#551 <https://github.com/fmtlib/fmt/issues/551>`_), ill-formed macros (`#542 <https://github.com/fmtlib/fmt/pull/542>`_) and ambiguous overloads (`#580 <https://github.com/fmtlib/fmt/issues/580>`_). Thanks `@xylosper (Byoung-young Lee) <https://github.com/xylosper>`_.
|
* Various bugfixes: bad overflow check, unsupported implicit type conversion
|
||||||
|
when determining formatting function, test segfaults
|
||||||
|
(`#551 <https://github.com/fmtlib/fmt/issues/551>`_), ill-formed macros
|
||||||
|
(`#542 <https://github.com/fmtlib/fmt/pull/542>`_) and ambiguous overloads
|
||||||
|
(`#580 <https://github.com/fmtlib/fmt/issues/580>`_).
|
||||||
|
Thanks `@xylosper (Byoung-young Lee) <https://github.com/xylosper>`_.
|
||||||
|
|
||||||
* Prevented warnings on MSVC (`#605 <https://github.com/fmtlib/fmt/pull/605>`_, `#602 <https://github.com/fmtlib/fmt/pull/602>`_, and `#545 <https://github.com/fmtlib/fmt/pull/545>`_), clang (`#582 <https://github.com/fmtlib/fmt/pull/582>`_), GCC (`#573 <https://github.com/fmtlib/fmt/issues/573>`_), various conversion warnings (`#609 <https://github.com/fmtlib/fmt/pull/609>`_, `#567 <https://github.com/fmtlib/fmt/pull/567>`_, `#553 <https://github.com/fmtlib/fmt/pull/553>`_ and `#553 <https://github.com/fmtlib/fmt/pull/553>`_), and added ``override`` and ``[[noreturn]]`` (`#549 <https://github.com/fmtlib/fmt/pull/549>`_ and `#555 <https://github.com/fmtlib/fmt/issues/555>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_, `@virgiliofornazin (Virgilio Alexandre Fornazin) <https://gihtub.com/virgiliofornazin>`_, `@alexanderbock (Alexander Bock) <https://github.com/alexanderbock>`_, `@yumetodo <https://github.com/yumetodo>`_, `@VaderY (Császár Mátyás) <https://github.com/VaderY>`_, `@jpcima (JP Cimalando) <https://github.com/jpcima>`_, `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and `@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
|
* Prevented warnings on MSVC (`#605 <https://github.com/fmtlib/fmt/pull/605>`_,
|
||||||
|
`#602 <https://github.com/fmtlib/fmt/pull/602>`_, and
|
||||||
|
`#545 <https://github.com/fmtlib/fmt/pull/545>`_),
|
||||||
|
clang (`#582 <https://github.com/fmtlib/fmt/pull/582>`_),
|
||||||
|
GCC (`#573 <https://github.com/fmtlib/fmt/issues/573>`_),
|
||||||
|
various conversion warnings (`#609 <https://github.com/fmtlib/fmt/pull/609>`_,
|
||||||
|
`#567 <https://github.com/fmtlib/fmt/pull/567>`_,
|
||||||
|
`#553 <https://github.com/fmtlib/fmt/pull/553>`_ and
|
||||||
|
`#553 <https://github.com/fmtlib/fmt/pull/553>`_), and added ``override`` and
|
||||||
|
``[[noreturn]]`` (`#549 <https://github.com/fmtlib/fmt/pull/549>`_ and
|
||||||
|
`#555 <https://github.com/fmtlib/fmt/issues/555>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_,
|
||||||
|
`@virgiliofornazin (Virgilio Alexandre Fornazin)
|
||||||
|
<https://gihtub.com/virgiliofornazin>`_,
|
||||||
|
`@alexanderbock (Alexander Bock) <https://github.com/alexanderbock>`_,
|
||||||
|
`@yumetodo <https://github.com/yumetodo>`_,
|
||||||
|
`@VaderY (Császár Mátyás) <https://github.com/VaderY>`_,
|
||||||
|
`@jpcima (JP Cimalando) <https://github.com/jpcima>`_,
|
||||||
|
`@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and
|
||||||
|
`@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
|
||||||
|
|
||||||
* Improved CMake: Used GNUInstallDirs to set installation location (`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings (`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and `#556 <https://github.com/fmtlib/fmt/pull/556>`_). Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_, `@evgen231 <https://github.com/evgen231>`_ and `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_.
|
* Improved CMake: Used ``GNUInstallDirs`` to set installation location
|
||||||
|
(`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings
|
||||||
|
(`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and
|
||||||
|
`#556 <https://github.com/fmtlib/fmt/pull/556>`_).
|
||||||
|
Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_,
|
||||||
|
`@evgen231 <https://github.com/evgen231>`_ and
|
||||||
|
`@henryiii (Henry Schreiner) <https://github.com/henryiii>`_.
|
||||||
|
|
||||||
4.0.0 - 2017-06-27
|
4.0.0 - 2017-06-27
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Removed old compatibility headers ``cppformat/*.h`` and CMake options (`#527 <https://github.com/fmtlib/fmt/pull/527>`_). Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
* Removed old compatibility headers ``cppformat/*.h`` and CMake options
|
||||||
|
(`#527 <https://github.com/fmtlib/fmt/pull/527>`_).
|
||||||
|
Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
||||||
|
|
||||||
* Added ``string.h`` containing ``fmt::to_string()`` as alternative to ``std::to_string()`` as well as other string writer functionality (`#326 <https://github.com/fmtlib/fmt/issues/326>`_ and `#441 <https://github.com/fmtlib/fmt/pull/441>`_):
|
* Added ``string.h`` containing ``fmt::to_string()`` as alternative to
|
||||||
|
``std::to_string()`` as well as other string writer functionality
|
||||||
|
(`#326 <https://github.com/fmtlib/fmt/issues/326>`_ and
|
||||||
|
`#441 <https://github.com/fmtlib/fmt/pull/441>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
|
@ -36,9 +650,17 @@
|
||||||
|
|
||||||
std::string answer = fmt::to_string(42);
|
std::string answer = fmt::to_string(42);
|
||||||
|
|
||||||
Thanks to `@glebov-andrey (Andrey Glebov) <https://github.com/glebov-andrey>`_.
|
Thanks to `@glebov-andrey (Andrey Glebov)
|
||||||
|
<https://github.com/glebov-andrey>`_.
|
||||||
|
|
||||||
* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as generic specifier (`#453 <https://github.com/fmtlib/fmt/pull/453>`_), made ``%.f`` more conformant to regular ``printf()`` (`#490 <https://github.com/fmtlib/fmt/pull/490>`_), added custom writer support (`#476 <https://github.com/fmtlib/fmt/issues/476>`_) and implemented missing custom argument formatting (`#339 <https://github.com/fmtlib/fmt/pull/339>`_ and `#340 <https://github.com/fmtlib/fmt/pull/340>`_):
|
* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as
|
||||||
|
generic specifier (`#453 <https://github.com/fmtlib/fmt/pull/453>`_),
|
||||||
|
made ``%.f`` more conformant to regular ``printf()``
|
||||||
|
(`#490 <https://github.com/fmtlib/fmt/pull/490>`_), added custom writer
|
||||||
|
support (`#476 <https://github.com/fmtlib/fmt/issues/476>`_) and implemented
|
||||||
|
missing custom argument formatting
|
||||||
|
(`#339 <https://github.com/fmtlib/fmt/pull/339>`_ and
|
||||||
|
`#340 <https://github.com/fmtlib/fmt/pull/340>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
|
@ -47,11 +669,21 @@
|
||||||
// %s format specifier can be used with any argument type.
|
// %s format specifier can be used with any argument type.
|
||||||
fmt::printf("%s", 42);
|
fmt::printf("%s", 42);
|
||||||
|
|
||||||
Thanks `@mojoBrendan <https://github.com/mojoBrendan>`_, `@manylegged (Arthur Danskin) <https://github.com/manylegged>`_ and `@spacemoose (Glen Stark) <https://github.com/spacemoose>`_. See also `#360 <https://github.com/fmtlib/fmt/issues/360>`_, `#335 <https://github.com/fmtlib/fmt/issues/335>`_ and `#331 <https://github.com/fmtlib/fmt/issues/331>`_.
|
Thanks `@mojoBrendan <https://github.com/mojoBrendan>`_,
|
||||||
|
`@manylegged (Arthur Danskin) <https://github.com/manylegged>`_ and
|
||||||
|
`@spacemoose (Glen Stark) <https://github.com/spacemoose>`_.
|
||||||
|
See also `#360 <https://github.com/fmtlib/fmt/issues/360>`_,
|
||||||
|
`#335 <https://github.com/fmtlib/fmt/issues/335>`_ and
|
||||||
|
`#331 <https://github.com/fmtlib/fmt/issues/331>`_.
|
||||||
|
|
||||||
* Added ``container.h`` containing a ``BasicContainerWriter`` to write to containers like ``std::vector`` (`#450 <https://github.com/fmtlib/fmt/pull/450>`_). Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
|
* Added ``container.h`` containing a ``BasicContainerWriter``
|
||||||
|
to write to containers like ``std::vector``
|
||||||
|
(`#450 <https://github.com/fmtlib/fmt/pull/450>`_).
|
||||||
|
Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
|
||||||
|
|
||||||
* Added ``fmt::join()`` function that takes a range and formats its elements separated by a given string (`#466 <https://github.com/fmtlib/fmt/pull/466>`_):
|
* Added ``fmt::join()`` function that takes a range and formats
|
||||||
|
its elements separated by a given string
|
||||||
|
(`#466 <https://github.com/fmtlib/fmt/pull/466>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
|
@ -63,75 +695,174 @@
|
||||||
|
|
||||||
Thanks `@olivier80 <https://github.com/olivier80>`_.
|
Thanks `@olivier80 <https://github.com/olivier80>`_.
|
||||||
|
|
||||||
* Added support for custom formatting specifications to simplify customization of built-in formatting (`#444 <https://github.com/fmtlib/fmt/pull/444>`_). Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_. See also `#439 <https://github.com/fmtlib/fmt/issues/439>`_.
|
* Added support for custom formatting specifications to simplify customization
|
||||||
|
of built-in formatting (`#444 <https://github.com/fmtlib/fmt/pull/444>`_).
|
||||||
|
Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
|
||||||
|
See also `#439 <https://github.com/fmtlib/fmt/issues/439>`_.
|
||||||
|
|
||||||
* Added ``fmt::format_system_error()`` for error code formatting (`#323 <https://github.com/fmtlib/fmt/issues/323>`_ and `#526 <https://github.com/fmtlib/fmt/pull/526>`_). Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
* Added ``fmt::format_system_error()`` for error code formatting
|
||||||
|
(`#323 <https://github.com/fmtlib/fmt/issues/323>`_ and
|
||||||
|
`#526 <https://github.com/fmtlib/fmt/pull/526>`_).
|
||||||
|
Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
||||||
|
|
||||||
* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()`` as replacement for the standard version to ``time.h`` (`#396 <https://github.com/fmtlib/fmt/pull/396>`_). Thanks `@codicodi <https://github.com/codicodi>`_.
|
* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()``
|
||||||
|
as replacement for the standard version to ``time.h``
|
||||||
|
(`#396 <https://github.com/fmtlib/fmt/pull/396>`_).
|
||||||
|
Thanks `@codicodi <https://github.com/codicodi>`_.
|
||||||
|
|
||||||
* Internal improvements to ``NamedArg`` and ``ArgLists`` (`#389 <https://github.com/fmtlib/fmt/pull/389>`_ and `#390 <https://github.com/fmtlib/fmt/pull/390>`_). Thanks `@chronoxor <https://github.com/chronoxor>`_.
|
* Internal improvements to ``NamedArg`` and ``ArgLists``
|
||||||
|
(`#389 <https://github.com/fmtlib/fmt/pull/389>`_ and
|
||||||
|
`#390 <https://github.com/fmtlib/fmt/pull/390>`_).
|
||||||
|
Thanks `@chronoxor <https://github.com/chronoxor>`_.
|
||||||
|
|
||||||
* Fixed crash due to bug in ``FormatBuf`` (`#493 <https://github.com/fmtlib/fmt/pull/493>`_). Thanks `@effzeh <https://github.com/effzeh>`_. See also `#480 <https://github.com/fmtlib/fmt/issues/480>`_ and `#491 <https://github.com/fmtlib/fmt/issues/491>`_.
|
* Fixed crash due to bug in ``FormatBuf``
|
||||||
|
(`#493 <https://github.com/fmtlib/fmt/pull/493>`_).
|
||||||
|
Thanks `@effzeh <https://github.com/effzeh>`_. See also
|
||||||
|
`#480 <https://github.com/fmtlib/fmt/issues/480>`_ and
|
||||||
|
`#491 <https://github.com/fmtlib/fmt/issues/491>`_.
|
||||||
|
|
||||||
* Fixed handling of wide strings in ``fmt::StringWriter``.
|
* Fixed handling of wide strings in ``fmt::StringWriter``.
|
||||||
|
|
||||||
* Improved compiler error messages (`#357 <https://github.com/fmtlib/fmt/issues/357>`_).
|
* Improved compiler error messages
|
||||||
|
(`#357 <https://github.com/fmtlib/fmt/issues/357>`_).
|
||||||
|
|
||||||
* Fixed various warnings and issues with various compilers (`#494 <https://github.com/fmtlib/fmt/pull/494>`_, `#499 <https://github.com/fmtlib/fmt/pull/499>`_, `#483 <https://github.com/fmtlib/fmt/pull/483>`_, `#519 <https://github.com/fmtlib/fmt/pull/519>`_, `#485 <https://github.com/fmtlib/fmt/pull/485>`_, `#482 <https://github.com/fmtlib/fmt/pull/482>`_, `#475 <https://github.com/fmtlib/fmt/pull/475>`_, `#473 <https://github.com/fmtlib/fmt/pull/473>`_ and `#414 <https://github.com/fmtlib/fmt/pull/414>`_). Thanks `@chronoxor <https://github.com/chronoxor>`_, `@zhaohuaxishi <https://github.com/zhaohuaxishi>`_, `@pkestene (Pierre Kestener) <https://github.com/pkestene>`_, `@dschmidt (Dominik Schmidt) <https://github.com/dschmidt>`_ and `@0x414c (Alexey Gorishny) <https://github.com/0x414c>`_ .
|
* Fixed various warnings and issues with various compilers
|
||||||
|
(`#494 <https://github.com/fmtlib/fmt/pull/494>`_,
|
||||||
|
`#499 <https://github.com/fmtlib/fmt/pull/499>`_,
|
||||||
|
`#483 <https://github.com/fmtlib/fmt/pull/483>`_,
|
||||||
|
`#485 <https://github.com/fmtlib/fmt/pull/485>`_,
|
||||||
|
`#482 <https://github.com/fmtlib/fmt/pull/482>`_,
|
||||||
|
`#475 <https://github.com/fmtlib/fmt/pull/475>`_,
|
||||||
|
`#473 <https://github.com/fmtlib/fmt/pull/473>`_ and
|
||||||
|
`#414 <https://github.com/fmtlib/fmt/pull/414>`_).
|
||||||
|
Thanks `@chronoxor <https://github.com/chronoxor>`_,
|
||||||
|
`@zhaohuaxishi <https://github.com/zhaohuaxishi>`_,
|
||||||
|
`@pkestene (Pierre Kestener) <https://github.com/pkestene>`_,
|
||||||
|
`@dschmidt (Dominik Schmidt) <https://github.com/dschmidt>`_ and
|
||||||
|
`@0x414c (Alexey Gorishny) <https://github.com/0x414c>`_ .
|
||||||
|
|
||||||
* Improved CMake: targets are now namespaced (`#511 <https://github.com/fmtlib/fmt/pull/511>`_ and `#513 <https://github.com/fmtlib/fmt/pull/513>`_), supported header-only ``printf.h`` (`#354 <https://github.com/fmtlib/fmt/pull/354>`_), fixed issue with minimal supported library subset (`#418 <https://github.com/fmtlib/fmt/issues/418>`_, `#419 <https://github.com/fmtlib/fmt/pull/419>`_ and `#420 <https://github.com/fmtlib/fmt/pull/420>`_). Thanks `@bjoernthiel (Bjoern Thiel) <https://github.com/bjoernthiel>`_,
|
* Improved CMake: targets are now namespaced
|
||||||
`@niosHD (Mario Werner) <https://github.com/niosHD>`_, `@LogicalKnight (Sean LK) <https://github.com/LogicalKnight>`_ and `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
(`#511 <https://github.com/fmtlib/fmt/pull/511>`_ and
|
||||||
|
`#513 <https://github.com/fmtlib/fmt/pull/513>`_), supported header-only
|
||||||
|
``printf.h`` (`#354 <https://github.com/fmtlib/fmt/pull/354>`_), fixed issue
|
||||||
|
with minimal supported library subset
|
||||||
|
(`#418 <https://github.com/fmtlib/fmt/issues/418>`_,
|
||||||
|
`#419 <https://github.com/fmtlib/fmt/pull/419>`_ and
|
||||||
|
`#420 <https://github.com/fmtlib/fmt/pull/420>`_).
|
||||||
|
Thanks `@bjoernthiel (Bjoern Thiel) <https://github.com/bjoernthiel>`_,
|
||||||
|
`@niosHD (Mario Werner) <https://github.com/niosHD>`_,
|
||||||
|
`@LogicalKnight (Sean LK) <https://github.com/LogicalKnight>`_ and
|
||||||
|
`@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
* Improved documentation. Thanks to `@pwm1234 (Phil) <https://github.com/pwm1234>`_ for `#393 <https://github.com/fmtlib/fmt/pull/393>`_.
|
* Improved documentation. Thanks to
|
||||||
|
`@pwm1234 (Phil) <https://github.com/pwm1234>`_ for
|
||||||
|
`#393 <https://github.com/fmtlib/fmt/pull/393>`_.
|
||||||
|
|
||||||
3.0.2 - 2017-06-14
|
3.0.2 - 2017-06-14
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Added ``FMT_VERSION`` macro (`#411 <https://github.com/fmtlib/fmt/issues/411>`_).
|
* Added ``FMT_VERSION`` macro
|
||||||
|
(`#411 <https://github.com/fmtlib/fmt/issues/411>`_).
|
||||||
|
|
||||||
* Used ``FMT_NULL`` instead of literal ``0`` (`#409 <https://github.com/fmtlib/fmt/pull/409>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
* Used ``FMT_NULL`` instead of literal ``0``
|
||||||
|
(`#409 <https://github.com/fmtlib/fmt/pull/409>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
* Added extern templates for ``format_float`` (`#413 <https://github.com/fmtlib/fmt/issues/413>`_).
|
* Added extern templates for ``format_float``
|
||||||
|
(`#413 <https://github.com/fmtlib/fmt/issues/413>`_).
|
||||||
|
|
||||||
* Fixed implicit conversion issue (`#507 <https://github.com/fmtlib/fmt/issues/507>`_).
|
* Fixed implicit conversion issue
|
||||||
|
(`#507 <https://github.com/fmtlib/fmt/issues/507>`_).
|
||||||
|
|
||||||
* Fixed signbit detection (`#423 <https://github.com/fmtlib/fmt/issues/423>`_).
|
* Fixed signbit detection (`#423 <https://github.com/fmtlib/fmt/issues/423>`_).
|
||||||
|
|
||||||
* Fixed naming collision (`#425 <https://github.com/fmtlib/fmt/issues/425>`_).
|
* Fixed naming collision (`#425 <https://github.com/fmtlib/fmt/issues/425>`_).
|
||||||
|
|
||||||
* Fixed missing intrinsic for C++/CLI (`#457 <https://github.com/fmtlib/fmt/pull/457>`_). Thanks `@calumr (Calum Robinson) <https://github.com/calumr>`_
|
* Fixed missing intrinsic for C++/CLI
|
||||||
|
(`#457 <https://github.com/fmtlib/fmt/pull/457>`_).
|
||||||
|
Thanks `@calumr (Calum Robinson) <https://github.com/calumr>`_
|
||||||
|
|
||||||
* Fixed Android detection (`#458 <https://github.com/fmtlib/fmt/pull/458>`_). Thanks `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
|
* Fixed Android detection (`#458 <https://github.com/fmtlib/fmt/pull/458>`_).
|
||||||
|
Thanks `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
|
||||||
|
|
||||||
* Use lean ``windows.h`` if not in header-only mode (`#503 <https://github.com/fmtlib/fmt/pull/503>`_). Thanks `@Quentin01 (Quentin Buathier) <https://github.com/Quentin01>`_.
|
* Use lean ``windows.h`` if not in header-only mode
|
||||||
|
(`#503 <https://github.com/fmtlib/fmt/pull/503>`_).
|
||||||
|
Thanks `@Quentin01 (Quentin Buathier) <https://github.com/Quentin01>`_.
|
||||||
|
|
||||||
* Fixed issue with CMake exporting C++11 flag (`#445 <https://github.com/fmtlib/fmt/pull/455>`_). Thanks `@EricWF (Eric) <https://github.com/EricWF>`_.
|
* Fixed issue with CMake exporting C++11 flag
|
||||||
|
(`#445 <https://github.com/fmtlib/fmt/pull/455>`_).
|
||||||
|
Thanks `@EricWF (Eric) <https://github.com/EricWF>`_.
|
||||||
|
|
||||||
* Fixed issue with nvcc and MSVC compiler bug and MinGW (`#505 <https://github.com/fmtlib/fmt/issues/505>`_).
|
* Fixed issue with nvcc and MSVC compiler bug and MinGW
|
||||||
|
(`#505 <https://github.com/fmtlib/fmt/issues/505>`_).
|
||||||
|
|
||||||
* Fixed DLL issues (`#469 <https://github.com/fmtlib/fmt/pull/469>`_ and `#502 <https://github.com/fmtlib/fmt/pull/502>`_). Thanks `@richardeakin (Richard Eakin) <https://github.com/richardeakin>`_ and `@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_.
|
* Fixed DLL issues (`#469 <https://github.com/fmtlib/fmt/pull/469>`_ and
|
||||||
|
`#502 <https://github.com/fmtlib/fmt/pull/502>`_).
|
||||||
|
Thanks `@richardeakin (Richard Eakin) <https://github.com/richardeakin>`_ and
|
||||||
|
`@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_.
|
||||||
|
|
||||||
* Fixed test compilation under FreeBSD (`#433 <https://github.com/fmtlib/fmt/issues/433>`_).
|
* Fixed test compilation under FreeBSD
|
||||||
|
(`#433 <https://github.com/fmtlib/fmt/issues/433>`_).
|
||||||
|
|
||||||
* Fixed various warnings (`#403 <https://github.com/fmtlib/fmt/pull/403>`_, `#410 <https://github.com/fmtlib/fmt/pull/410>`_ and `#510 <https://github.com/fmtlib/fmt/pull/510>`_). Thanks `@Lecetem <https://github.com/Lectem>`_, `@chenhayat (Chen Hayat) <https://github.com/chenhayat>`_ and `@trozen <https://github.com/trozen>`_.
|
* Fixed various warnings (`#403 <https://github.com/fmtlib/fmt/pull/403>`_,
|
||||||
|
`#410 <https://github.com/fmtlib/fmt/pull/410>`_ and
|
||||||
|
`#510 <https://github.com/fmtlib/fmt/pull/510>`_).
|
||||||
|
Thanks `@Lecetem <https://github.com/Lectem>`_,
|
||||||
|
`@chenhayat (Chen Hayat) <https://github.com/chenhayat>`_ and
|
||||||
|
`@trozen <https://github.com/trozen>`_.
|
||||||
|
|
||||||
* Removed redundant include (`#479 <https://github.com/fmtlib/fmt/issues/479>`_).
|
* Worked around a broken ``__builtin_clz`` in clang with MS codegen
|
||||||
|
(`#519 <https://github.com/fmtlib/fmt/issues/519>`_).
|
||||||
|
|
||||||
|
* Removed redundant include
|
||||||
|
(`#479 <https://github.com/fmtlib/fmt/issues/479>`_).
|
||||||
|
|
||||||
* Fixed documentation issues.
|
* Fixed documentation issues.
|
||||||
|
|
||||||
3.0.1 - 2016-11-01
|
3.0.1 - 2016-11-01
|
||||||
------------------
|
------------------
|
||||||
* Fixed handling of thousands seperator (`#353 <https://github.com/fmtlib/fmt/issues/353>`_)
|
* Fixed handling of thousands seperator
|
||||||
|
(`#353 <https://github.com/fmtlib/fmt/issues/353>`_).
|
||||||
|
|
||||||
* Fixed handling of ``unsigned char`` strings (`#373 <https://github.com/fmtlib/fmt/issues/373>`_)
|
* Fixed handling of ``unsigned char`` strings
|
||||||
|
(`#373 <https://github.com/fmtlib/fmt/issues/373>`_).
|
||||||
|
|
||||||
* Corrected buffer growth when formatting time (`#367 <https://github.com/fmtlib/fmt/issues/367>`_)
|
* Corrected buffer growth when formatting time
|
||||||
|
(`#367 <https://github.com/fmtlib/fmt/issues/367>`_).
|
||||||
|
|
||||||
* Removed warnings under MSVC and clang (`#318 <https://github.com/fmtlib/fmt/issues/318>`_, `#250 <https://github.com/fmtlib/fmt/issues/250>`_, also merged `#385 <https://github.com/fmtlib/fmt/pull/385>`_ and `#361 <https://github.com/fmtlib/fmt/pull/361>`_). Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_ and `@nmoehrle (Nils Moehrle) <https://github.com/nmoehrle>`_.
|
* Removed warnings under MSVC and clang
|
||||||
|
(`#318 <https://github.com/fmtlib/fmt/issues/318>`_,
|
||||||
|
`#250 <https://github.com/fmtlib/fmt/issues/250>`_, also merged
|
||||||
|
`#385 <https://github.com/fmtlib/fmt/pull/385>`_ and
|
||||||
|
`#361 <https://github.com/fmtlib/fmt/pull/361>`_).
|
||||||
|
Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_
|
||||||
|
and `@nmoehrle (Nils Moehrle) <https://github.com/nmoehrle>`_.
|
||||||
|
|
||||||
* Fixed compilation issues under Android (`#327 <https://github.com/fmtlib/fmt/pull/327>`_, `#345 <https://github.com/fmtlib/fmt/issues/345>`_ and `#381 <https://github.com/fmtlib/fmt/pull/381>`_), FreeBSD (`#358 <https://github.com/fmtlib/fmt/pull/358>`_), Cygwin (`#388 <https://github.com/fmtlib/fmt/issues/388>`_), MinGW (`#355 <https://github.com/fmtlib/fmt/issues/355>`_) as well as other issues (`#350 <https://github.com/fmtlib/fmt/issues/350>`_, `#366 <https://github.com/fmtlib/fmt/issues/355>`_, `#348 <https://github.com/fmtlib/fmt/pull/348>`_, `#402 <https://github.com/fmtlib/fmt/pull/402>`_, `#405 <https://github.com/fmtlib/fmt/pull/405>`_). Thanks to `@dpantele (Dmitry) <https://github.com/dpantele>`_, `@hghwng (Hugh Wang) <https://github.com/hghwng>`_, `@arvedarved (Tilman Keskinöz) <https://github.com/arvedarved>`_, `@LogicalKnight (Sean) <https://github.com/LogicalKnight>`_ and `@JanHellwig (Jan Hellwig) <https://github.com/janhellwig>`_.
|
* Fixed compilation issues under Android
|
||||||
|
(`#327 <https://github.com/fmtlib/fmt/pull/327>`_,
|
||||||
* Fixed some documentation issues and extended specification (`#320 <https://github.com/fmtlib/fmt/issues/320>`_, `#333 <https://github.com/fmtlib/fmt/pull/333>`_, `#347 <https://github.com/fmtlib/fmt/issues/347>`_, `#362 <https://github.com/fmtlib/fmt/pull/362>`_). Thanks to `@smellman (Taro Matsuzawa aka. btm) <https://github.com/smellman>`_.
|
`#345 <https://github.com/fmtlib/fmt/issues/345>`_ and
|
||||||
|
`#381 <https://github.com/fmtlib/fmt/pull/381>`_),
|
||||||
|
FreeBSD (`#358 <https://github.com/fmtlib/fmt/pull/358>`_),
|
||||||
|
Cygwin (`#388 <https://github.com/fmtlib/fmt/issues/388>`_),
|
||||||
|
MinGW (`#355 <https://github.com/fmtlib/fmt/issues/355>`_) as well as other
|
||||||
|
issues (`#350 <https://github.com/fmtlib/fmt/issues/350>`_,
|
||||||
|
`#366 <https://github.com/fmtlib/fmt/issues/355>`_,
|
||||||
|
`#348 <https://github.com/fmtlib/fmt/pull/348>`_,
|
||||||
|
`#402 <https://github.com/fmtlib/fmt/pull/402>`_,
|
||||||
|
`#405 <https://github.com/fmtlib/fmt/pull/405>`_).
|
||||||
|
Thanks to `@dpantele (Dmitry) <https://github.com/dpantele>`_,
|
||||||
|
`@hghwng (Hugh Wang) <https://github.com/hghwng>`_,
|
||||||
|
`@arvedarved (Tilman Keskinöz) <https://github.com/arvedarved>`_,
|
||||||
|
`@LogicalKnight (Sean) <https://github.com/LogicalKnight>`_ and
|
||||||
|
`@JanHellwig (Jan Hellwig) <https://github.com/janhellwig>`_.
|
||||||
|
|
||||||
|
* Fixed some documentation issues and extended specification
|
||||||
|
(`#320 <https://github.com/fmtlib/fmt/issues/320>`_,
|
||||||
|
`#333 <https://github.com/fmtlib/fmt/pull/333>`_,
|
||||||
|
`#347 <https://github.com/fmtlib/fmt/issues/347>`_,
|
||||||
|
`#362 <https://github.com/fmtlib/fmt/pull/362>`_).
|
||||||
|
Thanks to `@smellman (Taro Matsuzawa aka. btm)
|
||||||
|
<https://github.com/smellman>`_.
|
||||||
|
|
||||||
3.0.0 - 2016-05-07
|
3.0.0 - 2016-05-07
|
||||||
------------------
|
------------------
|
||||||
|
@ -249,8 +980,8 @@
|
||||||
`@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_ and
|
`@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_ and
|
||||||
`@jwilk (Jakub Wilk) <https://github.com/jwilk>`_.
|
`@jwilk (Jakub Wilk) <https://github.com/jwilk>`_.
|
||||||
|
|
||||||
* Fixed compiler and sanitizer warnings (
|
* Fixed compiler and sanitizer warnings
|
||||||
`#244 <https://github.com/fmtlib/fmt/issues/244>`_,
|
(`#244 <https://github.com/fmtlib/fmt/issues/244>`_,
|
||||||
`#256 <https://github.com/fmtlib/fmt/pull/256>`_,
|
`#256 <https://github.com/fmtlib/fmt/pull/256>`_,
|
||||||
`#259 <https://github.com/fmtlib/fmt/pull/259>`_,
|
`#259 <https://github.com/fmtlib/fmt/pull/259>`_,
|
||||||
`#263 <https://github.com/fmtlib/fmt/issues/263>`_,
|
`#263 <https://github.com/fmtlib/fmt/issues/263>`_,
|
||||||
|
@ -670,8 +1401,8 @@ Fixes
|
||||||
`@Jopie64 (Johan) <https://github.com/Jopie64>`_.
|
`@Jopie64 (Johan) <https://github.com/Jopie64>`_.
|
||||||
|
|
||||||
* Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le,
|
* Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le,
|
||||||
s390x and SunOS 5.11 i386 (
|
s390x and SunOS 5.11 i386
|
||||||
`#138 <https://github.com/fmtlib/fmt/issues/138>`_,
|
(`#138 <https://github.com/fmtlib/fmt/issues/138>`_,
|
||||||
`#179 <https://github.com/fmtlib/fmt/issues/179>`_,
|
`#179 <https://github.com/fmtlib/fmt/issues/179>`_,
|
||||||
`#180 <https://github.com/fmtlib/fmt/issues/180>`_,
|
`#180 <https://github.com/fmtlib/fmt/issues/180>`_,
|
||||||
`#202 <https://github.com/fmtlib/fmt/issues/202>`_,
|
`#202 <https://github.com/fmtlib/fmt/issues/202>`_,
|
||||||
|
|
262
externals/fmt/README.rst
vendored
262
externals/fmt/README.rst
vendored
|
@ -11,50 +11,48 @@
|
||||||
:alt: Join the chat at https://gitter.im/fmtlib/fmt
|
:alt: Join the chat at https://gitter.im/fmtlib/fmt
|
||||||
:target: https://gitter.im/fmtlib/fmt
|
:target: https://gitter.im/fmtlib/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 alternative to printf or as a fast
|
It can be used as a safe and fast alternative to (s)printf and IOStreams.
|
||||||
alternative to IOStreams.
|
|
||||||
|
|
||||||
`Documentation <http://fmtlib.net/latest/>`_
|
`Documentation <http://fmtlib.net/latest/>`__
|
||||||
|
|
||||||
|
This is a development branch that implements the C++ standards proposal `P0645
|
||||||
|
Text Formatting <http://fmtlib.net/Text%20Formatting.html>`__.
|
||||||
|
Released versions are available from the `Releases page
|
||||||
|
<https://github.com/fmtlib/fmt/releases>`__.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* Two APIs: faster concatenation-based `write API
|
* Replacement-based `format API <http://fmtlib.net/dev/api.html>`_ with
|
||||||
<http://fmtlib.net/latest/api.html#write-api>`_ and slower,
|
positional arguments for localization.
|
||||||
but still very fast, replacement-based `format API
|
* `Format string syntax <http://fmtlib.net/dev/syntax.html>`_ similar to the one
|
||||||
<http://fmtlib.net/latest/api.html#format-api>`_ with positional arguments
|
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
|
||||||
for localization.
|
in Python.
|
||||||
* Write API similar to the one used by IOStreams but stateless allowing
|
|
||||||
faster implementation.
|
|
||||||
* Format API with `format string syntax
|
|
||||||
<http://fmtlib.net/latest/syntax.html>`_
|
|
||||||
similar to the one used by `str.format
|
|
||||||
<https://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.
|
|
||||||
* Safe `printf implementation
|
* Safe `printf implementation
|
||||||
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_
|
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_ including
|
||||||
including the POSIX extension for positional arguments.
|
the POSIX extension for positional arguments.
|
||||||
* Support for user-defined types.
|
* Support for user-defined types.
|
||||||
* High speed: performance of the format API is close to that of
|
* High speed: performance of the format API is close to that of glibc's `printf
|
||||||
glibc's `printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_
|
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the
|
||||||
and better than the performance of IOStreams. See `Speed tests`_ and
|
performance of 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 core library consists of a single
|
* Small code size both in terms of source code (the minimum configuration
|
||||||
header file and a single source file) and compiled code.
|
consists of just three header files, ``core.h``, ``format.h`` and
|
||||||
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>`_.
|
||||||
* Safety: the library is fully type safe, errors in format strings are
|
* Safety: the library is fully type safe, errors in format strings can be
|
||||||
reported using exceptions, automatic memory management prevents buffer
|
reported at compile time, automatic memory management prevents buffer overflow
|
||||||
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 BSD `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 consistent output
|
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with
|
||||||
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.
|
||||||
|
|
||||||
|
@ -77,56 +75,85 @@ Arguments can be accessed by position and arguments' indices can be repeated:
|
||||||
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
|
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
|
||||||
// s == "abracadabra"
|
// s == "abracadabra"
|
||||||
|
|
||||||
fmt can be used as a safe portable replacement for ``itoa``:
|
Format strings can be checked at compile time:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::MemoryWriter w;
|
// test.cc
|
||||||
w << 42; // replaces itoa(42, buffer, 10)
|
#define FMT_STRING_ALIAS 1
|
||||||
w << fmt::hex(42); // replaces itoa(42, buffer, 16)
|
#include <fmt/format.h>
|
||||||
// access the string using w.str() or w.c_str()
|
std::string s = format(fmt("{2}"), 42);
|
||||||
|
|
||||||
An object of any user-defined type for which there is an overloaded
|
.. code::
|
||||||
:code:`std::ostream` insertion operator (``operator<<``) can be formatted:
|
|
||||||
|
$ c++ -Iinclude -std=c++14 test.cc
|
||||||
|
...
|
||||||
|
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
|
||||||
|
std::string s = format(fmt("{2}"), 42);
|
||||||
|
^
|
||||||
|
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
|
||||||
|
ErrorHandler::on_error(message);
|
||||||
|
^
|
||||||
|
include/fmt/format.h:2226:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])'
|
||||||
|
context_.on_error("argument index out of range");
|
||||||
|
^
|
||||||
|
|
||||||
|
{fmt} can be used as a safe portable replacement for ``itoa``
|
||||||
|
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
#include "fmt/ostream.h"
|
fmt::memory_buffer buf;
|
||||||
|
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
|
||||||
|
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
|
||||||
|
// access the string using to_string(buf) or buf.data()
|
||||||
|
|
||||||
class Date {
|
Formatting of user-defined types is supported via a simple
|
||||||
int year_, month_, day_;
|
`extension API <http://fmtlib.net/latest/api.html#formatting-user-defined-types>`_:
|
||||||
public:
|
|
||||||
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
|
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
|
.. code:: c++
|
||||||
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
|
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
|
struct date {
|
||||||
|
int year, month, day;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<date> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const date &d, FormatContext &ctx) {
|
||||||
|
return format_to(ctx.begin(), "{}-{}-{}", d.year, d.month, d.day);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
|
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
|
||||||
// s == "The date is 2012-12-9"
|
// s == "The date is 2012-12-9"
|
||||||
|
|
||||||
You can use the `FMT_VARIADIC
|
You can create your own functions similar to `format
|
||||||
<http://fmtlib.net/latest/api.html#utilities>`_
|
|
||||||
macro to create your own functions similar to `format
|
|
||||||
<http://fmtlib.net/latest/api.html#format>`_ and
|
<http://fmtlib.net/latest/api.html#format>`_ and
|
||||||
`print <http://fmtlib.net/latest/api.html#print>`_
|
`print <http://fmtlib.net/latest/api.html#print>`_
|
||||||
which take arbitrary arguments:
|
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
// Prints formatted error message.
|
// Prints formatted error message.
|
||||||
void report_error(const char *format, fmt::ArgList args) {
|
void vreport_error(const char *format, fmt::format_args args) {
|
||||||
fmt::print("Error: ");
|
fmt::print("Error: ");
|
||||||
fmt::print(format, args);
|
fmt::vprint(format, args);
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
void report_error(const char *format, const Args & ... args) {
|
||||||
|
vreport_error(format, fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(void, report_error, const char *)
|
|
||||||
|
|
||||||
report_error("file not found: {}", path);
|
report_error("file not found: {}", path);
|
||||||
|
|
||||||
Note that you only need to define one function that takes ``fmt::ArgList``
|
Note that ``vreport_error`` is not parameterized on argument types which can
|
||||||
argument. ``FMT_VARIADIC`` automatically defines necessary wrappers that
|
improve compile times and reduce code size compared to fully parameterized version.
|
||||||
accept variable number of arguments.
|
|
||||||
|
|
||||||
Projects using this library
|
Projects using this library
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -135,15 +162,11 @@ Projects using this library
|
||||||
|
|
||||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||||
An open-source library for mathematical programming
|
An open-source library for mathematical programming
|
||||||
|
|
||||||
|
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft operations suite
|
||||||
|
|
||||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
|
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
|
||||||
|
|
||||||
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox for nonlinear dynamical systems (MIT)
|
|
||||||
|
|
||||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus (Lyft)
|
|
||||||
|
|
||||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
|
||||||
|
|
||||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
Player vs Player Gaming Network with tweaks
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
|
@ -155,14 +178,25 @@ Projects using this library
|
||||||
|
|
||||||
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
||||||
|
|
||||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to generate randomized datasets
|
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
||||||
|
for nonlinear dynamical systems (MIT)
|
||||||
|
|
||||||
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization framework
|
* `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/>`_:
|
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
|
||||||
An MMO server, compatible with most Ultima Online clients
|
An MMO server, compatible with most Ultima Online clients
|
||||||
|
|
||||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance, associative database
|
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||||
|
associative database
|
||||||
|
|
||||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
||||||
|
|
||||||
|
@ -244,7 +278,7 @@ Boost Format library
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This is a very powerful library which supports both printf-like format
|
This is a very powerful library which supports both printf-like format
|
||||||
strings and positional arguments. The main its drawback is performance.
|
strings and positional arguments. Its main drawback is performance.
|
||||||
According to various benchmarks it is much slower than other methods
|
According to various benchmarks it is much slower than other methods
|
||||||
considered here. Boost Format also has excessive build times and severe
|
considered here. Boost Format also has excessive build times and severe
|
||||||
code bloat issues (see `Benchmarks`_).
|
code bloat issues (see `Benchmarks`_).
|
||||||
|
@ -308,11 +342,12 @@ further details see the `source
|
||||||
================= ============= ===========
|
================= ============= ===========
|
||||||
Library Method Run Time, s
|
Library Method Run Time, s
|
||||||
================= ============= ===========
|
================= ============= ===========
|
||||||
EGLIBC 2.19 printf 1.30
|
libc printf 1.35
|
||||||
libstdc++ 4.8.2 std::ostream 1.85
|
libc++ std::ostream 3.42
|
||||||
fmt 1.0 fmt::print 1.42
|
fmt 534bff7 fmt::print 1.56
|
||||||
tinyformat 2.0.1 tfm::printf 2.25
|
tinyformat 2.0.1 tfm::printf 3.73
|
||||||
Boost Format 1.54 boost::format 9.94
|
Boost Format 1.54 boost::format 8.44
|
||||||
|
Folly Format folly::format 2.54
|
||||||
================= ============= ===========
|
================= ============= ===========
|
||||||
|
|
||||||
As you can see ``boost::format`` is much slower than the alternative methods; this
|
As you can see ``boost::format`` is much slower than the alternative methods; this
|
||||||
|
@ -332,38 +367,45 @@ from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
|
||||||
tests compile time and code bloat for nontrivial projects.
|
tests compile time and code bloat for nontrivial projects.
|
||||||
It generates 100 translation units and uses ``printf()`` or its alternative
|
It generates 100 translation units and uses ``printf()`` or its alternative
|
||||||
five times in each to simulate a medium sized project. The resulting
|
five times in each to simulate a medium sized project. The resulting
|
||||||
executable size and compile time (g++-4.8.1, Ubuntu GNU/Linux 13.10,
|
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
|
||||||
best of three) is shown in the following tables.
|
macOS Sierra, best of three) is shown in the following tables.
|
||||||
|
|
||||||
**Optimized build (-O3)**
|
**Optimized build (-O3)**
|
||||||
|
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
printf 2.6 41 30
|
printf 2.6 29 26
|
||||||
IOStreams 19.4 92 70
|
printf+string 16.4 29 26
|
||||||
fmt 46.8 46 34
|
IOStreams 31.1 59 55
|
||||||
tinyformat 64.6 418 386
|
fmt 19.0 37 34
|
||||||
Boost Format 222.8 990 923
|
tinyformat 44.0 103 97
|
||||||
============ =============== ==================== ==================
|
Boost Format 91.9 226 203
|
||||||
|
Folly Format 115.7 101 88
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
|
||||||
As you can see, fmt has two times less overhead in terms of resulting
|
As you can see, fmt has 60% less overhead in terms of resulting binary code
|
||||||
code size compared to IOStreams and comes pretty close to ``printf``.
|
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
|
||||||
Boost Format has by far the largest overheads.
|
and Folly Format have the largest overheads.
|
||||||
|
|
||||||
|
``printf+string`` is the same as ``printf`` but with extra ``<string>``
|
||||||
|
include to measure the overhead of the latter.
|
||||||
|
|
||||||
**Non-optimized build**
|
**Non-optimized build**
|
||||||
|
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
printf 2.1 41 30
|
printf 2.2 33 30
|
||||||
IOStreams 19.7 86 62
|
printf+string 16.0 33 30
|
||||||
fmt 47.9 108 86
|
IOStreams 28.3 56 52
|
||||||
tinyformat 27.7 234 190
|
fmt 18.2 59 50
|
||||||
Boost Format 122.6 884 763
|
tinyformat 32.6 88 82
|
||||||
============ =============== ==================== ==================
|
Boost Format 54.1 365 303
|
||||||
|
Folly Format 79.9 445 430
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
|
||||||
``libc``, ``libstdc++`` and ``libfmt`` are all linked as shared
|
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared
|
||||||
libraries to compare formatting function overhead only. Boost Format
|
libraries to compare formatting function overhead only. Boost Format
|
||||||
and tinyformat are header-only libraries so they don't provide any
|
and tinyformat are header-only libraries so they don't provide any
|
||||||
linkage options.
|
linkage options.
|
||||||
|
@ -393,6 +435,29 @@ or the bloat test::
|
||||||
|
|
||||||
$ make bloat-test
|
$ make bloat-test
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
|
||||||
|
Q: how can I capture formatting arguments and format them later?
|
||||||
|
|
||||||
|
A: use ``std::tuple``:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
auto capture(const Args&... args) {
|
||||||
|
return std::make_tuple(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto print_message = [](const auto&... args) {
|
||||||
|
fmt::print(args...);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Capture and store arguments:
|
||||||
|
auto args = capture("{} {}", 42, "foo");
|
||||||
|
// Do formatting:
|
||||||
|
std::apply(print_message, args);
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -412,10 +477,13 @@ It only applies if you distribute the documentation of fmt.
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The fmt library is maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`_)
|
The fmt library is maintained by Victor Zverovich (`vitaut
|
||||||
and Jonathan Müller (`foonathan <https://github.com/foonathan>`_) with contributions from many
|
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||||
other people. See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and `Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. Let us know if your contribution
|
<https://github.com/foonathan>`_) with contributions from many other people.
|
||||||
is not listed or mentioned incorrectly and we'll make it right.
|
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
|
||||||
|
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
|
||||||
|
Let us know if your contribution is not listed or mentioned incorrectly and
|
||||||
|
we'll make it right.
|
||||||
|
|
||||||
The benchmark section of this readme file and the performance tests are taken
|
The benchmark section of this readme file and the performance tests are taken
|
||||||
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
|
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
|
||||||
|
|
2
externals/fmt/doc/CMakeLists.txt
vendored
2
externals/fmt/doc/CMakeLists.txt
vendored
|
@ -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 build.py conf.py _templates/layout.html)
|
SOURCES api.rst syntax.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)
|
||||||
|
|
523
externals/fmt/doc/api.rst
vendored
523
externals/fmt/doc/api.rst
vendored
|
@ -4,52 +4,290 @@
|
||||||
API Reference
|
API Reference
|
||||||
*************
|
*************
|
||||||
|
|
||||||
All functions and classes provided by the fmt library reside
|
The {fmt} library API consists of the following parts:
|
||||||
in namespace ``fmt`` and macros have prefix ``FMT_``. For brevity the
|
|
||||||
namespace is usually omitted in examples.
|
|
||||||
|
|
||||||
Format API
|
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling
|
||||||
==========
|
facilities and a lightweight subset of formatting functions
|
||||||
|
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
|
||||||
|
format string checks, output iterator and user-defined type support
|
||||||
|
* :ref:`fmt/time.h <time-api>`: date and time formatting
|
||||||
|
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
|
||||||
|
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
|
||||||
|
|
||||||
The following functions defined in ``fmt/format.h`` use :ref:`format string
|
All functions and types provided by the library reside in namespace ``fmt`` and
|
||||||
syntax <syntax>` similar to the one used by Python's `str.format
|
macros have prefix ``FMT_`` or ``fmt``.
|
||||||
<http://docs.python.org/3/library/stdtypes.html#str.format>`_ function.
|
|
||||||
|
.. _core-api:
|
||||||
|
|
||||||
|
Core API
|
||||||
|
========
|
||||||
|
|
||||||
|
``fmt/core.h`` defines the core API which provides argument handling facilities
|
||||||
|
and a lightweight subset of formatting functions.
|
||||||
|
|
||||||
|
The following functions use :ref:`format string syntax <syntax>`
|
||||||
|
imilar to that of Python's `str.format
|
||||||
|
<http://docs.python.org/3/library/stdtypes.html#str.format>`_.
|
||||||
They take *format_str* and *args* as arguments.
|
They take *format_str* and *args* as arguments.
|
||||||
|
|
||||||
*format_str* is a format string that contains literal text and replacement
|
*format_str* is a format string that contains literal text and replacement
|
||||||
fields surrounded by braces ``{}``. The fields are replaced with formatted
|
fields surrounded by braces ``{}``. The fields are replaced with formatted
|
||||||
arguments in the resulting string.
|
arguments in the resulting string.
|
||||||
|
|
||||||
*args* is an argument list representing arbitrary arguments.
|
*args* is an argument list representing objects to be formatted.
|
||||||
|
|
||||||
The `performance of the format API
|
|
||||||
<https://github.com/fmtlib/fmt/blob/master/README.rst#speed-tests>`_ is close
|
|
||||||
to that of glibc's ``printf`` and better than the performance of IOStreams.
|
|
||||||
For even better speed use the `write API`_.
|
|
||||||
|
|
||||||
.. _format:
|
.. _format:
|
||||||
|
|
||||||
.. doxygenfunction:: format(CStringRef, ArgList)
|
.. doxygenfunction:: format(const String&, const Args&...)
|
||||||
|
.. doxygenfunction:: vformat(const String&, basic_format_args<typename buffer_context<Char>::type>)
|
||||||
.. doxygenfunction:: operator""_format(const char *, std::size_t)
|
|
||||||
|
|
||||||
.. _print:
|
.. _print:
|
||||||
|
|
||||||
.. doxygenfunction:: print(CStringRef, ArgList)
|
.. doxygenfunction:: print(string_view, const Args&...)
|
||||||
|
.. doxygenfunction:: vprint(string_view, format_args)
|
||||||
|
|
||||||
.. doxygenfunction:: print(std::FILE *, CStringRef, ArgList)
|
.. doxygenfunction:: print(std::FILE *, string_view, const Args&...)
|
||||||
|
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicFormatter
|
.. doxygenfunction:: print(std::FILE *, wstring_view, const Args&...)
|
||||||
|
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
|
||||||
|
|
||||||
|
Named arguments
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::arg(string_view, const T&)
|
||||||
|
|
||||||
|
Argument lists
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::make_format_args(const Args&...)
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::format_arg_store
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::basic_format_args
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. doxygenstruct:: fmt::format_args
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::basic_format_arg
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::basic_string_view
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. doxygentypedef:: fmt::string_view
|
||||||
|
.. doxygentypedef:: fmt::wstring_view
|
||||||
|
|
||||||
|
.. _format-api:
|
||||||
|
|
||||||
|
Format API
|
||||||
|
==========
|
||||||
|
|
||||||
|
``fmt/format.h`` defines the full format API providing compile-time format
|
||||||
|
string checks, output iterator and user-defined type support.
|
||||||
|
|
||||||
|
Compile-time format string checks
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
.. doxygendefine:: fmt
|
||||||
|
|
||||||
|
Formatting user-defined types
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
|
||||||
|
template and implement ``parse`` and ``format`` methods::
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
struct point { double x, y; };
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
template <>
|
||||||
|
struct formatter<point> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const point &p, FormatContext &ctx) {
|
||||||
|
return format_to(ctx.begin(), "({:.1f}, {:.1f})", p.x, p.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Then you can pass objects of type ``point`` to any formatting function::
|
||||||
|
|
||||||
|
point p = {1, 2};
|
||||||
|
std::string s = fmt::format("{}", p);
|
||||||
|
// s == "(1.0, 2.0)"
|
||||||
|
|
||||||
|
In the example above the ``formatter<point>::parse`` function ignores the
|
||||||
|
contents of the format string referred to by ``ctx.begin()`` so the object will
|
||||||
|
always be formatted in the same way. See ``formatter<tm>::parse`` in
|
||||||
|
:file:`fmt/time.h` for an advanced example of how to parse the format string and
|
||||||
|
customize the formatted output.
|
||||||
|
|
||||||
|
You can also reuse existing formatters, for example::
|
||||||
|
|
||||||
|
enum class color {red, green, blue};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<color>: formatter<string_view> {
|
||||||
|
// parse is inherited from formatter<string_view>.
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(color c, FormatContext &ctx) {
|
||||||
|
string_view name = "unknown";
|
||||||
|
switch (c) {
|
||||||
|
case color::red: name = "red"; break;
|
||||||
|
case color::green: name = "green"; break;
|
||||||
|
case color::blue: name = "blue"; break;
|
||||||
|
}
|
||||||
|
return formatter<string_view>::format(name, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
This section shows how to define a custom format function for a user-defined
|
||||||
|
type. The next section describes how to get ``fmt`` to use a conventional stream
|
||||||
|
output ``operator<<`` when one is defined for a user-defined type.
|
||||||
|
|
||||||
|
Output iterator support
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::format_to(OutputIt, string_view, const Args&...)
|
||||||
|
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...)
|
||||||
|
.. doxygenstruct:: fmt::format_to_n_result
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Literal-based API
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The following user-defined literals are defined in ``fmt/format.h``.
|
||||||
|
|
||||||
|
.. doxygenfunction:: operator""_format(const char *, std::size_t)
|
||||||
|
|
||||||
|
.. doxygenfunction:: operator""_a(const char *, std::size_t)
|
||||||
|
|
||||||
|
Utilities
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::to_string(const T&)
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::to_wstring(const T&)
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::basic_memory_buffer
|
||||||
|
:protected-members:
|
||||||
|
:members:
|
||||||
|
|
||||||
|
System errors
|
||||||
|
-------------
|
||||||
|
|
||||||
|
fmt does not use ``errno`` to communicate errors to the user, but it may call
|
||||||
|
system functions which set ``errno``. Users should not make any assumptions about
|
||||||
|
the value of ``errno`` being preserved by library functions.
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::system_error
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. doxygenfunction:: fmt::format_system_error
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::windows_error
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. _formatstrings:
|
||||||
|
|
||||||
|
Custom allocators
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The {fmt} library supports custom dynamic memory allocators.
|
||||||
|
A custom allocator class can be specified as a template argument to
|
||||||
|
:class:`fmt::basic_memory_buffer`::
|
||||||
|
|
||||||
|
using custom_memory_buffer =
|
||||||
|
fmt::basic_memory_buffer<char, fmt::inline_buffer_size, custom_allocator>;
|
||||||
|
|
||||||
|
It is also possible to write a formatting function that uses a custom
|
||||||
|
allocator::
|
||||||
|
|
||||||
|
using custom_string =
|
||||||
|
std::basic_string<char, std::char_traits<char>, custom_allocator>;
|
||||||
|
|
||||||
|
custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
|
||||||
|
fmt::format_args args) {
|
||||||
|
custom_memory_buffer buf(alloc);
|
||||||
|
fmt::vformat_to(buf, format_str, args);
|
||||||
|
return custom_string(buf.data(), buf.size(), alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline custom_string format(custom_allocator alloc,
|
||||||
|
fmt::string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
The allocator will be used for the output container only. If you are using named
|
||||||
|
arguments, the container that stores pointers to them will be allocated using
|
||||||
|
the default allocator. Also floating-point formatting falls back on ``sprintf``
|
||||||
|
which may do allocations.
|
||||||
|
|
||||||
|
Custom formatting of built-in types
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
It is possible to change the way arguments are formatted by providing a
|
||||||
|
custom argument formatter class::
|
||||||
|
|
||||||
|
using arg_formatter =
|
||||||
|
fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>>;
|
||||||
|
|
||||||
|
// A custom argument formatter that formats negative integers as unsigned
|
||||||
|
// with the ``x`` format specifier.
|
||||||
|
class custom_arg_formatter : public arg_formatter {
|
||||||
|
public:
|
||||||
|
custom_arg_formatter(fmt::format_context &ctx, fmt::format_specs &spec)
|
||||||
|
: arg_formatter(ctx, spec) {}
|
||||||
|
|
||||||
|
using arg_formatter::operator();
|
||||||
|
|
||||||
|
auto operator()(int value) {
|
||||||
|
if (spec().type() == 'x')
|
||||||
|
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
|
||||||
|
return arg_formatter::operator()(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||||
|
fmt::memory_buffer buffer;
|
||||||
|
// Pass custom argument formatter as a template arg to vformat_to.
|
||||||
|
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline std::string custom_format(
|
||||||
|
fmt::string_view format_str, const Args &... args) {
|
||||||
|
return custom_vformat(format_str, fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string s = custom_format("{:x}", -42); // s == "ffffffd6"
|
||||||
|
|
||||||
|
.. doxygenclass:: fmt::arg_formatter
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. _time-api:
|
||||||
|
|
||||||
Date and time formatting
|
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/time.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)
|
||||||
|
@ -58,134 +296,35 @@ formatting::
|
||||||
The format string syntax is described in the documentation of
|
The format string syntax is described in the documentation of
|
||||||
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
|
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
|
||||||
|
|
||||||
Formatting user-defined types
|
.. _ostream-api:
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
A custom ``format_arg`` function may be implemented and used to format any
|
|
||||||
user-defined type. That is how date and time formatting described in the
|
|
||||||
previous section is implemented in :file:`fmt/time.h`. The following example
|
|
||||||
shows how to implement custom formatting for a user-defined structure.
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
struct MyStruct { double a, b; };
|
|
||||||
|
|
||||||
void format_arg(fmt::BasicFormatter<char> &f,
|
|
||||||
const char *&format_str, const MyStruct &s) {
|
|
||||||
f.writer().write("[MyStruct: a={:.1f}, b={:.2f}]", s.a, s.b);
|
|
||||||
}
|
|
||||||
|
|
||||||
MyStruct m = { 1, 2 };
|
|
||||||
std::string s = fmt::format("m={}", n);
|
|
||||||
// s == "m=[MyStruct: a=1.0, b=2.00]"
|
|
||||||
|
|
||||||
Note in the example above the ``format_arg`` function ignores the contents of
|
|
||||||
``format_str`` so the type will always be formatted as specified. See
|
|
||||||
``format_arg`` in :file:`fmt/time.h` for an advanced example of how to use
|
|
||||||
the ``format_str`` argument to customize the formatted output.
|
|
||||||
|
|
||||||
This technique can also be used for formatting class hierarchies::
|
|
||||||
|
|
||||||
namespace local {
|
|
||||||
struct Parent {
|
|
||||||
Parent(int p) : p(p) {}
|
|
||||||
virtual void write(fmt::Writer &w) const {
|
|
||||||
w.write("Parent : p={}", p);
|
|
||||||
}
|
|
||||||
int p;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Child : Parent {
|
|
||||||
Child(int c, int p) : Parent(p), c(c) {}
|
|
||||||
virtual void write(fmt::Writer &w) const {
|
|
||||||
w.write("Child c={} : ", c);
|
|
||||||
Parent::write(w);
|
|
||||||
}
|
|
||||||
int c;
|
|
||||||
};
|
|
||||||
|
|
||||||
void format_arg(fmt::BasicFormatter<char> &f,
|
|
||||||
const char *&format_str, const Parent &p) {
|
|
||||||
p.write(f.writer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Local::Child c(1,2);
|
|
||||||
Local::Parent &p = c;
|
|
||||||
fmt::print("via ref to base: {}\n", p);
|
|
||||||
fmt::print("direct to child: {}\n", c);
|
|
||||||
|
|
||||||
This section shows how to define a custom format function for a user-defined
|
|
||||||
type. The next section describes how to get ``fmt`` to use a conventional stream
|
|
||||||
output ``operator<<`` when one is defined for a user-defined type.
|
|
||||||
|
|
||||||
``std::ostream`` support
|
``std::ostream`` support
|
||||||
------------------------
|
========================
|
||||||
|
|
||||||
The header ``fmt/ostream.h`` provides ``std::ostream`` support including
|
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
|
||||||
formatting of user-defined types that have overloaded ``operator<<``::
|
user-defined types that have overloaded ``operator<<``::
|
||||||
|
|
||||||
#include "fmt/ostream.h"
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
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) {}
|
||||||
|
|
||||||
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_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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::ostream&, CStringRef, ArgList)
|
.. doxygenfunction:: print(std::ostream&, string_view, const Args&...)
|
||||||
|
|
||||||
Argument formatters
|
.. _printf-api:
|
||||||
-------------------
|
|
||||||
|
|
||||||
It is possible to change the way arguments are formatted by providing a
|
``printf`` formatting
|
||||||
custom argument formatter class::
|
=====================
|
||||||
|
|
||||||
// A custom argument formatter that formats negative integers as unsigned
|
|
||||||
// with the ``x`` format specifier.
|
|
||||||
class CustomArgFormatter :
|
|
||||||
public fmt::BasicArgFormatter<CustomArgFormatter, char> {
|
|
||||||
public:
|
|
||||||
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
|
|
||||||
fmt::FormatSpec &s, const char *fmt)
|
|
||||||
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
|
|
||||||
|
|
||||||
void visit_int(int value) {
|
|
||||||
if (spec().type() == 'x')
|
|
||||||
visit_uint(value); // convert to unsigned and format
|
|
||||||
else
|
|
||||||
fmt::BasicArgFormatter<CustomArgFormatter, char>::visit_int(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string custom_format(const char *format_str, fmt::ArgList args) {
|
|
||||||
fmt::MemoryWriter writer;
|
|
||||||
// Pass custom argument formatter as a template arg to BasicFormatter.
|
|
||||||
fmt::BasicFormatter<char, CustomArgFormatter> formatter(args, writer);
|
|
||||||
formatter.format(format_str);
|
|
||||||
return writer.str();
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(std::string, custom_format, const char *)
|
|
||||||
|
|
||||||
std::string s = custom_format("{:x}", -42); // s == "ffffffd6"
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::ArgVisitor
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicArgFormatter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::ArgFormatter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Printf formatting
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
|
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
|
||||||
The following functions use `printf format string syntax
|
The following functions use `printf format string syntax
|
||||||
|
@ -194,118 +333,10 @@ the POSIX extension for positional arguments. Unlike their standard
|
||||||
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
|
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
|
||||||
argument type doesn't match its format specification.
|
argument type doesn't match its format specification.
|
||||||
|
|
||||||
.. doxygenfunction:: printf(CStringRef, ArgList)
|
.. doxygenfunction:: printf(string_view, const Args&...)
|
||||||
|
|
||||||
.. doxygenfunction:: fprintf(std::FILE *, CStringRef, ArgList)
|
.. doxygenfunction:: fprintf(std::FILE *, string_view, const Args&...)
|
||||||
|
|
||||||
.. doxygenfunction:: fprintf(std::ostream&, CStringRef, ArgList)
|
.. doxygenfunction:: fprintf(std::ostream&, string_view, const Args&...)
|
||||||
|
|
||||||
.. doxygenfunction:: sprintf(CStringRef, ArgList)
|
.. doxygenfunction:: sprintf(string_view, const Args&...)
|
||||||
|
|
||||||
.. doxygenclass:: fmt::PrintfFormatter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicPrintfArgFormatter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::PrintfArgFormatter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Write API
|
|
||||||
=========
|
|
||||||
|
|
||||||
The write API provides classes for writing formatted data into character
|
|
||||||
streams. It is usually faster than the `format API`_ but, as IOStreams,
|
|
||||||
may result in larger compiled code size. The main writer class is
|
|
||||||
`~fmt::BasicMemoryWriter` which stores its output in a memory buffer and
|
|
||||||
provides direct access to it. It is possible to create custom writers that
|
|
||||||
store output elsewhere by subclassing `~fmt::BasicWriter`.
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicWriter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicMemoryWriter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicArrayWriter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicStringWriter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicContainerWriter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenfunction:: bin(int)
|
|
||||||
|
|
||||||
.. doxygenfunction:: oct(int)
|
|
||||||
|
|
||||||
.. doxygenfunction:: hex(int)
|
|
||||||
|
|
||||||
.. doxygenfunction:: hexu(int)
|
|
||||||
|
|
||||||
.. doxygenfunction:: pad(int, unsigned, Char)
|
|
||||||
|
|
||||||
Utilities
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::arg(StringRef, const T&)
|
|
||||||
|
|
||||||
.. doxygenfunction:: operator""_a(const char *, std::size_t)
|
|
||||||
|
|
||||||
.. doxygendefine:: FMT_CAPTURE
|
|
||||||
|
|
||||||
.. doxygendefine:: FMT_VARIADIC
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::ArgList
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::to_string(const T&)
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::to_wstring(const T&)
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicStringRef
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicCStringRef
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::Buffer
|
|
||||||
:protected-members:
|
|
||||||
:members:
|
|
||||||
|
|
||||||
System errors
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::SystemError
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::format_system_error
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::WindowsError
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. _formatstrings:
|
|
||||||
|
|
||||||
Custom allocators
|
|
||||||
=================
|
|
||||||
|
|
||||||
The fmt library supports custom dynamic memory allocators.
|
|
||||||
A custom allocator class can be specified as a template argument to
|
|
||||||
:class:`fmt::BasicMemoryWriter`::
|
|
||||||
|
|
||||||
typedef fmt::BasicMemoryWriter<char, CustomAllocator> CustomMemoryWriter;
|
|
||||||
|
|
||||||
It is also possible to write a formatting function that uses a custom
|
|
||||||
allocator::
|
|
||||||
|
|
||||||
typedef std::basic_string<char, std::char_traits<char>, CustomAllocator>
|
|
||||||
CustomString;
|
|
||||||
|
|
||||||
CustomString format(CustomAllocator alloc, fmt::CStringRef format_str,
|
|
||||||
fmt::ArgList args) {
|
|
||||||
CustomMemoryWriter writer(alloc);
|
|
||||||
writer.write(format_str, args);
|
|
||||||
return CustomString(writer.data(), writer.size(), alloc);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(CustomString, format, CustomAllocator, fmt::CStringRef)
|
|
||||||
|
|
17
externals/fmt/doc/build.py
vendored
17
externals/fmt/doc/build.py
vendored
|
@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
|
||||||
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
|
from 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']
|
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']
|
||||||
|
|
||||||
def pip_install(package, commit=None, **kwargs):
|
def pip_install(package, commit=None, **kwargs):
|
||||||
"Install package using pip."
|
"Install package using pip."
|
||||||
|
@ -56,14 +56,14 @@ def create_build_env(dirname='virtualenv'):
|
||||||
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
||||||
min_version='1.4.1.dev20160531')
|
min_version='1.4.1.dev20160531')
|
||||||
pip_install('michaeljones/breathe',
|
pip_install('michaeljones/breathe',
|
||||||
'6b1c5bb7a1866f15fc328b8716258354b10c1daa',
|
'129222318f7c8f865d2631e7da7b033567e7f56a',
|
||||||
min_version='4.2.0')
|
min_version='4.2.0')
|
||||||
|
|
||||||
def build_docs(version='dev', **kwargs):
|
def build_docs(version='dev', **kwargs):
|
||||||
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
|
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
|
||||||
work_dir = kwargs.get('work_dir', '.')
|
work_dir = kwargs.get('work_dir', '.')
|
||||||
include_dir = kwargs.get('include_dir',
|
include_dir = kwargs.get(
|
||||||
os.path.join(os.path.dirname(doc_dir), 'fmt'))
|
'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt'))
|
||||||
# Build docs.
|
# Build docs.
|
||||||
cmd = ['doxygen', '-']
|
cmd = ['doxygen', '-']
|
||||||
p = Popen(cmd, stdin=PIPE)
|
p = Popen(cmd, stdin=PIPE)
|
||||||
|
@ -74,8 +74,8 @@ 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}/container.h {0}/format.h {0}/ostream.h \
|
INPUT = {0}/core.h {0}/format.h {0}/ostream.h \
|
||||||
{0}/printf.h {0}/string.h
|
{0}/printf.h {0}/time.h
|
||||||
QUIET = YES
|
QUIET = YES
|
||||||
JAVADOC_AUTOBRIEF = YES
|
JAVADOC_AUTOBRIEF = YES
|
||||||
AUTOLINK_SUPPORT = NO
|
AUTOLINK_SUPPORT = NO
|
||||||
|
@ -89,7 +89,10 @@ def build_docs(version='dev', **kwargs):
|
||||||
FMT_USE_VARIADIC_TEMPLATES=1 \
|
FMT_USE_VARIADIC_TEMPLATES=1 \
|
||||||
FMT_USE_RVALUE_REFERENCES=1 \
|
FMT_USE_RVALUE_REFERENCES=1 \
|
||||||
FMT_USE_USER_DEFINED_LITERALS=1 \
|
FMT_USE_USER_DEFINED_LITERALS=1 \
|
||||||
FMT_API=
|
FMT_API= \
|
||||||
|
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
||||||
|
"FMT_END_NAMESPACE=}}" \
|
||||||
|
"FMT_STRING_ALIAS=1"
|
||||||
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:
|
||||||
|
|
2
externals/fmt/doc/conf.py
vendored
2
externals/fmt/doc/conf.py
vendored
|
@ -47,7 +47,7 @@ source_suffix = '.rst'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'fmt'
|
project = u'fmt'
|
||||||
copyright = u'2012-2015, Victor Zverovich'
|
copyright = u'2012-present, Victor Zverovich'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
|
132
externals/fmt/doc/index.rst
vendored
132
externals/fmt/doc/index.rst
vendored
|
@ -2,8 +2,7 @@ Overview
|
||||||
========
|
========
|
||||||
|
|
||||||
**fmt** (formerly cppformat) is an open-source formatting library.
|
**fmt** (formerly cppformat) is an open-source formatting library.
|
||||||
It can be used as a safe alternative to printf or as a fast
|
It can be used as a fast and safe alternative to printf and IOStreams.
|
||||||
alternative to C++ IOStreams.
|
|
||||||
|
|
||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ alternative to C++ IOStreams.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
.. _format-api:
|
.. _format-api-intro:
|
||||||
|
|
||||||
Format API
|
Format API
|
||||||
----------
|
----------
|
||||||
|
@ -25,21 +24,21 @@ The replacement-based Format API provides a safe alternative to ``printf``,
|
||||||
``sprintf`` and friends with comparable or `better performance
|
``sprintf`` and friends with comparable or `better performance
|
||||||
<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>`_.
|
||||||
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/2/library/stdtypes.html#str.format>`_
|
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_
|
||||||
in Python:
|
in Python:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::format("The answer is {}", 42);
|
fmt::format("The answer is {}.", 42);
|
||||||
|
|
||||||
The ``fmt::format`` function returns a string "The answer is 42". You can use
|
The ``fmt::format`` function returns a string "The answer is 42.". You can use
|
||||||
``fmt::MemoryWriter`` to avoid constructing ``std::string``:
|
``fmt::memory_buffer`` to avoid constructing ``std::string``:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer out;
|
||||||
w.write("Look, a {} string", 'C');
|
format_to(out, "For a moment, {} happened.", "nothing");
|
||||||
w.c_str(); // returns a C string (const char*)
|
out.data(); // returns a pointer to the formatted data
|
||||||
|
|
||||||
The ``fmt::print`` function performs formatting and writes the result to a file:
|
The ``fmt::print`` function performs formatting and writes the result to a file:
|
||||||
|
|
||||||
|
@ -54,11 +53,6 @@ The file argument can be omitted in which case the function prints to
|
||||||
|
|
||||||
fmt::print("Don't {}\n", "panic");
|
fmt::print("Don't {}\n", "panic");
|
||||||
|
|
||||||
If your compiler supports C++11, then the formatting functions are implemented
|
|
||||||
with variadic templates. Otherwise variadic functions are emulated by generating
|
|
||||||
a set of lightweight wrappers. This ensures compatibility with older compilers
|
|
||||||
while providing a natural API.
|
|
||||||
|
|
||||||
The Format API also supports positional arguments useful for localization:
|
The Format API also supports positional arguments useful for localization:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
@ -93,39 +87,31 @@ literal operators, they must be made visible with the directive
|
||||||
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
|
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
|
||||||
``_format`` but nothing else from the ``fmt`` namespace.
|
``_format`` but nothing else from the ``fmt`` namespace.
|
||||||
|
|
||||||
.. _write-api:
|
|
||||||
|
|
||||||
Write API
|
|
||||||
---------
|
|
||||||
|
|
||||||
The concatenation-based Write API (experimental) provides a `fast
|
|
||||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
|
|
||||||
stateless alternative to IOStreams:
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
fmt::MemoryWriter out;
|
|
||||||
out << "The answer in hexadecimal is " << hex(42);
|
|
||||||
|
|
||||||
.. _safety:
|
.. _safety:
|
||||||
|
|
||||||
Safety
|
Safety
|
||||||
------
|
------
|
||||||
|
|
||||||
The library is fully type safe, automatic memory management prevents buffer
|
The library is fully type safe, automatic memory management prevents buffer
|
||||||
overflow, errors in format strings are reported using exceptions. For example,
|
overflow, errors in format strings are reported using exceptions or at compile
|
||||||
the code
|
tim. For example, the code
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
fmt::format("The answer is {:d}", "forty-two");
|
fmt::format("The answer is {:d}", "forty-two");
|
||||||
|
|
||||||
throws a ``FormatError`` exception with description
|
throws a ``format_error`` exception with description "unknown format code 'd' for
|
||||||
"unknown format code 'd' for string", because the argument
|
string", because the argument ``"forty-two"`` is a string while the format code
|
||||||
``"forty-two"`` is a string while the format code ``d``
|
``d`` only applies to integers, while
|
||||||
only applies to integers.
|
|
||||||
|
|
||||||
Where possible, errors are caught at compile time. For example, the code
|
.. code:: c++
|
||||||
|
|
||||||
|
format(fmt("The answer is {:d}"), "forty-two");
|
||||||
|
|
||||||
|
reports a compile-time error for the same reason on compilers that support
|
||||||
|
relaxed ``constexpr``.
|
||||||
|
|
||||||
|
The following code
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
|
@ -143,44 +129,56 @@ 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.
|
||||||
|
|
||||||
Note that fmt does not use the value of the ``errno`` global to communicate
|
Compact binary code
|
||||||
errors to the user, but it may call system functions which set ``errno``. Since
|
-------------------
|
||||||
fmt does not attempt to preserve the value of ``errno``, users should not make
|
|
||||||
any assumptions about it and always set it to ``0`` before making any system
|
The library is designed to produce compact per-call compiled code. For example
|
||||||
calls that convey error information via ``errno``.
|
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("The answer is {}.", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiles to just
|
||||||
|
|
||||||
|
.. code:: asm
|
||||||
|
|
||||||
|
main: # @main
|
||||||
|
sub rsp, 24
|
||||||
|
mov qword ptr [rsp], 42
|
||||||
|
mov rcx, rsp
|
||||||
|
mov edi, offset .L.str
|
||||||
|
mov esi, 17
|
||||||
|
mov edx, 2
|
||||||
|
call fmt::v5::vprint(fmt::v5::basic_string_view<char>, fmt::v5::format_args)
|
||||||
|
xor eax, eax
|
||||||
|
add rsp, 24
|
||||||
|
ret
|
||||||
|
.L.str:
|
||||||
|
.asciz "The answer is {}."
|
||||||
|
|
||||||
.. _portability:
|
.. _portability:
|
||||||
|
|
||||||
Portability
|
Portability
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The library is highly portable. Here is an incomplete list of operating systems
|
The library is highly portable and relies only on a small set of C++11 features:
|
||||||
and compilers where it has been tested and known to work:
|
|
||||||
|
|
||||||
* 64-bit (amd64) GNU/Linux with GCC 4.4.3,
|
* variadic templates
|
||||||
`4.6.3 <https://travis-ci.org/fmtlib/fmt>`_, 4.7.2, 4.8.1, and Intel C++
|
* type traits
|
||||||
Compiler (ICC) 14.0.2
|
* rvalue references
|
||||||
|
* decltype
|
||||||
|
* trailing return types
|
||||||
|
* deleted functions
|
||||||
|
|
||||||
* 32-bit (i386) GNU/Linux with GCC 4.4.3, 4.6.3
|
These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older
|
||||||
|
compilers use fmt `version 4.x
|
||||||
* Mac OS X with GCC 4.2.1 and Clang 4.2, 5.1.0
|
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
|
||||||
|
maintained and only requires C++98.
|
||||||
* 64-bit Windows with Visual C++ 2010, 2013 and
|
|
||||||
`2015 <https://ci.appveyor.com/project/vitaut/fmt>`_
|
|
||||||
|
|
||||||
* 32-bit Windows with Visual C++ 2010
|
|
||||||
|
|
||||||
Although the library uses C++11 features when available, it also works with
|
|
||||||
older compilers and standard library implementations. The only thing to keep in
|
|
||||||
mind for C++98 portability:
|
|
||||||
|
|
||||||
* Variadic templates: minimum GCC 4.4, Clang 2.9 or VS2013. This feature allows
|
|
||||||
the Format API to accept an unlimited number of arguments. With older
|
|
||||||
compilers the maximum is 15.
|
|
||||||
|
|
||||||
* User-defined literals: minimum GCC 4.7, Clang 3.1 or VS2015. The suffixes
|
|
||||||
``_format`` and ``_a`` are functionally equivalent to the functions
|
|
||||||
``fmt::format`` and ``fmt::arg``.
|
|
||||||
|
|
||||||
The output of all formatting functions is consistent across platforms. In
|
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
|
||||||
|
@ -198,7 +196,7 @@ 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
|
||||||
a single header file and a single source file 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 BSD `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.
|
||||||
|
|
||||||
|
|
30
externals/fmt/doc/syntax.rst
vendored
30
externals/fmt/doc/syntax.rst
vendored
|
@ -4,8 +4,9 @@
|
||||||
Format String Syntax
|
Format String Syntax
|
||||||
********************
|
********************
|
||||||
|
|
||||||
Formatting functions such as :ref:`fmt::format() <format>` and :ref:`fmt::print() <print>`
|
Formatting functions such as :ref:`fmt::format() <format>` and
|
||||||
use the same format string syntax described in this section.
|
:ref:`fmt::print() <print>` use the same format string syntax described in this
|
||||||
|
section.
|
||||||
|
|
||||||
Format strings contain "replacement fields" surrounded by curly braces ``{}``.
|
Format strings contain "replacement fields" surrounded by curly braces ``{}``.
|
||||||
Anything that is not contained in braces is considered literal text, which is
|
Anything that is not contained in braces is considered literal text, which is
|
||||||
|
@ -35,6 +36,8 @@ If the numerical arg_ids in a format string are 0, 1, 2, ... in sequence,
|
||||||
they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be
|
they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be
|
||||||
automatically inserted in that order.
|
automatically inserted in that order.
|
||||||
|
|
||||||
|
Named arguments can be referred to by their names or indices.
|
||||||
|
|
||||||
Some simple format string examples::
|
Some simple format string examples::
|
||||||
|
|
||||||
"First, thou shalt count to {0}" // References the first argument
|
"First, thou shalt count to {0}" // References the first argument
|
||||||
|
@ -51,8 +54,8 @@ described in the next section.
|
||||||
|
|
||||||
A *format_spec* field can also include nested replacement fields in certain
|
A *format_spec* field can also include nested replacement fields in certain
|
||||||
positions within it. These nested replacement fields can contain only an
|
positions within it. These nested replacement fields can contain only an
|
||||||
argument id; format specifications are not allowed. This allows the
|
argument id; format specifications are not allowed. This allows the formatting
|
||||||
formatting of a value to be dynamically specified.
|
of a value to be dynamically specified.
|
||||||
|
|
||||||
See the :ref:`formatexamples` section for some examples.
|
See the :ref:`formatexamples` section for some examples.
|
||||||
|
|
||||||
|
@ -73,7 +76,7 @@ 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 '}'>
|
fill: <a character other than '{', '}' or '\0'>
|
||||||
align: "<" | ">" | "=" | "^"
|
align: "<" | ">" | "=" | "^"
|
||||||
sign: "+" | "-" | " "
|
sign: "+" | "-" | " "
|
||||||
width: `integer` | "{" `arg_id` "}"
|
width: `integer` | "{" `arg_id` "}"
|
||||||
|
@ -81,11 +84,11 @@ The general form of a *standard format specifier* is:
|
||||||
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" | "p" | "s"
|
||||||
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
|
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
|
||||||
|
|
||||||
The *fill* character can be any character other than '{' or '}'. The presence
|
The *fill* character can be any character other than '{', '}' or '\\0'. The
|
||||||
of a fill character is signaled by the character following it, which must be
|
presence of a fill character is signaled by the character following it, which
|
||||||
one of the alignment options. If the second character of *format_spec* is not
|
must be one of the alignment options. If the second character of *format_spec*
|
||||||
a valid alignment option, then it is assumed that both the fill character and
|
is not a valid alignment option, then it is assumed that both the fill character
|
||||||
the alignment option are absent.
|
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:
|
||||||
|
|
||||||
|
@ -360,7 +363,12 @@ Replacing ``%x`` and ``%o`` and converting the value to different bases::
|
||||||
// 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);
|
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::
|
||||||
|
|
||||||
|
format("{:#04x}", 0);
|
||||||
|
// Result: "0x00"
|
||||||
|
|
||||||
.. ifconfig:: False
|
.. ifconfig:: False
|
||||||
|
|
||||||
|
|
19
externals/fmt/doc/usage.rst
vendored
19
externals/fmt/doc/usage.rst
vendored
|
@ -61,19 +61,22 @@ __ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
|
||||||
Header-only usage with CMake
|
Header-only usage with CMake
|
||||||
============================
|
============================
|
||||||
|
|
||||||
In order to add ``fmtlib`` into an existing ``CMakeLists.txt`` file, you can add the ``fmt`` library directory into your main project, which will enable the ``fmt`` library::
|
You can add the ``fmt`` library directory into your project and include it in
|
||||||
|
your ``CMakeLists.txt`` file::
|
||||||
|
|
||||||
add_subdirectory(fmt)
|
add_subdirectory(fmt)
|
||||||
|
|
||||||
If you have a project called ``foo`` that you would like to link against the fmt library in a header-only fashion, you can enable with with::
|
|
||||||
|
|
||||||
target_link_libraries(foo PRIVATE fmt::fmt-header-only)
|
or
|
||||||
|
|
||||||
And then to ensure that the ``fmt`` library does not always get built, you can modify the call to ``add_subdirectory`` to read ::
|
::
|
||||||
|
|
||||||
add_subdirectory(fmt EXCLUDE_FROM_ALL)
|
add_subdirectory(fmt EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
This will ensure that the ``fmt`` library is exluded from calls to ``make``, ``make all``, or ``cmake --build .``.
|
to exclude it from ``make``, ``make all``, or ``cmake --build .``.
|
||||||
|
|
||||||
|
Settting up your target to use a header-only version of ``fmt`` is equaly easy::
|
||||||
|
|
||||||
|
target_link_libraries(<your-target> PRIVATE fmt-header-only)
|
||||||
|
|
||||||
Building the documentation
|
Building the documentation
|
||||||
==========================
|
==========================
|
||||||
|
|
94
externals/fmt/fmt/CMakeLists.txt
vendored
94
externals/fmt/fmt/CMakeLists.txt
vendored
|
@ -1,94 +0,0 @@
|
||||||
# Define the fmt library, its includes and the needed defines.
|
|
||||||
# *.cc are added to FMT_HEADERS for the header-only configuration.
|
|
||||||
set(FMT_HEADERS container.h format.h format.cc ostream.h ostream.cc printf.h
|
|
||||||
printf.cc string.h time.h)
|
|
||||||
if (HAVE_OPEN)
|
|
||||||
set(FMT_HEADERS ${FMT_HEADERS} posix.h)
|
|
||||||
set(FMT_SOURCES ${FMT_SOURCES} posix.cc)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} ../README.rst ../ChangeLog.rst)
|
|
||||||
add_library(fmt::fmt ALIAS fmt)
|
|
||||||
|
|
||||||
# Starting with cmake 3.1 the CXX_STANDARD property can be used instead.
|
|
||||||
# Note: Don't make -std=c++11 public or interface, since it breaks projects
|
|
||||||
# that use C++14.
|
|
||||||
target_compile_options(fmt PRIVATE ${CPP11_FLAG})
|
|
||||||
if (FMT_PEDANTIC)
|
|
||||||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
target_include_directories(fmt PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
|
||||||
$<INSTALL_INTERFACE:include>)
|
|
||||||
|
|
||||||
set_target_properties(fmt PROPERTIES
|
|
||||||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
|
|
||||||
|
|
||||||
if (BUILD_SHARED_LIBS)
|
|
||||||
if (UNIX AND NOT APPLE)
|
|
||||||
# Fix rpmlint warning:
|
|
||||||
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
|
|
||||||
target_link_libraries(fmt -Wl,--as-needed)
|
|
||||||
endif ()
|
|
||||||
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# additionally define a header only library when cmake is new enough
|
|
||||||
if (CMAKE_VERSION VERSION_GREATER 3.1.0 OR CMAKE_VERSION VERSION_EQUAL 3.1.0)
|
|
||||||
add_library(fmt-header-only INTERFACE)
|
|
||||||
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
|
||||||
|
|
||||||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
|
||||||
|
|
||||||
target_include_directories(fmt-header-only INTERFACE
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
|
||||||
$<INSTALL_INTERFACE:include>)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Install targets.
|
|
||||||
if (FMT_INSTALL)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
|
||||||
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
|
||||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
|
||||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
|
||||||
set(targets_export_name fmt-targets)
|
|
||||||
|
|
||||||
set (INSTALL_TARGETS fmt)
|
|
||||||
if (TARGET fmt-header-only)
|
|
||||||
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
|
||||||
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
|
|
||||||
|
|
||||||
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
|
||||||
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
|
||||||
|
|
||||||
# Generate the version, config and target files into the build directory.
|
|
||||||
write_basic_package_version_file(
|
|
||||||
${version_config}
|
|
||||||
VERSION ${FMT_VERSION}
|
|
||||||
COMPATIBILITY AnyNewerVersion)
|
|
||||||
configure_package_config_file(
|
|
||||||
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
|
|
||||||
${project_config}
|
|
||||||
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
|
||||||
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
|
||||||
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
|
||||||
|
|
||||||
# Install version, config and target files.
|
|
||||||
install(
|
|
||||||
FILES ${project_config} ${version_config}
|
|
||||||
DESTINATION ${FMT_CMAKE_DIR})
|
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
|
||||||
NAMESPACE fmt::)
|
|
||||||
|
|
||||||
# Install the library and headers.
|
|
||||||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
|
||||||
DESTINATION ${FMT_LIB_DIR})
|
|
||||||
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
|
|
||||||
endif ()
|
|
82
externals/fmt/fmt/container.h
vendored
82
externals/fmt/fmt/container.h
vendored
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++ - standard container utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_CONTAINER_H_
|
|
||||||
#define FMT_CONTAINER_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
A "buffer" that appends data to a standard container (e.g. typically a
|
|
||||||
``std::vector`` or ``std::basic_string``).
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Container>
|
|
||||||
class ContainerBuffer : public Buffer<typename Container::value_type> {
|
|
||||||
private:
|
|
||||||
Container& container_;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void grow(std::size_t size) FMT_OVERRIDE {
|
|
||||||
container_.resize(size);
|
|
||||||
this->ptr_ = &container_[0];
|
|
||||||
this->capacity_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ContainerBuffer(Container& container) : container_(container) {
|
|
||||||
this->size_ = container_.size();
|
|
||||||
if (this->size_ > 0) {
|
|
||||||
this->ptr_ = &container_[0];
|
|
||||||
this->capacity_ = this->size_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
This class template provides operations for formatting and appending data
|
|
||||||
to a standard *container* like ``std::vector`` or ``std::basic_string``.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
void vecformat(std::vector<char>& dest, fmt::BasicCStringRef<char> format,
|
|
||||||
fmt::ArgList args) {
|
|
||||||
fmt::BasicContainerWriter<std::vector<char> > appender(dest);
|
|
||||||
appender.write(format, args);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(void, vecformat, std::vector<char>&,
|
|
||||||
fmt::BasicCStringRef<char>);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <class Container>
|
|
||||||
class BasicContainerWriter
|
|
||||||
: public BasicWriter<typename Container::value_type> {
|
|
||||||
private:
|
|
||||||
internal::ContainerBuffer<Container> buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a :class:`fmt::BasicContainerWriter` object.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
explicit BasicContainerWriter(Container& dest)
|
|
||||||
: BasicWriter<typename Container::value_type>(buffer_), buffer_(dest) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#endif // FMT_CONTAINER_H_
|
|
495
externals/fmt/fmt/format.cc
vendored
495
externals/fmt/fmt/format.cc
vendored
|
@ -1,495 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <climits>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstddef> // for std::ptrdiff_t
|
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(__MINGW32__)
|
|
||||||
# include <cstring>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
|
||||||
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
|
||||||
# define WIN32_LEAN_AND_MEAN
|
|
||||||
# endif
|
|
||||||
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
|
||||||
# include <windows.h>
|
|
||||||
# else
|
|
||||||
# define NOMINMAX
|
|
||||||
# include <windows.h>
|
|
||||||
# undef NOMINMAX
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_EXCEPTIONS
|
|
||||||
# define FMT_TRY try
|
|
||||||
# define FMT_CATCH(x) catch (x)
|
|
||||||
#else
|
|
||||||
# define FMT_TRY if (true)
|
|
||||||
# define FMT_CATCH(x) if (false)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(push)
|
|
||||||
# pragma warning(disable: 4127) // conditional expression is constant
|
|
||||||
# pragma warning(disable: 4702) // unreachable code
|
|
||||||
// Disable deprecation warning for strerror. The latter is not called but
|
|
||||||
// MSVC fails to detect it.
|
|
||||||
# pragma warning(disable: 4996)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
|
||||||
// system functions are not available.
|
|
||||||
FMT_MAYBE_UNUSED
|
|
||||||
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
|
|
||||||
return fmt::internal::Null<>();
|
|
||||||
}
|
|
||||||
FMT_MAYBE_UNUSED
|
|
||||||
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
|
|
||||||
return fmt::internal::Null<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
|
|
||||||
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
|
|
||||||
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
# define FMT_SNPRINTF snprintf
|
|
||||||
#else // _MSC_VER
|
|
||||||
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
# define FMT_SNPRINTF fmt_snprintf
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
|
||||||
# define FMT_SWPRINTF snwprintf
|
|
||||||
#else
|
|
||||||
# define FMT_SWPRINTF swprintf
|
|
||||||
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
|
||||||
|
|
||||||
const char RESET_COLOR[] = "\x1b[0m";
|
|
||||||
|
|
||||||
typedef void (*FormatFunc)(Writer &, int, StringRef);
|
|
||||||
|
|
||||||
// Portable thread-safe version of strerror.
|
|
||||||
// Sets buffer to point to a string describing the error code.
|
|
||||||
// This can be either a pointer to a string stored in buffer,
|
|
||||||
// or a pointer to some static immutable string.
|
|
||||||
// Returns one of the following values:
|
|
||||||
// 0 - success
|
|
||||||
// ERANGE - buffer is not large enough to store the error message
|
|
||||||
// other - failure
|
|
||||||
// Buffer should be at least of size 1.
|
|
||||||
int safe_strerror(
|
|
||||||
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
|
||||||
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
|
||||||
|
|
||||||
class StrError {
|
|
||||||
private:
|
|
||||||
int error_code_;
|
|
||||||
char *&buffer_;
|
|
||||||
std::size_t buffer_size_;
|
|
||||||
|
|
||||||
// A noop assignment operator to avoid bogus warnings.
|
|
||||||
void operator=(const StrError &) {}
|
|
||||||
|
|
||||||
// Handle the result of XSI-compliant version of strerror_r.
|
|
||||||
int handle(int result) {
|
|
||||||
// glibc versions before 2.13 return result in errno.
|
|
||||||
return result == -1 ? errno : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the result of GNU-specific version of strerror_r.
|
|
||||||
int handle(char *message) {
|
|
||||||
// If the buffer is full then the message is probably truncated.
|
|
||||||
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
|
||||||
return ERANGE;
|
|
||||||
buffer_ = message;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the case when strerror_r is not available.
|
|
||||||
int handle(internal::Null<>) {
|
|
||||||
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to strerror_s when strerror_r is not available.
|
|
||||||
int fallback(int result) {
|
|
||||||
// If the buffer is full then the message is probably truncated.
|
|
||||||
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
|
|
||||||
ERANGE : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __c2__
|
|
||||||
# pragma clang diagnostic push
|
|
||||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fallback to strerror if strerror_r and strerror_s are not available.
|
|
||||||
int fallback(internal::Null<>) {
|
|
||||||
errno = 0;
|
|
||||||
buffer_ = strerror(error_code_);
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __c2__
|
|
||||||
# pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
StrError(int err_code, char *&buf, std::size_t buf_size)
|
|
||||||
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
|
||||||
|
|
||||||
int run() {
|
|
||||||
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return StrError(error_code, buffer, buffer_size).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
void format_error_code(Writer &out, int error_code,
|
|
||||||
StringRef message) FMT_NOEXCEPT {
|
|
||||||
// Report error code making sure that the output fits into
|
|
||||||
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
|
|
||||||
// bad_alloc.
|
|
||||||
out.clear();
|
|
||||||
static const char SEP[] = ": ";
|
|
||||||
static const char ERROR_STR[] = "error ";
|
|
||||||
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
|
||||||
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
|
||||||
typedef internal::IntTraits<int>::MainType MainType;
|
|
||||||
MainType abs_value = static_cast<MainType>(error_code);
|
|
||||||
if (internal::is_negative(error_code)) {
|
|
||||||
abs_value = 0 - abs_value;
|
|
||||||
++error_code_size;
|
|
||||||
}
|
|
||||||
error_code_size += internal::count_digits(abs_value);
|
|
||||||
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
|
|
||||||
out << message << SEP;
|
|
||||||
out << ERROR_STR << error_code;
|
|
||||||
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void report_error(FormatFunc func, int error_code,
|
|
||||||
StringRef message) FMT_NOEXCEPT {
|
|
||||||
MemoryWriter full_message;
|
|
||||||
func(full_message, error_code, message);
|
|
||||||
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
|
||||||
// allocation.
|
|
||||||
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
|
||||||
std::fputc('\n', stderr);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
FMT_FUNC void SystemError::init(
|
|
||||||
int err_code, CStringRef format_str, ArgList args) {
|
|
||||||
error_code_ = err_code;
|
|
||||||
MemoryWriter w;
|
|
||||||
format_system_error(w, err_code, format(format_str, args));
|
|
||||||
std::runtime_error &base = *this;
|
|
||||||
base = std::runtime_error(w.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
int internal::CharTraits<char>::format_float(
|
|
||||||
char *buffer, std::size_t size, const char *format,
|
|
||||||
unsigned width, int precision, T value) {
|
|
||||||
if (width == 0) {
|
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SNPRINTF(buffer, size, format, value) :
|
|
||||||
FMT_SNPRINTF(buffer, size, format, precision, value);
|
|
||||||
}
|
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SNPRINTF(buffer, size, format, width, value) :
|
|
||||||
FMT_SNPRINTF(buffer, size, format, width, precision, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
int internal::CharTraits<wchar_t>::format_float(
|
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
|
||||||
unsigned width, int precision, T value) {
|
|
||||||
if (width == 0) {
|
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SWPRINTF(buffer, size, format, value) :
|
|
||||||
FMT_SWPRINTF(buffer, size, format, precision, value);
|
|
||||||
}
|
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SWPRINTF(buffer, size, format, width, value) :
|
|
||||||
FMT_SWPRINTF(buffer, size, format, width, precision, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const char internal::BasicData<T>::DIGITS[] =
|
|
||||||
"0001020304050607080910111213141516171819"
|
|
||||||
"2021222324252627282930313233343536373839"
|
|
||||||
"4041424344454647484950515253545556575859"
|
|
||||||
"6061626364656667686970717273747576777879"
|
|
||||||
"8081828384858687888990919293949596979899";
|
|
||||||
|
|
||||||
#define FMT_POWERS_OF_10(factor) \
|
|
||||||
factor * 10, \
|
|
||||||
factor * 100, \
|
|
||||||
factor * 1000, \
|
|
||||||
factor * 10000, \
|
|
||||||
factor * 100000, \
|
|
||||||
factor * 1000000, \
|
|
||||||
factor * 10000000, \
|
|
||||||
factor * 100000000, \
|
|
||||||
factor * 1000000000
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
|
|
||||||
0, FMT_POWERS_OF_10(1)
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
|
|
||||||
0,
|
|
||||||
FMT_POWERS_OF_10(1),
|
|
||||||
FMT_POWERS_OF_10(ULongLong(1000000000)),
|
|
||||||
// Multiply several constants instead of using a single long long constant
|
|
||||||
// to avoid warnings about C++98 not supporting long long.
|
|
||||||
ULongLong(1000000000) * ULongLong(1000000000) * 10
|
|
||||||
};
|
|
||||||
|
|
||||||
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
|
|
||||||
(void)type;
|
|
||||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
|
||||||
FMT_THROW(FormatError(
|
|
||||||
format("unknown format code '{}' for {}", code, type)));
|
|
||||||
}
|
|
||||||
FMT_THROW(FormatError(
|
|
||||||
format("unknown format code '\\x{:02x}' for {}",
|
|
||||||
static_cast<unsigned>(code), type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
|
||||||
|
|
||||||
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
|
|
||||||
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
|
||||||
if (s.size() > INT_MAX)
|
|
||||||
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
|
||||||
int s_size = static_cast<int>(s.size());
|
|
||||||
int length = MultiByteToWideChar(
|
|
||||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
|
||||||
if (length == 0)
|
|
||||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
|
||||||
buffer_.resize(length + 1);
|
|
||||||
length = MultiByteToWideChar(
|
|
||||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
|
||||||
if (length == 0)
|
|
||||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
|
||||||
buffer_[length] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
|
|
||||||
if (int error_code = convert(s)) {
|
|
||||||
FMT_THROW(WindowsError(error_code,
|
|
||||||
"cannot convert string from UTF-16 to UTF-8"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
|
|
||||||
if (s.size() > INT_MAX)
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
|
||||||
int s_size = static_cast<int>(s.size());
|
|
||||||
int length = WideCharToMultiByte(
|
|
||||||
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
|
||||||
if (length == 0)
|
|
||||||
return GetLastError();
|
|
||||||
buffer_.resize(length + 1);
|
|
||||||
length = WideCharToMultiByte(
|
|
||||||
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
|
||||||
if (length == 0)
|
|
||||||
return GetLastError();
|
|
||||||
buffer_[length] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void WindowsError::init(
|
|
||||||
int err_code, CStringRef format_str, ArgList args) {
|
|
||||||
error_code_ = err_code;
|
|
||||||
MemoryWriter w;
|
|
||||||
internal::format_windows_error(w, err_code, format(format_str, args));
|
|
||||||
std::runtime_error &base = *this;
|
|
||||||
base = std::runtime_error(w.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void internal::format_windows_error(
|
|
||||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
|
||||||
FMT_TRY {
|
|
||||||
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
|
|
||||||
buffer.resize(INLINE_BUFFER_SIZE);
|
|
||||||
for (;;) {
|
|
||||||
wchar_t *system_message = &buffer[0];
|
|
||||||
int result = FormatMessageW(
|
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
|
|
||||||
if (result != 0) {
|
|
||||||
UTF16ToUTF8 utf8_message;
|
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
|
||||||
out << message << ": " << utf8_message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
break; // Can't get error message, report error code instead.
|
|
||||||
buffer.resize(buffer.size() * 2);
|
|
||||||
}
|
|
||||||
} FMT_CATCH(...) {}
|
|
||||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FMT_USE_WINDOWS_H
|
|
||||||
|
|
||||||
FMT_FUNC void format_system_error(
|
|
||||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
|
||||||
FMT_TRY {
|
|
||||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
|
|
||||||
buffer.resize(internal::INLINE_BUFFER_SIZE);
|
|
||||||
for (;;) {
|
|
||||||
char *system_message = &buffer[0];
|
|
||||||
int result = safe_strerror(error_code, system_message, buffer.size());
|
|
||||||
if (result == 0) {
|
|
||||||
out << message << ": " << system_message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (result != ERANGE)
|
|
||||||
break; // Can't get error message, report error code instead.
|
|
||||||
buffer.resize(buffer.size() * 2);
|
|
||||||
}
|
|
||||||
} FMT_CATCH(...) {}
|
|
||||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void internal::FixedBuffer<Char>::grow(std::size_t) {
|
|
||||||
FMT_THROW(std::runtime_error("buffer overflow"));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
|
|
||||||
unsigned arg_index, const char *&error) {
|
|
||||||
internal::Arg arg = args_[arg_index];
|
|
||||||
switch (arg.type) {
|
|
||||||
case internal::Arg::NONE:
|
|
||||||
error = "argument index out of range";
|
|
||||||
break;
|
|
||||||
case internal::Arg::NAMED_ARG:
|
|
||||||
arg = *static_cast<const internal::Arg*>(arg.pointer);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/*nothing*/;
|
|
||||||
}
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void report_system_error(
|
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
|
|
||||||
// 'fmt::' is for bcc32.
|
|
||||||
report_error(format_system_error, error_code, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
|
||||||
FMT_FUNC void report_windows_error(
|
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
|
|
||||||
// 'fmt::' is for bcc32.
|
|
||||||
report_error(internal::format_windows_error, error_code, message);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
w.write(format_str, args);
|
|
||||||
std::fwrite(w.data(), 1, w.size(), f);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void print(CStringRef format_str, ArgList args) {
|
|
||||||
print(stdout, format_str, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
|
|
||||||
char escape[] = "\x1b[30m";
|
|
||||||
escape[3] = static_cast<char>('0' + c);
|
|
||||||
std::fputs(escape, stdout);
|
|
||||||
print(format, args);
|
|
||||||
std::fputs(RESET_COLOR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
|
||||||
|
|
||||||
template struct internal::BasicData<void>;
|
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
|
||||||
|
|
||||||
template void internal::FixedBuffer<char>::grow(std::size_t);
|
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<char>::format_float(
|
|
||||||
char *buffer, std::size_t size, const char *format,
|
|
||||||
unsigned width, int precision, double value);
|
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<char>::format_float(
|
|
||||||
char *buffer, std::size_t size, const char *format,
|
|
||||||
unsigned width, int precision, long double value);
|
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
|
||||||
|
|
||||||
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
|
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
|
||||||
unsigned width, int precision, double value);
|
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
|
||||||
unsigned width, int precision, long double value);
|
|
||||||
|
|
||||||
#endif // FMT_HEADER_ONLY
|
|
||||||
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(pop)
|
|
||||||
#endif
|
|
4173
externals/fmt/fmt/format.h
vendored
4173
externals/fmt/fmt/format.h
vendored
File diff suppressed because it is too large
Load diff
35
externals/fmt/fmt/ostream.cc
vendored
35
externals/fmt/fmt/ostream.cc
vendored
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++ - std::ostream support
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ostream.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
FMT_FUNC void write(std::ostream &os, Writer &w) {
|
|
||||||
const char *data = w.data();
|
|
||||||
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
|
|
||||||
UnsignedStreamSize size = w.size();
|
|
||||||
UnsignedStreamSize max_size =
|
|
||||||
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
|
||||||
do {
|
|
||||||
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
|
||||||
os.write(data, static_cast<std::streamsize>(n));
|
|
||||||
data += n;
|
|
||||||
size -= n;
|
|
||||||
} while (size != 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
w.write(format_str, args);
|
|
||||||
internal::write(os, w);
|
|
||||||
}
|
|
||||||
} // namespace fmt
|
|
108
externals/fmt/fmt/ostream.h
vendored
108
externals/fmt/fmt/ostream.h
vendored
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++ - std::ostream support
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_OSTREAM_H_
|
|
||||||
#define FMT_OSTREAM_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
template <class Char>
|
|
||||||
class FormatBuf : public std::basic_streambuf<Char> {
|
|
||||||
private:
|
|
||||||
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
|
||||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
|
||||||
|
|
||||||
Buffer<Char> &buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The put-area is actually always empty. This makes the implementation
|
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
|
||||||
// results in a call to xsputn.
|
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
|
||||||
buffer_.append(s, s + count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Yes &convert(std::ostream &);
|
|
||||||
|
|
||||||
struct DummyStream : std::ostream {
|
|
||||||
DummyStream(); // Suppress a bogus warning in MSVC.
|
|
||||||
|
|
||||||
// Hide all operator<< overloads from std::ostream.
|
|
||||||
template <typename T>
|
|
||||||
typename EnableIf<sizeof(T) == 0>::type operator<<(const T &);
|
|
||||||
};
|
|
||||||
|
|
||||||
No &operator<<(std::ostream &, int);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct ConvertToIntImpl<T, true> {
|
|
||||||
// Convert to int only if T doesn't have an overloaded operator<<.
|
|
||||||
enum {
|
|
||||||
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write the content of w to os.
|
|
||||||
FMT_API void write(std::ostream &os, Writer &w);
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
// Formats a value.
|
|
||||||
template <typename Char, typename ArgFormatter_, typename T>
|
|
||||||
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
|
|
||||||
const Char *&format_str, const T &value) {
|
|
||||||
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
|
|
||||||
|
|
||||||
internal::FormatBuf<Char> format_buf(buffer);
|
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
|
||||||
output << value;
|
|
||||||
|
|
||||||
BasicStringRef<Char> str(&buffer[0], buffer.size());
|
|
||||||
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
|
|
||||||
format_str = f.format(format_str, MakeArg(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
print(cerr, "Don't {}!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
|
|
||||||
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#ifdef FMT_HEADER_ONLY
|
|
||||||
# include "ostream.cc"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_OSTREAM_H_
|
|
32
externals/fmt/fmt/printf.cc
vendored
32
externals/fmt/fmt/printf.cc
vendored
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
|
|
||||||
|
|
||||||
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
printf(w, format, args);
|
|
||||||
std::size_t size = w.size();
|
|
||||||
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
|
||||||
|
|
||||||
template void PrintfFormatter<char>::format(CStringRef format);
|
|
||||||
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
|
|
||||||
|
|
||||||
#endif // FMT_HEADER_ONLY
|
|
||||||
|
|
||||||
} // namespace fmt
|
|
603
externals/fmt/fmt/printf.h
vendored
603
externals/fmt/fmt/printf.h
vendored
|
@ -1,603 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_PRINTF_H_
|
|
||||||
#define FMT_PRINTF_H_
|
|
||||||
|
|
||||||
#include <algorithm> // std::fill_n
|
|
||||||
#include <limits> // std::numeric_limits
|
|
||||||
|
|
||||||
#include "ostream.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
|
||||||
// signed and unsigned integers.
|
|
||||||
template <bool IsSigned>
|
|
||||||
struct IntChecker {
|
|
||||||
template <typename T>
|
|
||||||
static bool fits_in_int(T value) {
|
|
||||||
unsigned max = std::numeric_limits<int>::max();
|
|
||||||
return value <= max;
|
|
||||||
}
|
|
||||||
static bool fits_in_int(bool) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct IntChecker<true> {
|
|
||||||
template <typename T>
|
|
||||||
static bool fits_in_int(T value) {
|
|
||||||
return value >= std::numeric_limits<int>::min() &&
|
|
||||||
value <= std::numeric_limits<int>::max();
|
|
||||||
}
|
|
||||||
static bool fits_in_int(int) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
|
|
||||||
public:
|
|
||||||
void report_unhandled_arg() {
|
|
||||||
FMT_THROW(FormatError("precision is not integer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
int visit_any_int(T value) {
|
|
||||||
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
|
||||||
FMT_THROW(FormatError("number is too big"));
|
|
||||||
return static_cast<int>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
|
|
||||||
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
bool visit_any_int(T value) { return value == 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns the default type for format specific "%s"
|
|
||||||
class DefaultType : public ArgVisitor<DefaultType, char> {
|
|
||||||
public:
|
|
||||||
char visit_char(int) { return 'c'; }
|
|
||||||
|
|
||||||
char visit_bool(bool) { return 's'; }
|
|
||||||
|
|
||||||
char visit_pointer(const void *) { return 'p'; }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
char visit_any_int(T) { return 'd'; }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
char visit_any_double(T) { return 'g'; }
|
|
||||||
|
|
||||||
char visit_unhandled_arg() { return 's'; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
struct is_same {
|
|
||||||
enum { value = 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_same<T, T> {
|
|
||||||
enum { value = 1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// An argument visitor that converts an integer argument to T for printf,
|
|
||||||
// if T is an integral type. If T is void, the argument is converted to
|
|
||||||
// corresponding signed or unsigned type depending on the type specifier:
|
|
||||||
// 'd' and 'i' - signed, other - unsigned)
|
|
||||||
template <typename T = void>
|
|
||||||
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
|
|
||||||
private:
|
|
||||||
internal::Arg &arg_;
|
|
||||||
wchar_t type_;
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
|
|
||||||
|
|
||||||
public:
|
|
||||||
ArgConverter(internal::Arg &arg, wchar_t type)
|
|
||||||
: arg_(arg), type_(type) {}
|
|
||||||
|
|
||||||
void visit_bool(bool value) {
|
|
||||||
if (type_ != 's')
|
|
||||||
visit_any_int(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit_char(int value) {
|
|
||||||
if (type_ != 's')
|
|
||||||
visit_any_int(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
void visit_any_int(U value) {
|
|
||||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
|
||||||
if (type_ == 's') {
|
|
||||||
is_signed = std::numeric_limits<U>::is_signed;
|
|
||||||
}
|
|
||||||
|
|
||||||
using internal::Arg;
|
|
||||||
typedef typename internal::Conditional<
|
|
||||||
is_same<T, void>::value, U, T>::type TargetType;
|
|
||||||
if (const_check(sizeof(TargetType) <= sizeof(int))) {
|
|
||||||
// Extra casts are used to silence warnings.
|
|
||||||
if (is_signed) {
|
|
||||||
arg_.type = Arg::INT;
|
|
||||||
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
|
|
||||||
} else {
|
|
||||||
arg_.type = Arg::UINT;
|
|
||||||
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
|
|
||||||
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (is_signed) {
|
|
||||||
arg_.type = Arg::LONG_LONG;
|
|
||||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
|
||||||
// std::printf("%lld", -42); // prints "4294967254"
|
|
||||||
// but we don't have to do the same because it's a UB.
|
|
||||||
arg_.long_long_value = static_cast<LongLong>(value);
|
|
||||||
} else {
|
|
||||||
arg_.type = Arg::ULONG_LONG;
|
|
||||||
arg_.ulong_long_value =
|
|
||||||
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Converts an integer argument to char for printf.
|
|
||||||
class CharConverter : public ArgVisitor<CharConverter, void> {
|
|
||||||
private:
|
|
||||||
internal::Arg &arg_;
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void visit_any_int(T value) {
|
|
||||||
arg_.type = internal::Arg::CHAR;
|
|
||||||
arg_.int_value = static_cast<char>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Checks if an argument is a valid printf width specifier and sets
|
|
||||||
// left alignment if it is negative.
|
|
||||||
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
|
|
||||||
private:
|
|
||||||
FormatSpec &spec_;
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
|
|
||||||
|
|
||||||
void report_unhandled_arg() {
|
|
||||||
FMT_THROW(FormatError("width is not integer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
unsigned visit_any_int(T value) {
|
|
||||||
typedef typename internal::IntTraits<T>::MainType UnsignedType;
|
|
||||||
UnsignedType width = static_cast<UnsignedType>(value);
|
|
||||||
if (internal::is_negative(value)) {
|
|
||||||
spec_.align_ = ALIGN_LEFT;
|
|
||||||
width = 0 - width;
|
|
||||||
}
|
|
||||||
unsigned int_max = std::numeric_limits<int>::max();
|
|
||||||
if (width > int_max)
|
|
||||||
FMT_THROW(FormatError("number is too big"));
|
|
||||||
return static_cast<unsigned>(width);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
A ``printf`` argument formatter based on the `curiously recurring template
|
|
||||||
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
|
|
||||||
|
|
||||||
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
|
|
||||||
or all of the visit methods with the same signatures as the methods in
|
|
||||||
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
|
|
||||||
Pass the subclass as the *Impl* template parameter. When a formatting
|
|
||||||
function processes an argument, it will dispatch to a visit method
|
|
||||||
specific to the argument type. For example, if the argument type is
|
|
||||||
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
|
|
||||||
will be called. If the subclass doesn't contain a method with this signature,
|
|
||||||
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
|
|
||||||
superclass will be called.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Impl, typename Char, typename Spec>
|
|
||||||
class BasicPrintfArgFormatter :
|
|
||||||
public internal::ArgFormatterBase<Impl, Char, Spec> {
|
|
||||||
private:
|
|
||||||
void write_null_pointer() {
|
|
||||||
this->spec().type_ = 0;
|
|
||||||
this->write("(nil)");
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs an argument formatter object.
|
|
||||||
*writer* is a reference to the output writer and *spec* contains format
|
|
||||||
specifier information for standard argument types.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
|
|
||||||
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
|
|
||||||
|
|
||||||
/** Formats an argument of type ``bool``. */
|
|
||||||
void visit_bool(bool value) {
|
|
||||||
Spec &fmt_spec = this->spec();
|
|
||||||
if (fmt_spec.type_ != 's')
|
|
||||||
return this->visit_any_int(value);
|
|
||||||
fmt_spec.type_ = 0;
|
|
||||||
this->write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a character. */
|
|
||||||
void visit_char(int value) {
|
|
||||||
const Spec &fmt_spec = this->spec();
|
|
||||||
BasicWriter<Char> &w = this->writer();
|
|
||||||
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
|
|
||||||
w.write_int(value, fmt_spec);
|
|
||||||
typedef typename BasicWriter<Char>::CharPtr CharPtr;
|
|
||||||
CharPtr out = CharPtr();
|
|
||||||
if (fmt_spec.width_ > 1) {
|
|
||||||
Char fill = ' ';
|
|
||||||
out = w.grow_buffer(fmt_spec.width_);
|
|
||||||
if (fmt_spec.align_ != ALIGN_LEFT) {
|
|
||||||
std::fill_n(out, fmt_spec.width_ - 1, fill);
|
|
||||||
out += fmt_spec.width_ - 1;
|
|
||||||
} else {
|
|
||||||
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out = w.grow_buffer(1);
|
|
||||||
}
|
|
||||||
*out = static_cast<Char>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a null-terminated C string. */
|
|
||||||
void visit_cstring(const char *value) {
|
|
||||||
if (value)
|
|
||||||
Base::visit_cstring(value);
|
|
||||||
else if (this->spec().type_ == 'p')
|
|
||||||
write_null_pointer();
|
|
||||||
else
|
|
||||||
this->write("(null)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a pointer. */
|
|
||||||
void visit_pointer(const void *value) {
|
|
||||||
if (value)
|
|
||||||
return Base::visit_pointer(value);
|
|
||||||
this->spec().type_ = 0;
|
|
||||||
write_null_pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
|
||||||
void visit_custom(internal::Arg::CustomValue c) {
|
|
||||||
BasicFormatter<Char> formatter(ArgList(), this->writer());
|
|
||||||
const Char format_str[] = {'}', 0};
|
|
||||||
const Char *format = format_str;
|
|
||||||
c.format(&formatter, c.value, &format);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The default printf argument formatter. */
|
|
||||||
template <typename Char>
|
|
||||||
class PrintfArgFormatter :
|
|
||||||
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> {
|
|
||||||
public:
|
|
||||||
/** Constructs an argument formatter object. */
|
|
||||||
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
|
|
||||||
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** This template formats data and writes the output to a writer. */
|
|
||||||
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
|
|
||||||
class PrintfFormatter : private internal::FormatterBase {
|
|
||||||
private:
|
|
||||||
BasicWriter<Char> &writer_;
|
|
||||||
|
|
||||||
void parse_flags(FormatSpec &spec, const Char *&s);
|
|
||||||
|
|
||||||
// Returns the argument with specified index or, if arg_index is equal
|
|
||||||
// to the maximum unsigned value, the next argument.
|
|
||||||
internal::Arg get_arg(
|
|
||||||
const Char *s,
|
|
||||||
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
|
|
||||||
|
|
||||||
// Parses argument index, flags and width and returns the argument index.
|
|
||||||
unsigned parse_header(const Char *&s, FormatSpec &spec);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a ``PrintfFormatter`` object. References to the arguments and
|
|
||||||
the writer are stored in the formatter object so make sure they have
|
|
||||||
appropriate lifetimes.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
|
|
||||||
: FormatterBase(al), writer_(w) {}
|
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the writer. */
|
|
||||||
void format(BasicCStringRef<Char> format_str);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
|
|
||||||
for (;;) {
|
|
||||||
switch (*s++) {
|
|
||||||
case '-':
|
|
||||||
spec.align_ = ALIGN_LEFT;
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
spec.fill_ = '0';
|
|
||||||
break;
|
|
||||||
case ' ':
|
|
||||||
spec.flags_ |= SIGN_FLAG;
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
spec.flags_ |= HASH_FLAG;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
--s;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
|
|
||||||
unsigned arg_index) {
|
|
||||||
(void)s;
|
|
||||||
const char *error = FMT_NULL;
|
|
||||||
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
|
|
||||||
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
|
|
||||||
if (error)
|
|
||||||
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
unsigned PrintfFormatter<Char, AF>::parse_header(
|
|
||||||
const Char *&s, FormatSpec &spec) {
|
|
||||||
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
|
||||||
Char c = *s;
|
|
||||||
if (c >= '0' && c <= '9') {
|
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
|
||||||
// preceded with '0' flag(s).
|
|
||||||
unsigned value = internal::parse_nonnegative_int(s);
|
|
||||||
if (*s == '$') { // value is an argument index
|
|
||||||
++s;
|
|
||||||
arg_index = value;
|
|
||||||
} else {
|
|
||||||
if (c == '0')
|
|
||||||
spec.fill_ = '0';
|
|
||||||
if (value != 0) {
|
|
||||||
// Nonzero value means that we parsed width and don't need to
|
|
||||||
// parse it or flags again, so return now.
|
|
||||||
spec.width_ = value;
|
|
||||||
return arg_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parse_flags(spec, s);
|
|
||||||
// Parse width.
|
|
||||||
if (*s >= '0' && *s <= '9') {
|
|
||||||
spec.width_ = internal::parse_nonnegative_int(s);
|
|
||||||
} else if (*s == '*') {
|
|
||||||
++s;
|
|
||||||
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
|
|
||||||
}
|
|
||||||
return arg_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
|
|
||||||
const Char *start = format_str.c_str();
|
|
||||||
const Char *s = start;
|
|
||||||
while (*s) {
|
|
||||||
Char c = *s++;
|
|
||||||
if (c != '%') continue;
|
|
||||||
if (*s == c) {
|
|
||||||
write(writer_, start, s);
|
|
||||||
start = ++s;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
write(writer_, start, s - 1);
|
|
||||||
|
|
||||||
FormatSpec spec;
|
|
||||||
spec.align_ = ALIGN_RIGHT;
|
|
||||||
|
|
||||||
// Parse argument index, flags and width.
|
|
||||||
unsigned arg_index = parse_header(s, spec);
|
|
||||||
|
|
||||||
// Parse precision.
|
|
||||||
if (*s == '.') {
|
|
||||||
++s;
|
|
||||||
if ('0' <= *s && *s <= '9') {
|
|
||||||
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
|
|
||||||
} else if (*s == '*') {
|
|
||||||
++s;
|
|
||||||
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
|
|
||||||
} else {
|
|
||||||
spec.precision_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using internal::Arg;
|
|
||||||
Arg arg = get_arg(s, arg_index);
|
|
||||||
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
|
|
||||||
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
|
|
||||||
if (spec.fill_ == '0') {
|
|
||||||
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
|
|
||||||
spec.align_ = ALIGN_NUMERIC;
|
|
||||||
else
|
|
||||||
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
|
||||||
using internal::ArgConverter;
|
|
||||||
switch (*s++) {
|
|
||||||
case 'h':
|
|
||||||
if (*s == 'h')
|
|
||||||
ArgConverter<signed char>(arg, *++s).visit(arg);
|
|
||||||
else
|
|
||||||
ArgConverter<short>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
if (*s == 'l')
|
|
||||||
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
|
|
||||||
else
|
|
||||||
ArgConverter<long>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
ArgConverter<intmax_t>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
ArgConverter<std::size_t>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
// printf produces garbage when 'L' is omitted for long double, no
|
|
||||||
// need to do the same.
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
--s;
|
|
||||||
ArgConverter<void>(arg, *s).visit(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse type.
|
|
||||||
if (!*s)
|
|
||||||
FMT_THROW(FormatError("invalid format string"));
|
|
||||||
spec.type_ = static_cast<char>(*s++);
|
|
||||||
|
|
||||||
if (spec.type_ == 's') {
|
|
||||||
// set the format type to the default if 's' is specified
|
|
||||||
spec.type_ = internal::DefaultType().visit(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
|
|
||||||
// Normalize type.
|
|
||||||
switch (spec.type_) {
|
|
||||||
case 'i': case 'u':
|
|
||||||
spec.type_ = 'd';
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
// TODO: handle wchar_t
|
|
||||||
internal::CharConverter(arg).visit(arg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start = s;
|
|
||||||
|
|
||||||
// Format argument.
|
|
||||||
AF(writer_, spec).visit(arg);
|
|
||||||
}
|
|
||||||
write(writer_, start, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void printf(Writer &w, CStringRef format, ArgList args) {
|
|
||||||
PrintfFormatter<char>(args, w).format(format);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(void, printf, Writer &, CStringRef)
|
|
||||||
|
|
||||||
inline void printf(WWriter &w, WCStringRef format, ArgList args) {
|
|
||||||
PrintfFormatter<wchar_t>(args, w).format(format);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Formats arguments and returns the result as a string.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
inline std::string sprintf(CStringRef format, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
printf(w, format, args);
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(std::string, sprintf, CStringRef)
|
|
||||||
|
|
||||||
inline std::wstring sprintf(WCStringRef format, ArgList args) {
|
|
||||||
WMemoryWriter w;
|
|
||||||
printf(w, format, args);
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the file *f*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
|
|
||||||
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to ``stdout``.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
inline int printf(CStringRef format, ArgList args) {
|
|
||||||
return fprintf(stdout, format, args);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(int, printf, CStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fprintf(cerr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
printf(w, format_str, args);
|
|
||||||
internal::write(os, w);
|
|
||||||
return static_cast<int>(w.size());
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#ifdef FMT_HEADER_ONLY
|
|
||||||
# include "printf.cc"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_PRINTF_H_
|
|
148
externals/fmt/fmt/string.h
vendored
148
externals/fmt/fmt/string.h
vendored
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++ - string utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef FMT_INCLUDE
|
|
||||||
# error "Add the fmt's parent directory and not fmt itself to includes."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FMT_STRING_H_
|
|
||||||
#define FMT_STRING_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// A buffer that stores data in ``std::basic_string``.
|
|
||||||
template <typename Char, typename Allocator = std::allocator<Char> >
|
|
||||||
class StringBuffer : public Buffer<Char> {
|
|
||||||
public:
|
|
||||||
typedef std::basic_string<Char, std::char_traits<Char>, Allocator> StringType;
|
|
||||||
|
|
||||||
private:
|
|
||||||
StringType data_;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void grow(std::size_t size) FMT_OVERRIDE {
|
|
||||||
data_.resize(size);
|
|
||||||
this->ptr_ = &data_[0];
|
|
||||||
this->capacity_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit StringBuffer(const Allocator &allocator = Allocator())
|
|
||||||
: data_(allocator) {}
|
|
||||||
|
|
||||||
// Moves the data to ``str`` clearing the buffer.
|
|
||||||
void move_to(StringType &str) {
|
|
||||||
data_.resize(this->size_);
|
|
||||||
str.swap(data_);
|
|
||||||
this->capacity_ = this->size_ = 0;
|
|
||||||
this->ptr_ = FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
This class template provides operations for formatting and writing data
|
|
||||||
into a character stream. The output is stored in a ``std::basic_string``
|
|
||||||
that grows dynamically.
|
|
||||||
|
|
||||||
You can use one of the following typedefs for common character types
|
|
||||||
and the standard allocator:
|
|
||||||
|
|
||||||
+---------------+----------------------------+
|
|
||||||
| Type | Definition |
|
|
||||||
+===============+============================+
|
|
||||||
| StringWriter | BasicStringWriter<char> |
|
|
||||||
+---------------+----------------------------+
|
|
||||||
| WStringWriter | BasicStringWriter<wchar_t> |
|
|
||||||
+---------------+----------------------------+
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
StringWriter out;
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
|
|
||||||
This will write the following output to the ``out`` object:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
The answer is 42
|
|
||||||
|
|
||||||
The output can be moved to a ``std::basic_string`` with ``out.move_to()``.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Char, typename Allocator = std::allocator<Char> >
|
|
||||||
class BasicStringWriter : public BasicWriter<Char> {
|
|
||||||
private:
|
|
||||||
internal::StringBuffer<Char, Allocator> buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a :class:`fmt::BasicStringWriter` object.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
explicit BasicStringWriter(const Allocator &allocator = Allocator())
|
|
||||||
: BasicWriter<Char>(buffer_), buffer_(allocator) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Moves the buffer content to *str* clearing the buffer.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
void move_to(std::basic_string<Char, std::char_traits<Char>, Allocator> &str) {
|
|
||||||
buffer_.move_to(str);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef BasicStringWriter<char> StringWriter;
|
|
||||||
typedef BasicStringWriter<wchar_t> WStringWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Converts *value* to ``std::string`` using the default format for type *T*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
#include "fmt/string.h"
|
|
||||||
|
|
||||||
std::string answer = fmt::to_string(42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
std::string to_string(const T &value) {
|
|
||||||
fmt::MemoryWriter w;
|
|
||||||
w << value;
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
#include "fmt/string.h"
|
|
||||||
|
|
||||||
std::wstring answer = fmt::to_wstring(42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
std::wstring to_wstring(const T &value) {
|
|
||||||
fmt::WMemoryWriter w;
|
|
||||||
w << value;
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FMT_STRING_H_
|
|
143
externals/fmt/fmt/time.h
vendored
143
externals/fmt/fmt/time.h
vendored
|
@ -1,143 +0,0 @@
|
||||||
/*
|
|
||||||
Formatting library for C++ - time formatting
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_TIME_H_
|
|
||||||
#define FMT_TIME_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(push)
|
|
||||||
# pragma warning(disable: 4702) // unreachable code
|
|
||||||
# pragma warning(disable: 4996) // "deprecated" functions
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
template <typename ArgFormatter>
|
|
||||||
void format_arg(BasicFormatter<char, ArgFormatter> &f,
|
|
||||||
const char *&format_str, const std::tm &tm) {
|
|
||||||
if (*format_str == ':')
|
|
||||||
++format_str;
|
|
||||||
const char *end = format_str;
|
|
||||||
while (*end && *end != '}')
|
|
||||||
++end;
|
|
||||||
if (*end != '}')
|
|
||||||
FMT_THROW(FormatError("missing '}' in format string"));
|
|
||||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
|
|
||||||
format.append(format_str, end + 1);
|
|
||||||
format[format.size() - 1] = '\0';
|
|
||||||
Buffer<char> &buffer = f.writer().buffer();
|
|
||||||
std::size_t start = buffer.size();
|
|
||||||
for (;;) {
|
|
||||||
std::size_t size = buffer.capacity() - start;
|
|
||||||
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
|
|
||||||
if (count != 0) {
|
|
||||||
buffer.resize(start + count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (size >= format.size() * 256) {
|
|
||||||
// If the buffer is 256 times larger than the format string, assume
|
|
||||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
|
||||||
// better way to distinguish the two cases:
|
|
||||||
// https://github.com/fmtlib/fmt/issues/367
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::size_t MIN_GROWTH = 10;
|
|
||||||
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
|
||||||
}
|
|
||||||
format_str = end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace internal{
|
|
||||||
inline Null<> localtime_r(...) { return Null<>(); }
|
|
||||||
inline Null<> localtime_s(...) { return Null<>(); }
|
|
||||||
inline Null<> gmtime_r(...) { return Null<>(); }
|
|
||||||
inline Null<> gmtime_s(...) { return Null<>(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-safe replacement for std::localtime
|
|
||||||
inline std::tm localtime(std::time_t time) {
|
|
||||||
struct LocalTime {
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
LocalTime(std::time_t t): time_(t) {}
|
|
||||||
|
|
||||||
bool run() {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return handle(localtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
|
||||||
|
|
||||||
bool handle(internal::Null<>) {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return fallback(localtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(int res) { return res == 0; }
|
|
||||||
|
|
||||||
bool fallback(internal::Null<>) {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
std::tm *tm = std::localtime(&time_);
|
|
||||||
if (tm) tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
LocalTime lt(time);
|
|
||||||
if (lt.run())
|
|
||||||
return lt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
FMT_THROW(fmt::FormatError("time_t value out of range"));
|
|
||||||
return std::tm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
|
||||||
inline std::tm gmtime(std::time_t time) {
|
|
||||||
struct GMTime {
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
GMTime(std::time_t t): time_(t) {}
|
|
||||||
|
|
||||||
bool run() {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return handle(gmtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
|
||||||
|
|
||||||
bool handle(internal::Null<>) {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return fallback(gmtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(int res) { return res == 0; }
|
|
||||||
|
|
||||||
bool fallback(internal::Null<>) {
|
|
||||||
std::tm *tm = std::gmtime(&time_);
|
|
||||||
if (tm != FMT_NULL) tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
GMTime gt(time);
|
|
||||||
if (gt.run())
|
|
||||||
return gt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
FMT_THROW(fmt::FormatError("time_t value out of range"));
|
|
||||||
return std::tm();
|
|
||||||
}
|
|
||||||
} //namespace fmt
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_TIME_H_
|
|
278
externals/fmt/include/fmt/color.h
vendored
Normal file
278
externals/fmt/include/fmt/color.h
vendored
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
// Experimental color support.
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32, // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// We use rgb as name because some editors will show it as color direct in the
|
||||||
|
// editor.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
||||||
|
: r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
void vprint_rgb(rgb fd, string_view format, format_args args);
|
||||||
|
void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify foreground color 'fd'.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(rgb fd, string_view format_str, const Args & ... args) {
|
||||||
|
vprint_rgb(fd, format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify foreground color 'fd' and background color 'bg'.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::color::red, fmt::color::black,
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(rgb fd, rgb bg, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_rgb(fd, bg, format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
namespace internal {
|
||||||
|
FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset) {
|
||||||
|
out[offset + 0] = static_cast<char>('0' + c / 100);
|
||||||
|
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
|
||||||
|
out[offset + 2] = static_cast<char>('0' + c % 10);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
inline void vprint_rgb(rgb fd, string_view format, format_args args) {
|
||||||
|
char escape_fd[] = "\x1b[38;2;000;000;000m";
|
||||||
|
internal::to_esc(fd.r, escape_fd, 7);
|
||||||
|
internal::to_esc(fd.g, escape_fd, 11);
|
||||||
|
internal::to_esc(fd.b, escape_fd, 15);
|
||||||
|
|
||||||
|
std::fputs(escape_fd, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) {
|
||||||
|
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
|
||||||
|
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
|
||||||
|
internal::to_esc(fd.r, escape_fd, 7);
|
||||||
|
internal::to_esc(fd.g, escape_fd, 11);
|
||||||
|
internal::to_esc(fd.b, escape_fd, 15);
|
||||||
|
|
||||||
|
internal::to_esc(bg.r, escape_bg, 7);
|
||||||
|
internal::to_esc(bg.g, escape_bg, 11);
|
||||||
|
internal::to_esc(bg.b, escape_bg, 15);
|
||||||
|
|
||||||
|
std::fputs(escape_fd, stdout);
|
||||||
|
std::fputs(escape_bg, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
1502
externals/fmt/include/fmt/core.h
vendored
Normal file
1502
externals/fmt/include/fmt/core.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
866
externals/fmt/include/fmt/format-inl.h
vendored
Normal file
866
externals/fmt/include/fmt/format-inl.h
vendored
Normal file
|
@ -0,0 +1,866 @@
|
||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_FORMAT_INL_H_
|
||||||
|
#define FMT_FORMAT_INL_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstddef> // for std::ptrdiff_t
|
||||||
|
#include <cstring> // for std::memmove
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
# include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
||||||
|
# include <windows.h>
|
||||||
|
# else
|
||||||
|
# define NOMINMAX
|
||||||
|
# include <windows.h>
|
||||||
|
# undef NOMINMAX
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_EXCEPTIONS
|
||||||
|
# define FMT_TRY try
|
||||||
|
# define FMT_CATCH(x) catch (x)
|
||||||
|
#else
|
||||||
|
# define FMT_TRY if (true)
|
||||||
|
# define FMT_CATCH(x) if (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable: 4127) // conditional expression is constant
|
||||||
|
# pragma warning(disable: 4702) // unreachable code
|
||||||
|
// Disable deprecation warning for strerror. The latter is not called but
|
||||||
|
// MSVC fails to detect it.
|
||||||
|
# pragma warning(disable: 4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||||
|
// system functions are not available.
|
||||||
|
inline fmt::internal::null<> strerror_r(int, char *, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# define FMT_SNPRINTF snprintf
|
||||||
|
#else // _MSC_VER
|
||||||
|
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
# define FMT_SNPRINTF fmt_snprintf
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
# define FMT_SWPRINTF snwprintf
|
||||||
|
#else
|
||||||
|
# define FMT_SWPRINTF swprintf
|
||||||
|
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
|
||||||
|
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
|
||||||
|
|
||||||
|
// Portable thread-safe version of strerror.
|
||||||
|
// Sets buffer to point to a string describing the error code.
|
||||||
|
// This can be either a pointer to a string stored in buffer,
|
||||||
|
// or a pointer to some static immutable string.
|
||||||
|
// Returns one of the following values:
|
||||||
|
// 0 - success
|
||||||
|
// ERANGE - buffer is not large enough to store the error message
|
||||||
|
// other - failure
|
||||||
|
// Buffer should be at least of size 1.
|
||||||
|
int safe_strerror(
|
||||||
|
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
||||||
|
|
||||||
|
class dispatcher {
|
||||||
|
private:
|
||||||
|
int error_code_;
|
||||||
|
char *&buffer_;
|
||||||
|
std::size_t buffer_size_;
|
||||||
|
|
||||||
|
// A noop assignment operator to avoid bogus warnings.
|
||||||
|
void operator=(const dispatcher &) {}
|
||||||
|
|
||||||
|
// Handle the result of XSI-compliant version of strerror_r.
|
||||||
|
int handle(int result) {
|
||||||
|
// glibc versions before 2.13 return result in errno.
|
||||||
|
return result == -1 ? errno : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the result of GNU-specific version of strerror_r.
|
||||||
|
int handle(char *message) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
||||||
|
return ERANGE;
|
||||||
|
buffer_ = message;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the case when strerror_r is not available.
|
||||||
|
int handle(internal::null<>) {
|
||||||
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to strerror_s when strerror_r is not available.
|
||||||
|
int fallback(int result) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
|
||||||
|
ERANGE : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||||
|
int fallback(internal::null<>) {
|
||||||
|
errno = 0;
|
||||||
|
buffer_ = strerror(error_code_);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
dispatcher(int err_code, char *&buf, std::size_t buf_size)
|
||||||
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return dispatcher(error_code, buffer, buffer_size).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_error_code(internal::buffer &out, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
// Report error code making sure that the output fits into
|
||||||
|
// inline_buffer_size to avoid dynamic memory allocation and potential
|
||||||
|
// bad_alloc.
|
||||||
|
out.resize(0);
|
||||||
|
static const char SEP[] = ": ";
|
||||||
|
static const char ERROR_STR[] = "error ";
|
||||||
|
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
||||||
|
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
||||||
|
typedef internal::int_traits<int>::main_type main_type;
|
||||||
|
main_type abs_value = static_cast<main_type>(error_code);
|
||||||
|
if (internal::is_negative(error_code)) {
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
++error_code_size;
|
||||||
|
}
|
||||||
|
error_code_size += internal::count_digits(abs_value);
|
||||||
|
writer w(out);
|
||||||
|
if (message.size() <= inline_buffer_size - error_code_size) {
|
||||||
|
w.write(message);
|
||||||
|
w.write(SEP);
|
||||||
|
}
|
||||||
|
w.write(ERROR_STR);
|
||||||
|
w.write(error_code);
|
||||||
|
assert(out.size() <= inline_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_error(FormatFunc func, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
memory_buffer full_message;
|
||||||
|
func(full_message, error_code, message);
|
||||||
|
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||||
|
// allocation.
|
||||||
|
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
||||||
|
std::fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
class locale {
|
||||||
|
private:
|
||||||
|
std::locale locale_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit locale(std::locale loc = std::locale()) : locale_(loc) {}
|
||||||
|
std::locale get() { return locale_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_FUNC size_t internal::count_code_points(u8string_view s) {
|
||||||
|
const char8_t *data = s.data();
|
||||||
|
int num_code_points = 0;
|
||||||
|
for (size_t i = 0, size = s.size(); i != size; ++i) {
|
||||||
|
if ((data[i].value & 0xc0) != 0x80)
|
||||||
|
++num_code_points;
|
||||||
|
}
|
||||||
|
return num_code_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
|
||||||
|
std::locale loc = lp ? lp->locale().get() : std::locale();
|
||||||
|
return std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
|
||||||
|
return FMT_STATIC_THOUSANDS_SEPARATOR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void system_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
format_system_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<char>::format_float(
|
||||||
|
char *buffer, std::size_t size, const char *format, int precision, T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SNPRINTF(buffer, size, format, value) :
|
||||||
|
FMT_SNPRINTF(buffer, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<wchar_t>::format_float(
|
||||||
|
wchar_t *buffer, std::size_t size, const wchar_t *format, int precision,
|
||||||
|
T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SWPRINTF(buffer, size, format, value) :
|
||||||
|
FMT_SWPRINTF(buffer, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::DIGITS[] =
|
||||||
|
"0001020304050607080910111213141516171819"
|
||||||
|
"2021222324252627282930313233343536373839"
|
||||||
|
"4041424344454647484950515253545556575859"
|
||||||
|
"6061626364656667686970717273747576777879"
|
||||||
|
"8081828384858687888990919293949596979899";
|
||||||
|
|
||||||
|
#define FMT_POWERS_OF_10(factor) \
|
||||||
|
factor * 10, \
|
||||||
|
factor * 100, \
|
||||||
|
factor * 1000, \
|
||||||
|
factor * 10000, \
|
||||||
|
factor * 100000, \
|
||||||
|
factor * 1000000, \
|
||||||
|
factor * 10000000, \
|
||||||
|
factor * 100000000, \
|
||||||
|
factor * 1000000000
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::POWERS_OF_10_32[] = {
|
||||||
|
1, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {
|
||||||
|
0, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
|
||||||
|
0,
|
||||||
|
FMT_POWERS_OF_10(1),
|
||||||
|
FMT_POWERS_OF_10(1000000000ull),
|
||||||
|
10000000000000000000ull
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||||
|
// These are generated by support/compute-powers.py.
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
||||||
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||||
|
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||||
|
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||||
|
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||||
|
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||||
|
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||||
|
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||||
|
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||||
|
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||||
|
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||||
|
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||||
|
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||||
|
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||||
|
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||||
|
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||||
|
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||||
|
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||||
|
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||||
|
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||||
|
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||||
|
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||||
|
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||||
|
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||||
|
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||||
|
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||||
|
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||||
|
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||||
|
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||||
|
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
|
// to significands above.
|
||||||
|
template <typename T>
|
||||||
|
const int16_t basic_data<T>::POW10_EXPONENTS[] = {
|
||||||
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||||
|
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||||
|
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||||
|
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||||
|
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||||
|
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||||
|
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||||
|
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
|
||||||
|
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
|
||||||
|
|
||||||
|
// A handmade floating-point number f * pow(2, e).
|
||||||
|
class fp {
|
||||||
|
private:
|
||||||
|
typedef uint64_t significand_type;
|
||||||
|
|
||||||
|
// All sizes are in bits.
|
||||||
|
static FMT_CONSTEXPR_DECL const int char_size =
|
||||||
|
std::numeric_limits<unsigned char>::digits;
|
||||||
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
|
// normalized form.
|
||||||
|
static FMT_CONSTEXPR_DECL const int double_significand_size =
|
||||||
|
std::numeric_limits<double>::digits - 1;
|
||||||
|
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
||||||
|
1ull << double_significand_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
significand_type f;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR_DECL const int significand_size =
|
||||||
|
sizeof(significand_type) * char_size;
|
||||||
|
|
||||||
|
fp(): f(0), e(0) {}
|
||||||
|
fp(uint64_t f, int e): f(f), e(e) {}
|
||||||
|
|
||||||
|
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
||||||
|
// errors on platforms where double is not IEEE754.
|
||||||
|
template <typename Double>
|
||||||
|
explicit fp(Double d) {
|
||||||
|
// Assume double is in the format [sign][exponent][significand].
|
||||||
|
typedef std::numeric_limits<Double> limits;
|
||||||
|
const int double_size = static_cast<int>(sizeof(Double) * char_size);
|
||||||
|
const int exponent_size =
|
||||||
|
double_size - double_significand_size - 1; // -1 for sign
|
||||||
|
const uint64_t significand_mask = implicit_bit - 1;
|
||||||
|
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
|
||||||
|
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
||||||
|
auto u = bit_cast<uint64_t>(d);
|
||||||
|
auto biased_e = (u & exponent_mask) >> double_significand_size;
|
||||||
|
f = u & significand_mask;
|
||||||
|
if (biased_e != 0)
|
||||||
|
f += implicit_bit;
|
||||||
|
else
|
||||||
|
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||||
|
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT = 0>
|
||||||
|
void normalize() {
|
||||||
|
// Handle subnormals.
|
||||||
|
auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||||
|
while ((f & shifted_implicit_bit) == 0) {
|
||||||
|
f <<= 1;
|
||||||
|
--e;
|
||||||
|
}
|
||||||
|
// Subtract 1 to account for hidden bit.
|
||||||
|
auto offset = significand_size - double_significand_size - SHIFT - 1;
|
||||||
|
f <<= offset;
|
||||||
|
e -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
|
||||||
|
// a boundary is a value half way between the number and its predecessor
|
||||||
|
// (lower) or successor (upper). The upper boundary is normalized and lower
|
||||||
|
// has the same exponent but may be not normalized.
|
||||||
|
void compute_boundaries(fp &lower, fp &upper) const {
|
||||||
|
lower = f == implicit_bit ?
|
||||||
|
fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
|
||||||
|
upper = fp((f << 1) + 1, e - 1);
|
||||||
|
upper.normalize<1>(); // 1 is to account for the exponent shift above.
|
||||||
|
lower.f <<= lower.e - upper.e;
|
||||||
|
lower.e = upper.e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns an fp number representing x - y. Result may not be normalized.
|
||||||
|
inline fp operator-(fp x, fp y) {
|
||||||
|
FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
|
||||||
|
return fp(x.f - y.f, x.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
|
||||||
|
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized.
|
||||||
|
FMT_API fp operator*(fp x, fp y);
|
||||||
|
|
||||||
|
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
|
||||||
|
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
|
||||||
|
FMT_API fp get_cached_power(int min_exponent, int &pow10_exponent);
|
||||||
|
|
||||||
|
FMT_FUNC fp operator*(fp x, fp y) {
|
||||||
|
// Multiply 32-bit parts of significands.
|
||||||
|
uint64_t mask = (1ULL << 32) - 1;
|
||||||
|
uint64_t a = x.f >> 32, b = x.f & mask;
|
||||||
|
uint64_t c = y.f >> 32, d = y.f & mask;
|
||||||
|
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
|
||||||
|
// Compute mid 64-bit of result and round.
|
||||||
|
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||||
|
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||||
|
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||||
|
int index = static_cast<int>(std::ceil(
|
||||||
|
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
|
||||||
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
|
const int first_dec_exp = -348;
|
||||||
|
// Difference between 2 consecutive decimal exponents in cached powers of 10.
|
||||||
|
const int dec_exp_step = 8;
|
||||||
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
|
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates output using Grisu2 digit-gen algorithm.
|
||||||
|
FMT_FUNC void grisu2_gen_digits(
|
||||||
|
const fp &scaled_value, const fp &scaled_upper, uint64_t delta,
|
||||||
|
char *buffer, size_t &size, int &dec_exp) {
|
||||||
|
internal::fp one(1ull << -scaled_upper.e, scaled_upper.e);
|
||||||
|
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
|
||||||
|
// hi = floor(scaled_upper / one).
|
||||||
|
uint32_t hi = static_cast<uint32_t>(scaled_upper.f >> -one.e);
|
||||||
|
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
|
||||||
|
// lo = scaled_upper mod 1.
|
||||||
|
uint64_t lo = scaled_upper.f & (one.f - 1);
|
||||||
|
size = 0;
|
||||||
|
auto exp = count_digits(hi); // kappa in Grisu.
|
||||||
|
while (exp > 0) {
|
||||||
|
uint32_t digit = 0;
|
||||||
|
// This optimization by miloyip reduces the number of integer divisions by
|
||||||
|
// one per iteration.
|
||||||
|
switch (exp) {
|
||||||
|
case 10: digit = hi / 1000000000; hi %= 1000000000; break;
|
||||||
|
case 9: digit = hi / 100000000; hi %= 100000000; break;
|
||||||
|
case 8: digit = hi / 10000000; hi %= 10000000; break;
|
||||||
|
case 7: digit = hi / 1000000; hi %= 1000000; break;
|
||||||
|
case 6: digit = hi / 100000; hi %= 100000; break;
|
||||||
|
case 5: digit = hi / 10000; hi %= 10000; break;
|
||||||
|
case 4: digit = hi / 1000; hi %= 1000; break;
|
||||||
|
case 3: digit = hi / 100; hi %= 100; break;
|
||||||
|
case 2: digit = hi / 10; hi %= 10; break;
|
||||||
|
case 1: digit = hi; hi = 0; break;
|
||||||
|
default:
|
||||||
|
FMT_ASSERT(false, "invalid number of digits");
|
||||||
|
}
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buffer[size++] = static_cast<char>('0' + digit);
|
||||||
|
--exp;
|
||||||
|
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
|
||||||
|
if (remainder <= delta) {
|
||||||
|
dec_exp += exp;
|
||||||
|
// TODO: use scaled_value
|
||||||
|
(void)scaled_value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
lo *= 10;
|
||||||
|
delta *= 10;
|
||||||
|
char digit = static_cast<char>(lo >> -one.e);
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buffer[size++] = static_cast<char>('0' + digit);
|
||||||
|
lo &= one.f - 1;
|
||||||
|
--exp;
|
||||||
|
if (lo < delta) {
|
||||||
|
dec_exp += exp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void grisu2_format_positive(double value, char *buffer, size_t &size,
|
||||||
|
int &dec_exp) {
|
||||||
|
FMT_ASSERT(value > 0, "value is nonpositive");
|
||||||
|
fp fp_value(value);
|
||||||
|
fp lower, upper; // w^- and w^+ in the Grisu paper.
|
||||||
|
fp_value.compute_boundaries(lower, upper);
|
||||||
|
// Find a cached power of 10 close to 1 / upper.
|
||||||
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
|
auto dec_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||||
|
min_exp - (upper.e + fp::significand_size), dec_exp);
|
||||||
|
dec_exp = -dec_exp;
|
||||||
|
fp_value.normalize();
|
||||||
|
fp scaled_value = fp_value * dec_pow;
|
||||||
|
fp scaled_lower = lower * dec_pow; // \tilde{M}^- in Grisu.
|
||||||
|
fp scaled_upper = upper * dec_pow; // \tilde{M}^+ in Grisu.
|
||||||
|
++scaled_lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||||
|
--scaled_upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||||
|
uint64_t delta = scaled_upper.f - scaled_lower.f;
|
||||||
|
grisu2_gen_digits(scaled_value, scaled_upper, delta, buffer, size, dec_exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void round(char *buffer, size_t &size, int &exp,
|
||||||
|
int digits_to_remove) {
|
||||||
|
size -= to_unsigned(digits_to_remove);
|
||||||
|
exp += digits_to_remove;
|
||||||
|
int digit = buffer[size] - '0';
|
||||||
|
// TODO: proper rounding and carry
|
||||||
|
if (digit > 5 || (digit == 5 && (digits_to_remove > 1 ||
|
||||||
|
(buffer[size - 1] - '0') % 2) != 0)) {
|
||||||
|
++buffer[size - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the exponent exp in the form "[+-]d{1,3}" to buffer.
|
||||||
|
FMT_FUNC char *write_exponent(char *buffer, int exp) {
|
||||||
|
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
|
||||||
|
if (exp < 0) {
|
||||||
|
*buffer++ = '-';
|
||||||
|
exp = -exp;
|
||||||
|
} else {
|
||||||
|
*buffer++ = '+';
|
||||||
|
}
|
||||||
|
if (exp >= 100) {
|
||||||
|
*buffer++ = static_cast<char>('0' + exp / 100);
|
||||||
|
exp %= 100;
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
*buffer++ = d[0];
|
||||||
|
*buffer++ = d[1];
|
||||||
|
} else {
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
*buffer++ = d[0];
|
||||||
|
*buffer++ = d[1];
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void format_exp_notation(
|
||||||
|
char *buffer, size_t &size, int exp, int precision, bool upper) {
|
||||||
|
// Insert a decimal point after the first digit and add an exponent.
|
||||||
|
std::memmove(buffer + 2, buffer + 1, size - 1);
|
||||||
|
buffer[1] = '.';
|
||||||
|
exp += static_cast<int>(size) - 1;
|
||||||
|
int num_digits = precision - static_cast<int>(size) + 1;
|
||||||
|
if (num_digits > 0) {
|
||||||
|
std::uninitialized_fill_n(buffer + size + 1, num_digits, '0');
|
||||||
|
size += to_unsigned(num_digits);
|
||||||
|
} else if (num_digits < 0) {
|
||||||
|
round(buffer, size, exp, -num_digits);
|
||||||
|
}
|
||||||
|
char *p = buffer + size + 1;
|
||||||
|
*p++ = upper ? 'E' : 'e';
|
||||||
|
size = to_unsigned(write_exponent(p, exp) - buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prettifies the output of the Grisu2 algorithm.
|
||||||
|
// The number is given as v = buffer * 10^exp.
|
||||||
|
FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
|
||||||
|
int precision, bool upper) {
|
||||||
|
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||||
|
int int_size = static_cast<int>(size);
|
||||||
|
int full_exp = int_size + exp;
|
||||||
|
const int exp_threshold = 21;
|
||||||
|
if (int_size <= full_exp && full_exp <= exp_threshold) {
|
||||||
|
// 1234e7 -> 12340000000[.0+]
|
||||||
|
std::uninitialized_fill_n(buffer + int_size, full_exp - int_size, '0');
|
||||||
|
char *p = buffer + full_exp;
|
||||||
|
if (precision > 0) {
|
||||||
|
*p++ = '.';
|
||||||
|
std::uninitialized_fill_n(p, precision, '0');
|
||||||
|
p += precision;
|
||||||
|
}
|
||||||
|
size = to_unsigned(p - buffer);
|
||||||
|
} else if (0 < full_exp && full_exp <= exp_threshold) {
|
||||||
|
// 1234e-2 -> 12.34[0+]
|
||||||
|
int fractional_size = -exp;
|
||||||
|
std::memmove(buffer + full_exp + 1, buffer + full_exp,
|
||||||
|
to_unsigned(fractional_size));
|
||||||
|
buffer[full_exp] = '.';
|
||||||
|
int num_zeros = precision - fractional_size;
|
||||||
|
if (num_zeros > 0) {
|
||||||
|
std::uninitialized_fill_n(buffer + size + 1, num_zeros, '0');
|
||||||
|
size += to_unsigned(num_zeros);
|
||||||
|
}
|
||||||
|
++size;
|
||||||
|
} else if (-6 < full_exp && full_exp <= 0) {
|
||||||
|
// 1234e-6 -> 0.001234
|
||||||
|
int offset = 2 - full_exp;
|
||||||
|
std::memmove(buffer + offset, buffer, size);
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '.';
|
||||||
|
std::uninitialized_fill_n(buffer + 2, -full_exp, '0');
|
||||||
|
size = to_unsigned(int_size + offset);
|
||||||
|
} else {
|
||||||
|
format_exp_notation(buffer, size, exp, precision, upper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_CLANG_VERSION
|
||||||
|
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
||||||
|
#elif FMT_GCC_VERSION >= 700
|
||||||
|
# define FMT_FALLTHROUGH [[gnu::fallthrough]];
|
||||||
|
#else
|
||||||
|
# define FMT_FALLTHROUGH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Formats a nonnegative value using Grisu2 algorithm. Grisu2 doesn't give any
|
||||||
|
// guarantees on the shortness of the result.
|
||||||
|
FMT_FUNC void grisu2_format(double value, char *buffer, size_t &size, char type,
|
||||||
|
int precision, bool write_decimal_point) {
|
||||||
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
int dec_exp = 0; // K in Grisu.
|
||||||
|
if (value > 0) {
|
||||||
|
grisu2_format_positive(value, buffer, size, dec_exp);
|
||||||
|
} else {
|
||||||
|
*buffer = '0';
|
||||||
|
size = 1;
|
||||||
|
}
|
||||||
|
const int default_precision = 6;
|
||||||
|
if (precision < 0)
|
||||||
|
precision = default_precision;
|
||||||
|
bool upper = false;
|
||||||
|
switch (type) {
|
||||||
|
case 'G':
|
||||||
|
upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case '\0': case 'g': {
|
||||||
|
int digits_to_remove = static_cast<int>(size) - precision;
|
||||||
|
if (digits_to_remove > 0) {
|
||||||
|
round(buffer, size, dec_exp, digits_to_remove);
|
||||||
|
// Remove trailing zeros.
|
||||||
|
while (size > 0 && buffer[size - 1] == '0') {
|
||||||
|
--size;
|
||||||
|
++dec_exp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
precision = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'F':
|
||||||
|
upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'f': {
|
||||||
|
int digits_to_remove = -dec_exp - precision;
|
||||||
|
if (digits_to_remove > 0) {
|
||||||
|
if (digits_to_remove >= static_cast<int>(size))
|
||||||
|
digits_to_remove = static_cast<int>(size) - 1;
|
||||||
|
round(buffer, size, dec_exp, digits_to_remove);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'e': case 'E':
|
||||||
|
format_exp_notation(buffer, size, dec_exp, precision, type == 'E');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (write_decimal_point && precision < 1)
|
||||||
|
precision = 1;
|
||||||
|
grisu2_prettify(buffer, size, dec_exp, precision, upper);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
|
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// MultiByteToWideChar does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_[length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC 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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC 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, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_[length] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::format_windows_error(
|
||||||
|
internal::buffer &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,
|
||||||
|
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
system_message, static_cast<uint32_t>(buf.size()), FMT_NULL);
|
||||||
|
if (result != 0) {
|
||||||
|
utf16_to_utf8 utf8_message;
|
||||||
|
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC void format_system_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
memory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
char *system_message = &buf[0];
|
||||||
|
int result = safe_strerror(error_code, system_message, buf.size());
|
||||||
|
if (result == 0) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(system_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result != ERANGE)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void basic_fixed_buffer<Char>::grow(std::size_t) {
|
||||||
|
FMT_THROW(std::runtime_error("buffer overflow"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::error_handler::on_error(const char *message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void report_system_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(format_system_error, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
FMT_FUNC void report_windows_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(internal::format_windows_error, error_code, message);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
vformat_to(buffer, format_str, args);
|
||||||
|
std::fwrite(buffer.data(), 1, buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
vformat_to(buffer, format_str, args);
|
||||||
|
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
FMT_FUNC locale locale_provider::locale() { return fmt::locale(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FMT_FORMAT_INL_H_
|
3720
externals/fmt/include/fmt/format.h
vendored
Normal file
3720
externals/fmt/include/fmt/format.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
157
externals/fmt/include/fmt/ostream.h
vendored
Normal file
157
externals/fmt/include/fmt/ostream.h
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OSTREAM_H_
|
||||||
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <class Char>
|
||||||
|
class formatbuf : public std::basic_streambuf<Char> {
|
||||||
|
private:
|
||||||
|
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||||
|
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||||
|
|
||||||
|
basic_buffer<Char> &buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The put-area is actually always empty. This makes the implementation
|
||||||
|
// simpler and has the advantage that the streambuf and the buffer are always
|
||||||
|
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||||
|
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||||
|
// to overflow. There is no disadvantage here for sputn since this always
|
||||||
|
// results in a call to xsputn.
|
||||||
|
|
||||||
|
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||||
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
|
buffer_.push_back(static_cast<Char>(ch));
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
||||||
|
buffer_.append(s, s + count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct test_stream : std::basic_ostream<Char> {
|
||||||
|
private:
|
||||||
|
struct null;
|
||||||
|
// Hide all operator<< from std::basic_ostream<Char>.
|
||||||
|
void operator<<(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
class is_streamable {
|
||||||
|
private:
|
||||||
|
template <typename U>
|
||||||
|
static decltype(
|
||||||
|
internal::declval<test_stream<Char>&>()
|
||||||
|
<< internal::declval<U>(), std::true_type()) test(int);
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
|
typedef decltype(test<T>(0)) result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value = result::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the content of buf to os.
|
||||||
|
template <typename Char>
|
||||||
|
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
||||||
|
const Char *data = buf.data();
|
||||||
|
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
|
||||||
|
UnsignedStreamSize size = buf.size();
|
||||||
|
UnsignedStreamSize max_size =
|
||||||
|
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||||
|
do {
|
||||||
|
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
||||||
|
os.write(data, static_cast<std::streamsize>(n));
|
||||||
|
data += n;
|
||||||
|
size -= n;
|
||||||
|
} while (size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename T>
|
||||||
|
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||||
|
internal::formatbuf<Char> format_buf(buffer);
|
||||||
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
|
output << value;
|
||||||
|
buffer.resize(buffer.size());
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Disable conversion to int if T has an overloaded operator<< which is a free
|
||||||
|
// function (not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct convert_to_int<T, Char, void> {
|
||||||
|
static const bool value =
|
||||||
|
convert_to_int<T, Char, int>::value &&
|
||||||
|
!internal::is_streamable<T, Char>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<T, Char,
|
||||||
|
typename std::enable_if<
|
||||||
|
internal::is_streamable<T, Char>::value &&
|
||||||
|
!internal::format_type<
|
||||||
|
typename buffer_context<Char>::type, T>::value>::type>
|
||||||
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::format_value(buffer, value);
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void vprint(std::basic_ostream<Char> &os,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(std::ostream &os, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint<char>(os, format_str, make_format_args<format_context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(std::wostream &os, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
|
@ -1,11 +1,9 @@
|
||||||
/*
|
// A C++ interface to POSIX functions.
|
||||||
A C++ interface to POSIX functions.
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_POSIX_H_
|
#ifndef FMT_POSIX_H_
|
||||||
#define FMT_POSIX_H_
|
#define FMT_POSIX_H_
|
||||||
|
@ -64,97 +62,102 @@
|
||||||
|
|
||||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
namespace fmt {
|
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.
|
// An error code.
|
||||||
class ErrorCode {
|
class error_code {
|
||||||
private:
|
private:
|
||||||
int value_;
|
int value_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||||
|
|
||||||
int get() const FMT_NOEXCEPT { return value_; }
|
int get() const FMT_NOEXCEPT { return value_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class BufferedFile {
|
class buffered_file {
|
||||||
private:
|
private:
|
||||||
FILE *file_;
|
FILE *file_;
|
||||||
|
|
||||||
friend class File;
|
friend class file;
|
||||||
|
|
||||||
explicit BufferedFile(FILE *f) : file_(f) {}
|
explicit buffered_file(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructs a BufferedFile object which doesn't represent any file.
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~BufferedFile() FMT_NOEXCEPT;
|
FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A proxy object to emulate a move constructor.
|
buffered_file(const buffered_file &) = delete;
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
void operator=(const buffered_file &) = delete;
|
||||||
struct Proxy {
|
|
||||||
FILE *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
// A "move constructor" for moving from a temporary.
|
|
||||||
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
|
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
|
||||||
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
|
|
||||||
f.file_ = FMT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
|
||||||
BufferedFile &operator=(Proxy p) {
|
|
||||||
close();
|
|
||||||
file_ = p.file;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
|
||||||
BufferedFile &operator=(BufferedFile &other) {
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = FMT_NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
|
||||||
// BufferedFile file = BufferedFile(...);
|
|
||||||
operator Proxy() FMT_NOEXCEPT {
|
|
||||||
Proxy p = {file_};
|
|
||||||
file_ = FMT_NULL;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
private:
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
|
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
|
||||||
other.file_ = FMT_NULL;
|
other.file_ = FMT_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedFile& operator=(BufferedFile &&other) {
|
buffered_file& operator=(buffered_file &&other) {
|
||||||
close();
|
close();
|
||||||
file_ = other.file_;
|
file_ = other.file_;
|
||||||
other.file_ = FMT_NULL;
|
other.file_ = FMT_NULL;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Opens a file.
|
// Opens a file.
|
||||||
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
FMT_API void close();
|
FMT_API void close();
|
||||||
|
@ -166,24 +169,28 @@ public:
|
||||||
// of MinGW that define fileno as a macro.
|
// of MinGW that define fileno as a macro.
|
||||||
FMT_API int (fileno)() const;
|
FMT_API int (fileno)() const;
|
||||||
|
|
||||||
void print(CStringRef format_str, const ArgList &args) {
|
void vprint(string_view format_str, format_args args) {
|
||||||
fmt::print(file_, format_str, 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...));
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(void, print, CStringRef)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A file. Closed file is represented by a File object with descriptor -1.
|
// A file. Closed file is represented by a file object with descriptor -1.
|
||||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||||
// fmt::SystemError in case of failure. Note that some errors such as
|
// 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
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
// than an exception. You can get standard behavior by overriding the
|
// than an exception. You can get standard behavior by overriding the
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
class File {
|
class file {
|
||||||
private:
|
private:
|
||||||
int fd_; // File descriptor.
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
// Constructs a File object with a given descriptor.
|
// Constructs a file object with a given descriptor.
|
||||||
explicit File(int fd) : fd_(fd) {}
|
explicit file(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Possible values for the oflag argument to the constructor.
|
// Possible values for the oflag argument to the constructor.
|
||||||
|
@ -193,74 +200,30 @@ class File {
|
||||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a File object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
File() FMT_NOEXCEPT : fd_(-1) {}
|
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
// Opens a file and constructs a File object representing this file.
|
// Opens a file and constructs a file object representing this file.
|
||||||
FMT_API File(CStringRef path, int oflag);
|
FMT_API file(cstring_view path, int oflag);
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A proxy object to emulate a move constructor.
|
file(const file &) = delete;
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
void operator=(const file &) = delete;
|
||||||
struct Proxy {
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// A "move constructor" for moving from a temporary.
|
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||||
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
|
||||||
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
|
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
file& operator=(file &&other) {
|
||||||
File &operator=(Proxy p) {
|
|
||||||
close();
|
|
||||||
fd_ = p.fd;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
|
||||||
File &operator=(File &other) {
|
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
|
||||||
// File file = File(...);
|
|
||||||
operator Proxy() FMT_NOEXCEPT {
|
|
||||||
Proxy p = {fd_};
|
|
||||||
fd_ = -1;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
private:
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(File);
|
|
||||||
|
|
||||||
public:
|
|
||||||
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
File& operator=(File &&other) {
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~File() FMT_NOEXCEPT;
|
FMT_API ~file() FMT_DTOR_NOEXCEPT;
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Returns the file descriptor.
|
||||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
|
@ -270,7 +233,7 @@ class File {
|
||||||
|
|
||||||
// Returns the file size. The size has signed type for consistency with
|
// Returns the file size. The size has signed type for consistency with
|
||||||
// stat::st_size.
|
// stat::st_size.
|
||||||
FMT_API LongLong size() const;
|
FMT_API long long size() const;
|
||||||
|
|
||||||
// Attempts to read count bytes from the file into the specified buffer.
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
FMT_API std::size_t read(void *buffer, std::size_t count);
|
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||||
|
@ -280,7 +243,7 @@ class File {
|
||||||
|
|
||||||
// Duplicates a file descriptor with the dup function and returns
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
// the duplicate as a file object.
|
// the duplicate as a file object.
|
||||||
FMT_API static File dup(int fd);
|
FMT_API static file dup(int fd);
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
|
@ -288,22 +251,23 @@ class File {
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
|
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
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
// and writing respectively.
|
// and writing respectively.
|
||||||
FMT_API static void pipe(File &read_end, File &write_end);
|
FMT_API static void pipe(file &read_end, file &write_end);
|
||||||
|
|
||||||
// Creates a BufferedFile object associated with this file and detaches
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
// this File object from the file.
|
// this file object from the file.
|
||||||
FMT_API BufferedFile fdopen(const char *mode);
|
FMT_API buffered_file fdopen(const char *mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||||
!defined(__ANDROID__) && !defined(__CYGWIN__)
|
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||||
|
!defined(__NEWLIB_H__)
|
||||||
# define FMT_LOCALE
|
# define FMT_LOCALE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -331,14 +295,15 @@ class Locale {
|
||||||
|
|
||||||
locale_t locale_;
|
locale_t locale_;
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
|
Locale(const Locale &) = delete;
|
||||||
|
void operator=(const Locale &) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef locale_t Type;
|
typedef locale_t Type;
|
||||||
|
|
||||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
||||||
if (!locale_)
|
if (!locale_)
|
||||||
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
|
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
}
|
}
|
||||||
~Locale() { freelocale(locale_); }
|
~Locale() { freelocale(locale_); }
|
||||||
|
|
||||||
|
@ -354,14 +319,6 @@ class Locale {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
} // namespace fmt
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
namespace std {
|
|
||||||
// For compatibility with C++98.
|
|
||||||
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; }
|
|
||||||
inline fmt::File &move(fmt::File &f) { return f; }
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_POSIX_H_
|
#endif // FMT_POSIX_H_
|
726
externals/fmt/include/fmt/printf.h
vendored
Normal file
726
externals/fmt/include/fmt/printf.h
vendored
Normal file
|
@ -0,0 +1,726 @@
|
||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_PRINTF_H_
|
||||||
|
#define FMT_PRINTF_H_
|
||||||
|
|
||||||
|
#include <algorithm> // std::fill_n
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
|
||||||
|
#include "ostream.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
|
// signed and unsigned integers.
|
||||||
|
template <bool IsSigned>
|
||||||
|
struct int_checker {
|
||||||
|
template <typename T>
|
||||||
|
static bool fits_in_int(T value) {
|
||||||
|
unsigned max = std::numeric_limits<int>::max();
|
||||||
|
return value <= max;
|
||||||
|
}
|
||||||
|
static bool fits_in_int(bool) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct int_checker<true> {
|
||||||
|
template <typename T>
|
||||||
|
static bool fits_in_int(T value) {
|
||||||
|
return value >= std::numeric_limits<int>::min() &&
|
||||||
|
value <= std::numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
static bool fits_in_int(int) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class printf_precision_handler: public function<int> {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, int>::type
|
||||||
|
operator()(T value) {
|
||||||
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(T) {
|
||||||
|
FMT_THROW(format_error("precision is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// An argument visitor that returns true iff arg is a zero integer.
|
||||||
|
class is_zero_int: public function<bool> {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, bool>::type
|
||||||
|
operator()(T value) { return value == 0; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, bool>::type
|
||||||
|
operator()(T) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct make_unsigned_or_bool<bool> {
|
||||||
|
typedef bool type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Context>
|
||||||
|
class arg_converter: public function<void> {
|
||||||
|
private:
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
|
||||||
|
basic_format_arg<Context> &arg_;
|
||||||
|
typename Context::char_type type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_converter(basic_format_arg<Context> &arg, Char type)
|
||||||
|
: arg_(arg), type_(type) {}
|
||||||
|
|
||||||
|
void operator()(bool value) {
|
||||||
|
if (type_ != 's')
|
||||||
|
operator()<bool>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename std::enable_if<std::is_integral<U>::value>::type
|
||||||
|
operator()(U value) {
|
||||||
|
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||||
|
typedef typename std::conditional<
|
||||||
|
std::is_same<T, void>::value, U, T>::type TargetType;
|
||||||
|
if (const_check(sizeof(TargetType) <= sizeof(int))) {
|
||||||
|
// Extra casts are used to silence warnings.
|
||||||
|
if (is_signed) {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<int>(static_cast<TargetType>(value)));
|
||||||
|
} else {
|
||||||
|
typedef typename make_unsigned_or_bool<TargetType>::type Unsigned;
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<unsigned>(static_cast<Unsigned>(value)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_signed) {
|
||||||
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
|
// but we don't have to do the same because it's a UB.
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
||||||
|
} else {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename std::enable_if<!std::is_integral<U>::value>::type operator()(U) {
|
||||||
|
// No coversion needed for non-integral types.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts an integer argument to T for printf, if T is an integral type.
|
||||||
|
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||||
|
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||||
|
// unsigned).
|
||||||
|
template <typename T, typename Context, typename Char>
|
||||||
|
void convert_arg(basic_format_arg<Context> &arg, Char type) {
|
||||||
|
fmt::visit(arg_converter<T, Context>(arg, type), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an integer argument to char for printf.
|
||||||
|
template <typename Context>
|
||||||
|
class char_converter: public function<void> {
|
||||||
|
private:
|
||||||
|
basic_format_arg<Context> &arg_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type
|
||||||
|
operator()(T value) {
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<Char>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value>::type operator()(T) {
|
||||||
|
// No coversion needed for non-integral types.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
|
// left alignment if it is negative.
|
||||||
|
template <typename Char>
|
||||||
|
class printf_width_handler: public function<unsigned> {
|
||||||
|
private:
|
||||||
|
typedef basic_format_specs<Char> format_specs;
|
||||||
|
|
||||||
|
format_specs &spec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit printf_width_handler(format_specs &spec) : spec_(spec) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, unsigned>::type
|
||||||
|
operator()(T value) {
|
||||||
|
typedef typename internal::int_traits<T>::main_type UnsignedType;
|
||||||
|
UnsignedType width = static_cast<UnsignedType>(value);
|
||||||
|
if (internal::is_negative(value)) {
|
||||||
|
spec_.align_ = ALIGN_LEFT;
|
||||||
|
width = 0 - width;
|
||||||
|
}
|
||||||
|
unsigned int_max = std::numeric_limits<int>::max();
|
||||||
|
if (width > int_max)
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<unsigned>(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, unsigned>::type
|
||||||
|
operator()(T) {
|
||||||
|
FMT_THROW(format_error("width is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter;
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename OutputIt, typename Char,
|
||||||
|
typename ArgFormatter =
|
||||||
|
printf_arg_formatter<back_insert_range<internal::basic_buffer<Char>>>>
|
||||||
|
class basic_printf_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
The ``printf`` argument formatter.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter:
|
||||||
|
public internal::function<
|
||||||
|
typename internal::arg_formatter_base<Range>::iterator>,
|
||||||
|
public internal::arg_formatter_base<Range> {
|
||||||
|
private:
|
||||||
|
typedef typename Range::value_type char_type;
|
||||||
|
typedef decltype(internal::declval<Range>().begin()) iterator;
|
||||||
|
typedef internal::arg_formatter_base<Range> base;
|
||||||
|
typedef basic_printf_context<iterator, char_type> context_type;
|
||||||
|
|
||||||
|
context_type &context_;
|
||||||
|
|
||||||
|
void write_null_pointer(char) {
|
||||||
|
this->spec()->type_ = 0;
|
||||||
|
this->write("(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_null_pointer(wchar_t) {
|
||||||
|
this->spec()->type_ = 0;
|
||||||
|
this->write(L"(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef typename base::format_specs format_specs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an argument formatter object.
|
||||||
|
*buffer* is a reference to the output buffer and *spec* contains format
|
||||||
|
specifier information for standard argument types.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
|
||||||
|
format_specs &spec, context_type &ctx)
|
||||||
|
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec),
|
||||||
|
context_(ctx) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, iterator>::type
|
||||||
|
operator()(T value) {
|
||||||
|
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||||
|
// use std::is_same instead.
|
||||||
|
if (std::is_same<T, bool>::value) {
|
||||||
|
format_specs &fmt_spec = *this->spec();
|
||||||
|
if (fmt_spec.type_ != 's')
|
||||||
|
return base::operator()(value ? 1 : 0);
|
||||||
|
fmt_spec.type_ = 0;
|
||||||
|
this->write(value != 0);
|
||||||
|
} else if (std::is_same<T, char_type>::value) {
|
||||||
|
format_specs &fmt_spec = *this->spec();
|
||||||
|
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
|
||||||
|
return (*this)(static_cast<int>(value));
|
||||||
|
fmt_spec.flags_ = 0;
|
||||||
|
fmt_spec.align_ = ALIGN_RIGHT;
|
||||||
|
return base::operator()(value);
|
||||||
|
} else {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
|
||||||
|
operator()(T value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated C string. */
|
||||||
|
iterator operator()(const char *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec()->type_ == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write("(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated wide C string. */
|
||||||
|
iterator operator()(const wchar_t *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec()->type_ == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write(L"(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(basic_string_view<char_type> value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(monostate value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a pointer. */
|
||||||
|
iterator operator()(const void *value) {
|
||||||
|
if (value)
|
||||||
|
return base::operator()(value);
|
||||||
|
this->spec()->type_ = 0;
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
|
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
|
handle.format(context_);
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct printf_formatter {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
internal::format_value(internal::get_container(ctx.out()), value);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This template formats data and writes the output to a writer. */
|
||||||
|
template <typename OutputIt, typename Char, typename ArgFormatter>
|
||||||
|
class basic_printf_context :
|
||||||
|
// Inherit publicly as a workaround for the icc bug
|
||||||
|
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
|
||||||
|
public internal::context_base<
|
||||||
|
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
|
||||||
|
public:
|
||||||
|
/** The character type for the output. */
|
||||||
|
typedef Char char_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct formatter_type { typedef printf_formatter<T> type; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
|
||||||
|
typedef typename base::format_arg format_arg;
|
||||||
|
typedef basic_format_specs<char_type> format_specs;
|
||||||
|
typedef internal::null_terminating_iterator<char_type> iterator;
|
||||||
|
|
||||||
|
void parse_flags(format_specs &spec, iterator &it);
|
||||||
|
|
||||||
|
// Returns the argument with specified index or, if arg_index is equal
|
||||||
|
// to the maximum unsigned value, the next argument.
|
||||||
|
format_arg get_arg(
|
||||||
|
iterator it,
|
||||||
|
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
|
||||||
|
|
||||||
|
// Parses argument index, flags and width and returns the argument index.
|
||||||
|
unsigned parse_header(iterator &it, format_specs &spec);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a ``printf_context`` object. References to the arguments and
|
||||||
|
the writer are stored in the context object so make sure they have
|
||||||
|
appropriate lifetimes.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||||
|
basic_format_args<basic_printf_context> args)
|
||||||
|
: base(out, format_str, args) {}
|
||||||
|
|
||||||
|
using base::parse_context;
|
||||||
|
using base::out;
|
||||||
|
using base::advance_to;
|
||||||
|
|
||||||
|
/** Formats stored arguments and writes the output to the range. */
|
||||||
|
void format();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
void basic_printf_context<OutputIt, Char, AF>::parse_flags(
|
||||||
|
format_specs &spec, iterator &it) {
|
||||||
|
for (;;) {
|
||||||
|
switch (*it++) {
|
||||||
|
case '-':
|
||||||
|
spec.align_ = ALIGN_LEFT;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
spec.fill_ = '0';
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
spec.flags_ |= SIGN_FLAG;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
spec.flags_ |= HASH_FLAG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
typename basic_printf_context<OutputIt, Char, AF>::format_arg
|
||||||
|
basic_printf_context<OutputIt, Char, AF>::get_arg(
|
||||||
|
iterator it, unsigned arg_index) {
|
||||||
|
(void)it;
|
||||||
|
if (arg_index == std::numeric_limits<unsigned>::max())
|
||||||
|
return this->do_get_arg(this->parse_context().next_arg_id());
|
||||||
|
return base::get_arg(arg_index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||||
|
iterator &it, format_specs &spec) {
|
||||||
|
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
||||||
|
char_type c = *it;
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
|
// preceded with '0' flag(s).
|
||||||
|
internal::error_handler eh;
|
||||||
|
unsigned value = parse_nonnegative_int(it, eh);
|
||||||
|
if (*it == '$') { // value is an argument index
|
||||||
|
++it;
|
||||||
|
arg_index = value;
|
||||||
|
} else {
|
||||||
|
if (c == '0')
|
||||||
|
spec.fill_ = '0';
|
||||||
|
if (value != 0) {
|
||||||
|
// Nonzero value means that we parsed width and don't need to
|
||||||
|
// parse it or flags again, so return now.
|
||||||
|
spec.width_ = value;
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_flags(spec, it);
|
||||||
|
// Parse width.
|
||||||
|
if (*it >= '0' && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
spec.width_ = parse_nonnegative_int(it, eh);
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
spec.width_ =
|
||||||
|
fmt::visit(internal::printf_width_handler<char_type>(spec), get_arg(it));
|
||||||
|
}
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||||
|
auto &buffer = internal::get_container(this->out());
|
||||||
|
auto start = iterator(this->parse_context());
|
||||||
|
auto it = start;
|
||||||
|
using internal::pointer_from;
|
||||||
|
while (*it) {
|
||||||
|
char_type c = *it++;
|
||||||
|
if (c != '%') continue;
|
||||||
|
if (*it == c) {
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
start = ++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it) - 1);
|
||||||
|
|
||||||
|
format_specs spec;
|
||||||
|
spec.align_ = ALIGN_RIGHT;
|
||||||
|
|
||||||
|
// Parse argument index, flags and width.
|
||||||
|
unsigned arg_index = parse_header(it, spec);
|
||||||
|
|
||||||
|
// Parse precision.
|
||||||
|
if (*it == '.') {
|
||||||
|
++it;
|
||||||
|
if ('0' <= *it && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
spec.precision_ = static_cast<int>(parse_nonnegative_int(it, eh));
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
spec.precision_ =
|
||||||
|
fmt::visit(internal::printf_precision_handler(), get_arg(it));
|
||||||
|
} else {
|
||||||
|
spec.precision_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format_arg arg = get_arg(it, arg_index);
|
||||||
|
if (spec.flag(HASH_FLAG) && fmt::visit(internal::is_zero_int(), arg))
|
||||||
|
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
|
||||||
|
if (spec.fill_ == '0') {
|
||||||
|
if (arg.is_arithmetic())
|
||||||
|
spec.align_ = ALIGN_NUMERIC;
|
||||||
|
else
|
||||||
|
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse length and convert the argument to the required type.
|
||||||
|
using internal::convert_arg;
|
||||||
|
switch (*it++) {
|
||||||
|
case 'h':
|
||||||
|
if (*it == 'h')
|
||||||
|
convert_arg<signed char>(arg, *++it);
|
||||||
|
else
|
||||||
|
convert_arg<short>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (*it == 'l')
|
||||||
|
convert_arg<long long>(arg, *++it);
|
||||||
|
else
|
||||||
|
convert_arg<long>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
convert_arg<intmax_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
convert_arg<std::size_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
convert_arg<std::ptrdiff_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
// printf produces garbage when 'L' is omitted for long double, no
|
||||||
|
// need to do the same.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
convert_arg<void>(arg, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type.
|
||||||
|
if (!*it)
|
||||||
|
FMT_THROW(format_error("invalid format string"));
|
||||||
|
spec.type_ = static_cast<char>(*it++);
|
||||||
|
if (arg.is_integral()) {
|
||||||
|
// Normalize type.
|
||||||
|
switch (spec.type_) {
|
||||||
|
case 'i': case 'u':
|
||||||
|
spec.type_ = 'd';
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
// TODO: handle wchar_t better?
|
||||||
|
fmt::visit(internal::char_converter<basic_printf_context>(arg), arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = it;
|
||||||
|
|
||||||
|
// Format argument.
|
||||||
|
fmt::visit(AF(buffer, spec, *this), arg);
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
void printf(internal::basic_buffer<Char> &buf, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
struct printf_context {
|
||||||
|
typedef basic_printf_context<
|
||||||
|
std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline format_arg_store<printf_context<internal::buffer>::type, Args...>
|
||||||
|
make_printf_args(const Args & ... args) {
|
||||||
|
return format_arg_store<printf_context<internal::buffer>::type, Args...>(
|
||||||
|
args...);
|
||||||
|
}
|
||||||
|
typedef basic_format_args<printf_context<internal::buffer>::type> printf_args;
|
||||||
|
typedef basic_format_args<printf_context<internal::wbuffer>::type> wprintf_args;
|
||||||
|
|
||||||
|
inline std::string vsprintf(string_view format, printf_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
printf(buffer, format, args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline std::string sprintf(string_view format_str, const Args & ... args) {
|
||||||
|
return vsprintf(format_str,
|
||||||
|
make_format_args<typename printf_context<internal::buffer>::type>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::wstring vsprintf(wstring_view format, wprintf_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
printf(buffer, format, args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline std::wstring sprintf(wstring_view format_str, const Args & ... args) {
|
||||||
|
return vsprintf(format_str,
|
||||||
|
make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline int vfprintf(std::FILE *f, basic_string_view<Char> format,
|
||||||
|
basic_format_args<typename printf_context<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, format, args);
|
||||||
|
std::size_t size = buffer.size();
|
||||||
|
return std::fwrite(
|
||||||
|
buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the file *f*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) {
|
||||||
|
auto vargs = make_format_args<
|
||||||
|
typename printf_context<internal::buffer>::type>(args...);
|
||||||
|
return vfprintf<char>(f, format_str, vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline int fprintf(std::FILE *f, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
return vfprintf(f, format_str,
|
||||||
|
make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vprintf(string_view format, printf_args args) {
|
||||||
|
return vfprintf(stdout, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vprintf(wstring_view format, wprintf_args args) {
|
||||||
|
return vfprintf(stdout, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to ``stdout``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline int printf(string_view format_str, const Args & ... args) {
|
||||||
|
return vprintf(format_str,
|
||||||
|
make_format_args<typename printf_context<internal::buffer>::type>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline int printf(wstring_view format_str, const Args & ... args) {
|
||||||
|
return vprintf(format_str,
|
||||||
|
make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vfprintf(std::ostream &os, string_view format_str,
|
||||||
|
printf_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
printf(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
return static_cast<int>(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vfprintf(std::wostream &os, wstring_view format_str,
|
||||||
|
wprintf_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
printf(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
return static_cast<int>(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(cerr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline int fprintf(std::ostream &os, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
auto vargs = make_format_args<
|
||||||
|
typename printf_context<internal::buffer>::type>(args...);
|
||||||
|
return vfprintf(os, format_str, vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline int fprintf(std::wostream &os, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
auto vargs = make_format_args<
|
||||||
|
typename printf_context<internal::buffer>::type>(args...);
|
||||||
|
return vfprintf(os, format_str, vargs);
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_PRINTF_H_
|
308
externals/fmt/include/fmt/ranges.h
vendored
Normal file
308
externals/fmt/include/fmt/ranges.h
vendored
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
// Formatting library for C++ - the core API
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
|
#ifndef FMT_RANGES_H_
|
||||||
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
// output only up to N items from the range.
|
||||||
|
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||||
|
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatting_base {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_range : formatting_base<Char> {
|
||||||
|
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||||
|
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_tuple : formatting_base<Char> {
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename RangeT, typename OutputIterator>
|
||||||
|
void copy(const RangeT &range, OutputIterator out) {
|
||||||
|
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||||
|
*out++ = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
void copy(const char *str, OutputIterator out) {
|
||||||
|
const char *p_curr = str;
|
||||||
|
while (*p_curr) {
|
||||||
|
*out++ = *p_curr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
void copy(char ch, OutputIterator out) {
|
||||||
|
*out++ = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true value if T has std::string interface, like std::string_view.
|
||||||
|
template <typename T>
|
||||||
|
class is_like_std_string {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U *p) ->
|
||||||
|
decltype(p->find('a'), p->length(), p->data(), int());
|
||||||
|
template <typename>
|
||||||
|
static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct conditional_helper {};
|
||||||
|
|
||||||
|
template <typename T, typename _ = void>
|
||||||
|
struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
|
template <typename T>
|
||||||
|
struct is_range_<T, typename std::conditional<
|
||||||
|
false,
|
||||||
|
conditional_helper<decltype(internal::declval<T>().begin()),
|
||||||
|
decltype(internal::declval<T>().end())>,
|
||||||
|
void>::type> : std::true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// tuple_size and tuple_element check.
|
||||||
|
template <typename T>
|
||||||
|
class is_tuple_like_ {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U *p) ->
|
||||||
|
decltype(std::tuple_size<U>::value,
|
||||||
|
internal::declval<typename std::tuple_element<0, U>::type>(), int());
|
||||||
|
template <typename>
|
||||||
|
static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for integer_sequence
|
||||||
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||||
|
template <typename T, T... N>
|
||||||
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = std::index_sequence<N...>;
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = std::make_index_sequence<N>;
|
||||||
|
#else
|
||||||
|
template <typename T, T... N>
|
||||||
|
struct integer_sequence {
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR std::size_t size() {
|
||||||
|
return sizeof...(N);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N, T... Ns>
|
||||||
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
|
template <typename T, T... Ns>
|
||||||
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class Tuple, class F, size_t... Is>
|
||||||
|
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
|
||||||
|
using std::get;
|
||||||
|
// using free function get<I>(T) now.
|
||||||
|
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||||
|
(void)_; // blocks warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
|
||||||
|
get_indexes(T const &) { return {}; }
|
||||||
|
|
||||||
|
template <class Tuple, class F>
|
||||||
|
void for_each(Tuple &&tup, F &&f) {
|
||||||
|
const auto indexes = get_indexes(tup);
|
||||||
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||||
|
typename std::enable_if<
|
||||||
|
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||||
|
return add_space ? " {}" : "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||||
|
typename std::enable_if<
|
||||||
|
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||||
|
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||||
|
return add_space ? " '{}'" : "'{}'";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_tuple_like {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TupleT, typename Char>
|
||||||
|
struct formatter<TupleT, Char,
|
||||||
|
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
|
||||||
|
private:
|
||||||
|
// C++11 generic lambda for format()
|
||||||
|
template <typename FormatContext>
|
||||||
|
struct format_each {
|
||||||
|
template <typename T>
|
||||||
|
void operator()(const T& v) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
|
v);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatting_tuple<Char>& formatting;
|
||||||
|
std::size_t& i;
|
||||||
|
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatting_tuple<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext = format_context>
|
||||||
|
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
std::size_t i = 0;
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
|
||||||
|
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_range {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename RangeT, typename Char>
|
||||||
|
struct formatter<RangeT, Char,
|
||||||
|
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
|
||||||
|
|
||||||
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
typename FormatContext::iterator format(
|
||||||
|
const RangeT &values, FormatContext &ctx) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
|
*it);
|
||||||
|
if (++i > formatting.range_length_limit) {
|
||||||
|
format_to(out, " ... <other elements>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_RANGES_H_
|
||||||
|
|
156
externals/fmt/include/fmt/time.h
vendored
Normal file
156
externals/fmt/include/fmt/time.h
vendored
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
// Formatting library for C++ - time formatting
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_TIME_H_
|
||||||
|
#define FMT_TIME_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
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<>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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; }
|
||||||
|
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
std::tm *tm = std::localtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dispatcher lt(time);
|
||||||
|
if (lt.run())
|
||||||
|
return lt.tm_;
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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; }
|
||||||
|
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
std::tm *tm = std::gmtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dispatcher gt(time);
|
||||||
|
if (gt.run())
|
||||||
|
return gt.tm_;
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = internal::null_terminating_iterator<Char>(ctx);
|
||||||
|
if (*it == ':')
|
||||||
|
++it;
|
||||||
|
auto end = it;
|
||||||
|
while (*end && *end != '}')
|
||||||
|
++end;
|
||||||
|
tm_format.reserve(end - it + 1);
|
||||||
|
using internal::pointer_from;
|
||||||
|
tm_format.append(pointer_from(it), pointer_from(end));
|
||||||
|
tm_format.push_back('\0');
|
||||||
|
return pointer_from(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
|
||||||
|
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 ctx.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_memory_buffer<Char> tm_format;
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_TIME_H_
|
53
externals/fmt/src/format.cc
vendored
Normal file
53
externals/fmt/src/format.cc
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include "fmt/format-inl.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template struct internal::basic_data<void>;
|
||||||
|
|
||||||
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
|
template FMT_API char internal::thousands_sep(locale_provider *lp);
|
||||||
|
|
||||||
|
template void internal::basic_buffer<char>::append(const char *, const char *);
|
||||||
|
|
||||||
|
template void basic_fixed_buffer<char>::grow(std::size_t);
|
||||||
|
|
||||||
|
template void internal::arg_map<format_context>::init(
|
||||||
|
const basic_format_args<format_context> &args);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
|
char *, std::size_t, const char *, int, double);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
|
char *, std::size_t, const char *, int, long double);
|
||||||
|
|
||||||
|
template FMT_API std::string internal::vformat<char>(
|
||||||
|
string_view, basic_format_args<format_context>);
|
||||||
|
|
||||||
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
|
template FMT_API wchar_t internal::thousands_sep(locale_provider *);
|
||||||
|
|
||||||
|
template void internal::basic_buffer<wchar_t>::append(
|
||||||
|
const wchar_t *, const wchar_t *);
|
||||||
|
|
||||||
|
template void basic_fixed_buffer<wchar_t>::grow(std::size_t);
|
||||||
|
|
||||||
|
template 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>(
|
||||||
|
wstring_view, basic_format_args<wformat_context>);
|
||||||
|
FMT_END_NAMESPACE
|
|
@ -1,18 +1,16 @@
|
||||||
/*
|
// A C++ interface to POSIX functions.
|
||||||
A C++ interface to POSIX functions.
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Disable bogus MSVC warnings.
|
// Disable bogus MSVC warnings.
|
||||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||||
# define _CRT_SECURE_NO_WARNINGS
|
# define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "posix.h"
|
#include "fmt/posix.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -66,38 +64,40 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||||
fmt::report_system_error(errno, "cannot close file");
|
report_system_error(errno, "cannot close file");
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::BufferedFile::BufferedFile(
|
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||||
fmt::CStringRef filename, fmt::CStringRef mode) {
|
FMT_RETRY_VAL(file_,
|
||||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
|
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
|
||||||
if (!file_)
|
if (!file_)
|
||||||
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
|
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void fmt::BufferedFile::close() {
|
void buffered_file::close() {
|
||||||
if (!file_)
|
if (!file_)
|
||||||
return;
|
return;
|
||||||
int result = FMT_SYSTEM(fclose(file_));
|
int result = FMT_SYSTEM(fclose(file_));
|
||||||
file_ = FMT_NULL;
|
file_ = FMT_NULL;
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot close file"));
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||||
#define FMT_ARGS
|
#define FMT_ARGS
|
||||||
|
|
||||||
int fmt::BufferedFile::fileno() const {
|
int buffered_file::fileno() const {
|
||||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
|
FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::File::File(fmt::CStringRef path, int oflag) {
|
file::file(cstring_view path, int oflag) {
|
||||||
int mode = S_IRUSR | S_IWUSR;
|
int mode = S_IRUSR | S_IWUSR;
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
|
@ -106,17 +106,17 @@ fmt::File::File(fmt::CStringRef path, int oflag) {
|
||||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||||
#endif
|
#endif
|
||||||
if (fd_ == -1)
|
if (fd_ == -1)
|
||||||
FMT_THROW(SystemError(errno, "cannot open file {}", path));
|
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::File::~File() FMT_NOEXCEPT {
|
file::~file() FMT_NOEXCEPT {
|
||||||
// Don't retry close in case of EINTR!
|
// Don't retry close in case of EINTR!
|
||||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||||
fmt::report_system_error(errno, "cannot close file");
|
report_system_error(errno, "cannot close file");
|
||||||
}
|
}
|
||||||
|
|
||||||
void fmt::File::close() {
|
void file::close() {
|
||||||
if (fd_ == -1)
|
if (fd_ == -1)
|
||||||
return;
|
return;
|
||||||
// Don't retry close in case of EINTR!
|
// Don't retry close in case of EINTR!
|
||||||
|
@ -124,10 +124,10 @@ void fmt::File::close() {
|
||||||
int result = FMT_POSIX_CALL(close(fd_));
|
int result = FMT_POSIX_CALL(close(fd_));
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot close file"));
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::LongLong fmt::File::size() const {
|
long long file::size() const {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||||
|
@ -138,63 +138,63 @@ fmt::LongLong fmt::File::size() const {
|
||||||
if (size_lower == INVALID_FILE_SIZE) {
|
if (size_lower == INVALID_FILE_SIZE) {
|
||||||
DWORD error = GetLastError();
|
DWORD error = GetLastError();
|
||||||
if (error != NO_ERROR)
|
if (error != NO_ERROR)
|
||||||
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
|
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||||
}
|
}
|
||||||
fmt::ULongLong long_size = size_upper;
|
unsigned long long long_size = size_upper;
|
||||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||||
#else
|
#else
|
||||||
typedef struct stat Stat;
|
typedef struct stat Stat;
|
||||||
Stat file_stat = Stat();
|
Stat file_stat = Stat();
|
||||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||||
FMT_THROW(SystemError(errno, "cannot get file attributes"));
|
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||||
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
|
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||||
"return type of File::size is not large enough");
|
"return type of file::size is not large enough");
|
||||||
return file_stat.st_size;
|
return file_stat.st_size;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t fmt::File::read(void *buffer, std::size_t count) {
|
std::size_t file::read(void *buffer, std::size_t count) {
|
||||||
RWResult result = 0;
|
RWResult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot read from file"));
|
FMT_THROW(system_error(errno, "cannot read from file"));
|
||||||
return internal::to_unsigned(result);
|
return internal::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
|
std::size_t file::write(const void *buffer, std::size_t count) {
|
||||||
RWResult result = 0;
|
RWResult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot write to file"));
|
FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
return internal::to_unsigned(result);
|
return internal::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::File fmt::File::dup(int fd) {
|
file file::dup(int fd) {
|
||||||
// Don't retry as dup doesn't return EINTR.
|
// Don't retry as dup doesn't return EINTR.
|
||||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||||
if (new_fd == -1)
|
if (new_fd == -1)
|
||||||
FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
|
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||||
return File(new_fd);
|
return file(new_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fmt::File::dup2(int fd) {
|
void file::dup2(int fd) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
FMT_THROW(SystemError(errno,
|
FMT_THROW(system_error(errno,
|
||||||
"cannot duplicate file descriptor {} to {}", fd_, fd));
|
"cannot duplicate file descriptor {} to {}", fd_, fd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
|
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
ec = ErrorCode(errno);
|
ec = error_code(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fmt::File::pipe(File &read_end, File &write_end) {
|
void file::pipe(file &read_end, file &write_end) {
|
||||||
// Close the descriptors first to make sure that assignments don't throw
|
// Close the descriptors first to make sure that assignments don't throw
|
||||||
// and there are no leaks.
|
// and there are no leaks.
|
||||||
read_end.close();
|
read_end.close();
|
||||||
|
@ -210,24 +210,25 @@ void fmt::File::pipe(File &read_end, File &write_end) {
|
||||||
int result = FMT_POSIX_CALL(pipe(fds));
|
int result = FMT_POSIX_CALL(pipe(fds));
|
||||||
#endif
|
#endif
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot create pipe"));
|
FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||||
// The following assignments don't throw because read_fd and write_fd
|
// The following assignments don't throw because read_fd and write_fd
|
||||||
// are closed.
|
// are closed.
|
||||||
read_end = File(fds[0]);
|
read_end = file(fds[0]);
|
||||||
write_end = File(fds[1]);
|
write_end = file(fds[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::BufferedFile fmt::File::fdopen(const char *mode) {
|
buffered_file file::fdopen(const char *mode) {
|
||||||
// Don't retry as fdopen doesn't return EINTR.
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
if (!f)
|
if (!f)
|
||||||
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
|
FMT_THROW(system_error(errno,
|
||||||
BufferedFile file(f);
|
"cannot associate stream with file descriptor"));
|
||||||
|
buffered_file bf(f);
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
return file;
|
return bf;
|
||||||
}
|
}
|
||||||
|
|
||||||
long fmt::getpagesize() {
|
long getpagesize() {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
GetSystemInfo(&si);
|
GetSystemInfo(&si);
|
||||||
|
@ -235,7 +236,9 @@ long fmt::getpagesize() {
|
||||||
#else
|
#else
|
||||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot get memory page size"));
|
FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||||
return size;
|
return size;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
|
@ -4,7 +4,7 @@ include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := fmt_static
|
LOCAL_MODULE := fmt_static
|
||||||
LOCAL_MODULE_FILENAME := libfmt
|
LOCAL_MODULE_FILENAME := libfmt
|
||||||
|
|
||||||
LOCAL_SRC_FILES := fmt/format.cc
|
LOCAL_SRC_FILES := ../src/format.cc
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
1
externals/fmt/support/AndroidManifest.xml
vendored
Normal file
1
externals/fmt/support/AndroidManifest.xml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<manifest package="fmt" />
|
2
externals/fmt/support/README
vendored
2
externals/fmt/support/README
vendored
|
@ -2,3 +2,5 @@ This directory contains build support files such as
|
||||||
|
|
||||||
* CMake modules
|
* CMake modules
|
||||||
* Build scripts
|
* Build scripts
|
||||||
|
* qmake (static build with dynamic libc only)
|
||||||
|
|
||||||
|
|
15
externals/fmt/support/appveyor-build.py
vendored
Executable file → Normal file
15
externals/fmt/support/appveyor-build.py
vendored
Executable file → Normal file
|
@ -6,9 +6,11 @@ from subprocess import check_call
|
||||||
|
|
||||||
build = os.environ['BUILD']
|
build = os.environ['BUILD']
|
||||||
config = os.environ['CONFIGURATION']
|
config = os.environ['CONFIGURATION']
|
||||||
platform = os.environ.get('PLATFORM')
|
platform = os.environ['PLATFORM']
|
||||||
path = os.environ['PATH']
|
path = os.environ['PATH']
|
||||||
cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config]
|
image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE']
|
||||||
|
jobid = os.environ['APPVEYOR_JOB_ID']
|
||||||
|
cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..']
|
||||||
if build == 'mingw':
|
if build == 'mingw':
|
||||||
cmake_command.append('-GMinGW Makefiles')
|
cmake_command.append('-GMinGW Makefiles')
|
||||||
build_command = ['mingw32-make', '-j4']
|
build_command = ['mingw32-make', '-j4']
|
||||||
|
@ -20,8 +22,13 @@ if build == 'mingw':
|
||||||
else:
|
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\14.0\Bin;' + path
|
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
|
||||||
generator = 'Visual Studio 14 2015'
|
if image == 'Visual Studio 2013':
|
||||||
|
generator = 'Visual Studio 12 2013'
|
||||||
|
elif image == 'Visual Studio 2015':
|
||||||
|
generator = 'Visual Studio 14 2015'
|
||||||
|
elif image == 'Visual Studio 2017':
|
||||||
|
generator = 'Visual Studio 15 2017'
|
||||||
if platform == 'x64':
|
if platform == 'x64':
|
||||||
generator += ' Win64'
|
generator += ' Win64'
|
||||||
cmake_command.append('-G' + generator)
|
cmake_command.append('-G' + generator)
|
||||||
|
|
24
externals/fmt/support/appveyor.yml
vendored
24
externals/fmt/support/appveyor.yml
vendored
|
@ -2,20 +2,28 @@ configuration:
|
||||||
- Debug
|
- Debug
|
||||||
- Release
|
- Release
|
||||||
|
|
||||||
|
clone_depth: 1
|
||||||
|
|
||||||
|
platform:
|
||||||
|
- Win32
|
||||||
|
- x64
|
||||||
|
|
||||||
|
image:
|
||||||
|
- Visual Studio 2013
|
||||||
|
- Visual Studio 2015
|
||||||
|
- Visual Studio 2017
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
CTEST_OUTPUT_ON_FAILURE: 1
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
matrix:
|
MSVC_DEFAULT_OPTIONS: ON
|
||||||
- BUILD: msvc
|
BUILD: msvc
|
||||||
- BUILD: msvc
|
|
||||||
PLATFORM: x64
|
|
||||||
- BUILD: mingw
|
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
# Workaround for CMake not wanting sh.exe on PATH for MinGW.
|
- mkdir build
|
||||||
- set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
- cd build
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- python support/appveyor-build.py
|
- python ../support/appveyor-build.py
|
||||||
|
|
||||||
on_failure:
|
on_failure:
|
||||||
- appveyor PushArtifact Testing/Temporary/LastTest.log
|
- appveyor PushArtifact Testing/Temporary/LastTest.log
|
||||||
|
|
98
externals/fmt/support/build.gradle
vendored
Normal file
98
externals/fmt/support/build.gradle
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// General gradle arguments for root project
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output: Shared library (.so) for Android
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 25 // Android 7.0
|
||||||
|
|
||||||
|
// Target ABI
|
||||||
|
// - This option controls target platform of module
|
||||||
|
// - The platform might be limited by compiler's support
|
||||||
|
// some can work with Clang(default), but some can work only with GCC...
|
||||||
|
// if bad, both toolchains might not support it
|
||||||
|
splits {
|
||||||
|
abi {
|
||||||
|
enable true
|
||||||
|
// Be general, as much as possible ...
|
||||||
|
// universalApk true
|
||||||
|
|
||||||
|
// Specify platforms for Application
|
||||||
|
reset()
|
||||||
|
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 21 // Android 5.0+
|
||||||
|
targetSdkVersion 25 // Follow Compile SDK
|
||||||
|
versionCode 16 // Follow release count
|
||||||
|
versionName "4.1.0" // Follow Official version
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
arguments "-DANDROID_STL=c++_shared" // Specify Android STL
|
||||||
|
arguments "-DBUILD_SHARED_LIBS=true" // Build shared object
|
||||||
|
arguments "-DFMT_TEST=false" // Skip test
|
||||||
|
arguments "-DFMT_DOC=false" // Skip document
|
||||||
|
cppFlags "-std=c++14"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("Gradle CMake Plugin: ")
|
||||||
|
println(externalNativeBuild.cmake.cppFlags)
|
||||||
|
println(externalNativeBuild.cmake.arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// External Native build
|
||||||
|
// - Use existing CMakeList.txt
|
||||||
|
// - Give path to CMake. This gradle file should be
|
||||||
|
// neighbor of the top level cmake
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
path "../CMakeLists.txt"
|
||||||
|
// buildStagingDirectory "./build" // Custom path for cmake output
|
||||||
|
}
|
||||||
|
//println(cmake.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets{
|
||||||
|
// Android Manifest for Gradle
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assemble.doLast
|
||||||
|
{
|
||||||
|
// Instead of `ninja install`, Gradle will deploy the files.
|
||||||
|
// We are doing this since FMT is dependent to the ANDROID_STL after build
|
||||||
|
copy {
|
||||||
|
from 'build/intermediates/cmake'
|
||||||
|
into '../libs'
|
||||||
|
}
|
||||||
|
// Copy debug binaries
|
||||||
|
copy {
|
||||||
|
from '../libs/debug/obj'
|
||||||
|
into '../libs/debug'
|
||||||
|
}
|
||||||
|
// Copy Release binaries
|
||||||
|
copy {
|
||||||
|
from '../libs/release/obj'
|
||||||
|
into '../libs/release'
|
||||||
|
}
|
||||||
|
// Remove empty directory
|
||||||
|
delete '../libs/debug/obj'
|
||||||
|
delete '../libs/release/obj'
|
||||||
|
}
|
85
externals/fmt/support/cmake/cxx11.cmake
vendored
85
externals/fmt/support/cmake/cxx11.cmake
vendored
|
@ -1,85 +0,0 @@
|
||||||
# C++11 feature support detection
|
|
||||||
|
|
||||||
if (NOT FMT_USE_CPP11)
|
|
||||||
return()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
include(CheckCXXCompilerFlag)
|
|
||||||
|
|
||||||
if (FMT_USE_CPP11)
|
|
||||||
check_cxx_compiler_flag(-std=c++11 HAVE_STD_CPP11_FLAG)
|
|
||||||
if (HAVE_STD_CPP11_FLAG)
|
|
||||||
# Check if including cmath works with -std=c++11 and -O3.
|
|
||||||
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
|
|
||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -O3")
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
#include <cmath>
|
|
||||||
int main() {}" FMT_CPP11_CMATH)
|
|
||||||
# Check if including <unistd.h> works with -std=c++11.
|
|
||||||
# It may not in MinGW due to bug http://sourceforge.net/p/mingw/bugs/2024/.
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
#include <unistd.h>
|
|
||||||
int main() {}" FMT_CPP11_UNISTD_H)
|
|
||||||
# Check if snprintf works with -std=c++11. It may not in MinGW.
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
#include <stdio.h>
|
|
||||||
int main() {
|
|
||||||
char buffer[10];
|
|
||||||
snprintf(buffer, 10, \"foo\");
|
|
||||||
}" FMT_CPP11_SNPRINTF)
|
|
||||||
if (FMT_CPP11_CMATH AND FMT_CPP11_UNISTD_H AND FMT_CPP11_SNPRINTF)
|
|
||||||
set(CPP11_FLAG -std=c++11)
|
|
||||||
else ()
|
|
||||||
check_cxx_compiler_flag(-std=gnu++11 HAVE_STD_GNUPP11_FLAG)
|
|
||||||
if (HAVE_STD_CPP11_FLAG)
|
|
||||||
set(CPP11_FLAG -std=gnu++11)
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
set(CMAKE_REQUIRED_FLAGS )
|
|
||||||
else ()
|
|
||||||
check_cxx_compiler_flag(-std=c++0x HAVE_STD_CPP0X_FLAG)
|
|
||||||
if (HAVE_STD_CPP0X_FLAG)
|
|
||||||
set(CPP11_FLAG -std=c++0x)
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_STANDARD)
|
|
||||||
# Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified.
|
|
||||||
set(CPP11_FLAG )
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
|
|
||||||
|
|
||||||
# Check if variadic templates are working and not affected by GCC bug 39653:
|
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
template <class T, class ...Types>
|
|
||||||
struct S { typedef typename S<Types...>::type type; };
|
|
||||||
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
|
|
||||||
|
|
||||||
# Check if initializer lists are supported.
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
#include <initializer_list>
|
|
||||||
int main() {}" SUPPORTS_INITIALIZER_LIST)
|
|
||||||
|
|
||||||
# Check if enum bases are available
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
enum C : char {A};
|
|
||||||
int main() {}"
|
|
||||||
SUPPORTS_ENUM_BASE)
|
|
||||||
|
|
||||||
# Check if type traits are available
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
#include <type_traits>
|
|
||||||
class C { void operator=(const C&); };
|
|
||||||
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
|
|
||||||
SUPPORTS_TYPE_TRAITS)
|
|
||||||
|
|
||||||
# Check if user-defined literals are available
|
|
||||||
check_cxx_source_compiles("
|
|
||||||
void operator\"\" _udl(long double);
|
|
||||||
int main() {}"
|
|
||||||
SUPPORTS_USER_DEFINED_LITERALS)
|
|
||||||
|
|
||||||
set(CMAKE_REQUIRED_FLAGS )
|
|
97
externals/fmt/support/cmake/cxx14.cmake
vendored
Normal file
97
externals/fmt/support/cmake/cxx14.cmake
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
# C++14 feature support detection
|
||||||
|
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
if (NOT CMAKE_CXX_STANDARD)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
endif()
|
||||||
|
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
|
||||||
|
|
||||||
|
if (CMAKE_CXX_STANDARD EQUAL 20)
|
||||||
|
check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
|
||||||
|
|
||||||
|
if (has_std_20_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++20)
|
||||||
|
elseif (has_std_2a_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++2a)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_CXX_STANDARD EQUAL 17)
|
||||||
|
check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
|
||||||
|
|
||||||
|
if (has_std_17_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++17)
|
||||||
|
elseif (has_std_1z_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++1z)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_CXX_STANDARD EQUAL 14)
|
||||||
|
check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
|
||||||
|
|
||||||
|
if (has_std_14_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++14)
|
||||||
|
elseif (has_std_1y_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++1y)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_CXX_STANDARD EQUAL 11)
|
||||||
|
check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
|
||||||
|
|
||||||
|
if (has_std_11_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++11)
|
||||||
|
elseif (has_std_0x_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++0x)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
|
||||||
|
|
||||||
|
# Check if variadic templates are working and not affected by GCC bug 39653:
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
template <class T, class ...Types>
|
||||||
|
struct S { typedef typename S<Types...>::type type; };
|
||||||
|
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
|
||||||
|
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
|
||||||
|
set (SUPPORTS_VARIADIC_TEMPLATES OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if initializer lists are supported.
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <initializer_list>
|
||||||
|
int main() {}" SUPPORTS_INITIALIZER_LIST)
|
||||||
|
if (NOT SUPPORTS_INITIALIZER_LIST)
|
||||||
|
set (SUPPORTS_INITIALIZER_LIST OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if enum bases are available
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
enum C : char {A};
|
||||||
|
int main() {}"
|
||||||
|
SUPPORTS_ENUM_BASE)
|
||||||
|
if (NOT SUPPORTS_ENUM_BASE)
|
||||||
|
set (SUPPORTS_ENUM_BASE OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if type traits are available
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <type_traits>
|
||||||
|
class C { void operator=(const C&); };
|
||||||
|
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
|
||||||
|
SUPPORTS_TYPE_TRAITS)
|
||||||
|
if (NOT SUPPORTS_TYPE_TRAITS)
|
||||||
|
set (SUPPORTS_TYPE_TRAITS OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if user-defined literals are available
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
void operator\"\" _udl(long double);
|
||||||
|
int main() {}"
|
||||||
|
SUPPORTS_USER_DEFINED_LITERALS)
|
||||||
|
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
|
||||||
|
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_FLAGS )
|
53
externals/fmt/support/compute-powers.py
vendored
Executable file
53
externals/fmt/support/compute-powers.py
vendored
Executable file
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print
|
||||||
|
# normalized (with most-significant bit equal to 1) significands in hexadecimal.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
min_exponent = -348
|
||||||
|
max_exponent = 340
|
||||||
|
step = 8
|
||||||
|
significand_size = 64
|
||||||
|
exp_offset = 2000
|
||||||
|
|
||||||
|
class fp:
|
||||||
|
pass
|
||||||
|
|
||||||
|
powers = []
|
||||||
|
for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)):
|
||||||
|
result = fp()
|
||||||
|
n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp
|
||||||
|
k = significand_size + 1
|
||||||
|
# Convert to binary and round.
|
||||||
|
binary = '{:b}'.format(n)
|
||||||
|
result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2
|
||||||
|
result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size
|
||||||
|
powers.append(result)
|
||||||
|
# Sanity check.
|
||||||
|
exp_offset10 = 400
|
||||||
|
actual = result.f * 10 ** exp_offset10
|
||||||
|
if result.e > 0:
|
||||||
|
actual *= 2 ** result.e
|
||||||
|
else:
|
||||||
|
for j in range(-result.e):
|
||||||
|
actual /= 2
|
||||||
|
expected = 10 ** (exp_offset10 + exp)
|
||||||
|
precision = len('{}'.format(expected)) - len('{}'.format(actual - expected))
|
||||||
|
if precision < 19:
|
||||||
|
print('low precision:', precision)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
print('Significands:', end='')
|
||||||
|
for i, fp in enumerate(powers):
|
||||||
|
if i % 3 == 0:
|
||||||
|
print(end='\n ')
|
||||||
|
print(' {:0<#16x}'.format(fp.f, ), end=',')
|
||||||
|
|
||||||
|
print('\n\nExponents:', end='')
|
||||||
|
for i, fp in enumerate(powers):
|
||||||
|
if i % 11 == 0:
|
||||||
|
print(end='\n ')
|
||||||
|
print(' {:5}'.format(fp.e), end=',')
|
||||||
|
|
||||||
|
print('\n\nMax exponent difference:',
|
||||||
|
max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:]))
|
27
externals/fmt/support/fmt.pro
vendored
Normal file
27
externals/fmt/support/fmt.pro
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Staticlib configuration for qmake builds
|
||||||
|
# For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc
|
||||||
|
# from compilation so it _MUST_ be called as qmake -nodepend
|
||||||
|
# A workaround is implemented below: a custom compiler is defined which does not track dependencies
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
|
||||||
|
TARGET = fmt
|
||||||
|
|
||||||
|
QMAKE_EXT_CPP = .cc
|
||||||
|
|
||||||
|
CONFIG = staticlib warn_on c++11
|
||||||
|
|
||||||
|
FMT_SOURCES = \
|
||||||
|
../src/format.cc \
|
||||||
|
../src/posix.cc
|
||||||
|
|
||||||
|
fmt.name = libfmt
|
||||||
|
fmt.input = FMT_SOURCES
|
||||||
|
fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
||||||
|
fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
||||||
|
fmt.depends = ${QMAKE_FILE_IN}
|
||||||
|
# QMAKE_RUN_CXX will not be expanded
|
||||||
|
fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN}
|
||||||
|
fmt.variable_out = OBJECTS
|
||||||
|
fmt.CONFIG = no_dependencies no_link
|
||||||
|
QMAKE_EXTRA_COMPILERS += fmt
|
17
externals/fmt/support/manage.py
vendored
17
externals/fmt/support/manage.py
vendored
|
@ -141,6 +141,7 @@ def update_site(env):
|
||||||
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
|
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
|
||||||
b.data = b.data.replace('std::FILE*', 'std::FILE *')
|
b.data = b.data.replace('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"" _')
|
||||||
# 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:
|
||||||
|
@ -151,7 +152,9 @@ def update_site(env):
|
||||||
if os.path.exists(html_dir):
|
if os.path.exists(html_dir):
|
||||||
shutil.rmtree(html_dir)
|
shutil.rmtree(html_dir)
|
||||||
include_dir = env.fmt_repo.dir
|
include_dir = env.fmt_repo.dir
|
||||||
if LooseVersion(version) >= LooseVersion('3.0.0'):
|
if LooseVersion(version) >= LooseVersion('5.0.0'):
|
||||||
|
include_dir = os.path.join(include_dir, 'include', 'fmt')
|
||||||
|
elif LooseVersion(version) >= LooseVersion('3.0.0'):
|
||||||
include_dir = os.path.join(include_dir, 'fmt')
|
include_dir = os.path.join(include_dir, 'fmt')
|
||||||
import build
|
import build
|
||||||
build.build_docs(version, doc_dir=target_doc_dir,
|
build.build_docs(version, doc_dir=target_doc_dir,
|
||||||
|
@ -242,12 +245,12 @@ def release(args):
|
||||||
id = r.json()['id']
|
id = r.json()['id']
|
||||||
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
|
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
|
||||||
package = 'fmt-{}.zip'.format(version)
|
package = 'fmt-{}.zip'.format(version)
|
||||||
with open('build/fmt/' + package, 'rb') as f:
|
r = requests.post(
|
||||||
r = requests.post(
|
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
||||||
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
headers={'Content-Type': 'application/zip'},
|
||||||
params=params, files={package: f})
|
params=params, data=open('build/fmt/' + package, 'rb'))
|
||||||
if r.status_code != 201:
|
if r.status_code != 201:
|
||||||
raise Exception('Failed to upload an asset ' + str(r))
|
raise Exception('Failed to upload an asset ' + str(r))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
24
externals/fmt/support/rst2md.py
vendored
24
externals/fmt/support/rst2md.py
vendored
|
@ -109,6 +109,30 @@ class Translator(nodes.NodeVisitor):
|
||||||
def depart_image(self, node):
|
def depart_image(self, node):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def write_row(self, row, widths):
|
||||||
|
for i, entry in enumerate(row):
|
||||||
|
text = entry[0][0] if len(entry) > 0 else ''
|
||||||
|
if i != 0:
|
||||||
|
self.write('|')
|
||||||
|
self.write('{:{}}'.format(text, widths[i]))
|
||||||
|
self.write('\n')
|
||||||
|
|
||||||
|
def visit_table(self, node):
|
||||||
|
table = node.children[0]
|
||||||
|
colspecs = table[:-2]
|
||||||
|
thead = table[-2]
|
||||||
|
tbody = table[-1]
|
||||||
|
widths = [int(cs['colwidth']) for cs in colspecs]
|
||||||
|
sep = '|'.join(['-' * w for w in widths]) + '\n'
|
||||||
|
self.write('\n\n')
|
||||||
|
self.write_row(thead[0], widths)
|
||||||
|
self.write(sep)
|
||||||
|
for row in tbody:
|
||||||
|
self.write_row(row, widths)
|
||||||
|
raise nodes.SkipChildren
|
||||||
|
|
||||||
|
def depart_table(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
class MDWriter(writers.Writer):
|
class MDWriter(writers.Writer):
|
||||||
"""GitHub-flavored markdown writer"""
|
"""GitHub-flavored markdown writer"""
|
||||||
|
|
27
externals/fmt/support/travis-build.py
vendored
27
externals/fmt/support/travis-build.py
vendored
|
@ -2,8 +2,8 @@
|
||||||
# Build the project on Travis CI.
|
# Build the project on Travis CI.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import errno, os, re, shutil, sys, tempfile, urllib
|
import errno, os, shutil, subprocess, sys, urllib
|
||||||
from subprocess import call, check_call, check_output, Popen, PIPE, STDOUT
|
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||||
|
|
||||||
def rmtree_if_exists(dir):
|
def rmtree_if_exists(dir):
|
||||||
try:
|
try:
|
||||||
|
@ -75,7 +75,7 @@ if build == 'Doc':
|
||||||
# Print the output without the key.
|
# Print the output without the key.
|
||||||
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
|
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise CalledProcessError(p.returncode, cmd)
|
raise subprocess.CalledProcessError(p.returncode, cmd)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
standard = os.environ['STANDARD']
|
standard = os.environ['STANDARD']
|
||||||
|
@ -85,16 +85,12 @@ test_build_dir = os.path.join(fmt_dir, "_build_test")
|
||||||
|
|
||||||
# Configure library.
|
# Configure library.
|
||||||
makedirs_if_not_exist(build_dir)
|
makedirs_if_not_exist(build_dir)
|
||||||
common_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
|
||||||
]
|
]
|
||||||
extra_cmake_flags = []
|
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] +
|
||||||
if standard != '0x':
|
cmake_flags, cwd=build_dir)
|
||||||
extra_cmake_flags = [
|
|
||||||
'-DCMAKE_CXX_FLAGS=-std=c++' + standard, '-DFMT_USE_CPP11=OFF'
|
|
||||||
]
|
|
||||||
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', fmt_dir] +
|
|
||||||
common_cmake_flags + extra_cmake_flags, cwd=build_dir)
|
|
||||||
|
|
||||||
# Build library.
|
# Build library.
|
||||||
check_call(['make', '-j4'], cwd=build_dir)
|
check_call(['make', '-j4'], cwd=build_dir)
|
||||||
|
@ -103,7 +99,7 @@ check_call(['make', '-j4'], cwd=build_dir)
|
||||||
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):
|
||||||
with open('Testing/Temporary/LastTest.log', 'r') as f:
|
with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f:
|
||||||
print(f.read())
|
print(f.read())
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
@ -112,7 +108,6 @@ check_call(['make', 'install'], cwd=build_dir)
|
||||||
|
|
||||||
# Test installation.
|
# Test installation.
|
||||||
makedirs_if_not_exist(test_build_dir)
|
makedirs_if_not_exist(test_build_dir)
|
||||||
check_call(['cmake', '-DCMAKE_CXX_FLAGS=-std=c++' + standard,
|
check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] +
|
||||||
os.path.join(fmt_dir, "test", "find-package-test")] +
|
cmake_flags, cwd=test_build_dir)
|
||||||
common_cmake_flags, cwd=test_build_dir)
|
|
||||||
check_call(['make', '-j4'], cwd=test_build_dir)
|
check_call(['make', '-j4'], cwd=test_build_dir)
|
||||||
|
|
77
externals/fmt/test/CMakeLists.txt
vendored
77
externals/fmt/test/CMakeLists.txt
vendored
|
@ -7,9 +7,8 @@
|
||||||
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
|
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
|
||||||
add_library(gmock STATIC
|
add_library(gmock STATIC
|
||||||
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
||||||
target_compile_options(gmock PUBLIC ${CPP11_FLAG})
|
|
||||||
target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
|
target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
|
||||||
target_include_directories(gmock PUBLIC .)
|
target_include_directories(gmock SYSTEM PUBLIC . gmock gtest)
|
||||||
|
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
if (Threads_FOUND)
|
if (Threads_FOUND)
|
||||||
|
@ -32,6 +31,10 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1)
|
target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# Silence MSVC tr1 deprecation warning in gmock.
|
||||||
|
target_compile_definitions(gmock
|
||||||
|
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=0)
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
# Build the actual library tests
|
# Build the actual library tests
|
||||||
|
|
||||||
|
@ -39,6 +42,7 @@ 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
|
target_compile_definitions(test-main PUBLIC
|
||||||
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
|
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
|
||||||
|
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
|
||||||
target_link_libraries(test-main gmock fmt)
|
target_link_libraries(test-main gmock fmt)
|
||||||
|
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
@ -53,7 +57,7 @@ endif ()
|
||||||
# Use less strict pedantic flags for the tests because GMock doesn't compile
|
# Use less strict pedantic flags for the tests because GMock doesn't compile
|
||||||
# cleanly with -pedantic and -std=c++98.
|
# cleanly with -pedantic and -std=c++98.
|
||||||
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
|
#set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
function(add_fmt_executable name)
|
function(add_fmt_executable name)
|
||||||
|
@ -69,41 +73,39 @@ function(add_fmt_test name)
|
||||||
add_fmt_executable(${name} ${name}.cc ${ARGN})
|
add_fmt_executable(${name} ${name}.cc ${ARGN})
|
||||||
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
|
target_compile_definitions(${name} PRIVATE
|
||||||
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
|
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
|
||||||
FMT_USE_ENUM_BASE=$<BOOL:${SUPPORTS_ENUM_BASE}>)
|
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 ()
|
||||||
|
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(container-test)
|
add_fmt_test(core-test)
|
||||||
add_fmt_test(gtest-extra-test)
|
add_fmt_test(gtest-extra-test)
|
||||||
add_fmt_test(format-test)
|
add_fmt_test(format-test mock-allocator.h)
|
||||||
add_fmt_test(format-impl-test)
|
add_fmt_test(format-impl-test)
|
||||||
add_fmt_test(ostream-test)
|
add_fmt_test(ostream-test)
|
||||||
add_fmt_test(printf-test)
|
add_fmt_test(printf-test)
|
||||||
add_fmt_test(string-test)
|
|
||||||
add_fmt_test(time-test)
|
add_fmt_test(time-test)
|
||||||
add_fmt_test(util-test mock-allocator.h)
|
|
||||||
add_fmt_test(macro-test)
|
|
||||||
add_fmt_test(custom-formatter-test)
|
add_fmt_test(custom-formatter-test)
|
||||||
|
add_fmt_test(ranges-test)
|
||||||
# Enable stricter options for one test to make sure that the header is free of
|
|
||||||
# warnings.
|
|
||||||
if (FMT_PEDANTIC AND MSVC)
|
|
||||||
target_compile_options(format-test PRIVATE /W4)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (HAVE_OPEN)
|
if (HAVE_OPEN)
|
||||||
add_fmt_executable(posix-mock-test
|
add_fmt_executable(posix-mock-test
|
||||||
posix-mock-test.cc ../fmt/format.cc ../fmt/printf.cc ${TEST_MAIN_SRC})
|
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
|
||||||
target_include_directories(posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_include_directories(
|
||||||
|
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
|
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)
|
||||||
|
if (FMT_PEDANTIC)
|
||||||
|
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
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(posix-test)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -111,27 +113,44 @@ endif ()
|
||||||
add_fmt_executable(header-only-test
|
add_fmt_executable(header-only-test
|
||||||
header-only-test.cc header-only-test2.cc test-main.cc)
|
header-only-test.cc header-only-test2.cc test-main.cc)
|
||||||
target_link_libraries(header-only-test gmock)
|
target_link_libraries(header-only-test gmock)
|
||||||
|
target_include_directories(header-only-test SYSTEM PUBLIC gtest gmock)
|
||||||
if (TARGET fmt-header-only)
|
if (TARGET fmt-header-only)
|
||||||
target_link_libraries(header-only-test fmt-header-only)
|
target_link_libraries(header-only-test fmt-header-only)
|
||||||
else ()
|
else ()
|
||||||
target_include_directories(header-only-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_include_directories(
|
||||||
|
header-only-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
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.
|
# Test that the library can be compiled with exceptions disabled.
|
||||||
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
|
||||||
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||||
add_library(noexception-test ../fmt/format.cc)
|
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
||||||
target_include_directories(noexception-test PRIVATE ${PROJECT_SOURCE_DIR})
|
|
||||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
||||||
|
add_library(noexception-test ../src/format.cc)
|
||||||
|
target_include_directories(
|
||||||
|
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
|
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||||
|
if (FMT_PEDANTIC)
|
||||||
|
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
endif ()
|
||||||
|
target_include_directories(noexception-test SYSTEM PUBLIC gtest gmock)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
|
||||||
|
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC)
|
||||||
# Test that the library compiles without windows.h.
|
# Test that the library compiles without windows.h.
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
add_library(no-windows-h-test ../fmt/format.cc)
|
add_library(no-windows-h-test ../src/format.cc)
|
||||||
target_include_directories(no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_include_directories(
|
||||||
|
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
|
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}
|
add_test(compile-test ${CMAKE_CTEST_COMMAND}
|
||||||
|
@ -142,7 +161,9 @@ if (FMT_PEDANTIC)
|
||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
"-DCPP11_FLAG=${CPP11_FLAG}"
|
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||||
|
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
|
||||||
|
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||||
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
|
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
|
||||||
|
|
||||||
# test if the targets are findable from the build directory
|
# test if the targets are findable from the build directory
|
||||||
|
@ -155,7 +176,9 @@ if (FMT_PEDANTIC)
|
||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
|
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||||
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
||||||
|
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
# test if the targets are findable when add_subdirectory is used
|
# test if the targets are findable when add_subdirectory is used
|
||||||
|
@ -168,5 +191,7 @@ if (FMT_PEDANTIC)
|
||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
|
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||||
|
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 2.8.12)
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
project(fmt-test)
|
project(fmt-test)
|
||||||
|
|
||||||
|
@ -6,8 +6,12 @@ add_subdirectory(../.. fmt)
|
||||||
|
|
||||||
add_executable(library-test "main.cc")
|
add_executable(library-test "main.cc")
|
||||||
target_link_libraries(library-test fmt::fmt)
|
target_link_libraries(library-test fmt::fmt)
|
||||||
|
target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
target_include_directories(library-test PUBLIC SYSTEM .)
|
||||||
|
|
||||||
if (TARGET fmt::fmt-header-only)
|
if (TARGET fmt::fmt-header-only)
|
||||||
add_executable(header-only-test "main.cc")
|
add_executable(header-only-test "main.cc")
|
||||||
target_link_libraries(header-only-test fmt::fmt-header-only)
|
target_link_libraries(header-only-test fmt::fmt-header-only)
|
||||||
|
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
target_include_directories(header-only-test PUBLIC SYSTEM .)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
39
externals/fmt/test/assert-test.cc
vendored
39
externals/fmt/test/assert-test.cc
vendored
|
@ -1,32 +1,12 @@
|
||||||
/*
|
// Formatting library for C++ - assertion tests
|
||||||
Assertion tests
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
Copyright (c) 2015, Victor Zverovich
|
#include "fmt/core.h"
|
||||||
All rights reserved.
|
#include "gtest.h"
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
#if GTEST_HAS_DEATH_TEST
|
#if GTEST_HAS_DEATH_TEST
|
||||||
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
|
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
|
||||||
|
@ -37,5 +17,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(AssertTest, Fail) {
|
TEST(AssertTest, Fail) {
|
||||||
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"), "don't panic!");
|
EXPECT_DEBUG_DEATH_IF_SUPPORTED(
|
||||||
|
FMT_ASSERT(false, "don't panic!"), "don't panic!");
|
||||||
}
|
}
|
||||||
|
|
41
externals/fmt/test/compile-test/CMakeLists.txt
vendored
41
externals/fmt/test/compile-test/CMakeLists.txt
vendored
|
@ -1,15 +1,17 @@
|
||||||
# Test if compile errors are produced where necessary.
|
# Test if compile errors are produced where necessary.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
include(CheckCXXSourceCompiles)
|
include(CheckCXXSourceCompiles)
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../..)
|
include(CheckCXXCompilerFlag)
|
||||||
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
|
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG} ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
|
||||||
function (generate_source result fragment)
|
function (generate_source result fragment)
|
||||||
set(${result} "
|
set(${result} "
|
||||||
#define FMT_HEADER_ONLY 1
|
#define FMT_HEADER_ONLY 1
|
||||||
#include \"fmt/posix.h\"
|
#include \"fmt/format.h\"
|
||||||
int main() {
|
int main() {
|
||||||
${fragment}
|
${fragment}
|
||||||
}
|
}
|
||||||
|
@ -47,24 +49,23 @@ endfunction ()
|
||||||
# check if the source file skeleton compiles
|
# check if the source file skeleton compiles
|
||||||
expect_compile("")
|
expect_compile("")
|
||||||
|
|
||||||
# MakeArg doesn't accept [const] volatile char *.
|
|
||||||
expect_compile_error("volatile char s[] = \"test\"; (fmt::internal::MakeArg<char>)(s);")
|
|
||||||
expect_compile_error("const volatile char s[] = \"test\"; (fmt::internal::MakeArg<char>)(s);")
|
|
||||||
|
|
||||||
# MakeArg<char> doesn't accept wchar_t.
|
|
||||||
expect_compile_error("fmt::internal::MakeValue<char>(L'a');")
|
|
||||||
expect_compile_error("fmt::internal::MakeValue<char>(L\"test\");")
|
|
||||||
|
|
||||||
# Writing a wide character to a character stream Writer is forbidden.
|
|
||||||
expect_compile_error("fmt::MemoryWriter() << L'a';")
|
|
||||||
expect_compile_error("fmt::MemoryWriter() << fmt::pad(\"abc\", 5, L' ');")
|
|
||||||
expect_compile_error("fmt::MemoryWriter() << fmt::pad(42, 5, L' ');")
|
|
||||||
|
|
||||||
# Formatting a wide character with a narrow format string is forbidden.
|
# Formatting a wide character with a narrow format string is forbidden.
|
||||||
expect_compile_error("fmt::format(\"{}\", L'a';")
|
expect_compile_error("fmt::format(\"{}\", L'a');")
|
||||||
|
|
||||||
expect_compile("FMT_STATIC_ASSERT(true, \"this should never happen\");")
|
# Formatting a wide string with a narrow format string is forbidden.
|
||||||
expect_compile_error("FMT_STATIC_ASSERT(0 > 1, \"oops\");")
|
expect_compile_error("fmt::format(\"{}\", L\"foo\");")
|
||||||
|
|
||||||
|
# Formatting a narrow string with a wide format string is forbidden because
|
||||||
|
# mixing UTF-8 with UTF-16/32 can result in an invalid output.
|
||||||
|
expect_compile_error("fmt::format(L\"{}\", \"foo\");")
|
||||||
|
|
||||||
|
# Formatting a wide string with a narrow format string is forbidden.
|
||||||
|
expect_compile_error("
|
||||||
|
struct S {
|
||||||
|
operator std::string() const { return std::string(); }
|
||||||
|
};
|
||||||
|
fmt::format(\"{}\", S());
|
||||||
|
")
|
||||||
|
|
||||||
# Make sure that compiler features detected in the header
|
# Make sure that compiler features detected in the header
|
||||||
# match the features detected in CMake.
|
# match the features detected in CMake.
|
||||||
|
|
94
externals/fmt/test/container-test.cc
vendored
94
externals/fmt/test/container-test.cc
vendored
|
@ -1,94 +0,0 @@
|
||||||
/*
|
|
||||||
Tests of container utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmt/container.h"
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
using fmt::internal::ContainerBuffer;
|
|
||||||
|
|
||||||
TEST(ContainerBufferTest, Empty) {
|
|
||||||
std::string data;
|
|
||||||
ContainerBuffer<std::string> buffer(data);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ContainerBufferTest, Reserve) {
|
|
||||||
std::string data;
|
|
||||||
ContainerBuffer<std::string> buffer(data);
|
|
||||||
std::size_t capacity = std::string().capacity() + 10;
|
|
||||||
buffer.reserve(capacity);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(capacity, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ContainerBufferTest, Resize) {
|
|
||||||
std::string data;
|
|
||||||
ContainerBuffer<std::string> buffer(data);
|
|
||||||
std::size_t size = std::string().capacity() + 10;
|
|
||||||
buffer.resize(size);
|
|
||||||
EXPECT_EQ(size, buffer.size());
|
|
||||||
EXPECT_EQ(size, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ContainerBufferTest, Append) {
|
|
||||||
std::string data("Why so");
|
|
||||||
const std::string serious(" serious");
|
|
||||||
ContainerBuffer<std::string> buffer(data);
|
|
||||||
buffer.append(serious.c_str(), serious.c_str() + serious.length());
|
|
||||||
EXPECT_EQ("Why so serious", data);
|
|
||||||
EXPECT_EQ(data.length(), buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BasicContainerWriterTest, String) {
|
|
||||||
std::string data;
|
|
||||||
fmt::BasicContainerWriter<std::string> out(data);
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
EXPECT_EQ("The answer is 42\n", data);
|
|
||||||
EXPECT_EQ(17u, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BasicContainerWriterTest, WString) {
|
|
||||||
std::wstring data;
|
|
||||||
fmt::BasicContainerWriter<std::wstring> out(data);
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
EXPECT_EQ(L"The answer is 42\n", data);
|
|
||||||
EXPECT_EQ(17u, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BasicContainerWriterTest, Vector) {
|
|
||||||
std::vector<char> data;
|
|
||||||
fmt::BasicContainerWriter<std::vector<char> > out(data);
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
EXPECT_EQ(17u, data.size());
|
|
||||||
EXPECT_EQ(out.size(), data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BasicContainerWriterTest, StringAppend) {
|
|
||||||
std::string data("The");
|
|
||||||
fmt::BasicContainerWriter<std::string> out(data);
|
|
||||||
EXPECT_EQ(3u, data.size());
|
|
||||||
EXPECT_EQ(3u, out.size());
|
|
||||||
out << " answer is " << 42 << "\n";
|
|
||||||
EXPECT_EQ("The answer is 42\n", data);
|
|
||||||
EXPECT_EQ(17u, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BasicContainerWriterTest, VectorAppend) {
|
|
||||||
std::vector<char> data;
|
|
||||||
data.push_back('T');
|
|
||||||
data.push_back('h');
|
|
||||||
data.push_back('e');
|
|
||||||
fmt::BasicContainerWriter<std::vector<char> > out(data);
|
|
||||||
EXPECT_EQ(3u, data.size());
|
|
||||||
EXPECT_EQ(3u, out.size());
|
|
||||||
out << " answer is " << 42 << "\n";
|
|
||||||
EXPECT_EQ(17u, data.size());
|
|
||||||
EXPECT_EQ(17u, out.size());
|
|
||||||
}
|
|
460
externals/fmt/test/core-test.cc
vendored
Normal file
460
externals/fmt/test/core-test.cc
vendored
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
// Formatting library for C++ - core tests
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "test-assert.h"
|
||||||
|
|
||||||
|
#include "gmock.h"
|
||||||
|
|
||||||
|
// Check if fmt/core.h compiles with windows.h included before it.
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
|
#undef min
|
||||||
|
#undef max
|
||||||
|
|
||||||
|
using fmt::basic_format_arg;
|
||||||
|
using fmt::internal::basic_buffer;
|
||||||
|
using fmt::internal::value;
|
||||||
|
using fmt::string_view;
|
||||||
|
|
||||||
|
using testing::_;
|
||||||
|
using testing::StrictMock;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct test_struct {};
|
||||||
|
|
||||||
|
template <typename Context, typename T>
|
||||||
|
basic_format_arg<Context> make_arg(const T &value) {
|
||||||
|
return fmt::internal::make_arg<Context>(value);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<test_struct, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::back_insert_iterator<basic_buffer<Char>> iterator;
|
||||||
|
|
||||||
|
auto format(test_struct, basic_format_context<iterator, char> &ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
const Char *test = "test";
|
||||||
|
return std::copy_n(test, std::strlen(test), ctx.out());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
TEST(BufferTest, Noncopyable) {
|
||||||
|
EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char> >::value);
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
// std::is_copy_assignable is broken in MSVC2013.
|
||||||
|
EXPECT_FALSE(std::is_copy_assignable<basic_buffer<char> >::value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BufferTest, Nonmoveable) {
|
||||||
|
EXPECT_FALSE(std::is_move_constructible<basic_buffer<char> >::value);
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
// std::is_move_assignable is broken in MSVC2013.
|
||||||
|
EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// A test buffer with a dummy grow method.
|
||||||
|
template <typename T>
|
||||||
|
struct test_buffer : basic_buffer<T> {
|
||||||
|
void grow(std::size_t capacity) { this->set(nullptr, capacity); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct mock_buffer : basic_buffer<T> {
|
||||||
|
MOCK_METHOD1(do_grow, void (std::size_t capacity));
|
||||||
|
|
||||||
|
void grow(std::size_t capacity) {
|
||||||
|
this->set(this->data(), capacity);
|
||||||
|
do_grow(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_buffer() {}
|
||||||
|
mock_buffer(T *data) { this->set(data, 0); }
|
||||||
|
mock_buffer(T *data, std::size_t capacity) { this->set(data, capacity); }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(BufferTest, Ctor) {
|
||||||
|
{
|
||||||
|
mock_buffer<int> buffer;
|
||||||
|
EXPECT_EQ(nullptr, &buffer[0]);
|
||||||
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||||
|
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int dummy;
|
||||||
|
mock_buffer<int> buffer(&dummy);
|
||||||
|
EXPECT_EQ(&dummy, &buffer[0]);
|
||||||
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||||
|
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int dummy;
|
||||||
|
std::size_t capacity = std::numeric_limits<std::size_t>::max();
|
||||||
|
mock_buffer<int> buffer(&dummy, capacity);
|
||||||
|
EXPECT_EQ(&dummy, &buffer[0]);
|
||||||
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||||
|
EXPECT_EQ(capacity, buffer.capacity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dying_buffer : test_buffer<int> {
|
||||||
|
MOCK_METHOD0(die, void());
|
||||||
|
~dying_buffer() { die(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(BufferTest, VirtualDtor) {
|
||||||
|
typedef StrictMock<dying_buffer> stict_mock_buffer;
|
||||||
|
stict_mock_buffer *mock_buffer = new stict_mock_buffer();
|
||||||
|
EXPECT_CALL(*mock_buffer, die());
|
||||||
|
basic_buffer<int> *buffer = mock_buffer;
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BufferTest, Access) {
|
||||||
|
char data[10];
|
||||||
|
mock_buffer<char> buffer(data, sizeof(data));
|
||||||
|
buffer[0] = 11;
|
||||||
|
EXPECT_EQ(11, buffer[0]);
|
||||||
|
buffer[3] = 42;
|
||||||
|
EXPECT_EQ(42, *(&buffer[0] + 3));
|
||||||
|
const basic_buffer<char> &const_buffer = buffer;
|
||||||
|
EXPECT_EQ(42, const_buffer[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BufferTest, Resize) {
|
||||||
|
char data[123];
|
||||||
|
mock_buffer<char> buffer(data, sizeof(data));
|
||||||
|
buffer[10] = 42;
|
||||||
|
EXPECT_EQ(42, buffer[10]);
|
||||||
|
buffer.resize(20);
|
||||||
|
EXPECT_EQ(20u, buffer.size());
|
||||||
|
EXPECT_EQ(123u, buffer.capacity());
|
||||||
|
EXPECT_EQ(42, buffer[10]);
|
||||||
|
buffer.resize(5);
|
||||||
|
EXPECT_EQ(5u, buffer.size());
|
||||||
|
EXPECT_EQ(123u, buffer.capacity());
|
||||||
|
EXPECT_EQ(42, buffer[10]);
|
||||||
|
// Check if resize calls grow.
|
||||||
|
EXPECT_CALL(buffer, do_grow(124));
|
||||||
|
buffer.resize(124);
|
||||||
|
EXPECT_CALL(buffer, do_grow(200));
|
||||||
|
buffer.resize(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BufferTest, Clear) {
|
||||||
|
test_buffer<char> buffer;
|
||||||
|
buffer.resize(20);
|
||||||
|
buffer.resize(0);
|
||||||
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||||
|
EXPECT_EQ(20u, buffer.capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BufferTest, Append) {
|
||||||
|
char data[15];
|
||||||
|
mock_buffer<char> buffer(data, 10);
|
||||||
|
const char *test = "test";
|
||||||
|
buffer.append(test, test + 5);
|
||||||
|
EXPECT_STREQ(test, &buffer[0]);
|
||||||
|
EXPECT_EQ(5u, buffer.size());
|
||||||
|
buffer.resize(10);
|
||||||
|
EXPECT_CALL(buffer, do_grow(12));
|
||||||
|
buffer.append(test, test + 2);
|
||||||
|
EXPECT_EQ('t', buffer[10]);
|
||||||
|
EXPECT_EQ('e', buffer[11]);
|
||||||
|
EXPECT_EQ(12u, buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BufferTest, AppendAllocatesEnoughStorage) {
|
||||||
|
char data[19];
|
||||||
|
mock_buffer<char> buffer(data, 10);
|
||||||
|
const char *test = "abcdefgh";
|
||||||
|
buffer.resize(10);
|
||||||
|
EXPECT_CALL(buffer, do_grow(19));
|
||||||
|
buffer.append(test, test + 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgTest, FormatArgs) {
|
||||||
|
fmt::format_args args;
|
||||||
|
EXPECT_FALSE(args.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct custom_context {
|
||||||
|
typedef char char_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct formatter_type {
|
||||||
|
struct type {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *format(const T &, custom_context& ctx) {
|
||||||
|
ctx.called = true;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool called;
|
||||||
|
|
||||||
|
fmt::parse_context parse_context() { return fmt::parse_context(""); }
|
||||||
|
void advance_to(const char *) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(ArgTest, MakeValueWithCustomContext) {
|
||||||
|
test_struct t;
|
||||||
|
fmt::internal::value<custom_context> arg =
|
||||||
|
fmt::internal::make_value<custom_context>(t);
|
||||||
|
custom_context ctx = {false};
|
||||||
|
arg.custom.format(&t, ctx);
|
||||||
|
EXPECT_TRUE(ctx.called);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
template <typename Char>
|
||||||
|
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
|
||||||
|
return lhs.value == rhs.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
// Use a unique result type to make sure that there are no undesirable
|
||||||
|
// conversions.
|
||||||
|
struct test_result {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct mock_visitor {
|
||||||
|
template <typename U>
|
||||||
|
struct result { typedef test_result type; };
|
||||||
|
|
||||||
|
mock_visitor() {
|
||||||
|
ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_METHOD1_T(visit, test_result (T value));
|
||||||
|
MOCK_METHOD0_T(unexpected, void ());
|
||||||
|
|
||||||
|
test_result operator()(T value) { return visit(value); }
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
test_result operator()(U) {
|
||||||
|
unexpected();
|
||||||
|
return test_result();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct visit_type { typedef T Type; };
|
||||||
|
|
||||||
|
#define VISIT_TYPE(Type_, visit_type_) \
|
||||||
|
template <> \
|
||||||
|
struct visit_type<Type_> { typedef visit_type_ Type; }
|
||||||
|
|
||||||
|
VISIT_TYPE(signed char, int);
|
||||||
|
VISIT_TYPE(unsigned char, unsigned);
|
||||||
|
VISIT_TYPE(short, int);
|
||||||
|
VISIT_TYPE(unsigned short, unsigned);
|
||||||
|
|
||||||
|
#if LONG_MAX == INT_MAX
|
||||||
|
VISIT_TYPE(long, int);
|
||||||
|
VISIT_TYPE(unsigned long, unsigned);
|
||||||
|
#else
|
||||||
|
VISIT_TYPE(long, long long);
|
||||||
|
VISIT_TYPE(unsigned long, unsigned long long);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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<basic_buffer<Char>> iterator; \
|
||||||
|
fmt::visit(visitor, \
|
||||||
|
make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECK_ARG(value, typename_) { \
|
||||||
|
typedef decltype(value) value_type; \
|
||||||
|
typename_ visit_type<value_type>::Type expected = value; \
|
||||||
|
CHECK_ARG_(char, expected, value) \
|
||||||
|
CHECK_ARG_(wchar_t, expected, value) \
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class NumericArgTest : public testing::Test {};
|
||||||
|
|
||||||
|
typedef ::testing::Types<
|
||||||
|
bool, signed char, unsigned char, signed, unsigned short,
|
||||||
|
int, unsigned, long, unsigned long, long long, unsigned long long,
|
||||||
|
float, double, long double> Types;
|
||||||
|
TYPED_TEST_CASE(NumericArgTest, Types);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
|
||||||
|
return static_cast<T>(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, T>::type
|
||||||
|
test_value() {
|
||||||
|
return static_cast<T>(4.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST(NumericArgTest, MakeAndVisit) {
|
||||||
|
CHECK_ARG(test_value<TypeParam>(), typename);
|
||||||
|
CHECK_ARG(std::numeric_limits<TypeParam>::min(), typename);
|
||||||
|
CHECK_ARG(std::numeric_limits<TypeParam>::max(), typename);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgTest, CharArg) {
|
||||||
|
CHECK_ARG_(char, 'a', 'a');
|
||||||
|
CHECK_ARG_(wchar_t, L'a', 'a');
|
||||||
|
CHECK_ARG_(wchar_t, L'a', L'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgTest, StringArg) {
|
||||||
|
char str_data[] = "test";
|
||||||
|
char *str = str_data;
|
||||||
|
const char *cstr = str;
|
||||||
|
CHECK_ARG_(char, cstr, str);
|
||||||
|
|
||||||
|
string_view sref(str);
|
||||||
|
CHECK_ARG_(char, sref, std::string(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgTest, WStringArg) {
|
||||||
|
wchar_t str_data[] = L"test";
|
||||||
|
wchar_t *str = str_data;
|
||||||
|
const wchar_t *cstr = str;
|
||||||
|
|
||||||
|
fmt::wstring_view sref(str);
|
||||||
|
CHECK_ARG_(wchar_t, cstr, str);
|
||||||
|
CHECK_ARG_(wchar_t, cstr, cstr);
|
||||||
|
CHECK_ARG_(wchar_t, sref, std::wstring(str));
|
||||||
|
CHECK_ARG_(wchar_t, sref, fmt::wstring_view(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgTest, PointerArg) {
|
||||||
|
void *p = nullptr;
|
||||||
|
const void *cp = nullptr;
|
||||||
|
CHECK_ARG_(char, cp, p);
|
||||||
|
CHECK_ARG_(wchar_t, cp, p);
|
||||||
|
CHECK_ARG(cp, );
|
||||||
|
}
|
||||||
|
|
||||||
|
struct check_custom {
|
||||||
|
test_result operator()(
|
||||||
|
fmt::basic_format_arg<fmt::format_context>::handle h) const {
|
||||||
|
struct test_buffer : fmt::internal::basic_buffer<char> {
|
||||||
|
char data[10];
|
||||||
|
test_buffer() : basic_buffer(data, 0, 10) {}
|
||||||
|
void grow(std::size_t) {}
|
||||||
|
} buffer;
|
||||||
|
fmt::internal::basic_buffer<char> &base = buffer;
|
||||||
|
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
|
||||||
|
h.format(ctx);
|
||||||
|
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
|
||||||
|
return test_result();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(ArgTest, CustomArg) {
|
||||||
|
test_struct test;
|
||||||
|
typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
|
||||||
|
visitor;
|
||||||
|
testing::StrictMock<visitor> v;
|
||||||
|
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom()));
|
||||||
|
fmt::visit(v, make_arg<fmt::format_context>(test));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgTest, VisitInvalidArg) {
|
||||||
|
testing::StrictMock< mock_visitor<fmt::monostate> > visitor;
|
||||||
|
EXPECT_CALL(visitor, visit(_));
|
||||||
|
fmt::basic_format_arg<fmt::format_context> arg;
|
||||||
|
visit(visitor, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringViewTest, Length) {
|
||||||
|
// Test that StringRef::size() returns string length, not buffer size.
|
||||||
|
char str[100] = "some string";
|
||||||
|
EXPECT_EQ(std::strlen(str), string_view(str).size());
|
||||||
|
EXPECT_LT(std::strlen(str), sizeof(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check string_view's comparison operator.
|
||||||
|
template <template <typename> class Op>
|
||||||
|
void check_op() {
|
||||||
|
const char *inputs[] = {"foo", "fop", "fo"};
|
||||||
|
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
|
||||||
|
for (std::size_t i = 0; i < num_inputs; ++i) {
|
||||||
|
for (std::size_t j = 0; j < num_inputs; ++j) {
|
||||||
|
string_view lhs(inputs[i]), rhs(inputs[j]);
|
||||||
|
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringViewTest, Compare) {
|
||||||
|
EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
|
||||||
|
EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
|
||||||
|
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
|
||||||
|
EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
|
||||||
|
EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
|
||||||
|
check_op<std::equal_to>();
|
||||||
|
check_op<std::not_equal_to>();
|
||||||
|
check_op<std::less>();
|
||||||
|
check_op<std::less_equal>();
|
||||||
|
check_op<std::greater>();
|
||||||
|
check_op<std::greater_equal>();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum basic_enum {};
|
||||||
|
|
||||||
|
TEST(CoreTest, ConvertToInt) {
|
||||||
|
EXPECT_FALSE((fmt::convert_to_int<char, char>::value));
|
||||||
|
EXPECT_FALSE((fmt::convert_to_int<const char *, char>::value));
|
||||||
|
EXPECT_TRUE((fmt::convert_to_int<basic_enum, char>::value));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum enum_with_underlying_type : char {};
|
||||||
|
|
||||||
|
TEST(CoreTest, IsEnumConvertibleToInt) {
|
||||||
|
EXPECT_TRUE((fmt::convert_to_int<enum_with_underlying_type, char>::value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CoreTest, Format) {
|
||||||
|
// This should work without including fmt/format.h.
|
||||||
|
#ifdef FMT_FORMAT_H_
|
||||||
|
# error fmt/format.h must not be included in the core test
|
||||||
|
#endif
|
||||||
|
EXPECT_EQ(fmt::format("{}", 42), "42");
|
||||||
|
}
|
82
externals/fmt/test/custom-formatter-test.cc
vendored
82
externals/fmt/test/custom-formatter-test.cc
vendored
|
@ -1,68 +1,52 @@
|
||||||
/*
|
// Formatting library for C++ - custom argument formatter tests
|
||||||
Custom argument formatter tests
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
Copyright (c) 2016, Victor Zverovich
|
#include "fmt/format.h"
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmt/printf.h"
|
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
using fmt::BasicPrintfArgFormatter;
|
// MSVC 2013 is known to be broken.
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
|
|
||||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
// A custom argument formatter that doesn't print `-` for floating-point values
|
||||||
// rounded to 0.
|
// rounded to 0.
|
||||||
class CustomArgFormatter
|
class custom_arg_formatter :
|
||||||
: public fmt::BasicArgFormatter<CustomArgFormatter, char> {
|
public fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>> {
|
||||||
public:
|
public:
|
||||||
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
|
typedef fmt::back_insert_range<fmt::internal::buffer> range;
|
||||||
fmt::FormatSpec &s, const char *fmt)
|
typedef fmt::arg_formatter<range> base;
|
||||||
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
|
|
||||||
|
|
||||||
void visit_double(double value) {
|
custom_arg_formatter(
|
||||||
if (round(value * pow(10, spec().precision())) == 0)
|
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
|
||||||
|
: base(ctx, s) {}
|
||||||
|
|
||||||
|
using base::operator();
|
||||||
|
|
||||||
|
iterator operator()(double value) {
|
||||||
|
// Comparing a float to 0.0 is safe
|
||||||
|
if (round(value * pow(10, spec()->precision())) == 0.0)
|
||||||
value = 0;
|
value = 0;
|
||||||
fmt::BasicArgFormatter<CustomArgFormatter, char>::visit_double(value);
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||||
// rounded to 0.
|
fmt::memory_buffer buffer;
|
||||||
class CustomPrintfArgFormatter :
|
// Pass custom argument formatter as a template arg to vwrite.
|
||||||
public BasicPrintfArgFormatter<CustomPrintfArgFormatter, char> {
|
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args);
|
||||||
public:
|
return std::string(buffer.data(), buffer.size());
|
||||||
typedef BasicPrintfArgFormatter<CustomPrintfArgFormatter, char> Base;
|
|
||||||
|
|
||||||
CustomPrintfArgFormatter(fmt::BasicWriter<char> &w, fmt::FormatSpec &spec)
|
|
||||||
: Base(w, spec) {}
|
|
||||||
|
|
||||||
void visit_double(double value) {
|
|
||||||
if (round(value * pow(10, spec().precision())) == 0)
|
|
||||||
value = 0;
|
|
||||||
Base::visit_double(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string custom_format(const char *format_str, fmt::ArgList args) {
|
|
||||||
fmt::MemoryWriter writer;
|
|
||||||
// Pass custom argument formatter as a template arg to BasicFormatter.
|
|
||||||
fmt::BasicFormatter<char, CustomArgFormatter> formatter(args, writer);
|
|
||||||
formatter.format(format_str);
|
|
||||||
return writer.str();
|
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(std::string, custom_format, const char *)
|
|
||||||
|
|
||||||
std::string custom_sprintf(const char* format_str, fmt::ArgList args){
|
template <typename... Args>
|
||||||
fmt::MemoryWriter writer;
|
std::string custom_format(const char *format_str, const Args & ... args) {
|
||||||
fmt::PrintfFormatter<char, CustomPrintfArgFormatter> formatter(args, writer);
|
auto va = fmt::make_format_args(args...);
|
||||||
formatter.format(format_str);
|
return custom_vformat(format_str, va);
|
||||||
return writer.str();
|
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(std::string, custom_sprintf, const char*);
|
|
||||||
|
|
||||||
TEST(CustomFormatterTest, Format) {
|
TEST(CustomFormatterTest, Format) {
|
||||||
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
||||||
EXPECT_EQ("0.00", custom_sprintf("%.2f", -.00001));
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 2.8.12)
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
project(fmt-test)
|
project(fmt-test)
|
||||||
|
|
||||||
|
@ -6,8 +6,12 @@ find_package(FMT REQUIRED)
|
||||||
|
|
||||||
add_executable(library-test main.cc)
|
add_executable(library-test main.cc)
|
||||||
target_link_libraries(library-test fmt::fmt)
|
target_link_libraries(library-test fmt::fmt)
|
||||||
|
target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
target_include_directories(library-test PUBLIC SYSTEM .)
|
||||||
|
|
||||||
if (TARGET fmt::fmt-header-only)
|
if (TARGET fmt::fmt-header-only)
|
||||||
add_executable(header-only-test main.cc)
|
add_executable(header-only-test main.cc)
|
||||||
target_link_libraries(header-only-test fmt::fmt-header-only)
|
target_link_libraries(header-only-test fmt::fmt-header-only)
|
||||||
|
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
target_include_directories(header-only-test PUBLIC SYSTEM .)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
187
externals/fmt/test/format-impl-test.cc
vendored
187
externals/fmt/test/format-impl-test.cc
vendored
|
@ -1,69 +1,133 @@
|
||||||
/*
|
// Formatting library for C++ - formatting library implementation tests
|
||||||
Formatting library implementation tests.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define FMT_NOEXCEPT
|
#define FMT_NOEXCEPT
|
||||||
#undef FMT_SHARED
|
#undef FMT_SHARED
|
||||||
#include "test-assert.h"
|
#include "test-assert.h"
|
||||||
|
|
||||||
// Include *.cc instead of *.h to test implementation-specific stuff.
|
// Include format.cc instead of format.h to test implementation.
|
||||||
#include "fmt/format.cc"
|
#include "../src/format.cc"
|
||||||
#include "fmt/printf.cc"
|
#include "fmt/color.h"
|
||||||
|
#include "fmt/printf.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#undef min
|
#undef min
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
|
using fmt::internal::fp;
|
||||||
|
|
||||||
|
template <bool is_iec559>
|
||||||
|
void test_construct_from_double() {
|
||||||
|
fmt::print("warning: double is not IEC559, skipping FP tests\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void test_construct_from_double<true>() {
|
||||||
|
auto v = fp(1.23);
|
||||||
|
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
|
||||||
|
EXPECT_EQ(v.e, -52);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, ConstructFromDouble) {
|
||||||
|
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, Normalize) {
|
||||||
|
auto v = fp(0xbeef, 42);
|
||||||
|
v.normalize();
|
||||||
|
EXPECT_EQ(0xbeef000000000000, v.f);
|
||||||
|
EXPECT_EQ(-6, v.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, ComputeBoundariesSubnormal) {
|
||||||
|
auto v = fp(0xbeef, 42);
|
||||||
|
fp lower, upper;
|
||||||
|
v.compute_boundaries(lower, upper);
|
||||||
|
EXPECT_EQ(0xbeee800000000000, lower.f);
|
||||||
|
EXPECT_EQ(-6, lower.e);
|
||||||
|
EXPECT_EQ(0xbeef800000000000, upper.f);
|
||||||
|
EXPECT_EQ(-6, upper.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, ComputeBoundaries) {
|
||||||
|
auto v = fp(0x10000000000000, 42);
|
||||||
|
fp lower, upper;
|
||||||
|
v.compute_boundaries(lower, upper);
|
||||||
|
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
|
||||||
|
EXPECT_EQ(31, lower.e);
|
||||||
|
EXPECT_EQ(0x8000000000000400, upper.f);
|
||||||
|
EXPECT_EQ(31, upper.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, Subtract) {
|
||||||
|
auto v = fp(123, 1) - fp(102, 1);
|
||||||
|
EXPECT_EQ(v.f, 21u);
|
||||||
|
EXPECT_EQ(v.e, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, Multiply) {
|
||||||
|
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
||||||
|
EXPECT_EQ(v.f, 123u * 56u);
|
||||||
|
EXPECT_EQ(v.e, 4 + 7 + 64);
|
||||||
|
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
|
||||||
|
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
|
||||||
|
EXPECT_EQ(v.e, 4 + 8 + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, GetCachedPower) {
|
||||||
|
typedef std::numeric_limits<double> limits;
|
||||||
|
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
|
||||||
|
int dec_exp = 0;
|
||||||
|
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
|
||||||
|
EXPECT_LE(exp, fp.e);
|
||||||
|
int dec_exp_step = 8;
|
||||||
|
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
|
||||||
|
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ValueExtractor: fmt::internal::function<T> {
|
||||||
|
T operator()(T value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
FMT_NORETURN T operator()(U) {
|
||||||
|
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TEST(FormatTest, ArgConverter) {
|
TEST(FormatTest, ArgConverter) {
|
||||||
using fmt::internal::Arg;
|
long long value = std::numeric_limits<long long>::max();
|
||||||
Arg arg = Arg();
|
auto arg = fmt::internal::make_arg<fmt::format_context>(value);
|
||||||
arg.type = Arg::LONG_LONG;
|
visit(fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
|
||||||
arg.long_long_value = std::numeric_limits<fmt::LongLong>::max();
|
arg);
|
||||||
fmt::internal::ArgConverter<fmt::LongLong>(arg, 'd').visit(arg);
|
EXPECT_EQ(value, visit(ValueExtractor<long long>(), arg));
|
||||||
EXPECT_EQ(Arg::LONG_LONG, arg.type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, FormatNegativeNaN) {
|
TEST(FormatTest, FormatNegativeNaN) {
|
||||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||||
if (fmt::internal::FPUtil::isnegative(-nan))
|
if (fmt::internal::fputil::isnegative(-nan))
|
||||||
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
||||||
else
|
else
|
||||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, StrError) {
|
TEST(FormatTest, StrError) {
|
||||||
char *message = 0;
|
char *message = nullptr;
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[BUFFER_SIZE];
|
||||||
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = 0, 0), "invalid buffer");
|
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = nullptr, 0), "invalid buffer");
|
||||||
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
|
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
|
||||||
"invalid buffer");
|
"invalid buffer");
|
||||||
buffer[0] = 'x';
|
buffer[0] = 'x';
|
||||||
|
@ -95,33 +159,44 @@ TEST(FormatTest, StrError) {
|
||||||
TEST(FormatTest, FormatErrorCode) {
|
TEST(FormatTest, FormatErrorCode) {
|
||||||
std::string msg = "error 42", sep = ": ";
|
std::string msg = "error 42", sep = ": ";
|
||||||
{
|
{
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
w << "garbage";
|
format_to(buffer, "garbage");
|
||||||
fmt::format_error_code(w, 42, "test");
|
fmt::format_error_code(buffer, 42, "test");
|
||||||
EXPECT_EQ("test: " + msg, w.str());
|
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
std::string prefix(
|
std::string prefix(
|
||||||
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
|
fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
|
||||||
fmt::format_error_code(w, 42, prefix);
|
fmt::format_error_code(buffer, 42, prefix);
|
||||||
EXPECT_EQ(msg, w.str());
|
EXPECT_EQ(msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
int codes[] = {42, -1};
|
int codes[] = {42, -1};
|
||||||
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
|
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
|
||||||
// Test maximum buffer size.
|
// Test maximum buffer size.
|
||||||
msg = fmt::format("error {}", codes[i]);
|
msg = fmt::format("error {}", codes[i]);
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
std::string prefix(
|
std::string prefix(
|
||||||
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
|
fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
|
||||||
fmt::format_error_code(w, codes[i], prefix);
|
fmt::format_error_code(buffer, codes[i], prefix);
|
||||||
EXPECT_EQ(prefix + sep + msg, w.str());
|
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
|
||||||
std::size_t size = fmt::internal::INLINE_BUFFER_SIZE;
|
std::size_t size = fmt::inline_buffer_size;
|
||||||
EXPECT_EQ(size, w.size());
|
EXPECT_EQ(size, buffer.size());
|
||||||
w.clear();
|
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(w, codes[i], prefix);
|
fmt::format_error_code(buffer, codes[i], prefix);
|
||||||
EXPECT_EQ(msg, w.str());
|
EXPECT_EQ(msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FormatTest, CountCodePoints) {
|
||||||
|
EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ColorsTest, Colors) {
|
||||||
|
EXPECT_WRITE(stdout, fmt::print(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(fmt::color::blue, "blue"),
|
||||||
|
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
||||||
|
}
|
||||||
|
|
1958
externals/fmt/test/format-test.cc
vendored
1958
externals/fmt/test/format-test.cc
vendored
File diff suppressed because it is too large
Load diff
2
externals/fmt/test/gmock-gtest-all.cc
vendored
2
externals/fmt/test/gmock-gtest-all.cc
vendored
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
// This line ensures that gtest.h can be compiled on its own, even
|
// This line ensures that gtest.h can be compiled on its own, even
|
||||||
// when it's fused.
|
// when it's fused.
|
||||||
#include "gtest/gtest.h"
|
#include "gtest.h"
|
||||||
|
|
||||||
// The following lines pull in the real gtest *.cc files.
|
// The following lines pull in the real gtest *.cc files.
|
||||||
// Copyright 2005, Google Inc.
|
// Copyright 2005, Google Inc.
|
||||||
|
|
5
externals/fmt/test/gmock/gmock.h
vendored
5
externals/fmt/test/gmock/gmock.h
vendored
|
@ -235,7 +235,7 @@
|
||||||
|
|
||||||
// Most of the types needed for porting Google Mock are also required
|
// Most of the types needed for porting Google Mock are also required
|
||||||
// for Google Test and are defined in gtest-port.h.
|
// for Google Test and are defined in gtest-port.h.
|
||||||
#include "gtest/gtest.h"
|
#include "gtest.h"
|
||||||
|
|
||||||
// To avoid conditional compilation everywhere, we make it
|
// To avoid conditional compilation everywhere, we make it
|
||||||
// gmock-port.h's responsibility to #include the header implementing
|
// gmock-port.h's responsibility to #include the header implementing
|
||||||
|
@ -825,8 +825,9 @@ template <typename T> struct DecayArray<T[]> {
|
||||||
// crashes).
|
// crashes).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T Invalid() {
|
inline T Invalid() {
|
||||||
|
void *p = NULL;
|
||||||
return const_cast<typename remove_reference<T>::type&>(
|
return const_cast<typename remove_reference<T>::type&>(
|
||||||
*static_cast<volatile typename remove_reference<T>::type*>(NULL));
|
*static_cast<volatile typename remove_reference<T>::type*>(p));
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline void Invalid<void>() {}
|
inline void Invalid<void>() {}
|
||||||
|
|
108
externals/fmt/test/gtest-extra-test.cc
vendored
108
externals/fmt/test/gtest-extra-test.cc
vendored
|
@ -1,29 +1,9 @@
|
||||||
/*
|
// Formatting library for C++ - tests of custom Google Test assertions
|
||||||
Tests of custom Google Test assertions.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
|
@ -78,7 +58,7 @@ void throw_exception() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_system_error() {
|
void throw_system_error() {
|
||||||
throw fmt::SystemError(EDOM, "test");
|
throw fmt::system_error(EDOM, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
|
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
|
||||||
|
@ -207,11 +187,11 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
|
||||||
// EXPECT_SYSTEM_ERROR macro.
|
// EXPECT_SYSTEM_ERROR macro.
|
||||||
TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
EXPECT_SYSTEM_ERROR(throw fmt::SystemError(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(EXPECT_SYSTEM_ERROR(
|
||||||
throw fmt::SystemError(EDOM, "aaa"), EDOM, "bbb"), "");
|
throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
||||||
|
@ -268,10 +248,10 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"),
|
EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"),
|
||||||
"Expected: throw_exception() throws an exception of "
|
"Expected: throw_exception() throws an exception of "
|
||||||
"type fmt::SystemError.\n Actual: it throws a different type.");
|
"type fmt::system_error.\n Actual: it throws a different type.");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"),
|
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"),
|
||||||
"Expected: do_nothing() throws an exception of type fmt::SystemError.\n"
|
"Expected: do_nothing() throws an exception of type fmt::system_error.\n"
|
||||||
" Actual: it throws nothing.");
|
" Actual: it throws nothing.");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"),
|
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"),
|
||||||
|
@ -319,27 +299,27 @@ TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatSystemError) {
|
TEST(UtilTest, FormatSystemError) {
|
||||||
fmt::MemoryWriter out;
|
fmt::memory_buffer out;
|
||||||
fmt::format_system_error(out, EDOM, "test message");
|
fmt::format_system_error(out, EDOM, "test message");
|
||||||
EXPECT_EQ(out.str(), 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_FILE_DESCRIPTORS
|
||||||
|
|
||||||
using fmt::BufferedFile;
|
using fmt::buffered_file;
|
||||||
using fmt::ErrorCode;
|
using fmt::error_code;
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
TEST(ErrorCodeTest, Ctor) {
|
TEST(ErrorCodeTest, Ctor) {
|
||||||
EXPECT_EQ(0, ErrorCode().get());
|
EXPECT_EQ(0, error_code().get());
|
||||||
EXPECT_EQ(42, ErrorCode(42).get());
|
EXPECT_EQ(42, error_code(42).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, ScopedRedirect) {
|
TEST(OutputRedirectTest, ScopedRedirect) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
{
|
{
|
||||||
BufferedFile file(write_end.fdopen("w"));
|
buffered_file file(write_end.fdopen("w"));
|
||||||
std::fprintf(file.get(), "[[[");
|
std::fprintf(file.get(), "[[[");
|
||||||
{
|
{
|
||||||
OutputRedirect redir(file.get());
|
OutputRedirect redir(file.get());
|
||||||
|
@ -352,53 +332,53 @@ TEST(OutputRedirectTest, ScopedRedirect) {
|
||||||
|
|
||||||
// Test that OutputRedirect handles errors in flush correctly.
|
// Test that OutputRedirect handles errors in flush correctly.
|
||||||
TEST(OutputRedirectTest, FlushErrorInCtor) {
|
TEST(OutputRedirectTest, FlushErrorInCtor) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
File write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
BufferedFile f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
// 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;
|
scoped_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, "cannot flush stream");
|
EBADF, "cannot flush stream");
|
||||||
redir.reset();
|
redir.reset(nullptr);
|
||||||
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, DupErrorInCtor) {
|
TEST(OutputRedirectTest, DupErrorInCtor) {
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int fd = (f.fileno)();
|
int fd = (f.fileno)();
|
||||||
File copy = File::dup(fd);
|
file copy = file::dup(fd);
|
||||||
FMT_POSIX(close(fd));
|
FMT_POSIX(close(fd));
|
||||||
scoped_ptr<OutputRedirect> redir;
|
scoped_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, fmt::format("cannot duplicate file descriptor {}", fd));
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, RestoreAndRead) {
|
TEST(OutputRedirectTest, RestoreAndRead) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
BufferedFile file(write_end.fdopen("w"));
|
buffered_file file(write_end.fdopen("w"));
|
||||||
std::fprintf(file.get(), "[[[");
|
std::fprintf(file.get(), "[[[");
|
||||||
OutputRedirect redir(file.get());
|
OutputRedirect redir(file.get());
|
||||||
std::fprintf(file.get(), "censored");
|
std::fprintf(file.get(), "censored");
|
||||||
EXPECT_EQ("censored", sanitize(redir.restore_and_read()));
|
EXPECT_EQ("censored", sanitize(redir.restore_and_read()));
|
||||||
EXPECT_EQ("", sanitize(redir.restore_and_read()));
|
EXPECT_EQ("", sanitize(redir.restore_and_read()));
|
||||||
std::fprintf(file.get(), "]]]");
|
std::fprintf(file.get(), "]]]");
|
||||||
file = BufferedFile();
|
file = buffered_file();
|
||||||
EXPECT_READ(read_end, "[[[]]]");
|
EXPECT_READ(read_end, "[[[]]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that OutputRedirect handles errors in flush correctly.
|
// Test that OutputRedirect handles errors in flush correctly.
|
||||||
TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
File write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
BufferedFile f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
OutputRedirect redir(f.get());
|
OutputRedirect redir(f.get());
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
|
@ -409,11 +389,11 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, ErrorInDtor) {
|
TEST(OutputRedirectTest, ErrorInDtor) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
File write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
BufferedFile f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
|
scoped_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()));
|
||||||
|
@ -423,9 +403,9 @@ TEST(OutputRedirectTest, ErrorInDtor) {
|
||||||
// output in EXPECT_STDERR and the second close will break output
|
// output in EXPECT_STDERR and the second close will break output
|
||||||
// redirection.
|
// redirection.
|
||||||
FMT_POSIX(close(write_fd));
|
FMT_POSIX(close(write_fd));
|
||||||
SUPPRESS_ASSERT(redir.reset());
|
SUPPRESS_ASSERT(redir.reset(nullptr));
|
||||||
}, format_system_error(EBADF, "cannot flush stream"));
|
}, format_system_error(EBADF, "cannot flush stream"));
|
||||||
write_copy.dup2(write_fd); // "undo" close or dtor of BufferedFile will fail
|
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
60
externals/fmt/test/gtest-extra.cc
vendored
60
externals/fmt/test/gtest-extra.cc
vendored
|
@ -1,35 +1,15 @@
|
||||||
/*
|
// Formatting library for C++ - custom Google Test assertions
|
||||||
Custom Google Test assertions.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
void OutputRedirect::flush() {
|
void OutputRedirect::flush() {
|
||||||
#if EOF != -1
|
#if EOF != -1
|
||||||
|
@ -38,7 +18,7 @@ void OutputRedirect::flush() {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, fflush(file_));
|
FMT_RETRY(result, fflush(file_));
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
throw fmt::SystemError(errno, "cannot flush stream");
|
throw fmt::system_error(errno, "cannot flush stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputRedirect::restore() {
|
void OutputRedirect::restore() {
|
||||||
|
@ -50,14 +30,14 @@ void OutputRedirect::restore() {
|
||||||
original_.close();
|
original_.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputRedirect::OutputRedirect(FILE *file) : file_(file) {
|
OutputRedirect::OutputRedirect(FILE *f) : file_(f) {
|
||||||
flush();
|
flush();
|
||||||
int fd = FMT_POSIX(fileno(file));
|
int fd = FMT_POSIX(fileno(f));
|
||||||
// Create a File object referring to the original file.
|
// Create a file object referring to the original file.
|
||||||
original_ = File::dup(fd);
|
original_ = file::dup(fd);
|
||||||
// Create a pipe.
|
// Create a pipe.
|
||||||
File write_end;
|
file write_end;
|
||||||
File::pipe(read_end_, write_end);
|
file::pipe(read_end_, write_end);
|
||||||
// Connect the passed FILE object to the write end of the pipe.
|
// Connect the passed FILE object to the write end of the pipe.
|
||||||
write_end.dup2(fd);
|
write_end.dup2(fd);
|
||||||
}
|
}
|
||||||
|
@ -89,13 +69,13 @@ 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 {
|
||||||
n = f.read(&buffer[offset], count - offset);
|
n = f.read(&buffer[offset], count - offset);
|
||||||
// We can't read more than size_t bytes since count has type size_t.
|
// We can't read more than size_t bytes since count has type size_t.
|
||||||
offset += static_cast<std::size_t>(n);
|
offset += n;
|
||||||
} while (offset < count && n != 0);
|
} while (offset < count && n != 0);
|
||||||
buffer.resize(offset);
|
buffer.resize(offset);
|
||||||
return buffer;
|
return buffer;
|
||||||
|
@ -103,8 +83,8 @@ std::string read(File &f, std::size_t count) {
|
||||||
|
|
||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::StringRef message) {
|
std::string format_system_error(int error_code, fmt::string_view message) {
|
||||||
fmt::MemoryWriter out;
|
fmt::memory_buffer out;
|
||||||
fmt::format_system_error(out, error_code, message);
|
format_system_error(out, error_code, message);
|
||||||
return out.str();
|
return to_string(out);
|
||||||
}
|
}
|
||||||
|
|
48
externals/fmt/test/gtest-extra.h
vendored
48
externals/fmt/test/gtest-extra.h
vendored
|
@ -1,37 +1,17 @@
|
||||||
/*
|
// Formatting library for C++ - custom Google Test assertions
|
||||||
Custom Google Test assertions.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_GTEST_EXTRA_H_
|
#ifndef FMT_GTEST_EXTRA_H_
|
||||||
#define FMT_GTEST_EXTRA_H_
|
#define FMT_GTEST_EXTRA_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <gmock/gmock.h>
|
#include "gmock.h"
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#ifndef FMT_USE_FILE_DESCRIPTORS
|
#ifndef FMT_USE_FILE_DESCRIPTORS
|
||||||
# define FMT_USE_FILE_DESCRIPTORS 0
|
# define FMT_USE_FILE_DESCRIPTORS 0
|
||||||
|
@ -81,10 +61,10 @@
|
||||||
FMT_TEST_THROW_(statement, expected_exception, \
|
FMT_TEST_THROW_(statement, expected_exception, \
|
||||||
expected_message, GTEST_NONFATAL_FAILURE_)
|
expected_message, GTEST_NONFATAL_FAILURE_)
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::StringRef 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::SystemError, \
|
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_FILE_DESCRIPTORS
|
||||||
|
@ -94,8 +74,8 @@ std::string format_system_error(int error_code, fmt::StringRef message);
|
||||||
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.
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
|
||||||
|
|
||||||
|
@ -165,7 +145,7 @@ class SuppressAssert {
|
||||||
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)))
|
||||||
|
@ -175,7 +155,7 @@ std::string read(fmt::File &f, std::size_t count);
|
||||||
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 = 0; }
|
~ScopedMock() { Mock::instance = nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FMT_GTEST_EXTRA_H_
|
#endif // FMT_GTEST_EXTRA_H_
|
||||||
|
|
2
externals/fmt/test/header-only-test.cc
vendored
2
externals/fmt/test/header-only-test.cc
vendored
|
@ -1,3 +1,3 @@
|
||||||
// Header-only configuration test
|
// Header-only configuration test
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
|
2
externals/fmt/test/header-only-test2.cc
vendored
2
externals/fmt/test/header-only-test2.cc
vendored
|
@ -1,3 +1,3 @@
|
||||||
// Additional translation unit for the header-only configuration test
|
// Additional translation unit for the header-only configuration test
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
|
115
externals/fmt/test/macro-test.cc
vendored
115
externals/fmt/test/macro-test.cc
vendored
|
@ -1,115 +0,0 @@
|
||||||
/*
|
|
||||||
Tests of variadic function emulation macros.
|
|
||||||
|
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#define FMT_USE_VARIADIC_TEMPLATES 0
|
|
||||||
#include "fmt/format.h"
|
|
||||||
|
|
||||||
#define IDENTITY(x) x
|
|
||||||
|
|
||||||
TEST(UtilTest, Gen) {
|
|
||||||
int values[] = {FMT_GEN(10, IDENTITY)};
|
|
||||||
for (int i = 0; i < 10; ++i)
|
|
||||||
EXPECT_EQ(i, values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAKE_PAIR(x, y) std::make_pair(x, y)
|
|
||||||
|
|
||||||
TEST(UtilTest, ForEach) {
|
|
||||||
std::pair<char, int> values[] = {
|
|
||||||
FMT_FOR_EACH10(MAKE_PAIR, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')
|
|
||||||
};
|
|
||||||
for (int i = 0; i < 10; ++i) {
|
|
||||||
EXPECT_EQ('a' + i, values[i].first);
|
|
||||||
EXPECT_EQ(i, values[i].second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, NArg) {
|
|
||||||
EXPECT_EQ(1, FMT_NARG(a));
|
|
||||||
EXPECT_EQ(2, FMT_NARG(a, b));
|
|
||||||
EXPECT_EQ(3, FMT_NARG(a, b, c));
|
|
||||||
EXPECT_EQ(4, FMT_NARG(a, b, c, d));
|
|
||||||
EXPECT_EQ(5, FMT_NARG(a, b, c, d, e));
|
|
||||||
EXPECT_EQ(6, FMT_NARG(a, b, c, d, e, f));
|
|
||||||
EXPECT_EQ(7, FMT_NARG(a, b, c, d, e, f, g));
|
|
||||||
EXPECT_EQ(8, FMT_NARG(a, b, c, d, e, f, g, h));
|
|
||||||
EXPECT_EQ(9, FMT_NARG(a, b, c, d, e, f, g, h, i));
|
|
||||||
EXPECT_EQ(10, FMT_NARG(a, b, c, d, e, f, g, h, i, j));
|
|
||||||
}
|
|
||||||
|
|
||||||
int result;
|
|
||||||
|
|
||||||
#define MAKE_TEST(func) \
|
|
||||||
void func(const char *, const fmt::ArgList &args) { \
|
|
||||||
result = 0; \
|
|
||||||
for (unsigned i = 0; args[i].type; ++i) \
|
|
||||||
result += args[i].int_value; \
|
|
||||||
}
|
|
||||||
|
|
||||||
MAKE_TEST(test_func)
|
|
||||||
|
|
||||||
typedef char Char;
|
|
||||||
FMT_WRAP1(test_func, const char *, 1)
|
|
||||||
|
|
||||||
TEST(UtilTest, Wrap1) {
|
|
||||||
result = 0;
|
|
||||||
test_func("", 42);
|
|
||||||
EXPECT_EQ(42, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
MAKE_TEST(test_variadic_void)
|
|
||||||
FMT_VARIADIC_VOID(test_variadic_void, const char *)
|
|
||||||
|
|
||||||
TEST(UtilTest, VariadicVoid) {
|
|
||||||
result = 0;
|
|
||||||
test_variadic_void("", 10, 20, 30, 40, 50, 60, 70, 80, 90, 100);
|
|
||||||
EXPECT_EQ(550, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int>
|
|
||||||
struct S {};
|
|
||||||
|
|
||||||
#define GET_TYPE(n) S<n>
|
|
||||||
|
|
||||||
int test_variadic(FMT_GEN(10, GET_TYPE), const fmt::ArgList &args) { \
|
|
||||||
int result = 0; \
|
|
||||||
for (unsigned i = 0; args[i].type; ++i) \
|
|
||||||
result += args[i].int_value; \
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(int, test_variadic,
|
|
||||||
S<0>, S<1>, S<2>, S<3>, S<4>, S<5>, S<6>, S<7>, S<8>, S<9>)
|
|
||||||
|
|
||||||
#define MAKE_ARG(n) S<n>()
|
|
||||||
|
|
||||||
TEST(UtilTest, Variadic) {
|
|
||||||
EXPECT_EQ(550, test_variadic(FMT_GEN(10, MAKE_ARG),
|
|
||||||
10, 20, 30, 40, 50, 60, 70, 80, 90, 100));
|
|
||||||
}
|
|
93
externals/fmt/test/mock-allocator.h
vendored
93
externals/fmt/test/mock-allocator.h
vendored
|
@ -1,91 +1,62 @@
|
||||||
/*
|
// Formatting library for C++ - mock allocator
|
||||||
Mock allocator.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_MOCK_ALLOCATOR_H_
|
#ifndef FMT_MOCK_ALLOCATOR_H_
|
||||||
#define FMT_MOCK_ALLOCATOR_H_
|
#define FMT_MOCK_ALLOCATOR_H_
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class MockAllocator {
|
class mock_allocator {
|
||||||
public:
|
public:
|
||||||
MockAllocator() {}
|
mock_allocator() {}
|
||||||
MockAllocator(const MockAllocator &) {}
|
mock_allocator(const mock_allocator &) {}
|
||||||
typedef T value_type;
|
typedef T value_type;
|
||||||
MOCK_METHOD2_T(allocate, T *(std::size_t n, const void *h));
|
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 AllocatorRef {
|
class allocator_ref {
|
||||||
private:
|
private:
|
||||||
Allocator *alloc_;
|
Allocator *alloc_;
|
||||||
|
|
||||||
|
void move(allocator_ref &other) {
|
||||||
|
alloc_ = other.alloc_;
|
||||||
|
other.alloc_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef typename Allocator::value_type value_type;
|
typedef typename Allocator::value_type value_type;
|
||||||
|
|
||||||
explicit AllocatorRef(Allocator *alloc = 0) : alloc_(alloc) {}
|
explicit allocator_ref(Allocator *alloc = nullptr) : alloc_(alloc) {}
|
||||||
|
|
||||||
AllocatorRef(const AllocatorRef &other) : alloc_(other.alloc_) {}
|
allocator_ref(const allocator_ref &other) : alloc_(other.alloc_) {}
|
||||||
|
allocator_ref(allocator_ref &&other) { move(other); }
|
||||||
|
|
||||||
AllocatorRef& operator=(const AllocatorRef &other) {
|
allocator_ref& operator=(allocator_ref &&other) {
|
||||||
alloc_ = other.alloc_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_RVALUE_REFERENCES
|
|
||||||
private:
|
|
||||||
void move(AllocatorRef &other) {
|
|
||||||
alloc_ = other.alloc_;
|
|
||||||
other.alloc_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
AllocatorRef(AllocatorRef &&other) {
|
|
||||||
move(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
AllocatorRef& operator=(AllocatorRef &&other) {
|
|
||||||
assert(this != &other);
|
assert(this != &other);
|
||||||
move(other);
|
move(other);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
allocator_ref& operator=(const allocator_ref &other) {
|
||||||
|
alloc_ = other.alloc_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
Allocator *get() const { return alloc_; }
|
Allocator *get() const { return alloc_; }
|
||||||
|
|
||||||
value_type *allocate(std::size_t n, const void *h) {
|
value_type* allocate(std::size_t n) {
|
||||||
#if FMT_USE_ALLOCATOR_TRAITS
|
return fmt::internal::allocate(*alloc_, n);
|
||||||
return std::allocator_traits<Allocator>::allocate(*alloc_, n, h);
|
|
||||||
#else
|
|
||||||
return alloc_->allocate(n, h);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
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); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FMT_MOCK_ALLOCATOR_H_
|
#endif // FMT_MOCK_ALLOCATOR_H_
|
||||||
|
|
186
externals/fmt/test/ostream-test.cc
vendored
186
externals/fmt/test/ostream-test.cc
vendored
|
@ -1,77 +1,66 @@
|
||||||
/*
|
// Formatting library for C++ - std::ostream support tests
|
||||||
std::ostream support tests
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2016, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
#define FMT_STRING_ALIAS 1
|
||||||
#include "fmt/ostream.h"
|
#include "fmt/ostream.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "gmock/gmock.h"
|
#include "gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::format;
|
using fmt::format;
|
||||||
using fmt::FormatError;
|
using fmt::format_error;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {};
|
enum TestEnum {};
|
||||||
std::ostream &operator<<(std::ostream &os, TestEnum) {
|
static std::ostream &operator<<(std::ostream &os, TestEnum) {
|
||||||
return os << "TestEnum";
|
return os << "TestEnum";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::wostream &operator<<(std::wostream &os, TestEnum) {
|
||||||
|
return os << L"TestEnum";
|
||||||
|
}
|
||||||
|
|
||||||
enum TestEnum2 {A};
|
enum TestEnum2 {A};
|
||||||
|
|
||||||
TEST(OStreamTest, Enum) {
|
TEST(OStreamTest, Enum) {
|
||||||
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum>::value);
|
EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value));
|
||||||
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
|
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
|
||||||
EXPECT_EQ("0", fmt::format("{}", A));
|
EXPECT_EQ("0", fmt::format("{}", A));
|
||||||
|
EXPECT_FALSE((fmt::convert_to_int<TestEnum, wchar_t>::value));
|
||||||
|
EXPECT_EQ(L"TestEnum", fmt::format(L"{}", TestEnum()));
|
||||||
|
EXPECT_EQ(L"0", fmt::format(L"{}", A));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> {
|
typedef fmt::back_insert_range<fmt::internal::buffer> range;
|
||||||
TestArgFormatter(fmt::BasicFormatter<char, TestArgFormatter> &f,
|
|
||||||
fmt::FormatSpec &s, const char *fmt)
|
struct test_arg_formatter: fmt::arg_formatter<range> {
|
||||||
: fmt::BasicArgFormatter<TestArgFormatter, char>(f, s, fmt) {}
|
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
|
||||||
|
: fmt::arg_formatter<range>(ctx, &s) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(OStreamTest, CustomArg) {
|
TEST(OStreamTest, CustomArg) {
|
||||||
fmt::MemoryWriter writer;
|
fmt::memory_buffer buffer;
|
||||||
typedef fmt::BasicFormatter<char, TestArgFormatter> Formatter;
|
fmt::internal::buffer &base = buffer;
|
||||||
Formatter formatter(fmt::ArgList(), writer);
|
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
|
||||||
fmt::FormatSpec spec;
|
fmt::format_specs spec;
|
||||||
TestArgFormatter af(formatter, spec, "}");
|
test_arg_formatter af(ctx, spec);
|
||||||
af.visit(fmt::internal::MakeArg<Formatter>(TestEnum()));
|
visit(af, fmt::internal::make_arg<fmt::format_context>(TestEnum()));
|
||||||
EXPECT_EQ("TestEnum", writer.str());
|
EXPECT_EQ("TestEnum", std::string(buffer.data(), buffer.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, Format) {
|
TEST(OStreamTest, Format) {
|
||||||
|
@ -87,19 +76,19 @@ 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")),
|
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
|
||||||
FormatError, "format specifier '=' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
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()),
|
||||||
FormatError, "format specifier '+' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0:-}", TestString()),
|
EXPECT_THROW_MSG(format("{0:-}", TestString()),
|
||||||
FormatError, "format specifier '-' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0: }", TestString()),
|
EXPECT_THROW_MSG(format("{0: }", TestString()),
|
||||||
FormatError, "format specifier ' ' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0:#}", TestString()),
|
EXPECT_THROW_MSG(format("{0:#}", TestString()),
|
||||||
FormatError, "format specifier '#' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0:05}", TestString()),
|
EXPECT_THROW_MSG(format("{0:05}", TestString()),
|
||||||
FormatError, "format specifier '0' requires numeric argument");
|
format_error, "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")));
|
||||||
|
@ -107,7 +96,7 @@ TEST(OStreamTest, FormatSpecs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmptyTest {};
|
struct EmptyTest {};
|
||||||
std::ostream &operator<<(std::ostream &os, EmptyTest) {
|
static std::ostream &operator<<(std::ostream &os, EmptyTest) {
|
||||||
return os << "";
|
return os << "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,13 +108,17 @@ TEST(OStreamTest, Print) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
fmt::print(os, "Don't {}!", "panic");
|
fmt::print(os, "Don't {}!", "panic");
|
||||||
EXPECT_EQ("Don't panic!", os.str());
|
EXPECT_EQ("Don't panic!", os.str());
|
||||||
|
std::wostringstream wos;
|
||||||
|
fmt::print(wos, L"Don't {}!", L"panic");
|
||||||
|
EXPECT_EQ(L"Don't panic!", wos.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, WriteToOStream) {
|
TEST(OStreamTest, WriteToOStream) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
w << "foo";
|
const char *foo = "foo";
|
||||||
fmt::internal::write(os, w);
|
buffer.append(foo, foo + std::strlen(foo));
|
||||||
|
fmt::internal::write(os, buffer);
|
||||||
EXPECT_EQ("foo", os.str());
|
EXPECT_EQ("foo", os.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,55 +128,74 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
||||||
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
|
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
class TestWriter : public fmt::BasicWriter<char> {
|
struct test_buffer : fmt::internal::buffer {
|
||||||
private:
|
explicit test_buffer(std::size_t size) { resize(size); }
|
||||||
struct TestBuffer : fmt::Buffer<char> {
|
void grow(std::size_t) {}
|
||||||
explicit TestBuffer(std::size_t size) { size_ = size; }
|
} buffer(max_size);
|
||||||
void grow(std::size_t) {}
|
|
||||||
} buffer_;
|
|
||||||
public:
|
|
||||||
explicit TestWriter(std::size_t size)
|
|
||||||
: fmt::BasicWriter<char>(buffer_), buffer_(size) {}
|
|
||||||
} w(max_size);
|
|
||||||
|
|
||||||
struct MockStreamBuf : 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);
|
||||||
}
|
}
|
||||||
} buffer;
|
} streambuf;
|
||||||
|
|
||||||
struct TestOStream : std::ostream {
|
struct test_ostream : std::ostream {
|
||||||
explicit TestOStream(MockStreamBuf &buffer) : std::ostream(&buffer) {}
|
explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {}
|
||||||
} os(buffer);
|
} os(streambuf);
|
||||||
|
|
||||||
testing::InSequence sequence;
|
testing::InSequence sequence;
|
||||||
const char *data = 0;
|
const char *data = nullptr;
|
||||||
std::size_t size = max_size;
|
std::size_t size = max_size;
|
||||||
do {
|
do {
|
||||||
typedef fmt::internal::MakeUnsigned<std::streamsize>::Type UStreamSize;
|
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
|
||||||
UStreamSize n = std::min<UStreamSize>(
|
ustreamsize n = std::min<ustreamsize>(
|
||||||
size, fmt::internal::to_unsigned(max_streamsize));
|
size, fmt::internal::to_unsigned(max_streamsize));
|
||||||
EXPECT_CALL(buffer, xsputn(data, static_cast<std::streamsize>(n)))
|
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
|
||||||
.WillOnce(testing::Return(max_streamsize));
|
.WillOnce(testing::Return(max_streamsize));
|
||||||
data += n;
|
data += n;
|
||||||
size -= static_cast<std::size_t>(n);
|
size -= n;
|
||||||
} while (size != 0);
|
} while (size != 0);
|
||||||
fmt::internal::write(os, w);
|
fmt::internal::write(os, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConvertibleToInt {
|
TEST(OStreamTest, Join) {
|
||||||
template <typename ValueType>
|
int v[3] = {1, 2, 3};
|
||||||
operator ValueType() const {
|
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &o, ConvertibleToInt) {
|
|
||||||
return o << "foo";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(FormatTest, FormatConvertibleToInt) {
|
|
||||||
EXPECT_EQ("foo", fmt::format("{}", ConvertibleToInt()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
TEST(OStreamTest, ConstexprString) {
|
||||||
|
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace fmt_test {
|
||||||
|
struct ABC {};
|
||||||
|
|
||||||
|
template <typename Output> Output &operator<<(Output &out, ABC) {
|
||||||
|
out << "ABC";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
} // namespace fmt_test
|
||||||
|
|
||||||
|
TEST(FormatTest, FormatToN) {
|
||||||
|
char buffer[4];
|
||||||
|
buffer[3] = 'x';
|
||||||
|
auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::ABC());
|
||||||
|
EXPECT_EQ(3u, result.size);
|
||||||
|
EXPECT_EQ(buffer + 3, result.out);
|
||||||
|
EXPECT_EQ("ABCx", fmt::string_view(buffer, 4));
|
||||||
|
result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::ABC());
|
||||||
|
EXPECT_EQ(5u, result.size);
|
||||||
|
EXPECT_EQ(buffer + 3, result.out);
|
||||||
|
EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_USER_DEFINED_LITERALS
|
||||||
|
TEST(FormatTest, UDL) {
|
||||||
|
using namespace fmt::literals;
|
||||||
|
EXPECT_EQ("{}"_format("test"), "test");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
169
externals/fmt/test/posix-mock-test.cc
vendored
169
externals/fmt/test/posix-mock-test.cc
vendored
|
@ -1,35 +1,17 @@
|
||||||
/*
|
// Tests of the C++ interface to POSIX functions that require mocks
|
||||||
Tests of the C++ interface to POSIX functions that require mocks
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2015, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Disable bogus MSVC warnings.
|
// Disable bogus MSVC warnings.
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#ifdef _MSC_VER
|
||||||
|
# define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "posix-mock.h"
|
#include "posix-mock.h"
|
||||||
#include "fmt/posix.cc"
|
#include "../src/posix.cc"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -41,13 +23,13 @@
|
||||||
# undef ERROR
|
# undef ERROR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::BufferedFile;
|
using fmt::buffered_file;
|
||||||
using fmt::ErrorCode;
|
using fmt::error_code;
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
using testing::internal::scoped_ptr;
|
using testing::internal::scoped_ptr;
|
||||||
using testing::_;
|
using testing::_;
|
||||||
|
@ -151,7 +133,7 @@ int test::dup2(int fildes, int fildes2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *test::fdopen(int fildes, const char *mode) {
|
FILE *test::fdopen(int fildes, const char *mode) {
|
||||||
EMULATE_EINTR(fdopen, 0);
|
EMULATE_EINTR(fdopen, nullptr);
|
||||||
return ::FMT_POSIX(fdopen(fildes, mode));
|
return ::FMT_POSIX(fdopen(fildes, mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +162,7 @@ int test::pipe(int *pfds, unsigned psize, int textmode) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE *test::fopen(const char *filename, const char *mode) {
|
FILE *test::fopen(const char *filename, const char *mode) {
|
||||||
EMULATE_EINTR(fopen, 0);
|
EMULATE_EINTR(fopen, nullptr);
|
||||||
return ::fopen(filename, mode);
|
return ::fopen(filename, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,17 +195,11 @@ int (test::fileno)(FILE *stream) {
|
||||||
# define EXPECT_EQ_POSIX(expected, actual)
|
# define EXPECT_EQ_POSIX(expected, actual)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void write_file(fmt::CStringRef filename, fmt::StringRef content) {
|
static void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||||
fmt::BufferedFile f(filename, "w");
|
fmt::buffered_file f(filename, "w");
|
||||||
f.print("{}", content);
|
f.print("{}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, StaticAssert) {
|
|
||||||
FMT_STATIC_ASSERT(true, "success");
|
|
||||||
// Static assertion failure is tested in compile-test because it causes
|
|
||||||
// a compile-time error.
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, GetPageSize) {
|
TEST(UtilTest, GetPageSize) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SYSTEM_INFO si = {};
|
SYSTEM_INFO si = {};
|
||||||
|
@ -240,8 +216,8 @@ TEST(UtilTest, GetPageSize) {
|
||||||
|
|
||||||
TEST(FileTest, OpenRetry) {
|
TEST(FileTest, OpenRetry) {
|
||||||
write_file("test", "there must be something here");
|
write_file("test", "there must be something here");
|
||||||
scoped_ptr<File> f;
|
scoped_ptr<file> f{nullptr};
|
||||||
EXPECT_RETRY(f.reset(new File("test", File::RDONLY)),
|
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)),
|
||||||
open, "cannot open file test");
|
open, "cannot open file test");
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
char c = 0;
|
char c = 0;
|
||||||
|
@ -250,13 +226,13 @@ TEST(FileTest, OpenRetry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)));
|
scoped_ptr<file> f(new file(std::move(read_end)));
|
||||||
int saved_close_count = 0;
|
int saved_close_count = 0;
|
||||||
EXPECT_WRITE(stderr, {
|
EXPECT_WRITE(stderr, {
|
||||||
close_count = 1;
|
close_count = 1;
|
||||||
f.reset();
|
f.reset(nullptr);
|
||||||
saved_close_count = close_count;
|
saved_close_count = close_count;
|
||||||
close_count = 0;
|
close_count = 0;
|
||||||
}, format_system_error(EINTR, "cannot close file") + "\n");
|
}, format_system_error(EINTR, "cannot close file") + "\n");
|
||||||
|
@ -264,8 +240,8 @@ TEST(FileTest, CloseNoRetryInDtor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseNoRetry) {
|
TEST(FileTest, CloseNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
close_count = 1;
|
close_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
|
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
|
||||||
EXPECT_EQ(2, close_count);
|
EXPECT_EQ(2, close_count);
|
||||||
|
@ -275,15 +251,15 @@ 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("test", content);
|
||||||
File f("test", File::RDONLY);
|
file f("test", file::RDONLY);
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(content.size(), static_cast<fmt::ULongLong>(f.size()));
|
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
fmt::MemoryWriter message;
|
fmt::memory_buffer message;
|
||||||
fmt::internal::format_windows_error(
|
fmt::internal::format_windows_error(
|
||||||
message, ERROR_ACCESS_DENIED, "cannot get file size");
|
message, ERROR_ACCESS_DENIED, "cannot get file size");
|
||||||
fstat_sim = ERROR;
|
fstat_sim = ERROR;
|
||||||
EXPECT_THROW_MSG(f.size(), fmt::WindowsError, message.str());
|
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
|
||||||
fstat_sim = NONE;
|
fstat_sim = NONE;
|
||||||
#else
|
#else
|
||||||
f.close();
|
f.close();
|
||||||
|
@ -293,7 +269,7 @@ TEST(FileTest, Size) {
|
||||||
|
|
||||||
TEST(FileTest, MaxSize) {
|
TEST(FileTest, MaxSize) {
|
||||||
write_file("test", "");
|
write_file("test", "");
|
||||||
File f("test", File::RDONLY);
|
file f("test", 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());
|
||||||
|
@ -301,8 +277,8 @@ TEST(FileTest, MaxSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ReadRetry) {
|
TEST(FileTest, ReadRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
enum { SIZE = 4 };
|
enum { SIZE = 4 };
|
||||||
write_end.write("test", SIZE);
|
write_end.write("test", SIZE);
|
||||||
write_end.close();
|
write_end.close();
|
||||||
|
@ -314,8 +290,8 @@ TEST(FileTest, ReadRetry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, WriteRetry) {
|
TEST(FileTest, WriteRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
enum { SIZE = 4 };
|
enum { SIZE = 4 };
|
||||||
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),
|
||||||
|
@ -332,29 +308,29 @@ TEST(FileTest, WriteRetry) {
|
||||||
|
|
||||||
#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::SystemError);
|
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
|
||||||
read_count = 0;
|
read_count = 0;
|
||||||
EXPECT_EQ(UINT_MAX, read_nbyte);
|
EXPECT_EQ(UINT_MAX, read_nbyte);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ConvertWriteCount) {
|
TEST(FileTest, ConvertWriteCount) {
|
||||||
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;
|
||||||
write_count = 1;
|
write_count = 1;
|
||||||
write_nbyte = 0;
|
write_nbyte = 0;
|
||||||
EXPECT_THROW(write_end.write(&c, size), fmt::SystemError);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -363,14 +339,14 @@ TEST(FileTest, ConvertWriteCount) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2Retry) {
|
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()));
|
||||||
|
@ -378,8 +354,8 @@ TEST(FileTest, Dup2Retry) {
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExceptRetry) {
|
TEST(FileTest, Dup2NoExceptRetry) {
|
||||||
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);
|
||||||
ErrorCode ec;
|
error_code ec;
|
||||||
dup2_count = 1;
|
dup2_count = 1;
|
||||||
f1.dup2(f2.descriptor(), ec);
|
f1.dup2(f2.descriptor(), ec);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
@ -391,16 +367,16 @@ TEST(FileTest, Dup2NoExceptRetry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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, "cannot create pipe");
|
file::pipe(read_end, write_end), EINTR, "cannot create pipe");
|
||||||
pipe_count = 0;
|
pipe_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, FdopenNoRetry) {
|
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, "cannot associate stream with file descriptor");
|
EINTR, "cannot associate stream with file descriptor");
|
||||||
|
@ -409,24 +385,24 @@ TEST(FileTest, FdopenNoRetry) {
|
||||||
|
|
||||||
TEST(BufferedFileTest, OpenRetry) {
|
TEST(BufferedFileTest, OpenRetry) {
|
||||||
write_file("test", "there must be something here");
|
write_file("test", "there must be something here");
|
||||||
scoped_ptr<BufferedFile> f;
|
scoped_ptr<buffered_file> f{nullptr};
|
||||||
EXPECT_RETRY(f.reset(new BufferedFile("test", "r")),
|
EXPECT_RETRY(f.reset(new buffered_file("test", "r")),
|
||||||
fopen, "cannot open file test");
|
fopen, "cannot open file test");
|
||||||
#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::SystemError(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<BufferedFile> f(new BufferedFile(read_end.fdopen("r")));
|
scoped_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(stderr, {
|
||||||
fclose_count = 1;
|
fclose_count = 1;
|
||||||
f.reset();
|
f.reset(nullptr);
|
||||||
saved_fclose_count = fclose_count;
|
saved_fclose_count = fclose_count;
|
||||||
fclose_count = 0;
|
fclose_count = 0;
|
||||||
}, format_system_error(EINTR, "cannot close file") + "\n");
|
}, format_system_error(EINTR, "cannot close file") + "\n");
|
||||||
|
@ -434,9 +410,9 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseNoRetry) {
|
TEST(BufferedFileTest, CloseNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
BufferedFile f = read_end.fdopen("r");
|
buffered_file f = read_end.fdopen("r");
|
||||||
fclose_count = 1;
|
fclose_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
|
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
|
||||||
EXPECT_EQ(2, fclose_count);
|
EXPECT_EQ(2, fclose_count);
|
||||||
|
@ -444,9 +420,9 @@ TEST(BufferedFileTest, CloseNoRetry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, FilenoNoRetry) {
|
TEST(BufferedFileTest, FilenoNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
BufferedFile f = read_end.fdopen("r");
|
buffered_file f = read_end.fdopen("r");
|
||||||
fileno_count = 1;
|
fileno_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
|
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
|
||||||
EXPECT_EQ(2, fileno_count);
|
EXPECT_EQ(2, fileno_count);
|
||||||
|
@ -462,8 +438,9 @@ TEST(ScopedMock, Scope) {
|
||||||
ScopedMock<TestMock> mock;
|
ScopedMock<TestMock> mock;
|
||||||
EXPECT_EQ(&mock, TestMock::instance);
|
EXPECT_EQ(&mock, TestMock::instance);
|
||||||
TestMock © = mock;
|
TestMock © = mock;
|
||||||
|
static_cast<void>(copy);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(0, TestMock::instance);
|
EXPECT_EQ(nullptr, TestMock::instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
@ -498,25 +475,33 @@ double _strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||||
# pragma warning(pop)
|
# pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) {
|
#if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
|
||||||
|
#define FMT_LOCALE_THROW __THROW
|
||||||
|
#else
|
||||||
|
#define FMT_LOCALE_THROW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) FMT_LOCALE_THROW {
|
||||||
return LocaleMock::instance->newlocale(category_mask, locale, base);
|
return LocaleMock::instance->newlocale(category_mask, locale, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
#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) {
|
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
|
||||||
LocaleMock::instance->freelocale(locale);
|
LocaleMock::instance->freelocale(locale);
|
||||||
return FreeLocaleResult();
|
return FreeLocaleResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
double strtod_l(const char *nptr, char **endptr, LocaleType locale) {
|
double strtod_l(const char *nptr, char **endptr, LocaleType locale) FMT_LOCALE_THROW {
|
||||||
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
|
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef FMT_LOCALE_THROW
|
||||||
|
|
||||||
TEST(LocaleTest, LocaleMock) {
|
TEST(LocaleTest, LocaleMock) {
|
||||||
ScopedMock<LocaleMock> mock;
|
ScopedMock<LocaleMock> mock;
|
||||||
LocaleType locale = reinterpret_cast<LocaleType>(11);
|
LocaleType locale = reinterpret_cast<LocaleType>(11);
|
||||||
|
@ -530,7 +515,7 @@ TEST(LocaleTest, Locale) {
|
||||||
#endif
|
#endif
|
||||||
ScopedMock<LocaleMock> mock;
|
ScopedMock<LocaleMock> mock;
|
||||||
LocaleType impl = reinterpret_cast<LocaleType>(42);
|
LocaleType impl = reinterpret_cast<LocaleType>(42);
|
||||||
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), 0))
|
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 locale;
|
||||||
|
|
33
externals/fmt/test/posix-mock.h
vendored
33
externals/fmt/test/posix-mock.h
vendored
|
@ -1,29 +1,9 @@
|
||||||
/*
|
// Formatting library for C++ - mocks of POSIX functions
|
||||||
Mocks of POSIX functions
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2015, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_POSIX_TEST_H
|
#ifndef FMT_POSIX_TEST_H
|
||||||
#define FMT_POSIX_TEST_H
|
#define FMT_POSIX_TEST_H
|
||||||
|
@ -34,6 +14,7 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#else
|
#else
|
||||||
|
# include <sys/param.h> // for FreeBSD version
|
||||||
# include <sys/types.h> // for ssize_t
|
# include <sys/types.h> // for ssize_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
186
externals/fmt/test/posix-test.cc
vendored
186
externals/fmt/test/posix-test.cc
vendored
|
@ -1,29 +1,9 @@
|
||||||
/*
|
// Formatting library for C++ - tests of the C++ interface to POSIX functions
|
||||||
Tests of the C++ interface to POSIX functions
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2015, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdlib> // std::exit
|
#include <cstdlib> // std::exit
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -36,19 +16,19 @@
|
||||||
# undef fileno
|
# undef fileno
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using fmt::BufferedFile;
|
using fmt::buffered_file;
|
||||||
using fmt::ErrorCode;
|
using fmt::error_code;
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
using testing::internal::scoped_ptr;
|
using testing::internal::scoped_ptr;
|
||||||
|
|
||||||
// Checks if the file is open by reading one character from it.
|
// Checks if the file is open by reading one character from it.
|
||||||
bool isopen(int fd) {
|
static bool isopen(int fd) {
|
||||||
char buffer;
|
char buffer;
|
||||||
return FMT_POSIX(read(fd, &buffer, 1)) == 1;
|
return FMT_POSIX(read(fd, &buffer, 1)) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isclosed(int fd) {
|
static bool isclosed(int fd) {
|
||||||
char buffer;
|
char buffer;
|
||||||
std::streamsize result = 0;
|
std::streamsize result = 0;
|
||||||
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
|
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
|
||||||
|
@ -56,16 +36,16 @@ bool isclosed(int fd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opens a file for reading.
|
// Opens a file for reading.
|
||||||
File open_file() {
|
static file open_file() {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
||||||
write_end.close();
|
write_end.close();
|
||||||
return read_end;
|
return read_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to write a string to a file.
|
// Attempts to write a string to a file.
|
||||||
void write(File &f, fmt::StringRef 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 {
|
||||||
|
@ -73,57 +53,57 @@ void write(File &f, fmt::StringRef s) {
|
||||||
ptr += count;
|
ptr += count;
|
||||||
// We can't write more than size_t bytes since num_chars_left
|
// We can't write more than size_t bytes since num_chars_left
|
||||||
// has type size_t.
|
// has type size_t.
|
||||||
num_chars_left -= static_cast<std::size_t>(count);
|
num_chars_left -= count;
|
||||||
} while (num_chars_left != 0);
|
} while (num_chars_left != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, DefaultCtor) {
|
TEST(BufferedFileTest, DefaultCtor) {
|
||||||
BufferedFile f;
|
buffered_file f;
|
||||||
EXPECT_TRUE(f.get() == 0);
|
EXPECT_TRUE(f.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveCtor) {
|
TEST(BufferedFileTest, MoveCtor) {
|
||||||
BufferedFile bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
FILE *fp = bf.get();
|
FILE *fp = bf.get();
|
||||||
EXPECT_TRUE(fp != 0);
|
EXPECT_TRUE(fp != nullptr);
|
||||||
BufferedFile bf2(std::move(bf));
|
buffered_file bf2(std::move(bf));
|
||||||
EXPECT_EQ(fp, bf2.get());
|
EXPECT_EQ(fp, bf2.get());
|
||||||
EXPECT_TRUE(bf.get() == 0);
|
EXPECT_TRUE(bf.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveAssignment) {
|
TEST(BufferedFileTest, MoveAssignment) {
|
||||||
BufferedFile bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
FILE *fp = bf.get();
|
FILE *fp = bf.get();
|
||||||
EXPECT_TRUE(fp != 0);
|
EXPECT_TRUE(fp != nullptr);
|
||||||
BufferedFile 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() == 0);
|
EXPECT_TRUE(bf.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||||
BufferedFile bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
BufferedFile bf2 = open_buffered_file();
|
buffered_file bf2 = open_buffered_file();
|
||||||
int old_fd = bf2.fileno();
|
int old_fd = bf2.fileno();
|
||||||
bf2 = std::move(bf);
|
bf2 = std::move(bf);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
||||||
FILE *fp = 0;
|
FILE *fp = nullptr;
|
||||||
BufferedFile 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 = 0;
|
FILE *fp = nullptr;
|
||||||
BufferedFile f;
|
buffered_file f;
|
||||||
f = open_buffered_file(&fp);
|
f = open_buffered_file(&fp);
|
||||||
EXPECT_EQ(fp, f.get());
|
EXPECT_EQ(fp, f.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int old_fd = f.fileno();
|
int old_fd = f.fileno();
|
||||||
f = open_buffered_file();
|
f = open_buffered_file();
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
|
@ -132,60 +112,60 @@ TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||||
TEST(BufferedFileTest, CloseFileInDtor) {
|
TEST(BufferedFileTest, CloseFileInDtor) {
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
{
|
{
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
fd = f.fileno();
|
fd = f.fileno();
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseErrorInDtor) {
|
TEST(BufferedFileTest, CloseErrorInDtor) {
|
||||||
scoped_ptr<BufferedFile> f(new BufferedFile(open_buffered_file()));
|
scoped_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 close function must be called inside EXPECT_WRITE, otherwise
|
||||||
// the system may recycle closed file descriptor when redirecting the
|
// the system may recycle closed file descriptor when redirecting the
|
||||||
// output in EXPECT_STDERR and the second close will break output
|
// output in EXPECT_STDERR and the second close will break output
|
||||||
// redirection.
|
// redirection.
|
||||||
FMT_POSIX(close(f->fileno()));
|
FMT_POSIX(close(f->fileno()));
|
||||||
SUPPRESS_ASSERT(f.reset());
|
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||||
}, format_system_error(EBADF, "cannot close file") + "\n");
|
}, format_system_error(EBADF, "cannot close file") + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, Close) {
|
TEST(BufferedFileTest, Close) {
|
||||||
BufferedFile 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() == 0);
|
EXPECT_TRUE(f.get() == nullptr);
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseError) {
|
TEST(BufferedFileTest, CloseError) {
|
||||||
BufferedFile 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() == 0);
|
EXPECT_TRUE(f.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, Fileno) {
|
TEST(BufferedFileTest, Fileno) {
|
||||||
BufferedFile 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 {
|
try {
|
||||||
f.fileno();
|
f.fileno();
|
||||||
} catch (fmt::SystemError) {
|
} catch (const fmt::system_error&) {
|
||||||
std::exit(1);
|
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());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, FILE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, DefaultCtor) {
|
TEST(FileTest, DefaultCtor) {
|
||||||
File f;
|
file f;
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,64 +173,64 @@ 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);
|
||||||
ASSERT_TRUE(isopen(f.descriptor()));
|
ASSERT_TRUE(isopen(f.descriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, OpenBufferedFileError) {
|
TEST(FileTest, OpenBufferedFileError) {
|
||||||
EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
|
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY),
|
||||||
ENOENT, "cannot open file nonexistent");
|
ENOENT, "cannot open file nonexistent");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveCtor) {
|
TEST(FileTest, MoveCtor) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
EXPECT_NE(-1, fd);
|
EXPECT_NE(-1, fd);
|
||||||
File f2(std::move(f));
|
file f2(std::move(f));
|
||||||
EXPECT_EQ(fd, f2.descriptor());
|
EXPECT_EQ(fd, f2.descriptor());
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveAssignment) {
|
TEST(FileTest, MoveAssignment) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
EXPECT_NE(-1, fd);
|
EXPECT_NE(-1, fd);
|
||||||
File f2;
|
file f2;
|
||||||
f2 = std::move(f);
|
f2 = std::move(f);
|
||||||
EXPECT_EQ(fd, f2.descriptor());
|
EXPECT_EQ(fd, f2.descriptor());
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveAssignmentClosesFile) {
|
TEST(FileTest, MoveAssignmentClosesFile) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File f2 = open_file();
|
file f2 = open_file();
|
||||||
int old_fd = f2.descriptor();
|
int old_fd = f2.descriptor();
|
||||||
f2 = std::move(f);
|
f2 = std::move(f);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInCtor) {
|
TEST(FileTest, MoveFromTemporaryInCtor) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
File f(OpenBufferedFile(fd));
|
file f(OpenBufferedFile(fd));
|
||||||
EXPECT_EQ(fd, f.descriptor());
|
EXPECT_EQ(fd, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInAssignment) {
|
TEST(FileTest, MoveFromTemporaryInAssignment) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
File f;
|
file f;
|
||||||
f = OpenBufferedFile(fd);
|
f = OpenBufferedFile(fd);
|
||||||
EXPECT_EQ(fd, f.descriptor());
|
EXPECT_EQ(fd, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int old_fd = f.descriptor();
|
int old_fd = f.descriptor();
|
||||||
f = OpenBufferedFile(fd);
|
f = OpenBufferedFile(fd);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
|
@ -259,26 +239,26 @@ TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||||
TEST(FileTest, CloseFileInDtor) {
|
TEST(FileTest, CloseFileInDtor) {
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
{
|
{
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
fd = f.descriptor();
|
fd = f.descriptor();
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseErrorInDtor) {
|
TEST(FileTest, CloseErrorInDtor) {
|
||||||
scoped_ptr<File> f(new File(open_file()));
|
scoped_ptr<file> f(new file(open_file()));
|
||||||
EXPECT_WRITE(stderr, {
|
EXPECT_WRITE(stderr, {
|
||||||
// The close function must be called inside EXPECT_WRITE, otherwise
|
// The close function must be called inside EXPECT_WRITE, otherwise
|
||||||
// the system may recycle closed file descriptor when redirecting the
|
// the system may recycle closed file descriptor when redirecting the
|
||||||
// output in EXPECT_STDERR and the second close will break output
|
// output in EXPECT_STDERR and the second close will break output
|
||||||
// redirection.
|
// redirection.
|
||||||
FMT_POSIX(close(f->descriptor()));
|
FMT_POSIX(close(f->descriptor()));
|
||||||
SUPPRESS_ASSERT(f.reset());
|
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||||
}, format_system_error(EBADF, "cannot close file") + "\n");
|
}, format_system_error(EBADF, "cannot close file") + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Close) {
|
TEST(FileTest, Close) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
f.close();
|
f.close();
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
|
@ -286,19 +266,19 @@ TEST(FileTest, Close) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseError) {
|
TEST(FileTest, CloseError) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
FMT_POSIX(close(f.descriptor()));
|
FMT_POSIX(close(f.descriptor()));
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Read) {
|
TEST(FileTest, Read) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
EXPECT_READ(f, FILE_CONTENT);
|
EXPECT_READ(f, FILE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ReadError) {
|
TEST(FileTest, ReadError) {
|
||||||
File f("test-file", File::WRONLY);
|
file f("test-file", file::WRONLY);
|
||||||
char buf;
|
char buf;
|
||||||
// We intentionally read from a file opened in the write-only mode to
|
// We intentionally read from a file opened in the write-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
|
@ -306,23 +286,23 @@ TEST(FileTest, ReadError) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Write) {
|
TEST(FileTest, Write) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
write(write_end, "test");
|
write(write_end, "test");
|
||||||
write_end.close();
|
write_end.close();
|
||||||
EXPECT_READ(read_end, "test");
|
EXPECT_READ(read_end, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, WriteError) {
|
TEST(FileTest, WriteError) {
|
||||||
File f("test-file", File::RDONLY);
|
file f("test-file", file::RDONLY);
|
||||||
// We intentionally write to a file opened in the read-only mode to
|
// We intentionally write to a file opened in the read-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup) {
|
TEST(FileTest, Dup) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File copy = File::dup(f.descriptor());
|
file copy = file::dup(f.descriptor());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
|
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
|
||||||
}
|
}
|
||||||
|
@ -330,29 +310,29 @@ TEST(FileTest, Dup) {
|
||||||
#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, "cannot duplicate file descriptor -1");
|
EBADF, "cannot duplicate file descriptor -1");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(FileTest, Dup2) {
|
TEST(FileTest, Dup2) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File copy = open_file();
|
file copy = open_file();
|
||||||
f.dup2(copy.descriptor());
|
f.dup2(copy.descriptor());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, FILE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2Error) {
|
TEST(FileTest, Dup2Error) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
|
||||||
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExcept) {
|
TEST(FileTest, Dup2NoExcept) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File copy = open_file();
|
file copy = open_file();
|
||||||
ErrorCode ec;
|
error_code ec;
|
||||||
f.dup2(copy.descriptor(), ec);
|
f.dup2(copy.descriptor(), ec);
|
||||||
EXPECT_EQ(0, ec.get());
|
EXPECT_EQ(0, ec.get());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
|
@ -360,15 +340,15 @@ TEST(FileTest, Dup2NoExcept) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExceptError) {
|
TEST(FileTest, Dup2NoExceptError) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
ErrorCode ec;
|
error_code ec;
|
||||||
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
||||||
EXPECT_EQ(EBADF, ec.get());
|
EXPECT_EQ(EBADF, ec.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Pipe) {
|
TEST(FileTest, Pipe) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
EXPECT_NE(-1, read_end.descriptor());
|
EXPECT_NE(-1, read_end.descriptor());
|
||||||
EXPECT_NE(-1, write_end.descriptor());
|
EXPECT_NE(-1, write_end.descriptor());
|
||||||
write(write_end, "test");
|
write(write_end, "test");
|
||||||
|
@ -376,14 +356,14 @@ TEST(FileTest, Pipe) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Fdopen) {
|
TEST(FileTest, Fdopen) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int read_fd = read_end.descriptor();
|
int read_fd = read_end.descriptor();
|
||||||
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) {
|
TEST(FileTest, FdopenError) {
|
||||||
File f;
|
file f;
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||||
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
|
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
|
||||||
}
|
}
|
||||||
|
|
211
externals/fmt/test/printf-test.cc
vendored
211
externals/fmt/test/printf-test.cc
vendored
|
@ -1,51 +1,37 @@
|
||||||
/*
|
// Formatting library for C++ - printf tests
|
||||||
printf tests.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "fmt/core.h"
|
||||||
#include "fmt/printf.h"
|
#include "fmt/printf.h"
|
||||||
#include "fmt/format.h"
|
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::format;
|
using fmt::format;
|
||||||
using fmt::FormatError;
|
using fmt::format_error;
|
||||||
|
|
||||||
const unsigned BIG_NUM = INT_MAX + 1u;
|
const unsigned BIG_NUM = INT_MAX + 1u;
|
||||||
|
|
||||||
// Makes format string argument positional.
|
// Makes format string argument positional.
|
||||||
std::string make_positional(fmt::StringRef format) {
|
static std::string make_positional(fmt::string_view format) {
|
||||||
std::string s(format.to_string());
|
std::string s(format.data(), format.size());
|
||||||
s.replace(s.find('%'), 1, "%1$");
|
s.replace(s.find('%'), 1, "%1$");
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::wstring make_positional(fmt::wstring_view format) {
|
||||||
|
std::wstring s(format.data(), format.size());
|
||||||
|
s.replace(s.find(L'%'), 1, L"%1$");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
#define EXPECT_PRINTF(expected_output, format, arg) \
|
#define EXPECT_PRINTF(expected_output, format, arg) \
|
||||||
EXPECT_EQ(expected_output, fmt::sprintf(format, arg)) \
|
EXPECT_EQ(expected_output, fmt::sprintf(format, arg)) \
|
||||||
<< "format: " << format; \
|
<< "format: " << format; \
|
||||||
|
@ -53,6 +39,7 @@ std::string make_positional(fmt::StringRef format) {
|
||||||
|
|
||||||
TEST(PrintfTest, NoArgs) {
|
TEST(PrintfTest, NoArgs) {
|
||||||
EXPECT_EQ("test", fmt::sprintf("test"));
|
EXPECT_EQ("test", fmt::sprintf("test"));
|
||||||
|
EXPECT_EQ(L"test", fmt::sprintf(L"test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Escape) {
|
TEST(PrintfTest, Escape) {
|
||||||
|
@ -61,6 +48,11 @@ TEST(PrintfTest, Escape) {
|
||||||
EXPECT_EQ("% after", fmt::sprintf("%% after"));
|
EXPECT_EQ("% after", fmt::sprintf("%% after"));
|
||||||
EXPECT_EQ("before % after", fmt::sprintf("before %% after"));
|
EXPECT_EQ("before % after", fmt::sprintf("before %% after"));
|
||||||
EXPECT_EQ("%s", fmt::sprintf("%%s"));
|
EXPECT_EQ("%s", fmt::sprintf("%%s"));
|
||||||
|
EXPECT_EQ(L"%", fmt::sprintf(L"%%"));
|
||||||
|
EXPECT_EQ(L"before %", fmt::sprintf(L"before %%"));
|
||||||
|
EXPECT_EQ(L"% after", fmt::sprintf(L"%% after"));
|
||||||
|
EXPECT_EQ(L"before % after", fmt::sprintf(L"before %% after"));
|
||||||
|
EXPECT_EQ(L"%s", fmt::sprintf(L"%%s"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PositionalArgs) {
|
TEST(PrintfTest, PositionalArgs) {
|
||||||
|
@ -80,45 +72,45 @@ TEST(PrintfTest, AutomaticArgIndexing) {
|
||||||
|
|
||||||
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
|
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$", BIG_NUM)),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$", BIG_NUM)),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM)),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM)),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, SwitchArgIndexing) {
|
TEST(PrintfTest, SwitchArgIndexing) {
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2),
|
||||||
FormatError, "invalid format string");
|
format_error, "cannot switch from manual to automatic argument indexing");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2),
|
||||||
FormatError, "cannot switch from manual to automatic argument indexing");
|
format_error, "cannot switch from manual to automatic argument indexing");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2),
|
||||||
FormatError, "invalid format string");
|
format_error, "cannot switch from automatic to manual argument indexing");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2),
|
||||||
FormatError, "cannot switch from automatic to manual argument indexing");
|
format_error, "cannot switch from automatic to manual argument indexing");
|
||||||
|
|
||||||
// Indexing errors override width errors.
|
// Indexing errors override width errors.
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, InvalidArgIndex) {
|
TEST(PrintfTest, InvalidArgIndex) {
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%0$d", 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%0$d", 42), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%2$d", 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%2$d", 42), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", INT_MAX), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", INT_MAX), 42),
|
||||||
FormatError, "argument index out of range");
|
format_error, "argument index out of range");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%2$", 42),
|
EXPECT_THROW_MSG(fmt::sprintf("%2$", 42),
|
||||||
FormatError, "invalid format string");
|
format_error, "argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM), 42),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DefaultAlignRight) {
|
TEST(PrintfTest, DefaultAlignRight) {
|
||||||
|
@ -137,7 +129,7 @@ TEST(PrintfTest, ZeroFlag) {
|
||||||
EXPECT_PRINTF("+00042", "%00+6d", 42);
|
EXPECT_PRINTF("+00042", "%00+6d", 42);
|
||||||
|
|
||||||
// '0' flag is ignored for non-numeric types.
|
// '0' flag is ignored for non-numeric types.
|
||||||
EXPECT_PRINTF(" x", "%05c", 'x');
|
EXPECT_PRINTF("0000x", "%05c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PlusFlag) {
|
TEST(PrintfTest, PlusFlag) {
|
||||||
|
@ -188,8 +180,13 @@ TEST(PrintfTest, HashFlag) {
|
||||||
safe_sprintf(buffer, "%#E", -42.0);
|
safe_sprintf(buffer, "%#E", -42.0);
|
||||||
EXPECT_PRINTF(buffer, "%#E", -42.0);
|
EXPECT_PRINTF(buffer, "%#E", -42.0);
|
||||||
|
|
||||||
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
if (fmt::internal::use_grisu()) {
|
||||||
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
|
EXPECT_PRINTF("-42.0", "%#g", -42.0);
|
||||||
|
EXPECT_PRINTF("-42.0", "%#G", -42.0);
|
||||||
|
} else {
|
||||||
|
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
||||||
|
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
|
||||||
|
}
|
||||||
|
|
||||||
safe_sprintf(buffer, "%#a", 16.0);
|
safe_sprintf(buffer, "%#a", 16.0);
|
||||||
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
||||||
|
@ -202,27 +199,25 @@ TEST(PrintfTest, HashFlag) {
|
||||||
|
|
||||||
TEST(PrintfTest, Width) {
|
TEST(PrintfTest, Width) {
|
||||||
EXPECT_PRINTF(" abc", "%5s", "abc");
|
EXPECT_PRINTF(" abc", "%5s", "abc");
|
||||||
EXPECT_PRINTF(" -42", "%5s", "-42");
|
|
||||||
EXPECT_PRINTF(" 0.123456", "%10s", 0.123456);
|
|
||||||
|
|
||||||
// Width cannot be specified twice.
|
// Width cannot be specified twice.
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), format_error,
|
||||||
"unknown format code '-' for integer");
|
"invalid type specifier");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}d", BIG_NUM), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}d", BIG_NUM), 42),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%1${}d", BIG_NUM), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%1${}d", BIG_NUM), 42),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DynamicWidth) {
|
TEST(PrintfTest, DynamicWidth) {
|
||||||
EXPECT_EQ(" 42", fmt::sprintf("%*d", 5, 42));
|
EXPECT_EQ(" 42", fmt::sprintf("%*d", 5, 42));
|
||||||
EXPECT_EQ("42 ", fmt::sprintf("%*d", -5, 42));
|
EXPECT_EQ("42 ", fmt::sprintf("%*d", -5, 42));
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), format_error,
|
||||||
"width is not integer");
|
"width is not integer");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d"), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%*d"), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d", BIG_NUM, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%*d", BIG_NUM, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,41 +260,41 @@ TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
|
||||||
TEST(PrintfTest, DynamicPrecision) {
|
TEST(PrintfTest, DynamicPrecision) {
|
||||||
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
|
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
|
||||||
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
|
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), format_error,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
if (sizeof(fmt::LongLong) != sizeof(int)) {
|
if (sizeof(long long) != sizeof(int)) {
|
||||||
fmt::LongLong prec = static_cast<fmt::LongLong>(INT_MIN) - 1;
|
long long prec = static_cast<long long>(INT_MIN) - 1;
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct MakeSigned { typedef T Type; };
|
struct make_signed { typedef T type; };
|
||||||
|
|
||||||
#define SPECIALIZE_MAKE_SIGNED(T, S) \
|
#define SPECIALIZE_MAKE_SIGNED(T, S) \
|
||||||
template <> \
|
template <> \
|
||||||
struct MakeSigned<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);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned short, short);
|
SPECIALIZE_MAKE_SIGNED(unsigned short, short);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned, int);
|
SPECIALIZE_MAKE_SIGNED(unsigned, int);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned long, long);
|
SPECIALIZE_MAKE_SIGNED(unsigned long, long);
|
||||||
SPECIALIZE_MAKE_SIGNED(fmt::ULongLong, fmt::LongLong);
|
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) {
|
||||||
fmt::LongLong signed_value = 0;
|
long long signed_value = 0;
|
||||||
fmt::ULongLong 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;
|
using std::numeric_limits;
|
||||||
fmt::ULongLong max = numeric_limits<U>::max();
|
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>(numeric_limits<int>::max()))) {
|
||||||
signed_value = static_cast<int>(value);
|
signed_value = static_cast<int>(value);
|
||||||
|
@ -308,13 +303,13 @@ void TestLength(const char *length_spec, U value) {
|
||||||
signed_value = static_cast<unsigned>(value);
|
signed_value = static_cast<unsigned>(value);
|
||||||
unsigned_value = static_cast<unsigned>(value);
|
unsigned_value = static_cast<unsigned>(value);
|
||||||
}
|
}
|
||||||
using fmt::internal::MakeUnsigned;
|
|
||||||
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
|
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
|
||||||
signed_value = static_cast<fmt::LongLong>(value);
|
signed_value = static_cast<long long>(value);
|
||||||
unsigned_value = static_cast<typename MakeUnsigned<unsigned>::Type>(value);
|
unsigned_value =
|
||||||
|
static_cast<typename std::make_unsigned<unsigned>::type>(value);
|
||||||
} else {
|
} else {
|
||||||
signed_value = static_cast<typename MakeSigned<T>::Type>(value);
|
signed_value = static_cast<typename make_signed<T>::type>(value);
|
||||||
unsigned_value = static_cast<typename MakeUnsigned<T>::Type>(value);
|
unsigned_value = static_cast<typename std::make_unsigned<T>::type>(value);
|
||||||
}
|
}
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << signed_value;
|
os << signed_value;
|
||||||
|
@ -341,20 +336,20 @@ void TestLength(const char *length_spec) {
|
||||||
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, fmt::LongLong(min) - 1);
|
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
|
||||||
fmt::ULongLong long_long_max = std::numeric_limits<fmt::LongLong>::max();
|
unsigned long long long_long_max = std::numeric_limits<long long>::max();
|
||||||
if (static_cast<fmt::ULongLong>(max) < long_long_max)
|
if (static_cast<unsigned long long>(max) < long_long_max)
|
||||||
TestLength<T>(length_spec, fmt::LongLong(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, std::numeric_limits<unsigned short>::max());
|
||||||
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, std::numeric_limits<int>::max());
|
||||||
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, std::numeric_limits<unsigned>::max());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::LongLong>::min());
|
TestLength<T>(length_spec, std::numeric_limits<long long>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::LongLong>::max());
|
TestLength<T>(length_spec, std::numeric_limits<long long>::max());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::ULongLong>::min());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::ULongLong>::max());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Length) {
|
TEST(PrintfTest, Length) {
|
||||||
|
@ -365,8 +360,8 @@ TEST(PrintfTest, Length) {
|
||||||
TestLength<unsigned short>("h");
|
TestLength<unsigned short>("h");
|
||||||
TestLength<long>("l");
|
TestLength<long>("l");
|
||||||
TestLength<unsigned long>("l");
|
TestLength<unsigned long>("l");
|
||||||
TestLength<fmt::LongLong>("ll");
|
TestLength<long long>("ll");
|
||||||
TestLength<fmt::ULongLong>("ll");
|
TestLength<unsigned long long>("ll");
|
||||||
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");
|
||||||
|
@ -383,19 +378,17 @@ TEST(PrintfTest, Bool) {
|
||||||
TEST(PrintfTest, Int) {
|
TEST(PrintfTest, Int) {
|
||||||
EXPECT_PRINTF("-42", "%d", -42);
|
EXPECT_PRINTF("-42", "%d", -42);
|
||||||
EXPECT_PRINTF("-42", "%i", -42);
|
EXPECT_PRINTF("-42", "%i", -42);
|
||||||
EXPECT_PRINTF("-42", "%s", -42);
|
|
||||||
unsigned u = 0 - 42u;
|
unsigned u = 0 - 42u;
|
||||||
EXPECT_PRINTF(fmt::format("{}", u), "%u", -42);
|
EXPECT_PRINTF(fmt::format("{}", u), "%u", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42);
|
EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42);
|
EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
|
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{}", u), "%s", u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, LongLong) {
|
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.
|
||||||
fmt::LongLong max = std::numeric_limits<fmt::LongLong>::max();
|
long long max = std::numeric_limits<long long>::max();
|
||||||
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +398,6 @@ TEST(PrintfTest, Float) {
|
||||||
EXPECT_PRINTF("392.6", "%.1f", 392.65);
|
EXPECT_PRINTF("392.6", "%.1f", 392.65);
|
||||||
EXPECT_PRINTF("393", "%.f", 392.65);
|
EXPECT_PRINTF("393", "%.f", 392.65);
|
||||||
EXPECT_PRINTF("392.650000", "%F", 392.65);
|
EXPECT_PRINTF("392.650000", "%F", 392.65);
|
||||||
EXPECT_PRINTF("392.65", "%s", 392.65);
|
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[BUFFER_SIZE];
|
||||||
safe_sprintf(buffer, "%e", 392.65);
|
safe_sprintf(buffer, "%e", 392.65);
|
||||||
EXPECT_PRINTF(buffer, "%e", 392.65);
|
EXPECT_PRINTF(buffer, "%e", 392.65);
|
||||||
|
@ -423,43 +415,52 @@ TEST(PrintfTest, Inf) {
|
||||||
double inf = std::numeric_limits<double>::infinity();
|
double inf = std::numeric_limits<double>::infinity();
|
||||||
for (const char* type = "fega"; *type; ++type) {
|
for (const char* type = "fega"; *type; ++type) {
|
||||||
EXPECT_PRINTF("inf", fmt::format("%{}", *type), inf);
|
EXPECT_PRINTF("inf", fmt::format("%{}", *type), inf);
|
||||||
char upper = std::toupper(*type);
|
char upper = static_cast<char>(std::toupper(*type));
|
||||||
EXPECT_PRINTF("INF", fmt::format("%{}", upper), inf);
|
EXPECT_PRINTF("INF", fmt::format("%{}", upper), inf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Char) {
|
TEST(PrintfTest, Char) {
|
||||||
EXPECT_PRINTF("x", "%c", 'x');
|
EXPECT_PRINTF("x", "%c", 'x');
|
||||||
EXPECT_PRINTF("x", "%s", 'x');
|
|
||||||
int max = std::numeric_limits<int>::max();
|
int max = std::numeric_limits<int>::max();
|
||||||
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');
|
||||||
// TODO: test wchar_t
|
EXPECT_PRINTF(L"x", L"%c", L'x');
|
||||||
|
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 = 0;
|
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);
|
||||||
// TODO: wide string
|
EXPECT_PRINTF(L"abc", L"%s", L"abc");
|
||||||
|
const wchar_t *null_wstr = nullptr;
|
||||||
|
EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
|
||||||
|
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Pointer) {
|
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);
|
||||||
EXPECT_PRINTF(fmt::format("{}", p), "%s", p);
|
p = nullptr;
|
||||||
p = 0;
|
|
||||||
EXPECT_PRINTF("(nil)", "%p", p);
|
EXPECT_PRINTF("(nil)", "%p", p);
|
||||||
EXPECT_PRINTF(" (nil)", "%10p", p);
|
EXPECT_PRINTF(" (nil)", "%10p", p);
|
||||||
EXPECT_PRINTF("(nil)", "%s", p);
|
|
||||||
EXPECT_PRINTF(" (nil)", "%10s", 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 = 0;
|
const char *null_str = nullptr;
|
||||||
EXPECT_PRINTF("(nil)", "%p", null_str);
|
EXPECT_PRINTF("(nil)", "%p", null_str);
|
||||||
EXPECT_PRINTF("(null)", "%s", null_str);
|
|
||||||
|
p = &n;
|
||||||
|
EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p);
|
||||||
|
p = nullptr;
|
||||||
|
EXPECT_PRINTF(L"(nil)", L"%p", p);
|
||||||
|
EXPECT_PRINTF(L" (nil)", L"%10p", p);
|
||||||
|
const wchar_t *w = L"test";
|
||||||
|
EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w);
|
||||||
|
const wchar_t *null_wstr = nullptr;
|
||||||
|
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Location) {
|
TEST(PrintfTest, Location) {
|
||||||
|
@ -482,8 +483,8 @@ TEST(PrintfTest, Examples) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PrintfError) {
|
TEST(PrintfTest, PrintfError) {
|
||||||
fmt::File read_end, write_end;
|
fmt::file read_end, write_end;
|
||||||
fmt::File::pipe(read_end, write_end);
|
fmt::file::pipe(read_end, write_end);
|
||||||
int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
|
int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
|
||||||
EXPECT_LT(result, 0);
|
EXPECT_LT(result, 0);
|
||||||
}
|
}
|
||||||
|
@ -503,9 +504,3 @@ TEST(PrintfTest, OStream) {
|
||||||
EXPECT_EQ("Don't panic!", os.str());
|
EXPECT_EQ("Don't panic!", os.str());
|
||||||
EXPECT_EQ(12, ret);
|
EXPECT_EQ(12, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Writer) {
|
|
||||||
fmt::MemoryWriter writer;
|
|
||||||
printf(writer, "%d", 42);
|
|
||||||
EXPECT_EQ("42", writer.str());
|
|
||||||
}
|
|
||||||
|
|
88
externals/fmt/test/ranges-test.cc
vendored
Normal file
88
externals/fmt/test/ranges-test.cc
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// Formatting library for C++ - the core API
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
|
#include "fmt/ranges.h"
|
||||||
|
|
||||||
|
/// Check if 'if constexpr' is supported.
|
||||||
|
#if (__cplusplus > 201402L) || \
|
||||||
|
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
|
||||||
|
|
||||||
|
#include "gtest.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
TEST(RangesTest, FormatVector) {
|
||||||
|
std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
|
||||||
|
auto ivf = fmt::format("{}", iv);
|
||||||
|
EXPECT_EQ("{1, 2, 3, 5, 7, 11}", ivf);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangesTest, FormatVector2) {
|
||||||
|
std::vector<std::vector<int32_t>> ivv{{1, 2}, {3, 5}, {7, 11}};
|
||||||
|
auto ivf = fmt::format("{}", ivv);
|
||||||
|
EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangesTest, FormatMap) {
|
||||||
|
std::map<std::string, int32_t> simap{{"one", 1}, {"two", 2}};
|
||||||
|
EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format("{}", simap));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangesTest, FormatPair) {
|
||||||
|
std::pair<int64_t, float> pa1{42, 3.14159265358979f};
|
||||||
|
EXPECT_EQ("(42, 3.14159)", fmt::format("{}", pa1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangesTest, FormatTuple) {
|
||||||
|
std::tuple<int64_t, float, std::string, char> tu1{42, 3.14159265358979f,
|
||||||
|
"this is tuple", 'i'};
|
||||||
|
EXPECT_EQ("(42, 3.14159, \"this is tuple\", 'i')", fmt::format("{}", tu1));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct my_struct {
|
||||||
|
int32_t i;
|
||||||
|
std::string str; // can throw
|
||||||
|
template <std::size_t N>
|
||||||
|
decltype(auto) get() const noexcept {
|
||||||
|
if constexpr (N == 0)
|
||||||
|
return i;
|
||||||
|
else if constexpr (N == 1)
|
||||||
|
return fmt::string_view{str};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
decltype(auto) get(const my_struct& s) noexcept {
|
||||||
|
return s.get<N>();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
struct tuple_element<N, my_struct> {
|
||||||
|
using type = decltype(std::declval<my_struct>().get<N>());
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
TEST(RangesTest, FormatStruct) {
|
||||||
|
my_struct mst{13, "my struct"};
|
||||||
|
EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
|
||||||
|
// 201402L && _MSC_VER >= 1910)
|
84
externals/fmt/test/string-test.cc
vendored
84
externals/fmt/test/string-test.cc
vendored
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
Tests of string utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmt/string.h"
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
using fmt::internal::StringBuffer;
|
|
||||||
|
|
||||||
TEST(StringBufferTest, Empty) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
std::string data;
|
|
||||||
// std::string may have initial capacity.
|
|
||||||
std::size_t capacity = data.capacity();
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ("", data);
|
|
||||||
EXPECT_EQ(capacity, data.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringBufferTest, Reserve) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
std::size_t capacity = std::string().capacity() + 10;
|
|
||||||
buffer.reserve(capacity);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(capacity, buffer.capacity());
|
|
||||||
std::string data;
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ("", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringBufferTest, Resize) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
std::size_t size = std::string().capacity() + 10;
|
|
||||||
buffer.resize(size);
|
|
||||||
EXPECT_EQ(size, buffer.size());
|
|
||||||
EXPECT_EQ(size, buffer.capacity());
|
|
||||||
std::string data;
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ(size, data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringBufferTest, MoveTo) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
std::size_t size = std::string().capacity() + 10;
|
|
||||||
buffer.resize(size);
|
|
||||||
const char *p = &buffer[0];
|
|
||||||
std::string data;
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ(p, &data[0]);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringWriterTest, MoveTo) {
|
|
||||||
fmt::StringWriter out;
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
std::string s;
|
|
||||||
out.move_to(s);
|
|
||||||
EXPECT_EQ("The answer is 42\n", s);
|
|
||||||
EXPECT_EQ(0u, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringWriterTest, WString) {
|
|
||||||
fmt::WStringWriter out;
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
std::wstring s;
|
|
||||||
out.move_to(s);
|
|
||||||
EXPECT_EQ(L"The answer is 42\n", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringTest, ToString) {
|
|
||||||
EXPECT_EQ("42", fmt::to_string(42));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringTest, ToWString) {
|
|
||||||
EXPECT_EQ(L"42", fmt::to_wstring(42));
|
|
||||||
}
|
|
49
externals/fmt/test/test-assert.h
vendored
49
externals/fmt/test/test-assert.h
vendored
|
@ -1,47 +1,26 @@
|
||||||
/*
|
// Formatting library for C++ - test version of FMT_ASSERT
|
||||||
Test version of FMT_ASSERT
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
Copyright (c) 2015, Victor Zverovich
|
#ifndef FMT_TEST_ASSERT_H_
|
||||||
All rights reserved.
|
#define FMT_TEST_ASSERT_H_
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_TEST_ASSERT_H
|
|
||||||
#define FMT_TEST_ASSERT_H
|
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include "gtest.h"
|
||||||
|
|
||||||
class AssertionFailure : public std::logic_error {
|
class assertion_failure : public std::logic_error {
|
||||||
public:
|
public:
|
||||||
explicit AssertionFailure(const char *message) : std::logic_error(message) {}
|
explicit assertion_failure(const char *message) : std::logic_error(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FMT_ASSERT(condition, message) \
|
#define FMT_ASSERT(condition, message) \
|
||||||
if (!(condition)) throw AssertionFailure(message);
|
if (!(condition)) throw assertion_failure(message);
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
|
||||||
|
|
||||||
// Expects an assertion failure.
|
// Expects an assertion failure.
|
||||||
#define EXPECT_ASSERT(stmt, message) \
|
#define EXPECT_ASSERT(stmt, message) \
|
||||||
EXPECT_THROW_MSG(stmt, AssertionFailure, message)
|
FMT_TEST_THROW_(stmt, assertion_failure, message, GTEST_NONFATAL_FAILURE_)
|
||||||
|
|
||||||
#endif // FMT_TEST_ASSERT_H
|
#endif // FMT_TEST_ASSERT_H_
|
||||||
|
|
34
externals/fmt/test/test-main.cc
vendored
34
externals/fmt/test/test-main.cc
vendored
|
@ -1,32 +1,12 @@
|
||||||
/*
|
// Formatting library for C++ - test main function.
|
||||||
Test main function.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <gtest/gtest.h>
|
#include "gtest.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
|
23
externals/fmt/test/time-test.cc
vendored
23
externals/fmt/test/time-test.cc
vendored
|
@ -1,16 +1,15 @@
|
||||||
/*
|
// Formatting library for C++ - time formatting tests
|
||||||
Time formatting tests
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock.h"
|
||||||
#include "fmt/time.h"
|
#include "fmt/time.h"
|
||||||
|
|
||||||
TEST(TimeTest, Format) {
|
TEST(TimeTest, Format) {
|
||||||
|
@ -27,7 +26,7 @@ TEST(TimeTest, GrowBuffer) {
|
||||||
for (int i = 0; i < 30; ++i)
|
for (int i = 0; i < 30; ++i)
|
||||||
s += "%c";
|
s += "%c";
|
||||||
s += "}\n";
|
s += "}\n";
|
||||||
std::time_t t = std::time(0);
|
std::time_t t = std::time(nullptr);
|
||||||
fmt::format(s, *std::localtime(&t));
|
fmt::format(s, *std::localtime(&t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ TEST(TimeTest, EmptyResult) {
|
||||||
EXPECT_EQ("", fmt::format("{}", std::tm()));
|
EXPECT_EQ("", fmt::format("{}", std::tm()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
|
static bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
|
||||||
return lhs.tm_sec == rhs.tm_sec &&
|
return lhs.tm_sec == rhs.tm_sec &&
|
||||||
lhs.tm_min == rhs.tm_min &&
|
lhs.tm_min == rhs.tm_min &&
|
||||||
lhs.tm_hour == rhs.tm_hour &&
|
lhs.tm_hour == rhs.tm_hour &&
|
||||||
|
@ -48,13 +47,13 @@ bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimeTest, LocalTime) {
|
TEST(TimeTest, LocalTime) {
|
||||||
std::time_t t = std::time(0);
|
std::time_t t = std::time(nullptr);
|
||||||
std::tm tm = *std::localtime(&t);
|
std::tm tm = *std::localtime(&t);
|
||||||
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
|
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimeTest, GMTime) {
|
TEST(TimeTest, GMTime) {
|
||||||
std::time_t t = std::time(0);
|
std::time_t t = std::time(nullptr);
|
||||||
std::tm tm = *std::gmtime(&t);
|
std::tm tm = *std::gmtime(&t);
|
||||||
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
||||||
}
|
}
|
||||||
|
|
985
externals/fmt/test/util-test.cc
vendored
985
externals/fmt/test/util-test.cc
vendored
|
@ -1,985 +0,0 @@
|
||||||
/*
|
|
||||||
Utility tests.
|
|
||||||
|
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "test-assert.h"
|
|
||||||
|
|
||||||
#include <cfloat>
|
|
||||||
#include <climits>
|
|
||||||
#include <cstring>
|
|
||||||
#include <functional>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#if FMT_USE_TYPE_TRAITS
|
|
||||||
# include <type_traits>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
|
||||||
#include "gtest-extra.h"
|
|
||||||
#include "mock-allocator.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
// Check if format.h compiles with windows.h included.
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
|
||||||
|
|
||||||
#undef max
|
|
||||||
|
|
||||||
using fmt::StringRef;
|
|
||||||
using fmt::internal::Arg;
|
|
||||||
using fmt::Buffer;
|
|
||||||
using fmt::internal::MemoryBuffer;
|
|
||||||
|
|
||||||
using testing::Return;
|
|
||||||
using testing::StrictMock;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct Test {};
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void format_arg(fmt::BasicFormatter<Char> &f, const Char *, Test) {
|
|
||||||
f.writer() << "test";
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename T>
|
|
||||||
Arg make_arg(const T &value) {
|
|
||||||
typedef fmt::internal::MakeValue< fmt::BasicFormatter<Char> > MakeValue;
|
|
||||||
Arg arg = MakeValue(value);
|
|
||||||
arg.type = static_cast<Arg::Type>(MakeValue::type(value));
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void CheckForwarding(
|
|
||||||
MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) {
|
|
||||||
int mem;
|
|
||||||
// Check if value_type is properly defined.
|
|
||||||
AllocatorRef< MockAllocator<int> >::value_type *ptr = &mem;
|
|
||||||
// Check forwarding.
|
|
||||||
EXPECT_CALL(alloc, allocate(42, 0)).WillOnce(Return(ptr));
|
|
||||||
ref.allocate(42, 0);
|
|
||||||
EXPECT_CALL(alloc, deallocate(ptr, 42));
|
|
||||||
ref.deallocate(ptr, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AllocatorTest, AllocatorRef) {
|
|
||||||
StrictMock< MockAllocator<int> > alloc;
|
|
||||||
typedef AllocatorRef< MockAllocator<int> > TestAllocatorRef;
|
|
||||||
TestAllocatorRef ref(&alloc);
|
|
||||||
// Check if AllocatorRef forwards to the underlying allocator.
|
|
||||||
CheckForwarding(alloc, ref);
|
|
||||||
TestAllocatorRef ref2(ref);
|
|
||||||
CheckForwarding(alloc, ref2);
|
|
||||||
TestAllocatorRef ref3;
|
|
||||||
EXPECT_EQ(0, ref3.get());
|
|
||||||
ref3 = ref;
|
|
||||||
CheckForwarding(alloc, ref3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_TYPE_TRAITS
|
|
||||||
TEST(BufferTest, Noncopyable) {
|
|
||||||
EXPECT_FALSE(std::is_copy_constructible<Buffer<char> >::value);
|
|
||||||
EXPECT_FALSE(std::is_copy_assignable<Buffer<char> >::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, Nonmoveable) {
|
|
||||||
EXPECT_FALSE(std::is_move_constructible<Buffer<char> >::value);
|
|
||||||
EXPECT_FALSE(std::is_move_assignable<Buffer<char> >::value);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A test buffer with a dummy grow method.
|
|
||||||
template <typename T>
|
|
||||||
struct TestBuffer : Buffer<T> {
|
|
||||||
void grow(std::size_t size) { this->capacity_ = size; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct MockBuffer : Buffer<T> {
|
|
||||||
MOCK_METHOD1(do_grow, void (std::size_t size));
|
|
||||||
|
|
||||||
void grow(std::size_t size) {
|
|
||||||
this->capacity_ = size;
|
|
||||||
do_grow(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
MockBuffer() {}
|
|
||||||
MockBuffer(T *ptr) : Buffer<T>(ptr) {}
|
|
||||||
MockBuffer(T *ptr, std::size_t capacity) : Buffer<T>(ptr, capacity) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(BufferTest, Ctor) {
|
|
||||||
{
|
|
||||||
MockBuffer<int> buffer;
|
|
||||||
EXPECT_EQ(0, &buffer[0]);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int dummy;
|
|
||||||
MockBuffer<int> buffer(&dummy);
|
|
||||||
EXPECT_EQ(&dummy, &buffer[0]);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int dummy;
|
|
||||||
std::size_t capacity = std::numeric_limits<std::size_t>::max();
|
|
||||||
MockBuffer<int> buffer(&dummy, capacity);
|
|
||||||
EXPECT_EQ(&dummy, &buffer[0]);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(capacity, buffer.capacity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DyingBuffer : TestBuffer<int> {
|
|
||||||
MOCK_METHOD0(die, void());
|
|
||||||
~DyingBuffer() { die(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(BufferTest, VirtualDtor) {
|
|
||||||
typedef StrictMock<DyingBuffer> StictMockBuffer;
|
|
||||||
StictMockBuffer *mock_buffer = new StictMockBuffer();
|
|
||||||
EXPECT_CALL(*mock_buffer, die());
|
|
||||||
Buffer<int> *buffer = mock_buffer;
|
|
||||||
delete buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, Access) {
|
|
||||||
char data[10];
|
|
||||||
MockBuffer<char> buffer(data, sizeof(data));
|
|
||||||
buffer[0] = 11;
|
|
||||||
EXPECT_EQ(11, buffer[0]);
|
|
||||||
buffer[3] = 42;
|
|
||||||
EXPECT_EQ(42, *(&buffer[0] + 3));
|
|
||||||
const Buffer<char> &const_buffer = buffer;
|
|
||||||
EXPECT_EQ(42, const_buffer[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, Resize) {
|
|
||||||
char data[123];
|
|
||||||
MockBuffer<char> buffer(data, sizeof(data));
|
|
||||||
buffer[10] = 42;
|
|
||||||
EXPECT_EQ(42, buffer[10]);
|
|
||||||
buffer.resize(20);
|
|
||||||
EXPECT_EQ(20u, buffer.size());
|
|
||||||
EXPECT_EQ(123u, buffer.capacity());
|
|
||||||
EXPECT_EQ(42, buffer[10]);
|
|
||||||
buffer.resize(5);
|
|
||||||
EXPECT_EQ(5u, buffer.size());
|
|
||||||
EXPECT_EQ(123u, buffer.capacity());
|
|
||||||
EXPECT_EQ(42, buffer[10]);
|
|
||||||
// Check if resize calls grow.
|
|
||||||
EXPECT_CALL(buffer, do_grow(124));
|
|
||||||
buffer.resize(124);
|
|
||||||
EXPECT_CALL(buffer, do_grow(200));
|
|
||||||
buffer.resize(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, Clear) {
|
|
||||||
TestBuffer<char> buffer;
|
|
||||||
buffer.resize(20);
|
|
||||||
buffer.clear();
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(20u, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, PushBack) {
|
|
||||||
int data[15];
|
|
||||||
MockBuffer<int> buffer(data, 10);
|
|
||||||
buffer.push_back(11);
|
|
||||||
EXPECT_EQ(11, buffer[0]);
|
|
||||||
EXPECT_EQ(1u, buffer.size());
|
|
||||||
buffer.resize(10);
|
|
||||||
EXPECT_CALL(buffer, do_grow(11));
|
|
||||||
buffer.push_back(22);
|
|
||||||
EXPECT_EQ(22, buffer[10]);
|
|
||||||
EXPECT_EQ(11u, buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, Append) {
|
|
||||||
char data[15];
|
|
||||||
MockBuffer<char> buffer(data, 10);
|
|
||||||
const char *test = "test";
|
|
||||||
buffer.append(test, test + 5);
|
|
||||||
EXPECT_STREQ(test, &buffer[0]);
|
|
||||||
EXPECT_EQ(5u, buffer.size());
|
|
||||||
buffer.resize(10);
|
|
||||||
EXPECT_CALL(buffer, do_grow(12));
|
|
||||||
buffer.append(test, test + 2);
|
|
||||||
EXPECT_EQ('t', buffer[10]);
|
|
||||||
EXPECT_EQ('e', buffer[11]);
|
|
||||||
EXPECT_EQ(12u, buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BufferTest, AppendAllocatesEnoughStorage) {
|
|
||||||
char data[19];
|
|
||||||
MockBuffer<char> buffer(data, 10);
|
|
||||||
const char *test = "abcdefgh";
|
|
||||||
buffer.resize(10);
|
|
||||||
EXPECT_CALL(buffer, do_grow(19));
|
|
||||||
buffer.append(test, test + 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MemoryBufferTest, Ctor) {
|
|
||||||
MemoryBuffer<char, 123> buffer;
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(123u, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_RVALUE_REFERENCES
|
|
||||||
|
|
||||||
typedef AllocatorRef< std::allocator<char> > TestAllocator;
|
|
||||||
|
|
||||||
void check_move_buffer(const char *str,
|
|
||||||
MemoryBuffer<char, 5, TestAllocator> &buffer) {
|
|
||||||
std::allocator<char> *alloc = buffer.get_allocator().get();
|
|
||||||
MemoryBuffer<char, 5, TestAllocator> buffer2(std::move(buffer));
|
|
||||||
// Move shouldn't destroy the inline content of the first buffer.
|
|
||||||
EXPECT_EQ(str, std::string(&buffer[0], buffer.size()));
|
|
||||||
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
|
|
||||||
EXPECT_EQ(5u, buffer2.capacity());
|
|
||||||
// Move should transfer allocator.
|
|
||||||
EXPECT_EQ(0, buffer.get_allocator().get());
|
|
||||||
EXPECT_EQ(alloc, buffer2.get_allocator().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MemoryBufferTest, MoveCtor) {
|
|
||||||
std::allocator<char> alloc;
|
|
||||||
MemoryBuffer<char, 5, TestAllocator> buffer((TestAllocator(&alloc)));
|
|
||||||
const char test[] = "test";
|
|
||||||
buffer.append(test, test + 4);
|
|
||||||
check_move_buffer("test", buffer);
|
|
||||||
// Adding one more character fills the inline buffer, but doesn't cause
|
|
||||||
// dynamic allocation.
|
|
||||||
buffer.push_back('a');
|
|
||||||
check_move_buffer("testa", buffer);
|
|
||||||
const char *inline_buffer_ptr = &buffer[0];
|
|
||||||
// Adding one more character causes the content to move from the inline to
|
|
||||||
// a dynamically allocated buffer.
|
|
||||||
buffer.push_back('b');
|
|
||||||
MemoryBuffer<char, 5, TestAllocator> buffer2(std::move(buffer));
|
|
||||||
// Move should rip the guts of the first buffer.
|
|
||||||
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
|
|
||||||
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
|
|
||||||
EXPECT_GT(buffer2.capacity(), 5u);
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_move_assign_buffer(const char *str, MemoryBuffer<char, 5> &buffer) {
|
|
||||||
MemoryBuffer<char, 5> buffer2;
|
|
||||||
buffer2 = std::move(buffer);
|
|
||||||
// Move shouldn't destroy the inline content of the first buffer.
|
|
||||||
EXPECT_EQ(str, std::string(&buffer[0], buffer.size()));
|
|
||||||
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
|
|
||||||
EXPECT_EQ(5u, buffer2.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MemoryBufferTest, MoveAssignment) {
|
|
||||||
MemoryBuffer<char, 5> buffer;
|
|
||||||
const char test[] = "test";
|
|
||||||
buffer.append(test, test + 4);
|
|
||||||
check_move_assign_buffer("test", buffer);
|
|
||||||
// Adding one more character fills the inline buffer, but doesn't cause
|
|
||||||
// dynamic allocation.
|
|
||||||
buffer.push_back('a');
|
|
||||||
check_move_assign_buffer("testa", buffer);
|
|
||||||
const char *inline_buffer_ptr = &buffer[0];
|
|
||||||
// Adding one more character causes the content to move from the inline to
|
|
||||||
// a dynamically allocated buffer.
|
|
||||||
buffer.push_back('b');
|
|
||||||
MemoryBuffer<char, 5> buffer2;
|
|
||||||
buffer2 = std::move(buffer);
|
|
||||||
// Move should rip the guts of the first buffer.
|
|
||||||
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
|
|
||||||
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
|
|
||||||
EXPECT_GT(buffer2.capacity(), 5u);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FMT_USE_RVALUE_REFERENCES
|
|
||||||
|
|
||||||
TEST(MemoryBufferTest, Grow) {
|
|
||||||
typedef AllocatorRef< MockAllocator<int> > Allocator;
|
|
||||||
typedef MemoryBuffer<int, 10, Allocator> Base;
|
|
||||||
MockAllocator<int> alloc;
|
|
||||||
struct TestMemoryBuffer : Base {
|
|
||||||
TestMemoryBuffer(Allocator alloc) : Base(alloc) {}
|
|
||||||
void grow(std::size_t size) { Base::grow(size); }
|
|
||||||
} buffer((Allocator(&alloc)));
|
|
||||||
buffer.resize(7);
|
|
||||||
using fmt::internal::to_unsigned;
|
|
||||||
for (int i = 0; i < 7; ++i)
|
|
||||||
buffer[to_unsigned(i)] = i * i;
|
|
||||||
EXPECT_EQ(10u, buffer.capacity());
|
|
||||||
int mem[20];
|
|
||||||
mem[7] = 0xdead;
|
|
||||||
EXPECT_CALL(alloc, allocate(20, 0)).WillOnce(Return(mem));
|
|
||||||
buffer.grow(20);
|
|
||||||
EXPECT_EQ(20u, buffer.capacity());
|
|
||||||
// Check if size elements have been copied
|
|
||||||
for (int i = 0; i < 7; ++i)
|
|
||||||
EXPECT_EQ(i * i, buffer[to_unsigned(i)]);
|
|
||||||
// and no more than that.
|
|
||||||
EXPECT_EQ(0xdead, buffer[7]);
|
|
||||||
EXPECT_CALL(alloc, deallocate(mem, 20));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MemoryBufferTest, Allocator) {
|
|
||||||
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
|
|
||||||
MemoryBuffer<char, 10, TestAllocator> buffer;
|
|
||||||
EXPECT_EQ(0, buffer.get_allocator().get());
|
|
||||||
StrictMock< MockAllocator<char> > alloc;
|
|
||||||
char mem;
|
|
||||||
{
|
|
||||||
MemoryBuffer<char, 10, TestAllocator> buffer2((TestAllocator(&alloc)));
|
|
||||||
EXPECT_EQ(&alloc, buffer2.get_allocator().get());
|
|
||||||
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
|
|
||||||
EXPECT_CALL(alloc, allocate(size, 0)).WillOnce(Return(&mem));
|
|
||||||
buffer2.reserve(size);
|
|
||||||
EXPECT_CALL(alloc, deallocate(&mem, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MemoryBufferTest, ExceptionInDeallocate) {
|
|
||||||
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
|
|
||||||
StrictMock< MockAllocator<char> > alloc;
|
|
||||||
MemoryBuffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc)));
|
|
||||||
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
|
|
||||||
std::vector<char> mem(size);
|
|
||||||
{
|
|
||||||
EXPECT_CALL(alloc, allocate(size, 0)).WillOnce(Return(&mem[0]));
|
|
||||||
buffer.resize(size);
|
|
||||||
std::fill(&buffer[0], &buffer[0] + size, 'x');
|
|
||||||
}
|
|
||||||
std::vector<char> mem2(2 * size);
|
|
||||||
{
|
|
||||||
EXPECT_CALL(alloc, allocate(2 * size, 0)).WillOnce(Return(&mem2[0]));
|
|
||||||
std::exception e;
|
|
||||||
EXPECT_CALL(alloc, deallocate(&mem[0], size)).WillOnce(testing::Throw(e));
|
|
||||||
EXPECT_THROW(buffer.reserve(2 * size), std::exception);
|
|
||||||
EXPECT_EQ(&mem2[0], &buffer[0]);
|
|
||||||
// Check that the data has been copied.
|
|
||||||
for (std::size_t i = 0; i < size; ++i)
|
|
||||||
EXPECT_EQ('x', buffer[i]);
|
|
||||||
}
|
|
||||||
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, Increment) {
|
|
||||||
char s[10] = "123";
|
|
||||||
increment(s);
|
|
||||||
EXPECT_STREQ("124", s);
|
|
||||||
s[2] = '8';
|
|
||||||
increment(s);
|
|
||||||
EXPECT_STREQ("129", s);
|
|
||||||
increment(s);
|
|
||||||
EXPECT_STREQ("130", s);
|
|
||||||
s[1] = s[2] = '9';
|
|
||||||
increment(s);
|
|
||||||
EXPECT_STREQ("200", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <Arg::Type>
|
|
||||||
struct ArgInfo;
|
|
||||||
|
|
||||||
#define ARG_INFO(type_code, Type, field) \
|
|
||||||
template <> \
|
|
||||||
struct ArgInfo<Arg::type_code> { \
|
|
||||||
static Type get(const Arg &arg) { return arg.field; } \
|
|
||||||
}
|
|
||||||
|
|
||||||
ARG_INFO(INT, int, int_value);
|
|
||||||
ARG_INFO(UINT, unsigned, uint_value);
|
|
||||||
ARG_INFO(LONG_LONG, fmt::LongLong, long_long_value);
|
|
||||||
ARG_INFO(ULONG_LONG, fmt::ULongLong, ulong_long_value);
|
|
||||||
ARG_INFO(BOOL, int, int_value);
|
|
||||||
ARG_INFO(CHAR, int, int_value);
|
|
||||||
ARG_INFO(DOUBLE, double, double_value);
|
|
||||||
ARG_INFO(LONG_DOUBLE, long double, long_double_value);
|
|
||||||
ARG_INFO(CSTRING, const char *, string.value);
|
|
||||||
ARG_INFO(STRING, const char *, string.value);
|
|
||||||
ARG_INFO(WSTRING, const wchar_t *, wstring.value);
|
|
||||||
ARG_INFO(POINTER, const void *, pointer);
|
|
||||||
ARG_INFO(CUSTOM, Arg::CustomValue, custom);
|
|
||||||
|
|
||||||
#define CHECK_ARG_INFO(Type, field, value) { \
|
|
||||||
Arg arg = Arg(); \
|
|
||||||
arg.field = value; \
|
|
||||||
EXPECT_EQ(value, ArgInfo<Arg::Type>::get(arg)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ArgTest, ArgInfo) {
|
|
||||||
CHECK_ARG_INFO(INT, int_value, 42);
|
|
||||||
CHECK_ARG_INFO(UINT, uint_value, 42u);
|
|
||||||
CHECK_ARG_INFO(LONG_LONG, long_long_value, 42);
|
|
||||||
CHECK_ARG_INFO(ULONG_LONG, ulong_long_value, 42u);
|
|
||||||
CHECK_ARG_INFO(DOUBLE, double_value, 4.2);
|
|
||||||
CHECK_ARG_INFO(LONG_DOUBLE, long_double_value, 4.2);
|
|
||||||
CHECK_ARG_INFO(CHAR, int_value, 'x');
|
|
||||||
const char STR[] = "abc";
|
|
||||||
CHECK_ARG_INFO(CSTRING, string.value, STR);
|
|
||||||
const wchar_t WSTR[] = L"abc";
|
|
||||||
CHECK_ARG_INFO(WSTRING, wstring.value, WSTR);
|
|
||||||
int p = 0;
|
|
||||||
CHECK_ARG_INFO(POINTER, pointer, &p);
|
|
||||||
Arg arg = Arg();
|
|
||||||
arg.custom.value = &p;
|
|
||||||
EXPECT_EQ(&p, ArgInfo<Arg::CUSTOM>::get(arg).value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_ARG_(Char, type_code, MakeArgType, ExpectedType, value) { \
|
|
||||||
MakeArgType input = static_cast<MakeArgType>(value); \
|
|
||||||
Arg arg = make_arg<Char>(input); \
|
|
||||||
EXPECT_EQ(Arg::type_code, arg.type); \
|
|
||||||
ExpectedType expected_value = static_cast<ExpectedType>(value); \
|
|
||||||
EXPECT_EQ(expected_value, ArgInfo<Arg::type_code>::get(arg)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_ARG(type_code, Type, value) \
|
|
||||||
EXPECT_ARG_(char, type_code, Type, Type, value)
|
|
||||||
|
|
||||||
#define EXPECT_ARGW(type_code, Type, value) \
|
|
||||||
EXPECT_ARG_(wchar_t, type_code, Type, Type, value)
|
|
||||||
|
|
||||||
TEST(ArgTest, MakeArg) {
|
|
||||||
// Test bool.
|
|
||||||
EXPECT_ARG_(char, BOOL, bool, int, true);
|
|
||||||
EXPECT_ARG_(wchar_t, BOOL, bool, int, true);
|
|
||||||
|
|
||||||
// Test char.
|
|
||||||
EXPECT_ARG(CHAR, char, 'a');
|
|
||||||
EXPECT_ARG(CHAR, char, CHAR_MIN);
|
|
||||||
EXPECT_ARG(CHAR, char, CHAR_MAX);
|
|
||||||
|
|
||||||
// Test wchar_t.
|
|
||||||
EXPECT_ARGW(CHAR, wchar_t, L'a');
|
|
||||||
EXPECT_ARGW(CHAR, wchar_t, WCHAR_MIN);
|
|
||||||
EXPECT_ARGW(CHAR, wchar_t, WCHAR_MAX);
|
|
||||||
|
|
||||||
// Test signed/unsigned char.
|
|
||||||
EXPECT_ARG(INT, signed char, 42);
|
|
||||||
EXPECT_ARG(INT, signed char, SCHAR_MIN);
|
|
||||||
EXPECT_ARG(INT, signed char, SCHAR_MAX);
|
|
||||||
EXPECT_ARG(UINT, unsigned char, 42);
|
|
||||||
EXPECT_ARG(UINT, unsigned char, UCHAR_MAX );
|
|
||||||
|
|
||||||
// Test short.
|
|
||||||
EXPECT_ARG(INT, short, 42);
|
|
||||||
EXPECT_ARG(INT, short, SHRT_MIN);
|
|
||||||
EXPECT_ARG(INT, short, SHRT_MAX);
|
|
||||||
EXPECT_ARG(UINT, unsigned short, 42);
|
|
||||||
EXPECT_ARG(UINT, unsigned short, USHRT_MAX);
|
|
||||||
|
|
||||||
// Test int.
|
|
||||||
EXPECT_ARG(INT, int, 42);
|
|
||||||
EXPECT_ARG(INT, int, INT_MIN);
|
|
||||||
EXPECT_ARG(INT, int, INT_MAX);
|
|
||||||
EXPECT_ARG(UINT, unsigned, 42);
|
|
||||||
EXPECT_ARG(UINT, unsigned, UINT_MAX);
|
|
||||||
|
|
||||||
// Test long.
|
|
||||||
#if LONG_MAX == INT_MAX
|
|
||||||
# define LONG INT
|
|
||||||
# define ULONG UINT
|
|
||||||
# define long_value int_value
|
|
||||||
# define ulong_value uint_value
|
|
||||||
#else
|
|
||||||
# define LONG LONG_LONG
|
|
||||||
# define ULONG ULONG_LONG
|
|
||||||
# define long_value long_long_value
|
|
||||||
# define ulong_value ulong_long_value
|
|
||||||
#endif
|
|
||||||
EXPECT_ARG(LONG, long, 42);
|
|
||||||
EXPECT_ARG(LONG, long, LONG_MIN);
|
|
||||||
EXPECT_ARG(LONG, long, LONG_MAX);
|
|
||||||
EXPECT_ARG(ULONG, unsigned long, 42);
|
|
||||||
EXPECT_ARG(ULONG, unsigned long, ULONG_MAX);
|
|
||||||
|
|
||||||
// Test long long.
|
|
||||||
EXPECT_ARG(LONG_LONG, fmt::LongLong, 42);
|
|
||||||
EXPECT_ARG(LONG_LONG, fmt::LongLong, LLONG_MIN);
|
|
||||||
EXPECT_ARG(LONG_LONG, fmt::LongLong, LLONG_MAX);
|
|
||||||
EXPECT_ARG(ULONG_LONG, fmt::ULongLong, 42);
|
|
||||||
EXPECT_ARG(ULONG_LONG, fmt::ULongLong, ULLONG_MAX);
|
|
||||||
|
|
||||||
// Test float.
|
|
||||||
EXPECT_ARG(DOUBLE, float, 4.2);
|
|
||||||
EXPECT_ARG(DOUBLE, float, FLT_MIN);
|
|
||||||
EXPECT_ARG(DOUBLE, float, FLT_MAX);
|
|
||||||
|
|
||||||
// Test double.
|
|
||||||
EXPECT_ARG(DOUBLE, double, 4.2);
|
|
||||||
EXPECT_ARG(DOUBLE, double, DBL_MIN);
|
|
||||||
EXPECT_ARG(DOUBLE, double, DBL_MAX);
|
|
||||||
|
|
||||||
// Test long double.
|
|
||||||
EXPECT_ARG(LONG_DOUBLE, long double, 4.2);
|
|
||||||
EXPECT_ARG(LONG_DOUBLE, long double, LDBL_MIN);
|
|
||||||
EXPECT_ARG(LONG_DOUBLE, long double, LDBL_MAX);
|
|
||||||
|
|
||||||
// Test string.
|
|
||||||
char STR[] = "test";
|
|
||||||
EXPECT_ARG(CSTRING, char*, STR);
|
|
||||||
EXPECT_ARG(CSTRING, const char*, STR);
|
|
||||||
EXPECT_ARG(STRING, std::string, STR);
|
|
||||||
EXPECT_ARG(STRING, fmt::StringRef, STR);
|
|
||||||
|
|
||||||
// Test wide string.
|
|
||||||
wchar_t WSTR[] = L"test";
|
|
||||||
EXPECT_ARGW(WSTRING, wchar_t*, WSTR);
|
|
||||||
EXPECT_ARGW(WSTRING, const wchar_t*, WSTR);
|
|
||||||
EXPECT_ARGW(WSTRING, std::wstring, WSTR);
|
|
||||||
EXPECT_ARGW(WSTRING, fmt::WStringRef, WSTR);
|
|
||||||
|
|
||||||
int n = 42;
|
|
||||||
EXPECT_ARG(POINTER, void*, &n);
|
|
||||||
EXPECT_ARG(POINTER, const void*, &n);
|
|
||||||
|
|
||||||
::Test t;
|
|
||||||
Arg arg = make_arg<char>(t);
|
|
||||||
EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type);
|
|
||||||
EXPECT_EQ(&t, arg.custom.value);
|
|
||||||
fmt::MemoryWriter w;
|
|
||||||
fmt::BasicFormatter<char> formatter(fmt::ArgList(), w);
|
|
||||||
const char *s = "}";
|
|
||||||
arg.custom.format(&formatter, &t, &s);
|
|
||||||
EXPECT_EQ("test", w.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, ArgList) {
|
|
||||||
fmt::ArgList args;
|
|
||||||
EXPECT_EQ(Arg::NONE, args[1].type);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CustomFormatter {
|
|
||||||
typedef char Char;
|
|
||||||
};
|
|
||||||
|
|
||||||
void format_arg(CustomFormatter &, const char *&s, const Test &) {
|
|
||||||
s = "custom_format";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, MakeValueWithCustomFormatter) {
|
|
||||||
::Test t;
|
|
||||||
Arg arg = fmt::internal::MakeValue<CustomFormatter>(t);
|
|
||||||
CustomFormatter formatter;
|
|
||||||
const char *s = "";
|
|
||||||
arg.custom.format(&formatter, &t, &s);
|
|
||||||
EXPECT_STREQ("custom_format", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Result {
|
|
||||||
Arg arg;
|
|
||||||
|
|
||||||
Result() : arg(make_arg<char>(0xdeadbeef)) {}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Result(const T& value) : arg(make_arg<char>(value)) {}
|
|
||||||
Result(const wchar_t *s) : arg(make_arg<wchar_t>(s)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TestVisitor : fmt::ArgVisitor<TestVisitor, Result> {
|
|
||||||
Result visit_int(int value) { return value; }
|
|
||||||
Result visit_uint(unsigned value) { return value; }
|
|
||||||
Result visit_long_long(fmt::LongLong value) { return value; }
|
|
||||||
Result visit_ulong_long(fmt::ULongLong value) { return value; }
|
|
||||||
Result visit_double(double value) { return value; }
|
|
||||||
Result visit_long_double(long double value) { return value; }
|
|
||||||
Result visit_char(int value) { return static_cast<char>(value); }
|
|
||||||
Result visit_cstring(const char *s) { return s; }
|
|
||||||
Result visit_string(fmt::internal::Arg::StringValue<char> s) {
|
|
||||||
return s.value;
|
|
||||||
}
|
|
||||||
Result visit_wstring(fmt::internal::Arg::StringValue<wchar_t> s) {
|
|
||||||
return s.value;
|
|
||||||
}
|
|
||||||
Result visit_pointer(const void *p) { return p; }
|
|
||||||
Result visit_custom(fmt::internal::Arg::CustomValue c) {
|
|
||||||
return *static_cast<const ::Test*>(c.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define EXPECT_RESULT_(Char, type_code, value) { \
|
|
||||||
Arg arg = make_arg<Char>(value); \
|
|
||||||
Result result = TestVisitor().visit(arg); \
|
|
||||||
EXPECT_EQ(Arg::type_code, result.arg.type); \
|
|
||||||
EXPECT_EQ(value, ArgInfo<Arg::type_code>::get(result.arg)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_RESULT(type_code, value) \
|
|
||||||
EXPECT_RESULT_(char, type_code, value)
|
|
||||||
#define EXPECT_RESULTW(type_code, value) \
|
|
||||||
EXPECT_RESULT_(wchar_t, type_code, value)
|
|
||||||
|
|
||||||
TEST(ArgVisitorTest, VisitAll) {
|
|
||||||
EXPECT_RESULT(INT, 42);
|
|
||||||
EXPECT_RESULT(UINT, 42u);
|
|
||||||
EXPECT_RESULT(LONG_LONG, 42ll);
|
|
||||||
EXPECT_RESULT(ULONG_LONG, 42ull);
|
|
||||||
EXPECT_RESULT(DOUBLE, 4.2);
|
|
||||||
EXPECT_RESULT(LONG_DOUBLE, 4.2l);
|
|
||||||
EXPECT_RESULT(CHAR, 'x');
|
|
||||||
const char STR[] = "abc";
|
|
||||||
EXPECT_RESULT(CSTRING, STR);
|
|
||||||
const wchar_t WSTR[] = L"abc";
|
|
||||||
EXPECT_RESULTW(WSTRING, WSTR);
|
|
||||||
const void *p = STR;
|
|
||||||
EXPECT_RESULT(POINTER, p);
|
|
||||||
::Test t;
|
|
||||||
Result result = TestVisitor().visit(make_arg<char>(t));
|
|
||||||
EXPECT_EQ(Arg::CUSTOM, result.arg.type);
|
|
||||||
EXPECT_EQ(&t, result.arg.custom.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestAnyVisitor : fmt::ArgVisitor<TestAnyVisitor, Result> {
|
|
||||||
template <typename T>
|
|
||||||
Result visit_any_int(T value) { return value; }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Result visit_any_double(T value) { return value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#undef EXPECT_RESULT
|
|
||||||
#define EXPECT_RESULT(type_code, value) { \
|
|
||||||
Result result = TestAnyVisitor().visit(make_arg<char>(value)); \
|
|
||||||
EXPECT_EQ(Arg::type_code, result.arg.type); \
|
|
||||||
EXPECT_EQ(value, ArgInfo<Arg::type_code>::get(result.arg)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ArgVisitorTest, VisitAny) {
|
|
||||||
EXPECT_RESULT(INT, 42);
|
|
||||||
EXPECT_RESULT(UINT, 42u);
|
|
||||||
EXPECT_RESULT(LONG_LONG, 42ll);
|
|
||||||
EXPECT_RESULT(ULONG_LONG, 42ull);
|
|
||||||
EXPECT_RESULT(DOUBLE, 4.2);
|
|
||||||
EXPECT_RESULT(LONG_DOUBLE, 4.2l);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestUnhandledVisitor :
|
|
||||||
fmt::ArgVisitor<TestUnhandledVisitor, const char *> {
|
|
||||||
const char *visit_unhandled_arg() { return "test"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define EXPECT_UNHANDLED(value) \
|
|
||||||
EXPECT_STREQ("test", TestUnhandledVisitor().visit(make_arg<wchar_t>(value)));
|
|
||||||
|
|
||||||
TEST(ArgVisitorTest, VisitUnhandledArg) {
|
|
||||||
EXPECT_UNHANDLED(42);
|
|
||||||
EXPECT_UNHANDLED(42u);
|
|
||||||
EXPECT_UNHANDLED(42ll);
|
|
||||||
EXPECT_UNHANDLED(42ull);
|
|
||||||
EXPECT_UNHANDLED(4.2);
|
|
||||||
EXPECT_UNHANDLED(4.2l);
|
|
||||||
EXPECT_UNHANDLED('x');
|
|
||||||
const char STR[] = "abc";
|
|
||||||
EXPECT_UNHANDLED(STR);
|
|
||||||
const wchar_t WSTR[] = L"abc";
|
|
||||||
EXPECT_UNHANDLED(WSTR);
|
|
||||||
const void *p = STR;
|
|
||||||
EXPECT_UNHANDLED(p);
|
|
||||||
EXPECT_UNHANDLED(::Test());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ArgVisitorTest, VisitInvalidArg) {
|
|
||||||
Arg arg = Arg();
|
|
||||||
arg.type = static_cast<Arg::Type>(Arg::NONE);
|
|
||||||
EXPECT_ASSERT(TestVisitor().visit(arg), "invalid argument type");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests fmt::internal::count_digits for integer type Int.
|
|
||||||
template <typename Int>
|
|
||||||
void test_count_digits() {
|
|
||||||
for (Int i = 0; i < 10; ++i)
|
|
||||||
EXPECT_EQ(1u, fmt::internal::count_digits(i));
|
|
||||||
for (Int i = 1, n = 1,
|
|
||||||
end = std::numeric_limits<Int>::max() / 10; n <= end; ++i) {
|
|
||||||
n *= 10;
|
|
||||||
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
|
|
||||||
EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, StringRef) {
|
|
||||||
// Test that StringRef::size() returns string length, not buffer size.
|
|
||||||
char str[100] = "some string";
|
|
||||||
EXPECT_EQ(std::strlen(str), StringRef(str).size());
|
|
||||||
EXPECT_LT(std::strlen(str), sizeof(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check StringRef's comparison operator.
|
|
||||||
template <template <typename> class Op>
|
|
||||||
void CheckOp() {
|
|
||||||
const char *inputs[] = {"foo", "fop", "fo"};
|
|
||||||
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
|
|
||||||
for (std::size_t i = 0; i < num_inputs; ++i) {
|
|
||||||
for (std::size_t j = 0; j < num_inputs; ++j) {
|
|
||||||
StringRef lhs(inputs[i]), rhs(inputs[j]);
|
|
||||||
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<StringRef>()(lhs, rhs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, StringRefCompare) {
|
|
||||||
EXPECT_EQ(0, StringRef("foo").compare(StringRef("foo")));
|
|
||||||
EXPECT_GT(StringRef("fop").compare(StringRef("foo")), 0);
|
|
||||||
EXPECT_LT(StringRef("foo").compare(StringRef("fop")), 0);
|
|
||||||
EXPECT_GT(StringRef("foo").compare(StringRef("fo")), 0);
|
|
||||||
EXPECT_LT(StringRef("fo").compare(StringRef("foo")), 0);
|
|
||||||
CheckOp<std::equal_to>();
|
|
||||||
CheckOp<std::not_equal_to>();
|
|
||||||
CheckOp<std::less>();
|
|
||||||
CheckOp<std::less_equal>();
|
|
||||||
CheckOp<std::greater>();
|
|
||||||
CheckOp<std::greater_equal>();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, CountDigits) {
|
|
||||||
test_count_digits<uint32_t>();
|
|
||||||
test_count_digits<uint64_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
TEST(UtilTest, UTF16ToUTF8) {
|
|
||||||
std::string s = "ёжик";
|
|
||||||
fmt::internal::UTF16ToUTF8 u(L"\x0451\x0436\x0438\x043A");
|
|
||||||
EXPECT_EQ(s, u.str());
|
|
||||||
EXPECT_EQ(s.size(), u.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, UTF8ToUTF16) {
|
|
||||||
std::string s = "лошадка";
|
|
||||||
fmt::internal::UTF8ToUTF16 u(s.c_str());
|
|
||||||
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
|
||||||
EXPECT_EQ(7, u.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Converter, typename Char>
|
|
||||||
void check_utf_conversion_error(
|
|
||||||
const char *message,
|
|
||||||
fmt::BasicStringRef<Char> str = fmt::BasicStringRef<Char>(0, 0)) {
|
|
||||||
fmt::MemoryWriter out;
|
|
||||||
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
|
||||||
fmt::SystemError error(0, "");
|
|
||||||
try {
|
|
||||||
(Converter)(str);
|
|
||||||
} catch (const fmt::SystemError &e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
|
|
||||||
EXPECT_EQ(out.str(), error.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, UTF16ToUTF8Error) {
|
|
||||||
check_utf_conversion_error<fmt::internal::UTF16ToUTF8, wchar_t>(
|
|
||||||
"cannot convert string from UTF-16 to UTF-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, UTF8ToUTF16Error) {
|
|
||||||
const char *message = "cannot convert string from UTF-8 to UTF-16";
|
|
||||||
check_utf_conversion_error<fmt::internal::UTF8ToUTF16, char>(message);
|
|
||||||
check_utf_conversion_error<fmt::internal::UTF8ToUTF16, char>(
|
|
||||||
message, fmt::StringRef("foo", INT_MAX + 1u));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, UTF16ToUTF8Convert) {
|
|
||||||
fmt::internal::UTF16ToUTF8 u;
|
|
||||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::WStringRef(0, 0)));
|
|
||||||
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
|
||||||
u.convert(fmt::WStringRef(L"foo", INT_MAX + 1u)));
|
|
||||||
}
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
typedef void (*FormatErrorMessage)(
|
|
||||||
fmt::Writer &out, int error_code, StringRef message);
|
|
||||||
|
|
||||||
template <typename Error>
|
|
||||||
void check_throw_error(int error_code, FormatErrorMessage format) {
|
|
||||||
fmt::SystemError error(0, "");
|
|
||||||
try {
|
|
||||||
throw Error(error_code, "test {}", "error");
|
|
||||||
} catch (const fmt::SystemError &e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
fmt::MemoryWriter message;
|
|
||||||
format(message, error_code, "test error");
|
|
||||||
EXPECT_EQ(message.str(), error.what());
|
|
||||||
EXPECT_EQ(error_code, error.error_code());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, FormatSystemError) {
|
|
||||||
fmt::MemoryWriter message;
|
|
||||||
fmt::format_system_error(message, EDOM, "test");
|
|
||||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str());
|
|
||||||
message.clear();
|
|
||||||
|
|
||||||
// Check if std::allocator throws on allocating max size_t / 2 chars.
|
|
||||||
size_t max_size = std::numeric_limits<size_t>::max() / 2;
|
|
||||||
bool throws_on_alloc = false;
|
|
||||||
try {
|
|
||||||
std::allocator<char> alloc;
|
|
||||||
alloc.deallocate(alloc.allocate(max_size), max_size);
|
|
||||||
} catch (std::bad_alloc) {
|
|
||||||
throws_on_alloc = true;
|
|
||||||
}
|
|
||||||
if (!throws_on_alloc) {
|
|
||||||
fmt::print("warning: std::allocator allocates {} chars", max_size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fmt::format_system_error(message, EDOM, fmt::StringRef(0, max_size));
|
|
||||||
EXPECT_EQ(fmt::format("error {}", EDOM), message.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, SystemError) {
|
|
||||||
fmt::SystemError e(EDOM, "test");
|
|
||||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), e.what());
|
|
||||||
EXPECT_EQ(EDOM, e.error_code());
|
|
||||||
check_throw_error<fmt::SystemError>(EDOM, fmt::format_system_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, ReportSystemError) {
|
|
||||||
fmt::MemoryWriter out;
|
|
||||||
fmt::format_system_error(out, EDOM, "test error");
|
|
||||||
out << '\n';
|
|
||||||
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"), out.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
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::UTF16ToUTF8 utf8_message(message);
|
|
||||||
LocalFree(message);
|
|
||||||
fmt::MemoryWriter actual_message;
|
|
||||||
fmt::internal::format_windows_error(
|
|
||||||
actual_message, ERROR_FILE_EXISTS, "test");
|
|
||||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
|
||||||
actual_message.str());
|
|
||||||
actual_message.clear();
|
|
||||||
fmt::internal::format_windows_error(
|
|
||||||
actual_message, ERROR_FILE_EXISTS,
|
|
||||||
fmt::StringRef(0, std::numeric_limits<size_t>::max()));
|
|
||||||
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS), actual_message.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
provisioning_not_allowed, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fmt::internal::UTF16ToUTF8 utf8_message(message);
|
|
||||||
LocalFree(message);
|
|
||||||
fmt::MemoryWriter actual_message;
|
|
||||||
fmt::internal::format_windows_error(
|
|
||||||
actual_message, provisioning_not_allowed, "test");
|
|
||||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
|
||||||
actual_message.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, WindowsError) {
|
|
||||||
check_throw_error<fmt::WindowsError>(
|
|
||||||
ERROR_FILE_EXISTS, fmt::internal::format_windows_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, ReportWindowsError) {
|
|
||||||
fmt::MemoryWriter out;
|
|
||||||
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
|
|
||||||
out << '\n';
|
|
||||||
EXPECT_WRITE(stderr,
|
|
||||||
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"), out.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
enum TestEnum2 {};
|
|
||||||
|
|
||||||
TEST(UtilTest, ConvertToInt) {
|
|
||||||
EXPECT_TRUE(fmt::internal::ConvertToInt<char>::enable_conversion);
|
|
||||||
EXPECT_FALSE(fmt::internal::ConvertToInt<const char *>::enable_conversion);
|
|
||||||
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum2>::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_ENUM_BASE
|
|
||||||
enum TestEnum : char {TestValue};
|
|
||||||
TEST(UtilTest, IsEnumConvertibleToInt) {
|
|
||||||
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum>::enable_conversion);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool check_enable_if(
|
|
||||||
typename fmt::internal::EnableIf<sizeof(T) == sizeof(int), T>::type *) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool check_enable_if(
|
|
||||||
typename fmt::internal::EnableIf<sizeof(T) != sizeof(int), T>::type *) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, EnableIf) {
|
|
||||||
int i = 0;
|
|
||||||
EXPECT_TRUE(check_enable_if<int>(&i));
|
|
||||||
char c = 0;
|
|
||||||
EXPECT_FALSE(check_enable_if<char>(&c));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, Conditional) {
|
|
||||||
int i = 0;
|
|
||||||
fmt::internal::Conditional<true, int, char>::type *pi = &i;
|
|
||||||
(void)pi;
|
|
||||||
char c = 0;
|
|
||||||
fmt::internal::Conditional<false, int, char>::type *pc = &c;
|
|
||||||
(void)pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestLConv {
|
|
||||||
char *thousands_sep;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EmptyLConv {};
|
|
||||||
|
|
||||||
TEST(UtilTest, ThousandsSep) {
|
|
||||||
char foo[] = "foo";
|
|
||||||
TestLConv lc = {foo};
|
|
||||||
EXPECT_EQ("foo", fmt::internal::thousands_sep(&lc).to_string());
|
|
||||||
EmptyLConv empty_lc;
|
|
||||||
EXPECT_EQ("", fmt::internal::thousands_sep(&empty_lc));
|
|
||||||
}
|
|
40
externals/fmt/test/util.cc
vendored
40
externals/fmt/test/util.cc
vendored
|
@ -1,29 +1,9 @@
|
||||||
/*
|
// Formatting library for C++ - test utilities
|
||||||
Test utilities.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -52,12 +32,12 @@ std::string get_system_error(int error_code) {
|
||||||
|
|
||||||
const char *const FILE_CONTENT = "Don't panic!";
|
const char *const FILE_CONTENT = "Don't panic!";
|
||||||
|
|
||||||
fmt::BufferedFile open_buffered_file(FILE **fp) {
|
fmt::buffered_file open_buffered_file(FILE **fp) {
|
||||||
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::BufferedFile f = read_end.fdopen("r");
|
fmt::buffered_file f = read_end.fdopen("r");
|
||||||
if (fp)
|
if (fp)
|
||||||
*fp = f.get();
|
*fp = f.get();
|
||||||
return f;
|
return f;
|
||||||
|
|
34
externals/fmt/test/util.h
vendored
34
externals/fmt/test/util.h
vendored
|
@ -1,29 +1,9 @@
|
||||||
/*
|
// Formatting library for C++ - test utilities
|
||||||
Test utilities.
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -55,7 +35,7 @@ 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::BufferedFile open_buffered_file(FILE **fp = 0);
|
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__)
|
||||||
|
|
Loading…
Reference in a new issue