mirror of
https://github.com/fmtlib/fmt.git
synced 2025-12-27 01:08:18 +01:00
Compare commits
160 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e75ad9822 | ||
|
|
4f043f8e00 | ||
|
|
cc02cbc455 | ||
|
|
73c0238e3b | ||
|
|
cb122a4d03 | ||
|
|
dc69cc45d2 | ||
|
|
9d8021f0d6 | ||
|
|
9d2221b954 | ||
|
|
70a6a4bb01 | ||
|
|
e4fc856c2f | ||
|
|
3f4984fb36 | ||
|
|
d43665056d | ||
|
|
894b6fac8e | ||
|
|
59f555ad8f | ||
|
|
a7e356cc80 | ||
|
|
e758bfbae1 | ||
|
|
66381e308d | ||
|
|
295a0d84d9 | ||
|
|
1fb1c4c912 | ||
|
|
465a593536 | ||
|
|
d62f4c3bc1 | ||
|
|
a243490ad7 | ||
|
|
9e12ca6069 | ||
|
|
fbca830dd1 | ||
|
|
6146248cf4 | ||
|
|
bc26fbf1b0 | ||
|
|
97cc889374 | ||
|
|
7110b46076 | ||
|
|
c8a8464f7d | ||
|
|
8cbfb6e727 | ||
|
|
6ffc828a79 | ||
|
|
aeb6add336 | ||
|
|
5614289dd8 | ||
|
|
10c7f89351 | ||
|
|
59c268a5f8 | ||
|
|
918bb1ce8f | ||
|
|
a3ba6b4f62 | ||
|
|
8671689449 | ||
|
|
cc10b4607f | ||
|
|
981797f059 | ||
|
|
2117775747 | ||
|
|
be0e268468 | ||
|
|
fbc38b9083 | ||
|
|
8dc69b9da9 | ||
|
|
1489d3b7fa | ||
|
|
dd8c5ce442 | ||
|
|
46484da711 | ||
|
|
802ff8866e | ||
|
|
95a718992c | ||
|
|
e483a01a0f | ||
|
|
f51080916e | ||
|
|
2a952dd0b2 | ||
|
|
0de44a469a | ||
|
|
f0d0a1ebd7 | ||
|
|
569ac91e0b | ||
|
|
a11eb3a090 | ||
|
|
62010520ed | ||
|
|
987514761e | ||
|
|
ba95e36a58 | ||
|
|
abde38b4fb | ||
|
|
18400503da | ||
|
|
9de312112a | ||
|
|
8bbb0b48b4 | ||
|
|
5c0101ab2d | ||
|
|
fbe6410e53 | ||
|
|
8b9fb9fb7e | ||
|
|
0f04ec68a9 | ||
|
|
809073851f | ||
|
|
5d02041c59 | ||
|
|
4b868b8922 | ||
|
|
4061a0d35d | ||
|
|
c68bab7014 | ||
|
|
0c63d15ee9 | ||
|
|
ce19309d09 | ||
|
|
c684349195 | ||
|
|
8db14efa84 | ||
|
|
ffe414cad1 | ||
|
|
c178ab440f | ||
|
|
5befe6584d | ||
|
|
35538ca66c | ||
|
|
4f16409730 | ||
|
|
2a4e948864 | ||
|
|
d778bded95 | ||
|
|
7b4f170c94 | ||
|
|
b1d10a2884 | ||
|
|
cf2719bd12 | ||
|
|
50584f42b4 | ||
|
|
73bed45b7a | ||
|
|
6eaa507473 | ||
|
|
48dff9f3c5 | ||
|
|
a9e261599b | ||
|
|
efd8ee8a7f | ||
|
|
8615ff2acc | ||
|
|
916ed99dab | ||
|
|
e7e9578ed4 | ||
|
|
c99a259739 | ||
|
|
e0f6a2f8be | ||
|
|
ae4a3945f5 | ||
|
|
a317448bd4 | ||
|
|
0eb01b832c | ||
|
|
2a4cd6d05e | ||
|
|
9c32e73abf | ||
|
|
e5c93108e6 | ||
|
|
60c662b3a7 | ||
|
|
f66ba6508a | ||
|
|
f21268aa72 | ||
|
|
07b690a679 | ||
|
|
f9e9bf0231 | ||
|
|
c2ce7e4f07 | ||
|
|
434eb9167e | ||
|
|
09d94162f9 | ||
|
|
e6362642cf | ||
|
|
f0110e8125 | ||
|
|
479ee2a8c6 | ||
|
|
e928b6724c | ||
|
|
ec218a3ad1 | ||
|
|
c04fb91b03 | ||
|
|
323b92bffe | ||
|
|
c6d9730ddb | ||
|
|
2e95823ef7 | ||
|
|
ab2d88ca8e | ||
|
|
3abd036c47 | ||
|
|
c2f3805488 | ||
|
|
ce500635c7 | ||
|
|
0508bbc7ae | ||
|
|
c2fbadb9cf | ||
|
|
47268ecd80 | ||
|
|
9ff3b6af2e | ||
|
|
e3707ef14b | ||
|
|
45fa4ee949 | ||
|
|
9c07b37fff | ||
|
|
5b5886a993 | ||
|
|
d2bfee13e2 | ||
|
|
aff6e45e14 | ||
|
|
1b8a7f8fa0 | ||
|
|
4bc26f0a7b | ||
|
|
fc6e0fe992 | ||
|
|
cd5b5670ba | ||
|
|
6322b47e60 | ||
|
|
691a7a91a1 | ||
|
|
dd1a5ef7f9 | ||
|
|
d5c462596b | ||
|
|
47d147b65d | ||
|
|
911a75114b | ||
|
|
94b476283c | ||
|
|
252f11f85d | ||
|
|
81d5663825 | ||
|
|
68f0ac8271 | ||
|
|
b60a5c5d49 | ||
|
|
8dc2360b11 | ||
|
|
4e4b8570e5 | ||
|
|
4027557958 | ||
|
|
5c32aa411c | ||
|
|
468c243ca8 | ||
|
|
2f257b7291 | ||
|
|
6a5bb6e268 | ||
|
|
e282d963fe | ||
|
|
e2cd521b8f | ||
|
|
fba352a92a | ||
|
|
6dcc526d5b |
162
.travis.yml
162
.travis.yml
@@ -1,10 +1,12 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: required # the doc target uses sudo to install dependencies
|
||||
sudo: false
|
||||
|
||||
os: linux
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
global:
|
||||
@@ -12,56 +14,116 @@ env:
|
||||
a1eovNn4uol9won7ghr67eD3/59oeESN+G9bWE+ecI1V6yRseG9whniGhIpC/YfMW/Qz5I
|
||||
5sxSmFjaw9bxCISNwUIrL1O5x2AmRYTnFcXk4dFsUvlZg+WeF/aKyBYCNRM8C2ndbBmtAO
|
||||
o1F2EwFbiso0EmtzhAPs19ujiVxkLn4=
|
||||
matrix:
|
||||
- BUILD=Doc
|
||||
- BUILD=Debug STANDARD=14
|
||||
- BUILD=Release STANDARD=14
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: BUILD=Doc
|
||||
include:
|
||||
- language: android
|
||||
android:
|
||||
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
|
||||
# Documentation
|
||||
- env: BUILD=Doc
|
||||
sudo: required
|
||||
# g++ 6 on Linux with C++14
|
||||
- env: COMPILER=g++-6 BUILD=Debug STANDARD=14
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-6
|
||||
- env: COMPILER=g++-6 BUILD=Release STANDARD=14
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-6
|
||||
# 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
|
||||
|
||||
# Install gcc-6 for extended constexpr support.
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-6
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-6; fi
|
||||
before_script:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi
|
||||
- if [[ "${BUILD}" != "Doc" ]]; then ${CXX} --version; fi
|
||||
|
||||
script:
|
||||
- support/travis-build.py
|
||||
|
||||
@@ -1,12 +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)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.11)
|
||||
endif()
|
||||
|
||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||
# or if it is the master project.
|
||||
set(MASTER_PROJECT OFF)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(MASTER_PROJECT ON)
|
||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||
endif ()
|
||||
|
||||
# Joins arguments and places the results in ${result_var}.
|
||||
@@ -28,12 +34,12 @@ if (NOT CMAKE_BUILD_TYPE)
|
||||
endif ()
|
||||
|
||||
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
||||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF)
|
||||
|
||||
# Options that control generation of various targets.
|
||||
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
|
||||
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
||||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
||||
option(FMT_USE_CPP14 "Enable the addition of C++14 compiler flags." ON)
|
||||
|
||||
project(FMT)
|
||||
|
||||
@@ -58,9 +64,58 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
|
||||
|
||||
include(cxx14)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wshadow -pedantic)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
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 ()
|
||||
|
||||
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
@@ -95,8 +150,8 @@ function(add_headers VAR)
|
||||
endfunction()
|
||||
|
||||
# Define the fmt library, its includes and the needed defines.
|
||||
add_headers(FMT_HEADERS core.h format.h format-inl.h ostream.h printf.h time.h
|
||||
ranges.h)
|
||||
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)
|
||||
@@ -106,9 +161,9 @@ 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.
|
||||
# Don't export -std since it may break projects that use other standards.
|
||||
target_compile_options(fmt PRIVATE ${CPP14_FLAG})
|
||||
if (FMT_WERROR)
|
||||
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
|
||||
endif ()
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
@@ -130,17 +185,14 @@ if (BUILD_SHARED_LIBS)
|
||||
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)
|
||||
add_library(fmt-header-only INTERFACE)
|
||||
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
||||
|
||||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
||||
target_compile_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>)
|
||||
endif ()
|
||||
target_include_directories(fmt-header-only INTERFACE
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
# Install targets.
|
||||
if (FMT_INSTALL)
|
||||
|
||||
233
ChangeLog.rst
233
ChangeLog.rst
@@ -1,3 +1,220 @@
|
||||
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
|
||||
------------------
|
||||
|
||||
@@ -134,8 +351,8 @@
|
||||
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>`_,
|
||||
* 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>`_.
|
||||
|
||||
@@ -207,8 +424,8 @@
|
||||
* 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>`_,
|
||||
* 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>`_,
|
||||
@@ -257,8 +474,8 @@
|
||||
(`#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>`_,
|
||||
* 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>`_,
|
||||
@@ -1184,8 +1401,8 @@ Fixes
|
||||
`@Jopie64 (Johan) <https://github.com/Jopie64>`_.
|
||||
|
||||
* Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le,
|
||||
s390x and SunOS 5.11 i386 (
|
||||
`#138 <https://github.com/fmtlib/fmt/issues/138>`_,
|
||||
s390x and SunOS 5.11 i386
|
||||
(`#138 <https://github.com/fmtlib/fmt/issues/138>`_,
|
||||
`#179 <https://github.com/fmtlib/fmt/issues/179>`_,
|
||||
`#180 <https://github.com/fmtlib/fmt/issues/180>`_,
|
||||
`#202 <https://github.com/fmtlib/fmt/issues/202>`_,
|
||||
|
||||
95
README.rst
95
README.rst
@@ -80,20 +80,21 @@ Format strings can be checked at compile time:
|
||||
.. code:: c++
|
||||
|
||||
// test.cc
|
||||
#define FMT_STRING_ALIAS 1
|
||||
#include <fmt/format.h>
|
||||
std::string s = fmt::format(fmt("{2}"), 42);
|
||||
std::string s = format(fmt("{2}"), 42);
|
||||
|
||||
.. code::
|
||||
|
||||
$ g++ -Iinclude test.cc -std=c++14
|
||||
$ c++ -Iinclude -std=c++14 test.cc
|
||||
...
|
||||
test.cc:2:22: note: in instantiation of function template specialization 'fmt::format<S, int>' requested here
|
||||
std::string s = fmt::format(fmt("{2}"), 42);
|
||||
^
|
||||
include/fmt/core.h:749:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
|
||||
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:2081:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])'
|
||||
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");
|
||||
^
|
||||
|
||||
@@ -107,24 +108,29 @@ Format strings can be checked at compile time:
|
||||
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
|
||||
// access the string using to_string(buf) or buf.data()
|
||||
|
||||
An object of any user-defined type for which there is an overloaded
|
||||
:code:`std::ostream` insertion operator (``operator<<``) can be formatted:
|
||||
Formatting of user-defined types is supported via a simple
|
||||
`extension API <http://fmtlib.net/latest/api.html#formatting-user-defined-types>`_:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
class Date {
|
||||
int year_, month_, day_;
|
||||
public:
|
||||
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
|
||||
struct date {
|
||||
int year, month, day;
|
||||
};
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
|
||||
return os << d.year_ << '-' << d.month_ << '-' << d.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"
|
||||
|
||||
You can create your own functions similar to `format
|
||||
@@ -272,7 +278,7 @@ Boost Format library
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
considered here. Boost Format also has excessive build times and severe
|
||||
code bloat issues (see `Benchmarks`_).
|
||||
@@ -369,18 +375,18 @@ macOS Sierra, best of three) is shown in the following tables.
|
||||
============= =============== ==================== ==================
|
||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||
============= =============== ==================== ==================
|
||||
printf 2.7 29 26
|
||||
printf+string 18.4 29 26
|
||||
IOStreams 34.6 59 55
|
||||
fmt 22.0 37 34
|
||||
tinyformat 51.8 103 97
|
||||
Boost Format 120.5 762 739
|
||||
Folly Format 158.7 102 87
|
||||
printf 2.6 29 26
|
||||
printf+string 16.4 29 26
|
||||
IOStreams 31.1 59 55
|
||||
fmt 19.0 37 34
|
||||
tinyformat 44.0 103 97
|
||||
Boost Format 91.9 226 203
|
||||
Folly Format 115.7 101 88
|
||||
============= =============== ==================== ==================
|
||||
|
||||
As you can see, fmt has 60% less overhead in terms of resulting binary code
|
||||
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
|
||||
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.
|
||||
@@ -390,13 +396,13 @@ include to measure the overhead of the latter.
|
||||
============= =============== ==================== ==================
|
||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||
============= =============== ==================== ==================
|
||||
printf 2.4 33 30
|
||||
printf+string 18.5 33 30
|
||||
IOStreams 31.9 56 52
|
||||
fmt 20.9 56 51
|
||||
tinyformat 38.9 88 82
|
||||
Boost Format 64.8 366 304
|
||||
Folly Format 113.5 442 428
|
||||
printf 2.2 33 30
|
||||
printf+string 16.0 33 30
|
||||
IOStreams 28.3 56 52
|
||||
fmt 18.2 59 50
|
||||
tinyformat 32.6 88 82
|
||||
Boost Format 54.1 365 303
|
||||
Folly Format 79.9 445 430
|
||||
============= =============== ==================== ==================
|
||||
|
||||
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared
|
||||
@@ -429,6 +435,29 @@ or the 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
|
||||
-------
|
||||
|
||||
|
||||
35
doc/api.rst
35
doc/api.rst
@@ -38,8 +38,8 @@ arguments in the resulting string.
|
||||
|
||||
.. _format:
|
||||
|
||||
.. doxygenfunction:: format(string_view, const Args&...)
|
||||
.. doxygenfunction:: vformat(string_view, format_args)
|
||||
.. doxygenfunction:: format(const String&, const Args&...)
|
||||
.. doxygenfunction:: vformat(const String&, basic_format_args<typename buffer_context<Char>::type>)
|
||||
|
||||
.. _print:
|
||||
|
||||
@@ -49,6 +49,9 @@ arguments in the resulting string.
|
||||
.. doxygenfunction:: print(std::FILE *, string_view, const Args&...)
|
||||
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
|
||||
|
||||
.. doxygenfunction:: print(std::FILE *, wstring_view, const Args&...)
|
||||
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
|
||||
|
||||
Named arguments
|
||||
---------------
|
||||
|
||||
@@ -127,6 +130,25 @@ 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.
|
||||
@@ -135,7 +157,7 @@ Output iterator support
|
||||
-----------------------
|
||||
|
||||
.. doxygenfunction:: fmt::format_to(OutputIt, string_view, const Args&...)
|
||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, string_view, const Args&...)
|
||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...)
|
||||
.. doxygenstruct:: fmt::format_to_n_result
|
||||
:members:
|
||||
|
||||
@@ -231,11 +253,10 @@ custom argument formatter class::
|
||||
|
||||
using arg_formatter::operator();
|
||||
|
||||
void operator()(int value) {
|
||||
auto operator()(int value) {
|
||||
if (spec().type() == 'x')
|
||||
(*this)(static_cast<unsigned>(value)); // convert to unsigned and format
|
||||
else
|
||||
arg_formatter::operator()(value);
|
||||
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
|
||||
return arg_formatter::operator()(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
|
||||
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0']
|
||||
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):
|
||||
"Install package using pip."
|
||||
@@ -56,7 +56,7 @@ def create_build_env(dirname='virtualenv'):
|
||||
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
||||
min_version='1.4.1.dev20160531')
|
||||
pip_install('michaeljones/breathe',
|
||||
'6b1c5bb7a1866f15fc328b8716258354b10c1daa',
|
||||
'129222318f7c8f865d2631e7da7b033567e7f56a',
|
||||
min_version='4.2.0')
|
||||
|
||||
def build_docs(version='dev', **kwargs):
|
||||
@@ -91,7 +91,8 @@ def build_docs(version='dev', **kwargs):
|
||||
FMT_USE_USER_DEFINED_LITERALS=1 \
|
||||
FMT_API= \
|
||||
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
||||
"FMT_END_NAMESPACE=}}"
|
||||
"FMT_END_NAMESPACE=}}" \
|
||||
"FMT_STRING_ALIAS=1"
|
||||
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
|
||||
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
|
||||
if p.returncode != 0:
|
||||
|
||||
@@ -76,7 +76,7 @@ The general form of a *standard format specifier* is:
|
||||
|
||||
.. productionlist:: sf
|
||||
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
|
||||
fill: <a character other than '{' or '}'>
|
||||
fill: <a character other than '{', '}' or '\0'>
|
||||
align: "<" | ">" | "=" | "^"
|
||||
sign: "+" | "-" | " "
|
||||
width: `integer` | "{" `arg_id` "}"
|
||||
@@ -84,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"
|
||||
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
|
||||
|
||||
The *fill* character can be any character other than '{' or '}'. The presence
|
||||
of a fill character is signaled by the character following it, which must be
|
||||
one of the alignment options. If the second character of *format_spec* is not
|
||||
a valid alignment option, then it is assumed that both the fill character and
|
||||
the alignment option are absent.
|
||||
The *fill* character can be any character other than '{', '}' or '\\0'. The
|
||||
presence of a fill character is signaled by the character following it, which
|
||||
must be one of the alignment options. If the second character of *format_spec*
|
||||
is not a valid alignment option, then it is assumed that both the fill character
|
||||
and the alignment option are absent.
|
||||
|
||||
The meaning of the various alignment options is as follows:
|
||||
|
||||
|
||||
278
include/fmt/color.h
Normal file
278
include/fmt/color.h
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_
|
||||
@@ -9,14 +9,14 @@
|
||||
#define FMT_CORE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdio> // std::FILE
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||
#define FMT_VERSION 50000
|
||||
#define FMT_VERSION 50201
|
||||
|
||||
#ifdef __has_feature
|
||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||
@@ -24,12 +24,19 @@
|
||||
# define FMT_HAS_FEATURE(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
||||
(!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1600)
|
||||
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
||||
#else
|
||||
# define FMT_HAS_INCLUDE(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
|
||||
#else
|
||||
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
#else
|
||||
@@ -48,7 +55,7 @@
|
||||
# define FMT_MSC_VER 0
|
||||
#endif
|
||||
|
||||
// Check if relaxed c++14 constexpr is supported.
|
||||
// Check if relaxed C++14 constexpr is supported.
|
||||
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
|
||||
#ifndef FMT_USE_CONSTEXPR
|
||||
# define FMT_USE_CONSTEXPR \
|
||||
@@ -63,18 +70,26 @@
|
||||
# define FMT_CONSTEXPR_DECL
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_CONSTEXPR11
|
||||
# define FMT_USE_CONSTEXPR11 \
|
||||
(FMT_MSC_VER >= 1900 || FMT_GCC_VERSION >= 406 || FMT_USE_CONSTEXPR)
|
||||
#endif
|
||||
#if FMT_USE_CONSTEXPR11
|
||||
# define FMT_CONSTEXPR11 constexpr
|
||||
#else
|
||||
# define FMT_CONSTEXPR11
|
||||
#endif
|
||||
|
||||
#ifndef FMT_OVERRIDE
|
||||
# if FMT_HAS_FEATURE(cxx_override) || \
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
|
||||
FMT_MSC_VER >= 1900
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
||||
# define FMT_OVERRIDE override
|
||||
# else
|
||||
# define FMT_OVERRIDE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_HAS_FEATURE(cxx_explicit_conversions) || \
|
||||
FMT_MSC_VER >= 1800
|
||||
#if FMT_HAS_FEATURE(cxx_explicit_conversions) || FMT_MSC_VER >= 1800
|
||||
# define FMT_EXPLICIT explicit
|
||||
#else
|
||||
# define FMT_EXPLICIT
|
||||
@@ -82,8 +97,7 @@
|
||||
|
||||
#ifndef FMT_NULL
|
||||
# if FMT_HAS_FEATURE(cxx_nullptr) || \
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
|
||||
FMT_MSC_VER >= 1600
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600
|
||||
# define FMT_NULL nullptr
|
||||
# define FMT_USE_NULLPTR 1
|
||||
# else
|
||||
@@ -95,6 +109,12 @@
|
||||
# define FMT_USE_NULLPTR 0
|
||||
#endif
|
||||
|
||||
#if FMT_HAS_CPP_ATTRIBUTE(noreturn)
|
||||
# define FMT_NORETURN [[noreturn]]
|
||||
#else
|
||||
# define FMT_NORETURN
|
||||
#endif
|
||||
|
||||
// Check if exceptions are disabled.
|
||||
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
|
||||
# define FMT_EXCEPTIONS 0
|
||||
@@ -111,15 +131,16 @@
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
|
||||
FMT_MSC_VER >= 1900
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
||||
# define FMT_DETECTED_NOEXCEPT noexcept
|
||||
# define FMT_HAS_CXX11_NOEXCEPT 1
|
||||
#else
|
||||
# define FMT_DETECTED_NOEXCEPT throw()
|
||||
# define FMT_HAS_CXX11_NOEXCEPT 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NOEXCEPT
|
||||
# if FMT_EXCEPTIONS
|
||||
# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
|
||||
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
|
||||
# else
|
||||
# define FMT_NOEXCEPT
|
||||
@@ -161,13 +182,6 @@
|
||||
# define FMT_ASSERT(condition, message) assert((condition) && message)
|
||||
#endif
|
||||
|
||||
#define FMT_DELETED = delete
|
||||
|
||||
// A macro to disallow the copy construction and assignment.
|
||||
#define FMT_DISALLOW_COPY_AND_ASSIGN(Type) \
|
||||
Type(const Type &) FMT_DELETED; \
|
||||
void operator=(const Type &) FMT_DELETED
|
||||
|
||||
// libc++ supports string_view in pre-c++17.
|
||||
#if (FMT_HAS_INCLUDE(<string_view>) && \
|
||||
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
|
||||
@@ -186,13 +200,41 @@
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
// An implementation of declval for pre-C++11 compilers such as gcc 4.
|
||||
namespace internal {
|
||||
template <typename T>
|
||||
typename std::add_rvalue_reference<T>::type declval() FMT_NOEXCEPT;
|
||||
|
||||
template <typename>
|
||||
struct result_of;
|
||||
|
||||
template <typename F, typename... Args>
|
||||
struct result_of<F(Args...)> {
|
||||
// A workaround for gcc 4.4 that doesn't allow F to be a reference.
|
||||
typedef typename std::result_of<
|
||||
typename std::remove_reference<F>::type(Args...)>::type type;
|
||||
};
|
||||
|
||||
// Casts nonnegative integer to unsigned.
|
||||
template <typename Int>
|
||||
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
|
||||
FMT_ASSERT(value >= 0, "negative value");
|
||||
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
||||
}
|
||||
|
||||
// A constexpr std::char_traits::length replacement for pre-C++17.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR size_t length(const Char *s) {
|
||||
const Char *start = s;
|
||||
while (*s) ++s;
|
||||
return s - start;
|
||||
}
|
||||
#if FMT_GCC_VERSION
|
||||
FMT_CONSTEXPR size_t length(const char *s) { return std::strlen(s); }
|
||||
#endif
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
|
||||
subset of the API. ``fmt::basic_string_view`` is used for format strings even
|
||||
@@ -218,15 +260,15 @@ class basic_string_view {
|
||||
#else
|
||||
struct type {
|
||||
const char *data() const { return FMT_NULL; }
|
||||
size_t size() const { return 0; };
|
||||
size_t size() const { return 0; }
|
||||
};
|
||||
#endif
|
||||
|
||||
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(FMT_NULL), size_(0) {}
|
||||
|
||||
/** Constructs a string reference object from a C string and a size. */
|
||||
FMT_CONSTEXPR basic_string_view(const Char *s, size_t size) FMT_NOEXCEPT
|
||||
: data_(s), size_(size) {}
|
||||
FMT_CONSTEXPR basic_string_view(const Char *s, size_t count) FMT_NOEXCEPT
|
||||
: data_(s), size_(count) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -234,20 +276,20 @@ class basic_string_view {
|
||||
the size with ``std::char_traits<Char>::length``.
|
||||
\endrst
|
||||
*/
|
||||
basic_string_view(const Char *s)
|
||||
: data_(s), size_(std::char_traits<Char>::length(s)) {}
|
||||
FMT_CONSTEXPR basic_string_view(const Char *s)
|
||||
: data_(s), size_(internal::length(s)) {}
|
||||
|
||||
/** Constructs a string reference from a ``std::basic_string`` object. */
|
||||
template <typename Alloc>
|
||||
FMT_CONSTEXPR basic_string_view(
|
||||
const std::basic_string<Char, Alloc> &s) FMT_NOEXCEPT
|
||||
: data_(s.c_str()), size_(s.size()) {}
|
||||
: data_(s.data()), size_(s.size()) {}
|
||||
|
||||
FMT_CONSTEXPR basic_string_view(type s) FMT_NOEXCEPT
|
||||
: data_(s.data()), size_(s.size()) {}
|
||||
|
||||
/** Returns a pointer to the string data. */
|
||||
const Char *data() const { return data_; }
|
||||
FMT_CONSTEXPR const Char *data() const { return data_; }
|
||||
|
||||
/** Returns the string size. */
|
||||
FMT_CONSTEXPR size_t size() const { return size_; }
|
||||
@@ -262,8 +304,8 @@ class basic_string_view {
|
||||
|
||||
// Lexicographically compare this string reference to other.
|
||||
int compare(basic_string_view other) const {
|
||||
size_t size = size_ < other.size_ ? size_ : other.size_;
|
||||
int result = std::char_traits<Char>::compare(data_, other.data_, size);
|
||||
size_t str_size = size_ < other.size_ ? size_ : other.size_;
|
||||
int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
|
||||
if (result == 0)
|
||||
result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
|
||||
return result;
|
||||
@@ -298,9 +340,29 @@ class basic_format_arg;
|
||||
template <typename Context>
|
||||
class basic_format_args;
|
||||
|
||||
template <typename T>
|
||||
struct no_formatter_error : std::false_type {};
|
||||
|
||||
// A formatter for objects of type T.
|
||||
template <typename T, typename Char = char, typename Enable = void>
|
||||
struct formatter;
|
||||
struct formatter {
|
||||
static_assert(no_formatter_error<T>::value,
|
||||
"don't know how to format the type, include fmt/ostream.h if it provides "
|
||||
"an operator<< that should be used");
|
||||
|
||||
// The following functions are not defined intentionally.
|
||||
template <typename ParseContext>
|
||||
typename ParseContext::iterator parse(ParseContext &);
|
||||
template <typename FormatContext>
|
||||
auto format(const T &val, FormatContext &ctx) -> decltype(ctx.out());
|
||||
};
|
||||
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
struct convert_to_int {
|
||||
enum {
|
||||
value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
|
||||
};
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
@@ -308,20 +370,24 @@ namespace internal {
|
||||
template <typename T>
|
||||
class basic_buffer {
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer);
|
||||
basic_buffer(const basic_buffer &) = delete;
|
||||
void operator=(const basic_buffer &) = delete;
|
||||
|
||||
T *ptr_;
|
||||
std::size_t size_;
|
||||
std::size_t capacity_;
|
||||
|
||||
protected:
|
||||
basic_buffer(T *p = FMT_NULL, std::size_t size = 0, std::size_t capacity = 0)
|
||||
FMT_NOEXCEPT: ptr_(p), size_(size), capacity_(capacity) {}
|
||||
// Don't initialize ptr_ since it is not accessed to save a few cycles.
|
||||
basic_buffer(std::size_t sz) FMT_NOEXCEPT: size_(sz), capacity_(sz) {}
|
||||
|
||||
basic_buffer(T *p = FMT_NULL, std::size_t sz = 0, std::size_t cap = 0)
|
||||
FMT_NOEXCEPT: ptr_(p), size_(sz), capacity_(cap) {}
|
||||
|
||||
/** Sets the buffer data and capacity. */
|
||||
void set(T *data, std::size_t capacity) FMT_NOEXCEPT {
|
||||
ptr_ = data;
|
||||
capacity_ = capacity;
|
||||
void set(T *buf_data, std::size_t buf_capacity) FMT_NOEXCEPT {
|
||||
ptr_ = buf_data;
|
||||
capacity_ = buf_capacity;
|
||||
}
|
||||
|
||||
/** Increases the buffer capacity to hold at least *capacity* elements. */
|
||||
@@ -356,10 +422,13 @@ class basic_buffer {
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
/** Clears this buffer. */
|
||||
void clear() { size_ = 0; }
|
||||
|
||||
/** Reserves space to store at least *capacity* elements. */
|
||||
void reserve(std::size_t capacity) {
|
||||
if (capacity > capacity_)
|
||||
grow(capacity);
|
||||
void reserve(std::size_t new_capacity) {
|
||||
if (new_capacity > capacity_)
|
||||
grow(new_capacity);
|
||||
}
|
||||
|
||||
void push_back(const T &value) {
|
||||
@@ -392,8 +461,7 @@ class container_buffer : public basic_buffer<typename Container::value_type> {
|
||||
|
||||
public:
|
||||
explicit container_buffer(Container &c)
|
||||
: basic_buffer<typename Container::value_type>(&c[0], c.size(), c.size()),
|
||||
container_(c) {}
|
||||
: basic_buffer<typename Container::value_type>(c.size()), container_(c) {}
|
||||
};
|
||||
|
||||
struct error_handler {
|
||||
@@ -428,7 +496,7 @@ template <typename T, typename Char>
|
||||
struct is_named_arg<named_arg<T, Char>> : std::true_type {};
|
||||
|
||||
enum type {
|
||||
none_type, name_arg_type,
|
||||
none_type, named_arg_type,
|
||||
// Integer types should go first,
|
||||
int_type, uint_type, long_long_type, ulong_long_type, bool_type, char_type,
|
||||
last_integer_type = char_type,
|
||||
@@ -438,22 +506,15 @@ enum type {
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR bool is_integral(type t) {
|
||||
FMT_ASSERT(t != internal::name_arg_type, "invalid argument type");
|
||||
FMT_ASSERT(t != internal::named_arg_type, "invalid argument type");
|
||||
return t > internal::none_type && t <= internal::last_integer_type;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool is_arithmetic(type t) {
|
||||
FMT_ASSERT(t != internal::name_arg_type, "invalid argument type");
|
||||
FMT_ASSERT(t != internal::named_arg_type, "invalid argument type");
|
||||
return t > internal::none_type && t <= internal::last_numeric_type;
|
||||
}
|
||||
|
||||
template <typename T, typename Char, bool ENABLE = true>
|
||||
struct convert_to_int {
|
||||
enum {
|
||||
value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct string_value {
|
||||
const Char *value;
|
||||
@@ -533,12 +594,14 @@ class value {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Context, type TYPE>
|
||||
struct typed_value : value<Context> {
|
||||
// Value initializer used to delay conversion to value and reduce memory churn.
|
||||
template <typename Context, typename T, type TYPE>
|
||||
struct init {
|
||||
T val;
|
||||
static const type type_tag = TYPE;
|
||||
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR typed_value(const T &val) : value<Context>(val) {}
|
||||
FMT_CONSTEXPR init(const T &v) : val(v) {}
|
||||
FMT_CONSTEXPR operator value<Context>() const { return value<Context>(val); }
|
||||
};
|
||||
|
||||
template <typename Context, typename T>
|
||||
@@ -546,15 +609,19 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T &value);
|
||||
|
||||
#define FMT_MAKE_VALUE(TAG, ArgType, ValueType) \
|
||||
template <typename C> \
|
||||
FMT_CONSTEXPR typed_value<C, TAG> make_value(ArgType val) { \
|
||||
FMT_CONSTEXPR init<C, ValueType, TAG> make_value(ArgType val) { \
|
||||
return static_cast<ValueType>(val); \
|
||||
}
|
||||
|
||||
#define FMT_MAKE_VALUE_SAME(TAG, Type) \
|
||||
template <typename C> \
|
||||
FMT_CONSTEXPR init<C, Type, TAG> make_value(Type val) { return val; }
|
||||
|
||||
FMT_MAKE_VALUE(bool_type, bool, int)
|
||||
FMT_MAKE_VALUE(int_type, short, int)
|
||||
FMT_MAKE_VALUE(uint_type, unsigned short, unsigned)
|
||||
FMT_MAKE_VALUE(int_type, int, int)
|
||||
FMT_MAKE_VALUE(uint_type, unsigned, unsigned)
|
||||
FMT_MAKE_VALUE_SAME(int_type, int)
|
||||
FMT_MAKE_VALUE_SAME(uint_type, unsigned)
|
||||
|
||||
// To minimize the number of types we need to deal with, long is translated
|
||||
// either to int or to long long depending on its size.
|
||||
@@ -568,23 +635,23 @@ FMT_MAKE_VALUE(
|
||||
(sizeof(unsigned long) == sizeof(unsigned) ? uint_type : ulong_long_type),
|
||||
unsigned long, ulong_type)
|
||||
|
||||
FMT_MAKE_VALUE(long_long_type, long long, long long)
|
||||
FMT_MAKE_VALUE(ulong_long_type, unsigned long long, unsigned long long)
|
||||
FMT_MAKE_VALUE_SAME(long_long_type, long long)
|
||||
FMT_MAKE_VALUE_SAME(ulong_long_type, unsigned long long)
|
||||
FMT_MAKE_VALUE(int_type, signed char, int)
|
||||
FMT_MAKE_VALUE(uint_type, unsigned char, unsigned)
|
||||
FMT_MAKE_VALUE(char_type, char, int)
|
||||
|
||||
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
|
||||
template <typename C>
|
||||
inline typed_value<C, char_type> make_value(wchar_t val) {
|
||||
inline init<C, int, char_type> make_value(wchar_t val) {
|
||||
require_wchar<typename C::char_type>();
|
||||
return static_cast<int>(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
FMT_MAKE_VALUE(double_type, float, double)
|
||||
FMT_MAKE_VALUE(double_type, double, double)
|
||||
FMT_MAKE_VALUE(long_double_type, long double, long double)
|
||||
FMT_MAKE_VALUE_SAME(double_type, double)
|
||||
FMT_MAKE_VALUE_SAME(long_double_type, long double)
|
||||
|
||||
// Formatting of wide strings into a narrow buffer and multibyte strings
|
||||
// into a wide buffer is disallowed (https://github.com/fmtlib/fmt/pull/606).
|
||||
@@ -594,18 +661,17 @@ FMT_MAKE_VALUE(cstring_type, const typename C::char_type*,
|
||||
const typename C::char_type*)
|
||||
|
||||
FMT_MAKE_VALUE(cstring_type, signed char*, const signed char*)
|
||||
FMT_MAKE_VALUE(cstring_type, const signed char*, const signed char*)
|
||||
FMT_MAKE_VALUE_SAME(cstring_type, const signed char*)
|
||||
FMT_MAKE_VALUE(cstring_type, unsigned char*, const unsigned char*)
|
||||
FMT_MAKE_VALUE(cstring_type, const unsigned char*, const unsigned char*)
|
||||
FMT_MAKE_VALUE(string_type, basic_string_view<typename C::char_type>,
|
||||
basic_string_view<typename C::char_type>)
|
||||
FMT_MAKE_VALUE_SAME(cstring_type, const unsigned char*)
|
||||
FMT_MAKE_VALUE_SAME(string_type, basic_string_view<typename C::char_type>)
|
||||
FMT_MAKE_VALUE(string_type,
|
||||
typename basic_string_view<typename C::char_type>::type,
|
||||
basic_string_view<typename C::char_type>)
|
||||
FMT_MAKE_VALUE(string_type, const std::basic_string<typename C::char_type>&,
|
||||
basic_string_view<typename C::char_type>)
|
||||
FMT_MAKE_VALUE(pointer_type, void*, const void*)
|
||||
FMT_MAKE_VALUE(pointer_type, const void*, const void*)
|
||||
FMT_MAKE_VALUE_SAME(pointer_type, const void*)
|
||||
|
||||
#if FMT_USE_NULLPTR
|
||||
FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void*)
|
||||
@@ -624,20 +690,27 @@ typename std::enable_if<!std::is_same<T, typename C::char_type>::value>::type
|
||||
template <typename C, typename T>
|
||||
inline typename std::enable_if<
|
||||
std::is_enum<T>::value && convert_to_int<T, typename C::char_type>::value,
|
||||
typed_value<C, int_type>>::type
|
||||
init<C, int, int_type>>::type
|
||||
make_value(const T &val) { return static_cast<int>(val); }
|
||||
|
||||
template <typename C, typename T, typename Char = typename C::char_type>
|
||||
inline typename std::enable_if<
|
||||
std::is_constructible<basic_string_view<Char>, T>::value,
|
||||
init<C, basic_string_view<Char>, string_type>>::type
|
||||
make_value(const T &val) { return basic_string_view<Char>(val); }
|
||||
|
||||
template <typename C, typename T, typename Char = typename C::char_type>
|
||||
inline typename std::enable_if<
|
||||
!convert_to_int<T, Char>::value &&
|
||||
!std::is_convertible<T, basic_string_view<Char>>::value,
|
||||
!std::is_convertible<T, basic_string_view<Char>>::value &&
|
||||
!std::is_constructible<basic_string_view<Char>, T>::value,
|
||||
// Implicit conversion to std::string is not handled here because it's
|
||||
// unsafe: https://github.com/fmtlib/fmt/issues/729
|
||||
typed_value<C, custom_type>>::type
|
||||
init<C, const T &, custom_type>>::type
|
||||
make_value(const T &val) { return val; }
|
||||
|
||||
template <typename C, typename T>
|
||||
typed_value<C, name_arg_type>
|
||||
init<C, const void*, named_arg_type>
|
||||
make_value(const named_arg<T, typename C::char_type> &val) {
|
||||
basic_format_arg<C> arg = make_arg<C>(val.value);
|
||||
std::memcpy(val.data, &arg, sizeof(arg));
|
||||
@@ -649,17 +722,7 @@ enum { max_packed_args = 15 };
|
||||
|
||||
template <typename Context>
|
||||
class arg_map;
|
||||
|
||||
template <typename>
|
||||
struct result_of;
|
||||
|
||||
template <typename F, typename... Args>
|
||||
struct result_of<F(Args...)> {
|
||||
// A workaround for gcc 4.4 that doesn't allow F to be a reference.
|
||||
typedef typename std::result_of<
|
||||
typename std::remove_reference<F>::type(Args...)>::type type;
|
||||
};
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
// A formatting argument. It is a trivially copyable/constructible type to
|
||||
// allow storage in basic_memory_buffer.
|
||||
@@ -675,7 +738,7 @@ class basic_format_arg {
|
||||
|
||||
template <typename Visitor, typename Ctx>
|
||||
friend FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
|
||||
visit(Visitor &&vis, basic_format_arg<Ctx> arg);
|
||||
visit(Visitor &&vis, const basic_format_arg<Ctx> &arg);
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
friend class internal::arg_map<Context>;
|
||||
@@ -705,6 +768,54 @@ class basic_format_arg {
|
||||
bool is_arithmetic() const { return internal::is_arithmetic(type_); }
|
||||
};
|
||||
|
||||
struct monostate {};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Visits an argument dispatching to the appropriate visit method based on
|
||||
the argument type. For example, if the argument type is ``double`` then
|
||||
``vis(value)`` will be called with the value of type ``double``.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Visitor, typename Context>
|
||||
FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
|
||||
visit(Visitor &&vis, const basic_format_arg<Context> &arg) {
|
||||
typedef typename Context::char_type char_type;
|
||||
switch (arg.type_) {
|
||||
case internal::none_type:
|
||||
break;
|
||||
case internal::named_arg_type:
|
||||
FMT_ASSERT(false, "invalid argument type");
|
||||
break;
|
||||
case internal::int_type:
|
||||
return vis(arg.value_.int_value);
|
||||
case internal::uint_type:
|
||||
return vis(arg.value_.uint_value);
|
||||
case internal::long_long_type:
|
||||
return vis(arg.value_.long_long_value);
|
||||
case internal::ulong_long_type:
|
||||
return vis(arg.value_.ulong_long_value);
|
||||
case internal::bool_type:
|
||||
return vis(arg.value_.int_value != 0);
|
||||
case internal::char_type:
|
||||
return vis(static_cast<char_type>(arg.value_.int_value));
|
||||
case internal::double_type:
|
||||
return vis(arg.value_.double_value);
|
||||
case internal::long_double_type:
|
||||
return vis(arg.value_.long_double_value);
|
||||
case internal::cstring_type:
|
||||
return vis(arg.value_.string.value);
|
||||
case internal::string_type:
|
||||
return vis(basic_string_view<char_type>(
|
||||
arg.value_.string.value, arg.value_.string.size));
|
||||
case internal::pointer_type:
|
||||
return vis(arg.value_.pointer);
|
||||
case internal::custom_type:
|
||||
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
|
||||
}
|
||||
return vis(monostate());
|
||||
}
|
||||
|
||||
// Parsing context consisting of a format string range being parsed and an
|
||||
// argument counter for automatic indexing.
|
||||
template <typename Char, typename ErrorHandler = internal::error_handler>
|
||||
@@ -732,7 +843,7 @@ class basic_parse_context : private ErrorHandler {
|
||||
|
||||
// Advances the begin iterator to ``it``.
|
||||
FMT_CONSTEXPR void advance_to(iterator it) {
|
||||
format_str_.remove_prefix(it - begin());
|
||||
format_str_.remove_prefix(internal::to_unsigned(it - begin()));
|
||||
}
|
||||
|
||||
// Returns the next argument index.
|
||||
@@ -763,7 +874,8 @@ namespace internal {
|
||||
template <typename Context>
|
||||
class arg_map {
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(arg_map);
|
||||
arg_map(const arg_map &) = delete;
|
||||
void operator=(const arg_map &) = delete;
|
||||
|
||||
typedef typename Context::char_type char_type;
|
||||
|
||||
@@ -788,7 +900,7 @@ class arg_map {
|
||||
|
||||
basic_format_arg<Context> find(basic_string_view<char_type> name) const {
|
||||
// The list is unsorted, so just return the first matching name.
|
||||
for (auto it = map_, end = map_ + size_; it != end; ++it) {
|
||||
for (entry *it = map_, *end = map_ + size_; it != end; ++it) {
|
||||
if (it->name == name)
|
||||
return it->arg;
|
||||
}
|
||||
@@ -811,8 +923,8 @@ class context_base {
|
||||
typedef basic_format_arg<Context> format_arg;
|
||||
|
||||
context_base(OutputIt out, basic_string_view<char_type> format_str,
|
||||
basic_format_args<Context> args)
|
||||
: parse_context_(format_str), out_(out), args_(args) {}
|
||||
basic_format_args<Context> ctx_args)
|
||||
: parse_context_(format_str), out_(out), args_(ctx_args) {}
|
||||
|
||||
// Returns the argument with specified index.
|
||||
format_arg do_get_arg(unsigned arg_id) {
|
||||
@@ -878,7 +990,8 @@ class basic_format_context :
|
||||
private:
|
||||
internal::arg_map<basic_format_context> map_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(basic_format_context);
|
||||
basic_format_context(const basic_format_context &) = delete;
|
||||
void operator=(const basic_format_context &) = delete;
|
||||
|
||||
typedef internal::context_base<OutputIt, basic_format_context, Char> base;
|
||||
typedef typename base::format_arg format_arg;
|
||||
@@ -892,8 +1005,8 @@ class basic_format_context :
|
||||
stored in the object so make sure they have appropriate lifetimes.
|
||||
*/
|
||||
basic_format_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||
basic_format_args<basic_format_context> args)
|
||||
: base(out, format_str, args) {}
|
||||
basic_format_args<basic_format_context> ctx_args)
|
||||
: base(out, format_str, ctx_args) {}
|
||||
|
||||
format_arg next_arg() {
|
||||
return this->do_get_arg(this->parse_context().next_arg_id());
|
||||
@@ -922,10 +1035,10 @@ struct get_type {
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
FMT_CONSTEXPR uint64_t get_types() { return 0; }
|
||||
FMT_CONSTEXPR11 unsigned long long get_types() { return 0; }
|
||||
|
||||
template <typename Context, typename Arg, typename... Args>
|
||||
FMT_CONSTEXPR uint64_t get_types() {
|
||||
FMT_CONSTEXPR11 unsigned long long get_types() {
|
||||
return get_type<Context, Arg>::value | (get_types<Context, Args...>() << 4);
|
||||
}
|
||||
|
||||
@@ -948,7 +1061,7 @@ inline typename std::enable_if<!IS_PACKED, basic_format_arg<Context>>::type
|
||||
make_arg(const T &value) {
|
||||
return make_arg<Context>(value);
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -969,26 +1082,32 @@ class format_arg_store {
|
||||
internal::value<Context>, basic_format_arg<Context>>::type value_type;
|
||||
|
||||
// If the arguments are not packed, add one more element to mark the end.
|
||||
value_type data_[NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1)];
|
||||
static const size_t DATA_SIZE =
|
||||
NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1);
|
||||
value_type data_[DATA_SIZE];
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
|
||||
static FMT_CONSTEXPR uint64_t get_types() {
|
||||
return IS_PACKED ? internal::get_types<Context, Args...>()
|
||||
: -static_cast<int64_t>(NUM_ARGS);
|
||||
static FMT_CONSTEXPR11 long long get_types() {
|
||||
return IS_PACKED ?
|
||||
static_cast<long long>(internal::get_types<Context, Args...>()) :
|
||||
-static_cast<long long>(NUM_ARGS);
|
||||
}
|
||||
|
||||
public:
|
||||
#if FMT_USE_CONSTEXPR
|
||||
static constexpr uint64_t TYPES = get_types();
|
||||
#if FMT_USE_CONSTEXPR11
|
||||
static FMT_CONSTEXPR11 long long TYPES = get_types();
|
||||
#else
|
||||
static const uint64_t TYPES;
|
||||
static const long long TYPES;
|
||||
#endif
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405
|
||||
// Workaround an array initialization bug in gcc 4.5 and earlier.
|
||||
#if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 405) || \
|
||||
(FMT_MSC_VER && FMT_MSC_VER <= 1800)
|
||||
// Workaround array initialization issues in gcc <= 4.5 and MSVC <= 2013.
|
||||
format_arg_store(const Args &... args) {
|
||||
data_ = {internal::make_arg<IS_PACKED, Context>(args)...};
|
||||
value_type init[DATA_SIZE] =
|
||||
{internal::make_arg<IS_PACKED, Context>(args)...};
|
||||
std::memcpy(data_, init, sizeof(init));
|
||||
}
|
||||
#else
|
||||
format_arg_store(const Args &... args)
|
||||
@@ -996,16 +1115,16 @@ class format_arg_store {
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !FMT_USE_CONSTEXPR
|
||||
#if !FMT_USE_CONSTEXPR11
|
||||
template <typename Context, typename ...Args>
|
||||
const uint64_t format_arg_store<Context, Args...>::TYPES = get_types();
|
||||
const long long format_arg_store<Context, Args...>::TYPES = get_types();
|
||||
#endif
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::format_args`. `Context` can
|
||||
be omitted in which case it defaults to `~fmt::context`.
|
||||
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
|
||||
can be omitted in which case it defaults to `~fmt::context`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context, typename ...Args>
|
||||
@@ -1030,7 +1149,7 @@ class basic_format_args {
|
||||
private:
|
||||
// To reduce compiled code size per formatting function call, types of first
|
||||
// max_packed_args arguments are passed in the types_ field.
|
||||
uint64_t types_;
|
||||
unsigned long long types_;
|
||||
union {
|
||||
// If the number of arguments is less than max_packed_args, the argument
|
||||
// values are stored in values_, otherwise they are stored in args_.
|
||||
@@ -1043,7 +1162,7 @@ class basic_format_args {
|
||||
|
||||
typename internal::type type(unsigned index) const {
|
||||
unsigned shift = index * 4;
|
||||
uint64_t mask = 0xf;
|
||||
unsigned long long mask = 0xf;
|
||||
return static_cast<typename internal::type>(
|
||||
(types_ & (mask << shift)) >> shift);
|
||||
}
|
||||
@@ -1054,12 +1173,15 @@ class basic_format_args {
|
||||
void set_data(const format_arg *args) { args_ = args; }
|
||||
|
||||
format_arg do_get(size_type index) const {
|
||||
int64_t signed_types = static_cast<int64_t>(types_);
|
||||
if (signed_types < 0) {
|
||||
uint64_t num_args = -signed_types;
|
||||
return index < num_args ? args_[index] : format_arg();
|
||||
}
|
||||
format_arg arg;
|
||||
long long signed_types = static_cast<long long>(types_);
|
||||
if (signed_types < 0) {
|
||||
unsigned long long num_args =
|
||||
static_cast<unsigned long long>(-signed_types);
|
||||
if (index < num_args)
|
||||
arg = args_[index];
|
||||
return arg;
|
||||
}
|
||||
if (index > internal::max_packed_args)
|
||||
return arg;
|
||||
arg.type_ = type(index);
|
||||
@@ -1080,22 +1202,33 @@ class basic_format_args {
|
||||
*/
|
||||
template <typename... Args>
|
||||
basic_format_args(const format_arg_store<Context, Args...> &store)
|
||||
: types_(store.TYPES) {
|
||||
: types_(static_cast<unsigned long long>(store.TYPES)) {
|
||||
set_data(store.data_);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from a dynamic set of arguments.
|
||||
\endrst
|
||||
*/
|
||||
basic_format_args(const format_arg *args, size_type count)
|
||||
: types_(-static_cast<int64_t>(count)) {
|
||||
set_data(args);
|
||||
}
|
||||
|
||||
/** Returns the argument at specified index. */
|
||||
format_arg get(size_type index) const {
|
||||
format_arg arg = do_get(index);
|
||||
return arg.type_ == internal::name_arg_type ?
|
||||
arg.value_.as_named_arg().template deserialize<Context>() : arg;
|
||||
if (arg.type_ == internal::named_arg_type)
|
||||
arg = arg.value_.as_named_arg().template deserialize<Context>();
|
||||
return arg;
|
||||
}
|
||||
|
||||
unsigned max_size() const {
|
||||
int64_t signed_types = static_cast<int64_t>(types_);
|
||||
long long signed_types = static_cast<long long>(types_);
|
||||
return static_cast<unsigned>(
|
||||
signed_types < 0 ?
|
||||
-signed_types : static_cast<int64_t>(internal::max_packed_args));
|
||||
-signed_types : static_cast<long long>(internal::max_packed_args));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1161,30 +1294,69 @@ inline internal::named_arg<T, wchar_t> arg(wstring_view name, const T &arg) {
|
||||
// This function template is deleted intentionally to disable nested named
|
||||
// arguments as in ``format("{}", arg("a", arg("b", 42)))``.
|
||||
template <typename S, typename T, typename Char>
|
||||
void arg(S, internal::named_arg<T, Char>) FMT_DELETED;
|
||||
void arg(S, internal::named_arg<T, Char>) = delete;
|
||||
|
||||
enum color { black, red, green, yellow, blue, magenta, cyan, white };
|
||||
// A base class for compile-time strings. It is defined in the fmt namespace to
|
||||
// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
|
||||
struct compile_string {};
|
||||
|
||||
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);
|
||||
namespace internal {
|
||||
// If S is a format string type, format_string_traints<S>::char_type gives its
|
||||
// character type.
|
||||
template <typename S, typename Enable = void>
|
||||
struct format_string_traits {
|
||||
private:
|
||||
// Use constructability as a way to detect if format_string_traits is
|
||||
// specialized because other methods are broken on MSVC2013.
|
||||
format_string_traits();
|
||||
};
|
||||
|
||||
/**
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify color (experimental).
|
||||
Example:
|
||||
fmt::print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
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 Char>
|
||||
struct format_string_traits_base { typedef Char char_type; };
|
||||
|
||||
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...));
|
||||
}
|
||||
template <typename Char>
|
||||
struct format_string_traits<Char *> : format_string_traits_base<Char> {};
|
||||
|
||||
template <typename Char>
|
||||
struct format_string_traits<const Char *> : format_string_traits_base<Char> {};
|
||||
|
||||
template <typename Char, std::size_t N>
|
||||
struct format_string_traits<Char[N]> : format_string_traits_base<Char> {};
|
||||
|
||||
template <typename Char, std::size_t N>
|
||||
struct format_string_traits<const Char[N]> : format_string_traits_base<Char> {};
|
||||
|
||||
template <typename Char>
|
||||
struct format_string_traits<std::basic_string<Char>> :
|
||||
format_string_traits_base<Char> {};
|
||||
|
||||
template <typename S>
|
||||
struct format_string_traits<
|
||||
S, typename std::enable_if<std::is_base_of<
|
||||
basic_string_view<typename S::char_type>, S>::value>::type> :
|
||||
format_string_traits_base<typename S::char_type> {};
|
||||
|
||||
template <typename S>
|
||||
struct is_format_string :
|
||||
std::integral_constant<
|
||||
bool, std::is_constructible<format_string_traits<S>>::value> {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compile_string :
|
||||
std::integral_constant<bool, std::is_base_of<compile_string, S>::value> {};
|
||||
|
||||
template <typename... Args, typename S>
|
||||
inline typename std::enable_if<!is_compile_string<S>::value>::type
|
||||
check_format_string(const S &) {}
|
||||
template <typename... Args, typename S>
|
||||
typename std::enable_if<is_compile_string<S>::value>::type
|
||||
check_format_string(S);
|
||||
|
||||
template <typename Char>
|
||||
std::basic_string<Char> vformat(
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<typename buffer_context<Char>::type> args);
|
||||
} // namespace internal
|
||||
|
||||
format_context::iterator vformat_to(
|
||||
internal::buffer &buf, string_view format_str, format_args args);
|
||||
@@ -1206,10 +1378,9 @@ typename std::enable_if<
|
||||
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
|
||||
vformat_to(std::back_insert_iterator<Container> out,
|
||||
string_view format_str, format_args args) {
|
||||
auto& container = internal::get_container(out);
|
||||
internal::container_buffer<Container> buf(container);
|
||||
internal::container_buffer<Container> buf(internal::get_container(out));
|
||||
vformat_to(buf, format_str, args);
|
||||
return std::back_inserter(container);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
@@ -1217,14 +1388,38 @@ typename std::enable_if<
|
||||
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
|
||||
vformat_to(std::back_insert_iterator<Container> out,
|
||||
wstring_view format_str, wformat_args args) {
|
||||
auto& container = internal::get_container(out);
|
||||
internal::container_buffer<Container> buf(container);
|
||||
internal::container_buffer<Container> buf(internal::get_container(out));
|
||||
vformat_to(buf, format_str, args);
|
||||
return std::back_inserter(container);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string vformat(string_view format_str, format_args args);
|
||||
std::wstring vformat(wstring_view format_str, wformat_args args);
|
||||
template <typename Container, typename... Args>
|
||||
inline typename std::enable_if<
|
||||
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
|
||||
format_to(std::back_insert_iterator<Container> out,
|
||||
string_view format_str, const Args & ... args) {
|
||||
format_arg_store<format_context, Args...> as{args...};
|
||||
return vformat_to(out, format_str, as);
|
||||
}
|
||||
|
||||
template <typename Container, typename... Args>
|
||||
inline typename std::enable_if<
|
||||
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
|
||||
format_to(std::back_insert_iterator<Container> out,
|
||||
wstring_view format_str, const Args & ... args) {
|
||||
return vformat_to(out, format_str,
|
||||
make_format_args<wformat_context>(args...));
|
||||
}
|
||||
|
||||
template <
|
||||
typename String,
|
||||
typename Char = typename internal::format_string_traits<String>::char_type>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const String &format_str,
|
||||
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||
// Convert format string to string_view to reduce the number of overloads.
|
||||
return internal::vformat(basic_string_view<Char>(format_str), args);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -1236,18 +1431,19 @@ std::wstring vformat(wstring_view format_str, wformat_args args);
|
||||
std::string message = fmt::format("The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline std::string format(string_view format_str, const Args & ... args) {
|
||||
template <typename String, typename... Args>
|
||||
inline std::basic_string<
|
||||
typename internal::format_string_traits<String>::char_type>
|
||||
format(const String &format_str, const Args & ... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
// This should be just
|
||||
// return vformat(format_str, make_format_args(args...));
|
||||
// return vformat(format_str, make_format_args(args...));
|
||||
// but gcc has trouble optimizing the latter, so break it down.
|
||||
format_arg_store<format_context, Args...> as{args...};
|
||||
return vformat(format_str, as);
|
||||
}
|
||||
template <typename... Args>
|
||||
inline std::wstring format(wstring_view format_str, const Args & ... args) {
|
||||
format_arg_store<wformat_context, Args...> as{args...};
|
||||
return vformat(format_str, as);
|
||||
typedef typename internal::format_string_traits<String>::char_type char_t;
|
||||
typedef typename buffer_context<char_t>::type context_t;
|
||||
format_arg_store<context_t, Args...> as{args...};
|
||||
return internal::vformat(
|
||||
basic_string_view<char_t>(format_str), basic_format_args<context_t>(as));
|
||||
}
|
||||
|
||||
FMT_API void vprint(std::FILE *f, string_view format_str, format_args args);
|
||||
@@ -1267,8 +1463,13 @@ inline void print(std::FILE *f, string_view format_str, const Args & ... args) {
|
||||
format_arg_store<format_context, Args...> as(args...);
|
||||
vprint(f, format_str, as);
|
||||
}
|
||||
/**
|
||||
Prints formatted data to the file *f* which should be in wide-oriented mode
|
||||
set via ``fwide(f, 1)`` or ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline void print(std::FILE *f, wstring_view format_str, const Args & ... args) {
|
||||
inline void print(std::FILE *f, wstring_view format_str,
|
||||
const Args & ... args) {
|
||||
format_arg_store<wformat_context, Args...> as(args...);
|
||||
vprint(f, format_str, as);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,9 @@
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstddef> // for std::ptrdiff_t
|
||||
#include <locale>
|
||||
|
||||
#if defined(_WIN32) && defined(__MINGW32__)
|
||||
# include <cstring>
|
||||
#include <cstring> // for std::memmove
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
# include <locale>
|
||||
#endif
|
||||
|
||||
#if FMT_USE_WINDOWS_H
|
||||
@@ -45,12 +44,6 @@
|
||||
# define FMT_CATCH(x) if (false)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
// Disable the warning about declaration shadowing because it affects too
|
||||
// many valid cases.
|
||||
# pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // conditional expression is constant
|
||||
@@ -71,9 +64,6 @@ inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) {
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
FMT_FUNC format_error::~format_error() throw() {}
|
||||
FMT_FUNC system_error::~system_error() FMT_DTOR_NOEXCEPT {}
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef _MSC_VER
|
||||
@@ -95,9 +85,6 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
||||
# define FMT_SWPRINTF swprintf
|
||||
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||
|
||||
const char RESET_COLOR[] = "\x1b[0m";
|
||||
const wchar_t WRESET_COLOR[] = L"\x1b[0m";
|
||||
|
||||
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
|
||||
|
||||
// Portable thread-safe version of strerror.
|
||||
@@ -205,6 +192,7 @@ void report_error(FormatFunc func, int error_code,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
class locale {
|
||||
private:
|
||||
std::locale locale_;
|
||||
@@ -214,11 +202,27 @@ class locale {
|
||||
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) {
|
||||
@@ -232,30 +236,19 @@ FMT_FUNC void system_error::init(
|
||||
namespace internal {
|
||||
template <typename T>
|
||||
int char_traits<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);
|
||||
}
|
||||
char *buffer, std::size_t size, const char *format, int precision, T value) {
|
||||
return precision < 0 ?
|
||||
FMT_SNPRINTF(buffer, size, format, width, value) :
|
||||
FMT_SNPRINTF(buffer, size, format, width, precision, value);
|
||||
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,
|
||||
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);
|
||||
}
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format, int precision,
|
||||
T value) {
|
||||
return precision < 0 ?
|
||||
FMT_SWPRINTF(buffer, size, format, width, value) :
|
||||
FMT_SWPRINTF(buffer, size, format, width, precision, value);
|
||||
FMT_SWPRINTF(buffer, size, format, value) :
|
||||
FMT_SWPRINTF(buffer, size, format, precision, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -279,11 +272,16 @@ const char basic_data<T>::DIGITS[] =
|
||||
|
||||
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>::POWERS_OF_10_64[] = {
|
||||
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
|
||||
0,
|
||||
FMT_POWERS_OF_10(1),
|
||||
FMT_POWERS_OF_10(1000000000ull),
|
||||
@@ -294,35 +292,35 @@ const uint64_t basic_data<T>::POWERS_OF_10_64[] = {
|
||||
// These are generated by support/compute-powers.py.
|
||||
template <typename T>
|
||||
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
||||
0xfa8fd5a0081c0288ull, 0xbaaee17fa23ebf76ull, 0x8b16fb203055ac76ull,
|
||||
0xcf42894a5dce35eaull, 0x9a6bb0aa55653b2dull, 0xe61acf033d1a45dfull,
|
||||
0xab70fe17c79ac6caull, 0xff77b1fcbebcdc4full, 0xbe5691ef416bd60cull,
|
||||
0x8dd01fad907ffc3cull, 0xd3515c2831559a83ull, 0x9d71ac8fada6c9b5ull,
|
||||
0xea9c227723ee8bcbull, 0xaecc49914078536dull, 0x823c12795db6ce57ull,
|
||||
0xc21094364dfb5637ull, 0x9096ea6f3848984full, 0xd77485cb25823ac7ull,
|
||||
0xa086cfcd97bf97f4ull, 0xef340a98172aace5ull, 0xb23867fb2a35b28eull,
|
||||
0x84c8d4dfd2c63f3bull, 0xc5dd44271ad3cdbaull, 0x936b9fcebb25c996ull,
|
||||
0xdbac6c247d62a584ull, 0xa3ab66580d5fdaf6ull, 0xf3e2f893dec3f126ull,
|
||||
0xb5b5ada8aaff80b8ull, 0x87625f056c7c4a8bull, 0xc9bcff6034c13053ull,
|
||||
0x964e858c91ba2655ull, 0xdff9772470297ebdull, 0xa6dfbd9fb8e5b88full,
|
||||
0xf8a95fcf88747d94ull, 0xb94470938fa89bcfull, 0x8a08f0f8bf0f156bull,
|
||||
0xcdb02555653131b6ull, 0x993fe2c6d07b7facull, 0xe45c10c42a2b3b06ull,
|
||||
0xaa242499697392d3ull, 0xfd87b5f28300ca0eull, 0xbce5086492111aebull,
|
||||
0x8cbccc096f5088ccull, 0xd1b71758e219652cull, 0x9c40000000000000ull,
|
||||
0xe8d4a51000000000ull, 0xad78ebc5ac620000ull, 0x813f3978f8940984ull,
|
||||
0xc097ce7bc90715b3ull, 0x8f7e32ce7bea5c70ull, 0xd5d238a4abe98068ull,
|
||||
0x9f4f2726179a2245ull, 0xed63a231d4c4fb27ull, 0xb0de65388cc8ada8ull,
|
||||
0x83c7088e1aab65dbull, 0xc45d1df942711d9aull, 0x924d692ca61be758ull,
|
||||
0xda01ee641a708deaull, 0xa26da3999aef774aull, 0xf209787bb47d6b85ull,
|
||||
0xb454e4a179dd1877ull, 0x865b86925b9bc5c2ull, 0xc83553c5c8965d3dull,
|
||||
0x952ab45cfa97a0b3ull, 0xde469fbd99a05fe3ull, 0xa59bc234db398c25ull,
|
||||
0xf6c69a72a3989f5cull, 0xb7dcbf5354e9beceull, 0x88fcf317f22241e2ull,
|
||||
0xcc20ce9bd35c78a5ull, 0x98165af37b2153dfull, 0xe2a0b5dc971f303aull,
|
||||
0xa8d9d1535ce3b396ull, 0xfb9b7cd9a4a7443cull, 0xbb764c4ca7a44410ull,
|
||||
0x8bab8eefb6409c1aull, 0xd01fef10a657842cull, 0x9b10a4e5e9913129ull,
|
||||
0xe7109bfba19c0c9dull, 0xac2820d9623bf429ull, 0x80444b5e7aa7cf85ull,
|
||||
0xbf21e44003acdd2dull, 0x8e679c2f5e44ff8full, 0xd433179d9c8cb841ull,
|
||||
0x9e19db92b4e31ba9ull, 0xeb96bf6ebadf77d9ull, 0xaf87023b9bf0ee6bull
|
||||
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
|
||||
@@ -339,6 +337,99 @@ const int16_t basic_data<T>::POW10_EXPONENTS[] = {
|
||||
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;
|
||||
@@ -349,6 +440,255 @@ FMT_FUNC fp operator*(fp x, fp y) {
|
||||
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
|
||||
@@ -513,23 +853,9 @@ FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
|
||||
vprint(stdout, format_str, args);
|
||||
}
|
||||
|
||||
FMT_FUNC 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(RESET_COLOR, stdout);
|
||||
}
|
||||
|
||||
FMT_FUNC 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(WRESET_COLOR, stdout);
|
||||
}
|
||||
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
FMT_FUNC locale locale_provider::locale() { return fmt::locale(); }
|
||||
#endif
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
||||
1090
include/fmt/format.h
1090
include/fmt/format.h
File diff suppressed because it is too large
Load Diff
@@ -53,8 +53,7 @@ struct test_stream : std::basic_ostream<Char> {
|
||||
void operator<<(null);
|
||||
};
|
||||
|
||||
// Checks if T has an overloaded operator<< which is a free function (not a
|
||||
// member of std::ostream).
|
||||
// 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:
|
||||
@@ -72,15 +71,6 @@ class is_streamable {
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// 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>
|
||||
class convert_to_int<T, Char, true> {
|
||||
public:
|
||||
static const bool value =
|
||||
convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
|
||||
};
|
||||
|
||||
// Write the content of buf to os.
|
||||
template <typename Char>
|
||||
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
||||
@@ -105,17 +95,24 @@ void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||
output << value;
|
||||
buffer.resize(buffer.size());
|
||||
}
|
||||
|
||||
// Disable builtin formatting of enums and use operator<< instead.
|
||||
template <typename T>
|
||||
struct format_enum<T,
|
||||
typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type {};
|
||||
} // 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>::type>
|
||||
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>
|
||||
@@ -123,8 +120,7 @@ struct formatter<T, Char,
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::format_value(buffer, value);
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
return ctx.out();
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -139,52 +139,10 @@ class buffered_file {
|
||||
// Destroys the object closing the file it represents if any.
|
||||
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:
|
||||
// A proxy object to emulate a move constructor.
|
||||
// It is private to make it impossible call operator Proxy directly.
|
||||
struct Proxy {
|
||||
FILE *file;
|
||||
};
|
||||
buffered_file(const buffered_file &) = delete;
|
||||
void operator=(const buffered_file &) = delete;
|
||||
|
||||
public:
|
||||
// A "move constructor" for moving from a temporary.
|
||||
buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
|
||||
|
||||
// A "move constructor" for moving from an lvalue.
|
||||
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) {
|
||||
f.file_ = FMT_NULL;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from a temporary.
|
||||
buffered_file &operator=(Proxy p) {
|
||||
close();
|
||||
file_ = p.file;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from an lvalue.
|
||||
buffered_file &operator=(buffered_file &other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = FMT_NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns a proxy object for moving from a temporary:
|
||||
// buffered_file file = buffered_file(...);
|
||||
operator Proxy() FMT_NOEXCEPT {
|
||||
Proxy p = {file_};
|
||||
file_ = FMT_NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
#else
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
@@ -197,7 +155,6 @@ public:
|
||||
other.file_ = FMT_NULL;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
@@ -249,52 +206,9 @@ class file {
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
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:
|
||||
// A proxy object to emulate a move constructor.
|
||||
// It is private to make it impossible call operator Proxy directly.
|
||||
struct Proxy {
|
||||
int fd;
|
||||
};
|
||||
|
||||
public:
|
||||
// A "move constructor" for moving from a temporary.
|
||||
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
||||
|
||||
// A "move constructor" for moving from an lvalue.
|
||||
file(file &other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from a temporary.
|
||||
file &operator=(Proxy p) {
|
||||
close();
|
||||
fd_ = p.fd;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from an lvalue.
|
||||
file &operator=(file &other) {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns a proxy object for moving from a temporary:
|
||||
// file f = file(...);
|
||||
operator Proxy() FMT_NOEXCEPT {
|
||||
Proxy p = {fd_};
|
||||
fd_ = -1;
|
||||
return p;
|
||||
}
|
||||
|
||||
#else
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(file);
|
||||
file(const file &) = delete;
|
||||
void operator=(const file &) = delete;
|
||||
|
||||
public:
|
||||
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||
@@ -307,7 +221,6 @@ class file {
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_DTOR_NOEXCEPT;
|
||||
@@ -353,7 +266,8 @@ class file {
|
||||
long getpagesize();
|
||||
|
||||
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__)
|
||||
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||
!defined(__NEWLIB_H__)
|
||||
# define FMT_LOCALE
|
||||
#endif
|
||||
|
||||
@@ -381,7 +295,8 @@ class Locale {
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
|
||||
Locale(const Locale &) = delete;
|
||||
void operator=(const Locale &) = delete;
|
||||
|
||||
public:
|
||||
typedef locale_t Type;
|
||||
@@ -406,12 +321,4 @@ class Locale {
|
||||
#endif // FMT_LOCALE
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#if !FMT_USE_RVALUE_REFERENCES
|
||||
namespace std {
|
||||
// For compatibility with C++98.
|
||||
inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; }
|
||||
inline fmt::file &move(fmt::file &f) { return f; }
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // FMT_POSIX_H_
|
||||
|
||||
@@ -49,8 +49,7 @@ class printf_precision_handler: public function<int> {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_integral<T>::value, int>::type
|
||||
operator()(T) {
|
||||
typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(T) {
|
||||
FMT_THROW(format_error("precision is not integer"));
|
||||
return 0;
|
||||
}
|
||||
@@ -134,7 +133,7 @@ class arg_converter: public function<void> {
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context> &arg, Char type) {
|
||||
visit(arg_converter<T, Context>(arg, type), arg);
|
||||
fmt::visit(arg_converter<T, Context>(arg, type), arg);
|
||||
}
|
||||
|
||||
// Converts an integer argument to char for printf.
|
||||
@@ -143,8 +142,6 @@ class char_converter: public function<void> {
|
||||
private:
|
||||
basic_format_arg<Context> &arg_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(char_converter);
|
||||
|
||||
public:
|
||||
explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
|
||||
|
||||
@@ -170,8 +167,6 @@ class printf_width_handler: public function<unsigned> {
|
||||
|
||||
format_specs &spec_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(printf_width_handler);
|
||||
|
||||
public:
|
||||
explicit printf_width_handler(format_specs &spec) : spec_(spec) {}
|
||||
|
||||
@@ -227,12 +222,12 @@ class printf_arg_formatter:
|
||||
context_type &context_;
|
||||
|
||||
void write_null_pointer(char) {
|
||||
this->spec().type_ = 0;
|
||||
this->spec()->type_ = 0;
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
void write_null_pointer(wchar_t) {
|
||||
this->spec().type_ = 0;
|
||||
this->spec()->type_ = 0;
|
||||
this->write(L"(nil)");
|
||||
}
|
||||
|
||||
@@ -248,28 +243,36 @@ class printf_arg_formatter:
|
||||
*/
|
||||
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),
|
||||
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec),
|
||||
context_(ctx) {}
|
||||
|
||||
using base::operator();
|
||||
|
||||
/** Formats an argument of type ``bool``. */
|
||||
iterator operator()(bool value) {
|
||||
format_specs &fmt_spec = this->spec();
|
||||
if (fmt_spec.type_ != 's')
|
||||
return (*this)(value ? 1 : 0);
|
||||
fmt_spec.type_ = 0;
|
||||
this->write(value);
|
||||
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();
|
||||
}
|
||||
|
||||
/** Formats a character. */
|
||||
iterator operator()(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;
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
|
||||
operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
@@ -277,7 +280,7 @@ class printf_arg_formatter:
|
||||
iterator operator()(const char *value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->spec().type_ == 'p')
|
||||
else if (this->spec()->type_ == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write("(null)");
|
||||
@@ -288,18 +291,26 @@ class printf_arg_formatter:
|
||||
iterator operator()(const wchar_t *value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->spec().type_ == 'p')
|
||||
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;
|
||||
this->spec()->type_ = 0;
|
||||
write_null_pointer(char_type());
|
||||
return this->out();
|
||||
}
|
||||
@@ -326,7 +337,9 @@ struct printf_formatter {
|
||||
/** This template formats data and writes the output to a writer. */
|
||||
template <typename OutputIt, typename Char, typename ArgFormatter>
|
||||
class basic_printf_context :
|
||||
private internal::context_base<
|
||||
// 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. */
|
||||
@@ -441,7 +454,7 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
spec.width_ =
|
||||
visit(internal::printf_width_handler<char_type>(spec), get_arg(it));
|
||||
fmt::visit(internal::printf_width_handler<char_type>(spec), get_arg(it));
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
@@ -477,14 +490,14 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
spec.precision_ =
|
||||
visit(internal::printf_precision_handler(), get_arg(it));
|
||||
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) && visit(internal::is_zero_int(), arg))
|
||||
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())
|
||||
@@ -538,7 +551,7 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
break;
|
||||
case 'c':
|
||||
// TODO: handle wchar_t better?
|
||||
visit(internal::char_converter<basic_printf_context>(arg), arg);
|
||||
fmt::visit(internal::char_converter<basic_printf_context>(arg), arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -546,7 +559,7 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
visit(AF(buffer, spec, *this), arg);
|
||||
fmt::visit(AF(buffer, spec, *this), arg);
|
||||
}
|
||||
buffer.append(pointer_from(start), pointer_from(it));
|
||||
}
|
||||
|
||||
@@ -87,24 +87,23 @@ class is_like_std_string {
|
||||
!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 {};
|
||||
|
||||
template <typename T>
|
||||
struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_range_<T>::value && !is_like_std_string<T>::value;
|
||||
};
|
||||
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>
|
||||
@@ -121,23 +120,38 @@ class is_tuple_like_ {
|
||||
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_tuple_like_<T>::value && !is_range_<T>::value;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
#if FMT_HAS_FEATURE(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
# define FMT_USE_INTEGER_SEQUENCE 1
|
||||
// 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
|
||||
# define FMT_USE_INTEGER_SEQUENCE 0
|
||||
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
|
||||
|
||||
#if FMT_USE_INTEGER_SEQUENCE
|
||||
namespace internal {
|
||||
template <size_t... Is, class Tuple, class F>
|
||||
void for_each(std::index_sequence<Is...>, Tuple &&tup, F &&f) noexcept {
|
||||
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)...};
|
||||
@@ -145,7 +159,7 @@ void for_each(std::index_sequence<Is...>, Tuple &&tup, F &&f) noexcept {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR std::make_index_sequence<std::tuple_size<T>::value>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
|
||||
get_indexes(T const &) { return {}; }
|
||||
|
||||
template <class Tuple, class F>
|
||||
@@ -153,13 +167,72 @@ 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::internal::is_tuple_like<TupleT>::value>::type> {
|
||||
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;
|
||||
}
|
||||
|
||||
fmt::formatting_tuple<Char> formatting;
|
||||
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()) {
|
||||
@@ -171,20 +244,8 @@ struct formatter<TupleT, Char,
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
internal::for_each(values, [&](const auto &v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
if (formatting.add_delimiter_spaces && i > 0) {
|
||||
format_to(out, " {}", v);
|
||||
} else {
|
||||
format_to(out, "{}", v);
|
||||
}
|
||||
++i;
|
||||
});
|
||||
|
||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
@@ -193,13 +254,18 @@ struct formatter<TupleT, Char,
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
#endif // FMT_USE_INTEGER_SEQUENCE
|
||||
|
||||
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::internal::is_range<RangeT>::value>::type> {
|
||||
struct formatter<RangeT, Char,
|
||||
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
|
||||
|
||||
fmt::formatting_range<Char> formatting;
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
@@ -219,11 +285,10 @@ struct formatter< RangeT, Char,
|
||||
}
|
||||
internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
if (formatting.add_delimiter_spaces && i > 0) {
|
||||
format_to(out, " {}", *it);
|
||||
} else {
|
||||
format_to(out, "{}", *it);
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
|
||||
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(...) { return null<>(); }
|
||||
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<>(); }
|
||||
@@ -54,7 +58,6 @@ inline std::tm localtime(std::time_t time) {
|
||||
return lt.tm_;
|
||||
// Too big time values may be unsupported.
|
||||
FMT_THROW(format_error("time_t value out of range"));
|
||||
return std::tm();
|
||||
}
|
||||
|
||||
// Thread-safe replacement for std::gmtime
|
||||
@@ -90,7 +93,6 @@ inline std::tm gmtime(std::time_t time) {
|
||||
return gt.tm_;
|
||||
// Too big time values may be unsupported.
|
||||
FMT_THROW(format_error("time_t value out of range"));
|
||||
return std::tm();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
@@ -8,40 +8,46 @@
|
||||
#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 *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, double value);
|
||||
char *, std::size_t, const char *, int, double);
|
||||
|
||||
template FMT_API int internal::char_traits<char>::format_float(
|
||||
char *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, long double value);
|
||||
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 *lp);
|
||||
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> &args);
|
||||
const basic_format_args<wformat_context> &);
|
||||
|
||||
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, double value);
|
||||
wchar_t *, std::size_t, const wchar_t *, int, double);
|
||||
|
||||
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, long double value);
|
||||
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
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := fmt_static
|
||||
LOCAL_MODULE_FILENAME := libfmt
|
||||
|
||||
LOCAL_SRC_FILES := fmt/format.cc
|
||||
LOCAL_SRC_FILES := ../src/format.cc
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
||||
13
support/appveyor-build.py
Executable file → Normal file
13
support/appveyor-build.py
Executable file → Normal file
@@ -6,9 +6,11 @@ from subprocess import check_call
|
||||
|
||||
build = os.environ['BUILD']
|
||||
config = os.environ['CONFIGURATION']
|
||||
platform = os.environ.get('PLATFORM')
|
||||
platform = os.environ['PLATFORM']
|
||||
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':
|
||||
cmake_command.append('-GMinGW Makefiles')
|
||||
build_command = ['mingw32-make', '-j4']
|
||||
@@ -21,7 +23,12 @@ else:
|
||||
# Add MSBuild 14.0 to PATH as described in
|
||||
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
|
||||
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
|
||||
generator = 'Visual Studio 15 2017'
|
||||
if image == 'Visual Studio 2013':
|
||||
generator = 'Visual Studio 12 2013'
|
||||
elif image == 'Visual Studio 2015':
|
||||
generator = 'Visual Studio 14 2015'
|
||||
elif image == 'Visual Studio 2017':
|
||||
generator = 'Visual Studio 15 2017'
|
||||
if platform == 'x64':
|
||||
generator += ' Win64'
|
||||
cmake_command.append('-G' + generator)
|
||||
|
||||
@@ -2,21 +2,28 @@ configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
image: Visual Studio 2017
|
||||
clone_depth: 1
|
||||
|
||||
platform:
|
||||
- Win32
|
||||
- x64
|
||||
|
||||
image:
|
||||
- Visual Studio 2013
|
||||
- Visual Studio 2015
|
||||
- Visual Studio 2017
|
||||
|
||||
environment:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
matrix:
|
||||
- BUILD: msvc
|
||||
- BUILD: msvc
|
||||
PLATFORM: x64
|
||||
MSVC_DEFAULT_OPTIONS: ON
|
||||
BUILD: msvc
|
||||
|
||||
before_build:
|
||||
# Workaround for CMake not wanting sh.exe on PATH for MinGW.
|
||||
- set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
||||
- mkdir build
|
||||
- cd build
|
||||
|
||||
build_script:
|
||||
- python support/appveyor-build.py
|
||||
- python ../support/appveyor-build.py
|
||||
|
||||
on_failure:
|
||||
- appveyor PushArtifact Testing/Temporary/LastTest.log
|
||||
|
||||
@@ -1,67 +1,52 @@
|
||||
# C++14 feature support detection
|
||||
|
||||
if (NOT FMT_USE_CPP14)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if (FMT_USE_CPP14)
|
||||
check_cxx_compiler_flag(-std=c++14 HAVE_STD_CPP14_FLAG)
|
||||
if (HAVE_STD_CPP14_FLAG)
|
||||
# Check if including cmath works with -std=c++14 and -O3.
|
||||
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++14 -O3")
|
||||
check_cxx_source_compiles("
|
||||
#include <cmath>
|
||||
int main() {}" FMT_CPP14_CMATH)
|
||||
# Check if including <unistd.h> works with -std=c++14.
|
||||
# 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_CPP14_UNISTD_H)
|
||||
# Check if snprintf works with -std=c++14. It may not in MinGW.
|
||||
check_cxx_source_compiles("
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
char buffer[10];
|
||||
snprintf(buffer, 10, \"foo\");
|
||||
}" FMT_CPP14_SNPRINTF)
|
||||
if (FMT_CPP14_CMATH AND FMT_CPP14_UNISTD_H AND FMT_CPP14_SNPRINTF)
|
||||
set(CPP14_FLAG -std=c++14)
|
||||
else ()
|
||||
check_cxx_compiler_flag(-std=gnu++14 HAVE_STD_GNUPP14_FLAG)
|
||||
if (HAVE_STD_CPP14_FLAG)
|
||||
set(CPP14_FLAG -std=gnu++14)
|
||||
endif ()
|
||||
endif ()
|
||||
set(CMAKE_REQUIRED_FLAGS )
|
||||
else ()
|
||||
check_cxx_compiler_flag(-std=c++1y HAVE_STD_CPP1Y_FLAG)
|
||||
if (HAVE_STD_CPP1Y_FLAG)
|
||||
set(CPP14_FLAG -std=c++1y)
|
||||
else ()
|
||||
# Fallback on c++11 if c++14 is not available.
|
||||
check_cxx_compiler_flag(-std=c++11 HAVE_STD_CPP11_FLAG)
|
||||
if (HAVE_STD_CPP11_FLAG)
|
||||
set(CPP14_FLAG -std=c++11)
|
||||
else ()
|
||||
check_cxx_compiler_flag(-std=c++0x HAVE_STD_CPP0X_FLAG)
|
||||
if (HAVE_STD_CPP0X_FLAG)
|
||||
set(CPP14_FLAG -std=c++0x)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
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 ()
|
||||
|
||||
if (CMAKE_CXX_STANDARD)
|
||||
# Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified.
|
||||
set(CPP14_FLAG )
|
||||
endif ()
|
||||
|
||||
message(STATUS "CPP14_FLAG: ${CPP14_FLAG}")
|
||||
set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
|
||||
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
|
||||
|
||||
# Check if variadic templates are working and not affected by GCC bug 39653:
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
||||
@@ -69,17 +54,26 @@ 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("
|
||||
@@ -87,11 +81,17 @@ check_cxx_source_compiles("
|
||||
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 )
|
||||
|
||||
@@ -41,10 +41,13 @@ print('Significands:', end='')
|
||||
for i, fp in enumerate(powers):
|
||||
if i % 3 == 0:
|
||||
print(end='\n ')
|
||||
print(' {:0<#16x}ull'.format(fp.f, ), end=',')
|
||||
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:]))
|
||||
|
||||
@@ -152,7 +152,9 @@ def update_site(env):
|
||||
if os.path.exists(html_dir):
|
||||
shutil.rmtree(html_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')
|
||||
import build
|
||||
build.build_docs(version, doc_dir=target_doc_dir,
|
||||
@@ -243,12 +245,12 @@ def release(args):
|
||||
id = r.json()['id']
|
||||
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
|
||||
package = 'fmt-{}.zip'.format(version)
|
||||
with open('build/fmt/' + package, 'rb') as f:
|
||||
r = requests.post(
|
||||
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
||||
params=params, files={package: f})
|
||||
if r.status_code != 201:
|
||||
raise Exception('Failed to upload an asset ' + str(r))
|
||||
r = requests.post(
|
||||
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
||||
headers={'Content-Type': 'application/zip'},
|
||||
params=params, data=open('build/fmt/' + package, 'rb'))
|
||||
if r.status_code != 201:
|
||||
raise Exception('Failed to upload an asset ' + str(r))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -109,6 +109,30 @@ class Translator(nodes.NodeVisitor):
|
||||
def depart_image(self, node):
|
||||
pass
|
||||
|
||||
def write_row(self, row, widths):
|
||||
for i, entry in enumerate(row):
|
||||
text = entry[0][0] if len(entry) > 0 else ''
|
||||
if i != 0:
|
||||
self.write('|')
|
||||
self.write('{:{}}'.format(text, widths[i]))
|
||||
self.write('\n')
|
||||
|
||||
def visit_table(self, node):
|
||||
table = node.children[0]
|
||||
colspecs = table[:-2]
|
||||
thead = table[-2]
|
||||
tbody = table[-1]
|
||||
widths = [int(cs['colwidth']) for cs in colspecs]
|
||||
sep = '|'.join(['-' * w for w in widths]) + '\n'
|
||||
self.write('\n\n')
|
||||
self.write_row(thead[0], widths)
|
||||
self.write(sep)
|
||||
for row in tbody:
|
||||
self.write_row(row, widths)
|
||||
raise nodes.SkipChildren
|
||||
|
||||
def depart_table(self, node):
|
||||
pass
|
||||
|
||||
class MDWriter(writers.Writer):
|
||||
"""GitHub-flavored markdown writer"""
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# Build the project on Travis CI.
|
||||
|
||||
from __future__ import print_function
|
||||
import errno, os, re, shutil, subprocess, sys, tempfile, urllib
|
||||
from subprocess import call, check_call, check_output, Popen, PIPE, STDOUT
|
||||
import errno, os, shutil, subprocess, sys, urllib
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
|
||||
def rmtree_if_exists(dir):
|
||||
try:
|
||||
@@ -85,14 +85,12 @@ test_build_dir = os.path.join(fmt_dir, "_build_test")
|
||||
|
||||
# Configure library.
|
||||
makedirs_if_not_exist(build_dir)
|
||||
common_cmake_flags = [
|
||||
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build
|
||||
cmake_flags = [
|
||||
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
|
||||
'-DCMAKE_CXX_STANDARD=' + standard
|
||||
]
|
||||
extra_cmake_flags = []
|
||||
if standard != '14':
|
||||
extra_cmake_flags = ['-DCMAKE_CXX_FLAGS=-std=c++' + standard]
|
||||
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', fmt_dir] +
|
||||
common_cmake_flags + extra_cmake_flags, cwd=build_dir)
|
||||
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] +
|
||||
cmake_flags, cwd=build_dir)
|
||||
|
||||
# Build library.
|
||||
check_call(['make', '-j4'], cwd=build_dir)
|
||||
@@ -101,7 +99,7 @@ check_call(['make', '-j4'], cwd=build_dir)
|
||||
env = os.environ.copy()
|
||||
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
|
||||
if call(['make', 'test'], env=env, cwd=build_dir):
|
||||
with open('Testing/Temporary/LastTest.log', 'r') as f:
|
||||
with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f:
|
||||
print(f.read())
|
||||
sys.exit(-1)
|
||||
|
||||
@@ -110,7 +108,6 @@ check_call(['make', 'install'], cwd=build_dir)
|
||||
|
||||
# Test installation.
|
||||
makedirs_if_not_exist(test_build_dir)
|
||||
check_call(['cmake', '-DCMAKE_CXX_FLAGS=-std=c++' + standard,
|
||||
os.path.join(fmt_dir, "test", "find-package-test")] +
|
||||
common_cmake_flags, cwd=test_build_dir)
|
||||
check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] +
|
||||
cmake_flags, cwd=test_build_dir)
|
||||
check_call(['make', '-j4'], cwd=test_build_dir)
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
|
||||
add_library(gmock STATIC
|
||||
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
||||
target_compile_options(gmock PUBLIC ${CPP14_FLAG})
|
||||
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)
|
||||
if (Threads_FOUND)
|
||||
@@ -43,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})
|
||||
target_compile_definitions(test-main PUBLIC
|
||||
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
|
||||
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
|
||||
target_link_libraries(test-main gmock fmt)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
@@ -57,7 +57,7 @@ endif ()
|
||||
# Use less strict pedantic flags for the tests because GMock doesn't compile
|
||||
# cleanly with -pedantic and -std=c++98.
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
|
||||
#set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
|
||||
endif ()
|
||||
|
||||
function(add_fmt_executable name)
|
||||
@@ -80,26 +80,21 @@ function(add_fmt_test name)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
|
||||
add_test(NAME ${name} COMMAND ${name})
|
||||
endfunction()
|
||||
|
||||
add_fmt_test(assert-test)
|
||||
add_fmt_test(core-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(ostream-test)
|
||||
add_fmt_test(printf-test)
|
||||
add_fmt_test(time-test)
|
||||
add_fmt_test(util-test mock-allocator.h)
|
||||
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)
|
||||
add_fmt_executable(posix-mock-test
|
||||
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
|
||||
@@ -107,6 +102,10 @@ if (HAVE_OPEN)
|
||||
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
|
||||
target_link_libraries(posix-mock-test gmock)
|
||||
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
add_test(NAME posix-mock-test COMMAND posix-mock-test)
|
||||
add_fmt_test(posix-test)
|
||||
endif ()
|
||||
@@ -114,6 +113,7 @@ endif ()
|
||||
add_fmt_executable(header-only-test
|
||||
header-only-test.cc header-only-test2.cc test-main.cc)
|
||||
target_link_libraries(header-only-test gmock)
|
||||
target_include_directories(header-only-test SYSTEM PUBLIC gtest gmock)
|
||||
if (TARGET fmt-header-only)
|
||||
target_link_libraries(header-only-test fmt-header-only)
|
||||
else ()
|
||||
@@ -123,23 +123,34 @@ else ()
|
||||
endif ()
|
||||
|
||||
# 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 (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
||||
endif ()
|
||||
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
||||
add_library(noexception-test ../src/format.cc)
|
||||
target_compile_options(noexception-test PUBLIC ${CPP14_FLAG})
|
||||
target_include_directories(
|
||||
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
target_include_directories(noexception-test SYSTEM PUBLIC gtest gmock)
|
||||
endif ()
|
||||
|
||||
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
|
||||
|
||||
if (FMT_PEDANTIC)
|
||||
# Test that the library compiles without windows.h.
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_library(no-windows-h-test ../src/format.cc)
|
||||
target_compile_options(no-windows-h-test PUBLIC ${CPP14_FLAG})
|
||||
target_include_directories(
|
||||
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(no-windows-h-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
target_include_directories(no-windows-h-test SYSTEM PUBLIC gtest gmock)
|
||||
endif ()
|
||||
|
||||
add_test(compile-test ${CMAKE_CTEST_COMMAND}
|
||||
@@ -148,9 +159,11 @@ if (FMT_PEDANTIC)
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/compile-test"
|
||||
--build-generator ${CMAKE_GENERATOR}
|
||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||
--build-options
|
||||
--build-options
|
||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||
"-DCPP14_FLAG=${CPP14_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}")
|
||||
|
||||
# test if the targets are findable from the build directory
|
||||
@@ -163,8 +176,9 @@ if (FMT_PEDANTIC)
|
||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||
--build-options
|
||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||
"-DCPP14_FLAG=${CPP14_FLAG}"
|
||||
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
||||
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||
|
||||
# test if the targets are findable when add_subdirectory is used
|
||||
@@ -177,6 +191,7 @@ if (FMT_PEDANTIC)
|
||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||
--build-options
|
||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||
"-DCPP14_FLAG=${CPP14_FLAG}"
|
||||
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||
endif ()
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
project(fmt-test)
|
||||
|
||||
add_subdirectory(../.. fmt)
|
||||
|
||||
add_executable(library-test "main.cc")
|
||||
target_compile_options(library-test PUBLIC ${CPP14_FLAG})
|
||||
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)
|
||||
add_executable(header-only-test "main.cc")
|
||||
target_compile_options(header-only-test PUBLIC ${CPP14_FLAG})
|
||||
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 ()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/core.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest.h"
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# Test if compile errors are produced where necessary.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
|
||||
set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
|
||||
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG} ${PEDANTIC_COMPILE_FLAGS})
|
||||
|
||||
function (generate_source result fragment)
|
||||
set(${result} "
|
||||
|
||||
460
test/core-test.cc
Normal file
460
test/core-test.cc
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");
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
#include "fmt/format.h"
|
||||
#include "gtest-extra.h"
|
||||
|
||||
// MSVC 2013 is known to be broken.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
|
||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
||||
// rounded to 0.
|
||||
class custom_arg_formatter :
|
||||
@@ -16,13 +19,15 @@ class custom_arg_formatter :
|
||||
typedef fmt::back_insert_range<fmt::internal::buffer> range;
|
||||
typedef fmt::arg_formatter<range> base;
|
||||
|
||||
custom_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
|
||||
custom_arg_formatter(
|
||||
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
|
||||
: base(ctx, s) {}
|
||||
|
||||
using base::operator();
|
||||
|
||||
iterator operator()(double value) {
|
||||
if (round(value * pow(10, spec().precision())) == 0)
|
||||
// Comparing a float to 0.0 is safe
|
||||
if (round(value * pow(10, spec()->precision())) == 0.0)
|
||||
value = 0;
|
||||
return base::operator()(value);
|
||||
}
|
||||
@@ -44,3 +49,4 @@ std::string custom_format(const char *format_str, const Args & ... args) {
|
||||
TEST(CustomFormatterTest, Format) {
|
||||
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
project(fmt-test)
|
||||
|
||||
find_package(FMT REQUIRED)
|
||||
|
||||
add_executable(library-test main.cc)
|
||||
target_compile_options(library-test PUBLIC ${CPP14_FLAG})
|
||||
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)
|
||||
add_executable(header-only-test main.cc)
|
||||
target_compile_options(header-only-test PUBLIC ${CPP14_FLAG})
|
||||
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 ()
|
||||
|
||||
@@ -11,18 +11,91 @@
|
||||
|
||||
// Include format.cc instead of format.h to test implementation.
|
||||
#include "../src/format.cc"
|
||||
#include "fmt/color.h"
|
||||
#include "fmt/printf.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef min
|
||||
#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) {
|
||||
@@ -30,9 +103,8 @@ struct ValueExtractor: fmt::internal::function<T> {
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
T operator()(U) {
|
||||
FMT_NORETURN T operator()(U) {
|
||||
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,9 +125,9 @@ TEST(FormatTest, FormatNegativeNaN) {
|
||||
}
|
||||
|
||||
TEST(FormatTest, StrError) {
|
||||
char *message = 0;
|
||||
char *message = nullptr;
|
||||
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),
|
||||
"invalid buffer");
|
||||
buffer[0] = 'x';
|
||||
@@ -117,3 +189,14 @@ TEST(FormatTest, FormatErrorCode) {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -14,22 +14,24 @@
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
// Test that the library compiles if None is defined to 0 as done by xlib.h.
|
||||
#define None 0
|
||||
// Check if fmt/format.h compiles with windows.h included before it.
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "mock-allocator.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "mock-allocator.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef ERROR
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
using std::size_t;
|
||||
|
||||
using fmt::basic_memory_buffer;
|
||||
using fmt::basic_writer;
|
||||
using fmt::format;
|
||||
using fmt::format_error;
|
||||
@@ -37,6 +39,9 @@ using fmt::string_view;
|
||||
using fmt::memory_buffer;
|
||||
using fmt::wmemory_buffer;
|
||||
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
|
||||
namespace {
|
||||
|
||||
// Format value using the standard library.
|
||||
@@ -106,6 +111,451 @@ struct WriteChecker {
|
||||
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
|
||||
} // namespace
|
||||
|
||||
// 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, CountDigits) {
|
||||
test_count_digits<uint32_t>();
|
||||
test_count_digits<uint64_t>();
|
||||
}
|
||||
|
||||
struct uint32_pair {
|
||||
uint32_t u[2];
|
||||
};
|
||||
|
||||
TEST(UtilTest, BitCast) {
|
||||
auto s = fmt::internal::bit_cast<uint32_pair>(uint64_t{42});
|
||||
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42ull);
|
||||
s = fmt::internal::bit_cast<uint32_pair>(uint64_t(~0ull));
|
||||
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST(UtilTest, ParseNonnegativeInt) {
|
||||
if (std::numeric_limits<int>::max() !=
|
||||
static_cast<int>(static_cast<unsigned>(1) << 31)) {
|
||||
fmt::print("Skipping parse_nonnegative_int test\n");
|
||||
return;
|
||||
}
|
||||
const char *s = "10000000000";
|
||||
EXPECT_THROW_MSG(
|
||||
parse_nonnegative_int(s, fmt::internal::error_handler()),
|
||||
fmt::format_error, "number is too big");
|
||||
s = "2147483649";
|
||||
EXPECT_THROW_MSG(
|
||||
parse_nonnegative_int(s, fmt::internal::error_handler()),
|
||||
fmt::format_error, "number is too big");
|
||||
}
|
||||
|
||||
TEST(IteratorTest, CountingIterator) {
|
||||
fmt::internal::counting_iterator<char> it;
|
||||
auto prev = it++;
|
||||
EXPECT_EQ(prev.count(), 0);
|
||||
EXPECT_EQ(it.count(), 1);
|
||||
}
|
||||
|
||||
TEST(IteratorTest, TruncatingIterator) {
|
||||
char *p = FMT_NULL;
|
||||
fmt::internal::truncating_iterator<char*> it(p, 3);
|
||||
auto prev = it++;
|
||||
EXPECT_EQ(prev.base(), p);
|
||||
EXPECT_EQ(it.base(), p + 1);
|
||||
}
|
||||
|
||||
TEST(MemoryBufferTest, Ctor) {
|
||||
basic_memory_buffer<char, 123> buffer;
|
||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||
EXPECT_EQ(123u, buffer.capacity());
|
||||
}
|
||||
|
||||
static void check_forwarding(
|
||||
mock_allocator<int> &alloc, allocator_ref<mock_allocator<int>> &ref) {
|
||||
int mem;
|
||||
// Check if value_type is properly defined.
|
||||
allocator_ref< mock_allocator<int> >::value_type *ptr = &mem;
|
||||
// Check forwarding.
|
||||
EXPECT_CALL(alloc, allocate(42)).WillOnce(testing::Return(ptr));
|
||||
ref.allocate(42);
|
||||
EXPECT_CALL(alloc, deallocate(ptr, 42));
|
||||
ref.deallocate(ptr, 42);
|
||||
}
|
||||
|
||||
TEST(AllocatorTest, allocator_ref) {
|
||||
StrictMock< mock_allocator<int> > alloc;
|
||||
typedef allocator_ref< mock_allocator<int> > test_allocator_ref;
|
||||
test_allocator_ref ref(&alloc);
|
||||
// Check if allocator_ref forwards to the underlying allocator.
|
||||
check_forwarding(alloc, ref);
|
||||
test_allocator_ref ref2(ref);
|
||||
check_forwarding(alloc, ref2);
|
||||
test_allocator_ref ref3;
|
||||
EXPECT_EQ(nullptr, ref3.get());
|
||||
ref3 = ref;
|
||||
check_forwarding(alloc, ref3);
|
||||
}
|
||||
|
||||
typedef allocator_ref< std::allocator<char> > TestAllocator;
|
||||
|
||||
static void check_move_buffer(const char *str,
|
||||
basic_memory_buffer<char, 5, TestAllocator> &buffer) {
|
||||
std::allocator<char> *alloc = buffer.get_allocator().get();
|
||||
basic_memory_buffer<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(nullptr, buffer.get_allocator().get());
|
||||
EXPECT_EQ(alloc, buffer2.get_allocator().get());
|
||||
}
|
||||
|
||||
TEST(MemoryBufferTest, MoveCtor) {
|
||||
std::allocator<char> alloc;
|
||||
basic_memory_buffer<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');
|
||||
basic_memory_buffer<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);
|
||||
}
|
||||
|
||||
static void check_move_assign_buffer(
|
||||
const char *str, basic_memory_buffer<char, 5> &buffer) {
|
||||
basic_memory_buffer<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) {
|
||||
basic_memory_buffer<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');
|
||||
basic_memory_buffer<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);
|
||||
}
|
||||
|
||||
TEST(MemoryBufferTest, Grow) {
|
||||
typedef allocator_ref< mock_allocator<int> > Allocator;
|
||||
typedef basic_memory_buffer<int, 10, Allocator> Base;
|
||||
mock_allocator<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)).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 allocator_ref< mock_allocator<char> > TestAllocator;
|
||||
basic_memory_buffer<char, 10, TestAllocator> buffer;
|
||||
EXPECT_EQ(nullptr, buffer.get_allocator().get());
|
||||
StrictMock< mock_allocator<char> > alloc;
|
||||
char mem;
|
||||
{
|
||||
basic_memory_buffer<char, 10, TestAllocator> buffer2((TestAllocator(&alloc)));
|
||||
EXPECT_EQ(&alloc, buffer2.get_allocator().get());
|
||||
std::size_t size = 2 * fmt::inline_buffer_size;
|
||||
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
|
||||
buffer2.reserve(size);
|
||||
EXPECT_CALL(alloc, deallocate(&mem, size));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MemoryBufferTest, ExceptionInDeallocate) {
|
||||
typedef allocator_ref< mock_allocator<char> > TestAllocator;
|
||||
StrictMock< mock_allocator<char> > alloc;
|
||||
basic_memory_buffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc)));
|
||||
std::size_t size = 2 * fmt::inline_buffer_size;
|
||||
std::vector<char> mem(size);
|
||||
{
|
||||
EXPECT_CALL(alloc, allocate(size)).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)).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(FixedBufferTest, Ctor) {
|
||||
char array[10] = "garbage";
|
||||
fmt::basic_fixed_buffer<char> buffer(array, sizeof(array));
|
||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||
EXPECT_EQ(10u, buffer.capacity());
|
||||
EXPECT_EQ(array, buffer.data());
|
||||
}
|
||||
|
||||
TEST(FixedBufferTest, CompileTimeSizeCtor) {
|
||||
char array[10] = "garbage";
|
||||
fmt::basic_fixed_buffer<char> buffer(array);
|
||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||
EXPECT_EQ(10u, buffer.capacity());
|
||||
EXPECT_EQ(array, buffer.data());
|
||||
}
|
||||
|
||||
TEST(FixedBufferTest, BufferOverflow) {
|
||||
char array[10];
|
||||
fmt::basic_fixed_buffer<char> buffer(array);
|
||||
buffer.resize(10);
|
||||
EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(UtilTest, UTF16ToUTF8) {
|
||||
std::string s = "ёжик";
|
||||
fmt::internal::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8EmptyString) {
|
||||
std::string s = "";
|
||||
fmt::internal::utf16_to_utf8 u(L"");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF8ToUTF16) {
|
||||
std::string s = "лошадка";
|
||||
fmt::internal::utf8_to_utf16 u(s.c_str());
|
||||
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
||||
EXPECT_EQ(7, u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF8ToUTF16EmptyString) {
|
||||
std::string s = "";
|
||||
fmt::internal::utf8_to_utf16 u(s.c_str());
|
||||
EXPECT_EQ(L"", u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
template <typename Converter, typename Char>
|
||||
void check_utf_conversion_error(
|
||||
const char *message,
|
||||
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
||||
fmt::system_error error(0, "");
|
||||
try {
|
||||
(Converter)(str);
|
||||
} catch (const fmt::system_error &e) {
|
||||
error = e;
|
||||
}
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
|
||||
EXPECT_EQ(fmt::to_string(out), error.what());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8Error) {
|
||||
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>(
|
||||
"cannot convert string from UTF-16 to UTF-8");
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF8ToUTF16Error) {
|
||||
const char *message = "cannot convert string from UTF-8 to UTF-16";
|
||||
check_utf_conversion_error<fmt::internal::utf8_to_utf16, char>(message);
|
||||
check_utf_conversion_error<fmt::internal::utf8_to_utf16, char>(
|
||||
message, fmt::string_view("foo", INT_MAX + 1u));
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8Convert) {
|
||||
fmt::internal::utf16_to_utf8 u;
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
||||
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
typedef void (*FormatErrorMessage)(
|
||||
fmt::internal::buffer &out, int error_code, string_view message);
|
||||
|
||||
template <typename Error>
|
||||
void check_throw_error(int error_code, FormatErrorMessage format) {
|
||||
fmt::system_error error(0, "");
|
||||
try {
|
||||
throw Error(error_code, "test {}", "error");
|
||||
} catch (const fmt::system_error &e) {
|
||||
error = e;
|
||||
}
|
||||
fmt::memory_buffer message;
|
||||
format(message, error_code, "test error");
|
||||
EXPECT_EQ(to_string(message), error.what());
|
||||
EXPECT_EQ(error_code, error.error_code());
|
||||
}
|
||||
|
||||
TEST(UtilTest, FormatSystemError) {
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_system_error(message, EDOM, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)),
|
||||
to_string(message));
|
||||
message = fmt::memory_buffer();
|
||||
|
||||
// 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 (const 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::string_view(nullptr, max_size));
|
||||
EXPECT_EQ(fmt::format("error {}", EDOM), to_string(message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, SystemError) {
|
||||
fmt::system_error e(EDOM, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), e.what());
|
||||
EXPECT_EQ(EDOM, e.error_code());
|
||||
check_throw_error<fmt::system_error>(EDOM, fmt::format_system_error);
|
||||
}
|
||||
|
||||
TEST(UtilTest, ReportSystemError) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::format_system_error(out, EDOM, "test error");
|
||||
out.push_back('\n');
|
||||
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"),
|
||||
to_string(out));
|
||||
}
|
||||
|
||||
#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::utf16_to_utf8 utf8_message(message);
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::internal::format_windows_error(
|
||||
actual_message, ERROR_FILE_EXISTS, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
actual_message.resize(0);
|
||||
fmt::internal::format_windows_error(
|
||||
actual_message, ERROR_FILE_EXISTS,
|
||||
fmt::string_view(0, std::numeric_limits<size_t>::max()));
|
||||
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
|
||||
fmt::to_string(actual_message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, FormatLongWindowsError) {
|
||||
LPWSTR message = 0;
|
||||
// this error code is not available on all Windows platforms and
|
||||
// Windows SDKs, so do not fail the test if the error string cannot
|
||||
// be retrieved.
|
||||
const int provisioning_not_allowed = 0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
|
||||
static_cast<DWORD>(provisioning_not_allowed),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
|
||||
return;
|
||||
}
|
||||
fmt::internal::utf16_to_utf8 utf8_message(message);
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::internal::format_windows_error(
|
||||
actual_message, provisioning_not_allowed, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, WindowsError) {
|
||||
check_throw_error<fmt::windows_error>(
|
||||
ERROR_FILE_EXISTS, fmt::internal::format_windows_error);
|
||||
}
|
||||
|
||||
TEST(UtilTest, ReportWindowsError) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
|
||||
out.push_back('\n');
|
||||
EXPECT_WRITE(stderr,
|
||||
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
|
||||
fmt::to_string(out));
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
TEST(StringViewTest, Ctor) {
|
||||
EXPECT_STREQ("abc", string_view("abc").data());
|
||||
EXPECT_EQ(3u, string_view("abc").size());
|
||||
@@ -232,6 +682,21 @@ TEST(FormatToTest, Format) {
|
||||
EXPECT_EQ("part1part2", s);
|
||||
}
|
||||
|
||||
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
|
||||
char buffer[16] = {};
|
||||
fmt::format_to(buffer, "{: =+}", 42.0);
|
||||
EXPECT_STREQ("+42", buffer);
|
||||
}
|
||||
|
||||
TEST(FormatToTest, FormatToMemoryBuffer) {
|
||||
fmt::basic_memory_buffer<char, 100> buffer;
|
||||
fmt::format_to(buffer, "{}", "foo");
|
||||
EXPECT_EQ("foo", to_string(buffer));
|
||||
fmt::wmemory_buffer wbuffer;
|
||||
fmt::format_to(wbuffer, L"{}", L"foo");
|
||||
EXPECT_EQ(L"foo", to_string(wbuffer));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, Escape) {
|
||||
EXPECT_EQ("{", format("{{"));
|
||||
EXPECT_EQ("before {", format("before {{"));
|
||||
@@ -321,6 +786,11 @@ TEST(FormatterTest, NamedArg) {
|
||||
EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4)));
|
||||
EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2)));
|
||||
EXPECT_EQ("1 2", format("{} {two}", 1, fmt::arg("two", 2)));
|
||||
EXPECT_EQ("42", format("{c}",
|
||||
fmt::arg("a", 0), fmt::arg("b", 0), fmt::arg("c", 42), fmt::arg("d", 0),
|
||||
fmt::arg("e", 0), fmt::arg("f", 0), fmt::arg("g", 0), fmt::arg("h", 0),
|
||||
fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0), fmt::arg("l", 0),
|
||||
fmt::arg("m", 0), fmt::arg("n", 0), fmt::arg("o", 0), fmt::arg("p", 0)));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, AutoArgIndex) {
|
||||
@@ -435,6 +905,7 @@ TEST(FormatterTest, Fill) {
|
||||
EXPECT_EQ("c****", format("{0:*<5}", 'c'));
|
||||
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
|
||||
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
|
||||
EXPECT_EQ("foo=", format("{:}=", "foo"));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, PlusSign) {
|
||||
@@ -542,7 +1013,10 @@ TEST(FormatterTest, HashFlag) {
|
||||
EXPECT_EQ("0x42", format("{0:#x}", 0x42ull));
|
||||
EXPECT_EQ("042", format("{0:#o}", 042ull));
|
||||
|
||||
EXPECT_EQ("-42.0000", format("{0:#}", -42.0));
|
||||
if (fmt::internal::use_grisu())
|
||||
EXPECT_EQ("-42.0", format("{0:#}", -42.0));
|
||||
else
|
||||
EXPECT_EQ("-42.0000", format("{0:#}", -42.0));
|
||||
EXPECT_EQ("-42.0000", format("{0:#}", -42.0l));
|
||||
EXPECT_THROW_MSG(format("{0:#", 'c'),
|
||||
format_error, "missing '}' in format string");
|
||||
@@ -1024,7 +1498,7 @@ TEST(FormatterTest, FormatCString) {
|
||||
EXPECT_EQ("test", format("{0:s}", "test"));
|
||||
char nonconst[] = "nonconst";
|
||||
EXPECT_EQ("nonconst", format("{0}", nonconst));
|
||||
EXPECT_THROW_MSG(format("{0}", reinterpret_cast<const char*>(0)),
|
||||
EXPECT_THROW_MSG(format("{0}", static_cast<const char*>(nullptr)),
|
||||
format_error, "string pointer is null");
|
||||
}
|
||||
|
||||
@@ -1046,7 +1520,7 @@ TEST(FormatterTest, FormatUCharString) {
|
||||
|
||||
TEST(FormatterTest, FormatPointer) {
|
||||
check_unknown_types(reinterpret_cast<void*>(0x1234), "p", "pointer");
|
||||
EXPECT_EQ("0x0", format("{0}", reinterpret_cast<void*>(0)));
|
||||
EXPECT_EQ("0x0", format("{0}", static_cast<void*>(nullptr)));
|
||||
EXPECT_EQ("0x1234", format("{0}", reinterpret_cast<void*>(0x1234)));
|
||||
EXPECT_EQ("0x1234", format("{0:p}", reinterpret_cast<void*>(0x1234)));
|
||||
EXPECT_EQ("0x" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f'),
|
||||
@@ -1072,14 +1546,45 @@ TEST(FormatterTest, FormatStdStringView) {
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ConvertibleToStringView {
|
||||
struct implicitly_convertible_to_string_view {
|
||||
operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatConvertibleToStringView) {
|
||||
EXPECT_EQ("foo", format("{}", ConvertibleToStringView()));
|
||||
TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
|
||||
EXPECT_EQ("foo", format("{}", implicitly_convertible_to_string_view()));
|
||||
}
|
||||
|
||||
// std::is_constructible is broken in MSVC until version 2015.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||
struct explicitly_convertible_to_string_view {
|
||||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
|
||||
EXPECT_EQ("foo", format("{}", explicitly_convertible_to_string_view()));
|
||||
}
|
||||
|
||||
struct explicitly_convertible_to_wstring_view {
|
||||
explicit operator fmt::wstring_view() const { return L"foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
|
||||
EXPECT_EQ(L"foo", format(L"{}", explicitly_convertible_to_wstring_view()));
|
||||
}
|
||||
|
||||
struct explicitly_convertible_to_string_like {
|
||||
template <
|
||||
typename String,
|
||||
typename = typename std::enable_if<
|
||||
std::is_constructible<String, const char*, std::size_t>::value>::type>
|
||||
explicit operator String() const { return String("foo", 3u); }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
|
||||
EXPECT_EQ("foo", format("{}", explicitly_convertible_to_string_like()));
|
||||
}
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <>
|
||||
struct formatter<Date> {
|
||||
@@ -1109,7 +1614,8 @@ class Answer {};
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <>
|
||||
struct formatter<Answer> : formatter<int> {
|
||||
auto format(Answer, fmt::format_context &ctx) -> decltype(ctx.out()) {
|
||||
template <typename FormatContext>
|
||||
auto format(Answer, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||
return formatter<int>::format(42, ctx);
|
||||
}
|
||||
};
|
||||
@@ -1120,6 +1626,13 @@ TEST(FormatterTest, CustomFormat) {
|
||||
EXPECT_EQ("0042", format("{:04}", Answer()));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, CustomFormatTo) {
|
||||
char buf[10] = {};
|
||||
auto end = fmt::format_to(buf, "{}", Answer());
|
||||
EXPECT_EQ(end, buf + 2);
|
||||
EXPECT_STREQ(buf, "42");
|
||||
}
|
||||
|
||||
TEST(FormatterTest, WideFormatString) {
|
||||
EXPECT_EQ(L"42", format(L"{}", 42));
|
||||
EXPECT_EQ(L"4.2", format(L"{}", 4.2));
|
||||
@@ -1148,7 +1661,7 @@ TEST(FormatterTest, FormatExamples) {
|
||||
FILE *ftest = safe_fopen(filename, "r");
|
||||
if (ftest) fclose(ftest);
|
||||
int error_code = errno;
|
||||
EXPECT_TRUE(ftest == 0);
|
||||
EXPECT_TRUE(ftest == nullptr);
|
||||
EXPECT_SYSTEM_ERROR({
|
||||
FILE *f = safe_fopen(filename, "r");
|
||||
if (!f)
|
||||
@@ -1259,18 +1772,26 @@ TEST(FormatTest, Print) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FMT_USE_FILE_DESCRIPTORS
|
||||
TEST(FormatTest, PrintColored) {
|
||||
EXPECT_WRITE(stdout, fmt::print_colored(fmt::red, "Hello, {}!\n", "world"),
|
||||
"\x1b[31mHello, world!\n\x1b[0m");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(FormatTest, Variadic) {
|
||||
EXPECT_EQ("abc1", format("{}c{}", "ab", 1));
|
||||
EXPECT_EQ(L"abc1", format(L"{}c{}", L"ab", 1));
|
||||
}
|
||||
|
||||
TEST(FormatTest, Dynamic) {
|
||||
using ctx = fmt::format_context;
|
||||
std::vector<fmt::basic_format_arg<ctx>> args;
|
||||
args.emplace_back(fmt::internal::make_arg<ctx>(42));
|
||||
args.emplace_back(fmt::internal::make_arg<ctx>("abc1"));
|
||||
args.emplace_back(fmt::internal::make_arg<ctx>(1.2f));
|
||||
|
||||
std::string result = fmt::vformat("{} and {} and {}",
|
||||
fmt::basic_format_args<ctx>(
|
||||
args.data(),
|
||||
static_cast<unsigned>(args.size())));
|
||||
|
||||
EXPECT_EQ("42 and abc1 and 1.2", result);
|
||||
}
|
||||
|
||||
TEST(FormatTest, JoinArg) {
|
||||
using fmt::join;
|
||||
int v1[3] = { 1, 2, 3 };
|
||||
@@ -1309,7 +1830,7 @@ TEST(StrTest, Convert) {
|
||||
EXPECT_EQ("2012-12-9", s);
|
||||
}
|
||||
|
||||
std::string vformat_message(int id, const char *format, fmt::format_args args) {
|
||||
static std::string vformat_message(int id, const char *format, fmt::format_args args) {
|
||||
fmt::memory_buffer buffer;
|
||||
format_to(buffer, "[{}] ", id);
|
||||
vformat_to(buffer, format, args);
|
||||
@@ -1383,6 +1904,10 @@ TEST(FormatTest, Enum) {
|
||||
EXPECT_EQ("0", fmt::format("{}", A));
|
||||
}
|
||||
|
||||
TEST(FormatTest, EnumFormatterUnambiguous) {
|
||||
fmt::formatter<TestEnum> f;
|
||||
}
|
||||
|
||||
#if FMT_HAS_FEATURE(cxx_strong_enums)
|
||||
enum TestFixedEnum : short { B };
|
||||
|
||||
@@ -1398,30 +1923,36 @@ class mock_arg_formatter:
|
||||
fmt::internal::arg_formatter_base<buffer_range>::iterator>,
|
||||
public fmt::internal::arg_formatter_base<buffer_range> {
|
||||
private:
|
||||
MOCK_METHOD1(call, void (int value));
|
||||
MOCK_METHOD1(call, void (long long value));
|
||||
|
||||
public:
|
||||
typedef fmt::internal::arg_formatter_base<buffer_range> base;
|
||||
typedef buffer_range range;
|
||||
|
||||
mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
|
||||
mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
|
||||
: base(fmt::internal::get_container(ctx.out()), s) {
|
||||
EXPECT_CALL(*this, call(42));
|
||||
}
|
||||
|
||||
using base::operator();
|
||||
|
||||
iterator operator()(int value) {
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, iterator>::type
|
||||
operator()(T value) {
|
||||
call(value);
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_integral<T>::value, iterator>::type
|
||||
operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
iterator operator()(fmt::basic_format_arg<fmt::format_context>::handle) {
|
||||
return base::operator()(fmt::monostate());
|
||||
}
|
||||
};
|
||||
|
||||
void custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||
static void custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::vformat_to<mock_arg_formatter>(buffer, format_str, args);
|
||||
}
|
||||
@@ -1518,6 +2049,15 @@ TEST(FormatTest, FormatToN) {
|
||||
EXPECT_EQ("foox", fmt::string_view(buffer, 4));
|
||||
}
|
||||
|
||||
TEST(FormatTest, WideFormatToN) {
|
||||
wchar_t buffer[4];
|
||||
buffer[3] = L'x';
|
||||
auto result = fmt::format_to_n(buffer, 3, L"{}", 12345);
|
||||
EXPECT_EQ(5u, result.size);
|
||||
EXPECT_EQ(buffer + 3, result.out);
|
||||
EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4));
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
struct test_arg_id_handler {
|
||||
enum result { NONE, EMPTY, INDEX, NAME, ERROR };
|
||||
@@ -1731,18 +2271,20 @@ struct test_format_string_handler {
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR void on_arg_id(T) {}
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(const char *) {}
|
||||
template <typename Iterator>
|
||||
FMT_CONSTEXPR void on_replacement_field(Iterator) {}
|
||||
|
||||
FMT_CONSTEXPR const char *on_format_specs(const char *s) { return s; }
|
||||
template <typename Iterator>
|
||||
FMT_CONSTEXPR Iterator on_format_specs(Iterator it) { return it; }
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char *) { error = true; }
|
||||
|
||||
bool error = false;
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR bool parse_string(const char *s) {
|
||||
FMT_CONSTEXPR bool parse_string(fmt::string_view s) {
|
||||
test_format_string_handler h;
|
||||
fmt::internal::parse_format_string(s, h);
|
||||
fmt::internal::parse_format_string<true>(s, h);
|
||||
return !h.error;
|
||||
}
|
||||
|
||||
@@ -1794,12 +2336,14 @@ FMT_CONSTEXPR bool test_error(const char *fmt, const char *expected_error) {
|
||||
return equal(actual_error, expected_error);
|
||||
}
|
||||
|
||||
#define EXPECT_ERROR_NOARGS(fmt, error) \
|
||||
static_assert(test_error(fmt, error), "")
|
||||
#define EXPECT_ERROR(fmt, error, ...) \
|
||||
static_assert(test_error<__VA_ARGS__>(fmt, error), "")
|
||||
|
||||
TEST(FormatTest, FormatStringErrors) {
|
||||
EXPECT_ERROR("foo", nullptr);
|
||||
EXPECT_ERROR("}", "unmatched '}' in format string");
|
||||
EXPECT_ERROR_NOARGS("foo", nullptr);
|
||||
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
|
||||
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
|
||||
#ifndef _MSC_VER
|
||||
// This causes an internal compiler error in MSVC2017.
|
||||
@@ -1807,7 +2351,7 @@ TEST(FormatTest, FormatStringErrors) {
|
||||
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
|
||||
EXPECT_ERROR("{:10000000000}", "number is too big", int);
|
||||
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
|
||||
EXPECT_ERROR("{:x}", "argument index out of range");
|
||||
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
|
||||
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
|
||||
const char *);
|
||||
EXPECT_ERROR("{:+}", "format specifier requires numeric argument",
|
||||
@@ -1834,15 +2378,15 @@ TEST(FormatTest, FormatStringErrors) {
|
||||
EXPECT_ERROR("{:s}", "invalid type specifier", void *);
|
||||
#endif
|
||||
EXPECT_ERROR("{foo", "missing '}' in format string", int);
|
||||
EXPECT_ERROR("{10000000000}", "number is too big");
|
||||
EXPECT_ERROR("{0x}", "invalid format string");
|
||||
EXPECT_ERROR("{-}", "invalid format string");
|
||||
EXPECT_ERROR_NOARGS("{10000000000}", "number is too big");
|
||||
EXPECT_ERROR_NOARGS("{0x}", "invalid format string");
|
||||
EXPECT_ERROR_NOARGS("{-}", "invalid format string");
|
||||
EXPECT_ERROR("{:{0x}}", "invalid format string", int);
|
||||
EXPECT_ERROR("{:{-}}", "invalid format string", int);
|
||||
EXPECT_ERROR("{:.{0x}}", "invalid format string", int);
|
||||
EXPECT_ERROR("{:.{-}}", "invalid format string", int);
|
||||
EXPECT_ERROR("{:.x}", "missing precision specifier", int);
|
||||
EXPECT_ERROR("{}", "argument index out of range");
|
||||
EXPECT_ERROR_NOARGS("{}", "argument index out of range");
|
||||
EXPECT_ERROR("{1}", "argument index out of range", int);
|
||||
EXPECT_ERROR("{1}{}",
|
||||
"cannot switch from manual to automatic argument indexing",
|
||||
@@ -1853,3 +2397,30 @@ TEST(FormatTest, FormatStringErrors) {
|
||||
}
|
||||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
TEST(FormatTest, ConstructU8StringViewFromCString) {
|
||||
fmt::u8string_view s("ab");
|
||||
EXPECT_EQ(s.size(), 2u);
|
||||
const fmt::char8_t *data = s.data();
|
||||
EXPECT_EQ(data[0].value, 'a');
|
||||
EXPECT_EQ(data[1].value, 'b');
|
||||
}
|
||||
|
||||
TEST(FormatTest, ConstructU8StringViewFromDataAndSize) {
|
||||
fmt::u8string_view s("foobar", 3);
|
||||
EXPECT_EQ(s.size(), 3u);
|
||||
const fmt::char8_t *data = s.data();
|
||||
EXPECT_EQ(data[0].value, 'f');
|
||||
EXPECT_EQ(data[1].value, 'o');
|
||||
EXPECT_EQ(data[2].value, 'o');
|
||||
}
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
TEST(FormatTest, U8StringViewLiteral) {
|
||||
using namespace fmt::literals;
|
||||
fmt::u8string_view s = "ab"_u;
|
||||
EXPECT_EQ(s.size(), 2u);
|
||||
const fmt::char8_t *data = s.data();
|
||||
EXPECT_EQ(data[0].value, 'a');
|
||||
EXPECT_EQ(data[1].value, 'b');
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
// This line ensures that gtest.h can be compiled on its own, even
|
||||
// when it's fused.
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest.h"
|
||||
|
||||
// The following lines pull in the real gtest *.cc files.
|
||||
// Copyright 2005, Google Inc.
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
|
||||
// Most of the types needed for porting Google Mock are also required
|
||||
// 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
|
||||
// gmock-port.h's responsibility to #include the header implementing
|
||||
|
||||
@@ -340,10 +340,10 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
|
||||
// Put a character in a file buffer.
|
||||
EXPECT_EQ('x', fputc('x', f.get()));
|
||||
FMT_POSIX(close(write_fd));
|
||||
scoped_ptr<OutputRedirect> redir;
|
||||
scoped_ptr<OutputRedirect> redir{nullptr};
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
|
||||
EBADF, "cannot flush stream");
|
||||
redir.reset();
|
||||
redir.reset(nullptr);
|
||||
write_copy.dup2(write_fd); // "undo" close or dtor will fail
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
|
||||
int fd = (f.fileno)();
|
||||
file copy = file::dup(fd);
|
||||
FMT_POSIX(close(fd));
|
||||
scoped_ptr<OutputRedirect> redir;
|
||||
scoped_ptr<OutputRedirect> redir{nullptr};
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
|
||||
EBADF, fmt::format("cannot duplicate file descriptor {}", fd));
|
||||
copy.dup2(fd); // "undo" close or dtor will fail
|
||||
@@ -403,7 +403,7 @@ TEST(OutputRedirectTest, ErrorInDtor) {
|
||||
// output in EXPECT_STDERR and the second close will break output
|
||||
// redirection.
|
||||
FMT_POSIX(close(write_fd));
|
||||
SUPPRESS_ASSERT(redir.reset());
|
||||
SUPPRESS_ASSERT(redir.reset(nullptr));
|
||||
}, format_system_error(EBADF, "cannot flush stream"));
|
||||
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ std::string read(file &f, std::size_t count) {
|
||||
do {
|
||||
n = f.read(&buffer[offset], count - offset);
|
||||
// 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);
|
||||
buffer.resize(offset);
|
||||
return buffer;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define FMT_GTEST_EXTRA_H_
|
||||
|
||||
#include <string>
|
||||
#include <gmock/gmock.h>
|
||||
#include "gmock.h"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
@@ -155,7 +155,7 @@ std::string read(fmt::file &f, std::size_t count);
|
||||
template <typename Mock>
|
||||
struct ScopedMock : testing::StrictMock<Mock> {
|
||||
ScopedMock() { Mock::instance = this; }
|
||||
~ScopedMock() { Mock::instance = 0; }
|
||||
~ScopedMock() { Mock::instance = nullptr; }
|
||||
};
|
||||
|
||||
#endif // FMT_GTEST_EXTRA_H_
|
||||
|
||||
@@ -8,54 +8,49 @@
|
||||
#ifndef FMT_MOCK_ALLOCATOR_H_
|
||||
#define FMT_MOCK_ALLOCATOR_H_
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gmock.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
template <typename T>
|
||||
class MockAllocator {
|
||||
class mock_allocator {
|
||||
public:
|
||||
MockAllocator() {}
|
||||
MockAllocator(const MockAllocator &) {}
|
||||
mock_allocator() {}
|
||||
mock_allocator(const mock_allocator &) {}
|
||||
typedef T value_type;
|
||||
MOCK_METHOD1_T(allocate, T* (std::size_t n));
|
||||
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
|
||||
};
|
||||
|
||||
template <typename Allocator>
|
||||
class AllocatorRef {
|
||||
class allocator_ref {
|
||||
private:
|
||||
Allocator *alloc_;
|
||||
|
||||
void move(allocator_ref &other) {
|
||||
alloc_ = other.alloc_;
|
||||
other.alloc_ = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
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) {
|
||||
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) {
|
||||
allocator_ref& operator=(allocator_ref &&other) {
|
||||
assert(this != &other);
|
||||
move(other);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
allocator_ref& operator=(const allocator_ref &other) {
|
||||
alloc_ = other.alloc_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
Allocator *get() const { return alloc_; }
|
||||
|
||||
value_type* allocate(std::size_t n) {
|
||||
|
||||
@@ -5,42 +5,43 @@
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#define FMT_STRING_ALIAS 1
|
||||
#include "fmt/ostream.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "gmock/gmock.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
using fmt::format;
|
||||
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();
|
||||
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();
|
||||
return os;
|
||||
}
|
||||
|
||||
enum TestEnum {};
|
||||
std::ostream &operator<<(std::ostream &os, TestEnum) {
|
||||
static std::ostream &operator<<(std::ostream &os, TestEnum) {
|
||||
return os << "TestEnum";
|
||||
}
|
||||
|
||||
std::wostream &operator<<(std::wostream &os, TestEnum) {
|
||||
static std::wostream &operator<<(std::wostream &os, TestEnum) {
|
||||
return os << L"TestEnum";
|
||||
}
|
||||
|
||||
enum TestEnum2 {A};
|
||||
|
||||
TEST(OStreamTest, Enum) {
|
||||
EXPECT_FALSE((fmt::internal::convert_to_int<TestEnum, char>::value));
|
||||
EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value));
|
||||
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
|
||||
EXPECT_EQ("0", fmt::format("{}", A));
|
||||
EXPECT_FALSE((fmt::internal::convert_to_int<TestEnum, wchar_t>::value));
|
||||
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));
|
||||
}
|
||||
@@ -49,7 +50,7 @@ typedef fmt::back_insert_range<fmt::internal::buffer> range;
|
||||
|
||||
struct test_arg_formatter: fmt::arg_formatter<range> {
|
||||
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
|
||||
: fmt::arg_formatter<range>(ctx, s) {}
|
||||
: fmt::arg_formatter<range>(ctx, &s) {}
|
||||
};
|
||||
|
||||
TEST(OStreamTest, CustomArg) {
|
||||
@@ -95,7 +96,7 @@ TEST(OStreamTest, FormatSpecs) {
|
||||
}
|
||||
|
||||
struct EmptyTest {};
|
||||
std::ostream &operator<<(std::ostream &os, EmptyTest) {
|
||||
static std::ostream &operator<<(std::ostream &os, EmptyTest) {
|
||||
return os << "";
|
||||
}
|
||||
|
||||
@@ -145,7 +146,7 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
||||
} os(streambuf);
|
||||
|
||||
testing::InSequence sequence;
|
||||
const char *data = 0;
|
||||
const char *data = nullptr;
|
||||
std::size_t size = max_size;
|
||||
do {
|
||||
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
|
||||
@@ -154,7 +155,7 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
||||
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
|
||||
.WillOnce(testing::Return(max_streamsize));
|
||||
data += n;
|
||||
size -= static_cast<std::size_t>(n);
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
fmt::internal::write(os, buffer);
|
||||
}
|
||||
@@ -163,3 +164,38 @@ TEST(OStreamTest, Join) {
|
||||
int v[3] = {1, 2, 3};
|
||||
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#ifdef _MSC_VER
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "posix-mock.h"
|
||||
#include "../src/posix.cc"
|
||||
@@ -21,7 +23,7 @@
|
||||
# undef ERROR
|
||||
#endif
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -131,7 +133,7 @@ int test::dup2(int fildes, int fildes2) {
|
||||
}
|
||||
|
||||
FILE *test::fdopen(int fildes, const char *mode) {
|
||||
EMULATE_EINTR(fdopen, 0);
|
||||
EMULATE_EINTR(fdopen, nullptr);
|
||||
return ::FMT_POSIX(fdopen(fildes, mode));
|
||||
}
|
||||
|
||||
@@ -160,7 +162,7 @@ int test::pipe(int *pfds, unsigned psize, int textmode) {
|
||||
#endif
|
||||
|
||||
FILE *test::fopen(const char *filename, const char *mode) {
|
||||
EMULATE_EINTR(fopen, 0);
|
||||
EMULATE_EINTR(fopen, nullptr);
|
||||
return ::fopen(filename, mode);
|
||||
}
|
||||
|
||||
@@ -193,7 +195,7 @@ int (test::fileno)(FILE *stream) {
|
||||
# define EXPECT_EQ_POSIX(expected, actual)
|
||||
#endif
|
||||
|
||||
void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||
static void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||
fmt::buffered_file f(filename, "w");
|
||||
f.print("{}", content);
|
||||
}
|
||||
@@ -214,7 +216,7 @@ TEST(UtilTest, GetPageSize) {
|
||||
|
||||
TEST(FileTest, OpenRetry) {
|
||||
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)),
|
||||
open, "cannot open file test");
|
||||
#ifndef _WIN32
|
||||
@@ -230,7 +232,7 @@ TEST(FileTest, CloseNoRetryInDtor) {
|
||||
int saved_close_count = 0;
|
||||
EXPECT_WRITE(stderr, {
|
||||
close_count = 1;
|
||||
f.reset();
|
||||
f.reset(nullptr);
|
||||
saved_close_count = close_count;
|
||||
close_count = 0;
|
||||
}, format_system_error(EINTR, "cannot close file") + "\n");
|
||||
@@ -383,7 +385,7 @@ TEST(FileTest, FdopenNoRetry) {
|
||||
|
||||
TEST(BufferedFileTest, OpenRetry) {
|
||||
write_file("test", "there must be something here");
|
||||
scoped_ptr<buffered_file> f;
|
||||
scoped_ptr<buffered_file> f{nullptr};
|
||||
EXPECT_RETRY(f.reset(new buffered_file("test", "r")),
|
||||
fopen, "cannot open file test");
|
||||
#ifndef _WIN32
|
||||
@@ -400,7 +402,7 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
||||
int saved_fclose_count = 0;
|
||||
EXPECT_WRITE(stderr, {
|
||||
fclose_count = 1;
|
||||
f.reset();
|
||||
f.reset(nullptr);
|
||||
saved_fclose_count = fclose_count;
|
||||
fclose_count = 0;
|
||||
}, format_system_error(EINTR, "cannot close file") + "\n");
|
||||
@@ -436,8 +438,9 @@ TEST(ScopedMock, Scope) {
|
||||
ScopedMock<TestMock> mock;
|
||||
EXPECT_EQ(&mock, TestMock::instance);
|
||||
TestMock © = mock;
|
||||
static_cast<void>(copy);
|
||||
}
|
||||
EXPECT_EQ(0, TestMock::instance);
|
||||
EXPECT_EQ(nullptr, TestMock::instance);
|
||||
}
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
@@ -472,7 +475,13 @@ double _strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||
# pragma warning(pop)
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -482,15 +491,17 @@ typedef int FreeLocaleResult;
|
||||
typedef void FreeLocaleResult;
|
||||
#endif
|
||||
|
||||
FreeLocaleResult freelocale(LocaleType locale) {
|
||||
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
|
||||
LocaleMock::instance->freelocale(locale);
|
||||
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);
|
||||
}
|
||||
|
||||
#undef FMT_LOCALE_THROW
|
||||
|
||||
TEST(LocaleTest, LocaleMock) {
|
||||
ScopedMock<LocaleMock> mock;
|
||||
LocaleType locale = reinterpret_cast<LocaleType>(11);
|
||||
@@ -504,7 +515,7 @@ TEST(LocaleTest, Locale) {
|
||||
#endif
|
||||
ScopedMock<LocaleMock> mock;
|
||||
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));
|
||||
EXPECT_CALL(mock, freelocale(impl));
|
||||
fmt::Locale locale;
|
||||
|
||||
@@ -23,12 +23,12 @@ using fmt::file;
|
||||
using testing::internal::scoped_ptr;
|
||||
|
||||
// Checks if the file is open by reading one character from it.
|
||||
bool isopen(int fd) {
|
||||
static bool isopen(int fd) {
|
||||
char buffer;
|
||||
return FMT_POSIX(read(fd, &buffer, 1)) == 1;
|
||||
}
|
||||
|
||||
bool isclosed(int fd) {
|
||||
static bool isclosed(int fd) {
|
||||
char buffer;
|
||||
std::streamsize result = 0;
|
||||
SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1)));
|
||||
@@ -36,7 +36,7 @@ bool isclosed(int fd) {
|
||||
}
|
||||
|
||||
// Opens a file for reading.
|
||||
file open_file() {
|
||||
static file open_file() {
|
||||
file read_end, write_end;
|
||||
file::pipe(read_end, write_end);
|
||||
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
||||
@@ -45,7 +45,7 @@ file open_file() {
|
||||
}
|
||||
|
||||
// Attempts to write a string to a file.
|
||||
void write(file &f, fmt::string_view s) {
|
||||
static void write(file &f, fmt::string_view s) {
|
||||
std::size_t num_chars_left = s.size();
|
||||
const char *ptr = s.data();
|
||||
do {
|
||||
@@ -53,32 +53,32 @@ void write(file &f, fmt::string_view s) {
|
||||
ptr += count;
|
||||
// We can't write more than size_t bytes since num_chars_left
|
||||
// has type size_t.
|
||||
num_chars_left -= static_cast<std::size_t>(count);
|
||||
num_chars_left -= count;
|
||||
} while (num_chars_left != 0);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, DefaultCtor) {
|
||||
buffered_file f;
|
||||
EXPECT_TRUE(f.get() == 0);
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveCtor) {
|
||||
buffered_file bf = open_buffered_file();
|
||||
FILE *fp = bf.get();
|
||||
EXPECT_TRUE(fp != 0);
|
||||
EXPECT_TRUE(fp != nullptr);
|
||||
buffered_file bf2(std::move(bf));
|
||||
EXPECT_EQ(fp, bf2.get());
|
||||
EXPECT_TRUE(bf.get() == 0);
|
||||
EXPECT_TRUE(bf.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveAssignment) {
|
||||
buffered_file bf = open_buffered_file();
|
||||
FILE *fp = bf.get();
|
||||
EXPECT_TRUE(fp != 0);
|
||||
EXPECT_TRUE(fp != nullptr);
|
||||
buffered_file bf2;
|
||||
bf2 = std::move(bf);
|
||||
EXPECT_EQ(fp, bf2.get());
|
||||
EXPECT_TRUE(bf.get() == 0);
|
||||
EXPECT_TRUE(bf.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||
@@ -90,13 +90,13 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
||||
FILE *fp = 0;
|
||||
FILE *fp = nullptr;
|
||||
buffered_file f(open_buffered_file(&fp));
|
||||
EXPECT_EQ(fp, f.get());
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
||||
FILE *fp = 0;
|
||||
FILE *fp = nullptr;
|
||||
buffered_file f;
|
||||
f = open_buffered_file(&fp);
|
||||
EXPECT_EQ(fp, f.get());
|
||||
@@ -126,7 +126,7 @@ TEST(BufferedFileTest, CloseErrorInDtor) {
|
||||
// output in EXPECT_STDERR and the second close will break output
|
||||
// redirection.
|
||||
FMT_POSIX(close(f->fileno()));
|
||||
SUPPRESS_ASSERT(f.reset());
|
||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||
}, format_system_error(EBADF, "cannot close file") + "\n");
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ TEST(BufferedFileTest, Close) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int fd = f.fileno();
|
||||
f.close();
|
||||
EXPECT_TRUE(f.get() == 0);
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
EXPECT_TRUE(isclosed(fd));
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ TEST(BufferedFileTest, CloseError) {
|
||||
buffered_file f = open_buffered_file();
|
||||
FMT_POSIX(close(f.fileno()));
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||
EXPECT_TRUE(f.get() == 0);
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(BufferedFileTest, Fileno) {
|
||||
@@ -153,7 +153,7 @@ TEST(BufferedFileTest, Fileno) {
|
||||
EXPECT_DEATH_IF_SUPPORTED({
|
||||
try {
|
||||
f.fileno();
|
||||
} catch (fmt::system_error) {
|
||||
} catch (const fmt::system_error&) {
|
||||
std::exit(1);
|
||||
}
|
||||
}, "");
|
||||
@@ -209,7 +209,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
|
||||
EXPECT_TRUE(isclosed(old_fd));
|
||||
}
|
||||
|
||||
file OpenBufferedFile(int &fd) {
|
||||
static file OpenBufferedFile(int &fd) {
|
||||
file f = open_file();
|
||||
fd = f.descriptor();
|
||||
return f;
|
||||
@@ -253,7 +253,7 @@ TEST(FileTest, CloseErrorInDtor) {
|
||||
// output in EXPECT_STDERR and the second close will break output
|
||||
// redirection.
|
||||
FMT_POSIX(close(f->descriptor()));
|
||||
SUPPRESS_ASSERT(f.reset());
|
||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||
}, format_system_error(EBADF, "cannot close file") + "\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ using fmt::format_error;
|
||||
const unsigned BIG_NUM = INT_MAX + 1u;
|
||||
|
||||
// Makes format string argument positional.
|
||||
std::string make_positional(fmt::string_view format) {
|
||||
static std::string make_positional(fmt::string_view format) {
|
||||
std::string s(format.data(), format.size());
|
||||
s.replace(s.find('%'), 1, "%1$");
|
||||
return s;
|
||||
}
|
||||
|
||||
std::wstring make_positional(fmt::wstring_view format) {
|
||||
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;
|
||||
@@ -180,8 +180,13 @@ TEST(PrintfTest, HashFlag) {
|
||||
safe_sprintf(buffer, "%#E", -42.0);
|
||||
EXPECT_PRINTF(buffer, "%#E", -42.0);
|
||||
|
||||
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
||||
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
|
||||
if (fmt::internal::use_grisu()) {
|
||||
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);
|
||||
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
||||
@@ -410,7 +415,7 @@ TEST(PrintfTest, Inf) {
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
for (const char* type = "fega"; *type; ++type) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -426,11 +431,11 @@ TEST(PrintfTest, Char) {
|
||||
|
||||
TEST(PrintfTest, String) {
|
||||
EXPECT_PRINTF("abc", "%s", "abc");
|
||||
const char *null_str = 0;
|
||||
const char *null_str = nullptr;
|
||||
EXPECT_PRINTF("(null)", "%s", null_str);
|
||||
EXPECT_PRINTF(" (null)", "%10s", null_str);
|
||||
EXPECT_PRINTF(L"abc", L"%s", L"abc");
|
||||
const wchar_t *null_wstr = 0;
|
||||
const wchar_t *null_wstr = nullptr;
|
||||
EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
|
||||
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
||||
}
|
||||
@@ -439,22 +444,22 @@ TEST(PrintfTest, Pointer) {
|
||||
int n;
|
||||
void *p = &n;
|
||||
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
|
||||
p = 0;
|
||||
p = nullptr;
|
||||
EXPECT_PRINTF("(nil)", "%p", p);
|
||||
EXPECT_PRINTF(" (nil)", "%10p", p);
|
||||
const char *s = "test";
|
||||
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
|
||||
const char *null_str = 0;
|
||||
const char *null_str = nullptr;
|
||||
EXPECT_PRINTF("(nil)", "%p", null_str);
|
||||
|
||||
p = &n;
|
||||
EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p);
|
||||
p = 0;
|
||||
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 = 0;
|
||||
const wchar_t *null_wstr = nullptr;
|
||||
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
|
||||
#include "fmt/ranges.h"
|
||||
|
||||
#include "gtest/gtest.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>
|
||||
@@ -30,10 +34,9 @@ TEST(RangesTest, FormatVector2) {
|
||||
EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf);
|
||||
}
|
||||
|
||||
#if FMT_USE_INTEGER_SEQUENCE
|
||||
TEST(RangesTest, FormatMap) {
|
||||
std::map<std::string, int32_t> simap{{"one", 1}, {"two", 2}};
|
||||
EXPECT_EQ("{(one, 1), (two, 2)}", fmt::format("{}", simap));
|
||||
EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format("{}", simap));
|
||||
}
|
||||
|
||||
TEST(RangesTest, FormatPair) {
|
||||
@@ -42,15 +45,11 @@ TEST(RangesTest, FormatPair) {
|
||||
}
|
||||
|
||||
TEST(RangesTest, FormatTuple) {
|
||||
std::tuple<int64_t, float, std::string> tu1{42, 3.14159265358979f,
|
||||
"this is tuple"};
|
||||
EXPECT_EQ("(42, 3.14159, this is tuple)", fmt::format("{}", tu1));
|
||||
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));
|
||||
}
|
||||
|
||||
/// Check if 'if constexpr' is supported.
|
||||
#if (__cplusplus > 201402L) || \
|
||||
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
|
||||
|
||||
struct my_struct {
|
||||
int32_t i;
|
||||
std::string str; // can throw
|
||||
@@ -82,9 +81,8 @@ struct tuple_element<N, my_struct> {
|
||||
|
||||
TEST(RangesTest, FormatStruct) {
|
||||
my_struct mst{13, "my struct"};
|
||||
EXPECT_EQ("(13, my struct)", fmt::format("{}", mst));
|
||||
EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
|
||||
}
|
||||
|
||||
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
|
||||
// 201402L && _MSC_VER >= 1910)
|
||||
#endif // FMT_USE_INTEGER_SEQUENCE
|
||||
|
||||
@@ -5,23 +5,22 @@
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_TEST_ASSERT_H
|
||||
#define FMT_TEST_ASSERT_H
|
||||
#ifndef FMT_TEST_ASSERT_H_
|
||||
#define FMT_TEST_ASSERT_H_
|
||||
|
||||
#include <stdexcept>
|
||||
#include "gtest.h"
|
||||
|
||||
class AssertionFailure : public std::logic_error {
|
||||
class assertion_failure : public std::logic_error {
|
||||
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) \
|
||||
if (!(condition)) throw AssertionFailure(message);
|
||||
|
||||
#include "gtest-extra.h"
|
||||
if (!(condition)) throw assertion_failure(message);
|
||||
|
||||
// Expects an assertion failure.
|
||||
#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_
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <gtest/gtest.h>
|
||||
#include "gtest.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gmock.h"
|
||||
#include "fmt/time.h"
|
||||
|
||||
TEST(TimeTest, Format) {
|
||||
@@ -26,7 +26,7 @@ TEST(TimeTest, GrowBuffer) {
|
||||
for (int i = 0; i < 30; ++i)
|
||||
s += "%c";
|
||||
s += "}\n";
|
||||
std::time_t t = std::time(0);
|
||||
std::time_t t = std::time(nullptr);
|
||||
fmt::format(s, *std::localtime(&t));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ TEST(TimeTest, EmptyResult) {
|
||||
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 &&
|
||||
lhs.tm_min == rhs.tm_min &&
|
||||
lhs.tm_hour == rhs.tm_hour &&
|
||||
@@ -47,13 +47,13 @@ bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
|
||||
}
|
||||
|
||||
TEST(TimeTest, LocalTime) {
|
||||
std::time_t t = std::time(0);
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
|
||||
}
|
||||
|
||||
TEST(TimeTest, GMTime) {
|
||||
std::time_t t = std::time(0);
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::gmtime(&t);
|
||||
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
||||
}
|
||||
|
||||
@@ -1,897 +0,0 @@
|
||||
// Formatting library for C++ - utility tests
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#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/core.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
using fmt::basic_format_arg;
|
||||
using fmt::internal::basic_buffer;
|
||||
using fmt::basic_memory_buffer;
|
||||
using fmt::string_view;
|
||||
using fmt::internal::fp;
|
||||
using fmt::internal::value;
|
||||
|
||||
using testing::_;
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Test {};
|
||||
|
||||
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, 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, 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
|
||||
|
||||
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)).WillOnce(Return(ptr));
|
||||
ref.allocate(42);
|
||||
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<basic_buffer<char> >::value);
|
||||
EXPECT_FALSE(std::is_copy_assignable<basic_buffer<char> >::value);
|
||||
}
|
||||
|
||||
TEST(BufferTest, Nonmoveable) {
|
||||
EXPECT_FALSE(std::is_move_constructible<basic_buffer<char> >::value);
|
||||
EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value);
|
||||
}
|
||||
#endif
|
||||
|
||||
// A test buffer with a dummy grow method.
|
||||
template <typename T>
|
||||
struct TestBuffer : basic_buffer<T> {
|
||||
void grow(std::size_t capacity) { this->set(0, capacity); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct MockBuffer : 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);
|
||||
}
|
||||
|
||||
MockBuffer() {}
|
||||
MockBuffer(T *data) { this->set(data, 0); }
|
||||
MockBuffer(T *data, std::size_t capacity) { this->set(data, 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());
|
||||
basic_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 basic_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.resize(0);
|
||||
EXPECT_EQ(0u, buffer.size());
|
||||
EXPECT_EQ(20u, buffer.capacity());
|
||||
}
|
||||
|
||||
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) {
|
||||
basic_memory_buffer<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,
|
||||
basic_memory_buffer<char, 5, TestAllocator> &buffer) {
|
||||
std::allocator<char> *alloc = buffer.get_allocator().get();
|
||||
basic_memory_buffer<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;
|
||||
basic_memory_buffer<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');
|
||||
basic_memory_buffer<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, basic_memory_buffer<char, 5> &buffer) {
|
||||
basic_memory_buffer<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) {
|
||||
basic_memory_buffer<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');
|
||||
basic_memory_buffer<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 basic_memory_buffer<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)).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;
|
||||
basic_memory_buffer<char, 10, TestAllocator> buffer;
|
||||
EXPECT_EQ(0, buffer.get_allocator().get());
|
||||
StrictMock< MockAllocator<char> > alloc;
|
||||
char mem;
|
||||
{
|
||||
basic_memory_buffer<char, 10, TestAllocator> buffer2((TestAllocator(&alloc)));
|
||||
EXPECT_EQ(&alloc, buffer2.get_allocator().get());
|
||||
std::size_t size = 2 * fmt::inline_buffer_size;
|
||||
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
|
||||
buffer2.reserve(size);
|
||||
EXPECT_CALL(alloc, deallocate(&mem, size));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MemoryBufferTest, ExceptionInDeallocate) {
|
||||
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
|
||||
StrictMock< MockAllocator<char> > alloc;
|
||||
basic_memory_buffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc)));
|
||||
std::size_t size = 2 * fmt::inline_buffer_size;
|
||||
std::vector<char> mem(size);
|
||||
{
|
||||
EXPECT_CALL(alloc, allocate(size)).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)).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(FixedBufferTest, Ctor) {
|
||||
char array[10] = "garbage";
|
||||
fmt::basic_fixed_buffer<char> buffer(array, sizeof(array));
|
||||
EXPECT_EQ(0u, buffer.size());
|
||||
EXPECT_EQ(10u, buffer.capacity());
|
||||
EXPECT_EQ(array, buffer.data());
|
||||
}
|
||||
|
||||
TEST(FixedBufferTest, CompileTimeSizeCtor) {
|
||||
char array[10] = "garbage";
|
||||
fmt::basic_fixed_buffer<char> buffer(array);
|
||||
EXPECT_EQ(0u, buffer.size());
|
||||
EXPECT_EQ(10u, buffer.capacity());
|
||||
EXPECT_EQ(array, buffer.data());
|
||||
}
|
||||
|
||||
TEST(FixedBufferTest, BufferOverflow) {
|
||||
char array[10];
|
||||
fmt::basic_fixed_buffer<char> buffer(array);
|
||||
buffer.resize(10);
|
||||
EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow");
|
||||
}
|
||||
|
||||
struct uint32_pair {
|
||||
uint32_t u[2];
|
||||
};
|
||||
|
||||
TEST(UtilTest, BitCast) {
|
||||
auto s = fmt::internal::bit_cast<uint32_pair>(uint64_t{42});
|
||||
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42u);
|
||||
s = fmt::internal::bit_cast<uint32_pair>(uint64_t(~0ull));
|
||||
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST(UtilTest, 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 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
bool called;
|
||||
|
||||
fmt::parse_context parse_context() { return fmt::parse_context(""); }
|
||||
void advance_to(const char *) {}
|
||||
};
|
||||
|
||||
TEST(UtilTest, MakeValueWithCustomFormatter) {
|
||||
::Test 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 Result {};
|
||||
|
||||
template <typename T>
|
||||
struct MockVisitor: fmt::internal::function<Result> {
|
||||
MockVisitor() {
|
||||
ON_CALL(*this, visit(_)).WillByDefault(Return(Result()));
|
||||
}
|
||||
|
||||
MOCK_METHOD1_T(visit, Result (T value));
|
||||
MOCK_METHOD0_T(unexpected, void ());
|
||||
|
||||
Result operator()(T value) { return visit(value); }
|
||||
|
||||
template <typename U>
|
||||
Result operator()(U) {
|
||||
unexpected();
|
||||
return Result();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct VisitType { typedef T Type; };
|
||||
|
||||
#define VISIT_TYPE(Type_, VisitType_) \
|
||||
template <> \
|
||||
struct VisitType<Type_> { typedef VisitType_ 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<MockVisitor<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_ VisitType<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(UtilTest, CharArg) {
|
||||
CHECK_ARG_(char, 'a', 'a');
|
||||
CHECK_ARG_(wchar_t, L'a', 'a');
|
||||
CHECK_ARG_(wchar_t, L'a', L'a');
|
||||
}
|
||||
|
||||
TEST(UtilTest, 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(UtilTest, 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(UtilTest, PointerArg) {
|
||||
void *p = 0;
|
||||
const void *cp = 0;
|
||||
CHECK_ARG_(char, cp, p);
|
||||
CHECK_ARG_(wchar_t, cp, p);
|
||||
CHECK_ARG(cp, );
|
||||
}
|
||||
|
||||
struct check_custom {
|
||||
Result operator()(fmt::basic_format_arg<fmt::format_context>::handle h) const {
|
||||
fmt::memory_buffer 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 Result();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(UtilTest, CustomArg) {
|
||||
::Test test;
|
||||
typedef MockVisitor<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(ArgVisitorTest, VisitInvalidArg) {
|
||||
typedef MockVisitor<fmt::monostate> Visitor;
|
||||
testing::StrictMock<Visitor> visitor;
|
||||
EXPECT_CALL(visitor, visit(_));
|
||||
fmt::basic_format_arg<fmt::format_context> arg;
|
||||
visit(visitor, arg);
|
||||
}
|
||||
|
||||
// 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), string_view(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) {
|
||||
string_view lhs(inputs[i]), rhs(inputs[j]);
|
||||
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UtilTest, StringRefCompare) {
|
||||
EXPECT_EQ(0, string_view("foo").compare(string_view("foo")));
|
||||
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);
|
||||
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::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8EmptyString) {
|
||||
std::string s = "";
|
||||
fmt::internal::utf16_to_utf8 u(L"");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF8ToUTF16) {
|
||||
std::string s = "лошадка";
|
||||
fmt::internal::utf8_to_utf16 u(s.c_str());
|
||||
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
||||
EXPECT_EQ(7, u.size());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF8ToUTF16EmptyString) {
|
||||
std::string s = "";
|
||||
fmt::internal::utf8_to_utf16 u(s.c_str());
|
||||
EXPECT_EQ(L"", u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
template <typename Converter, typename Char>
|
||||
void check_utf_conversion_error(
|
||||
const char *message,
|
||||
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
||||
fmt::system_error error(0, "");
|
||||
try {
|
||||
(Converter)(str);
|
||||
} catch (const fmt::system_error &e) {
|
||||
error = e;
|
||||
}
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
|
||||
EXPECT_EQ(fmt::to_string(out), error.what());
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8Error) {
|
||||
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>(
|
||||
"cannot convert string from UTF-16 to UTF-8");
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF8ToUTF16Error) {
|
||||
const char *message = "cannot convert string from UTF-8 to UTF-16";
|
||||
check_utf_conversion_error<fmt::internal::utf8_to_utf16, char>(message);
|
||||
check_utf_conversion_error<fmt::internal::utf8_to_utf16, char>(
|
||||
message, fmt::string_view("foo", INT_MAX + 1u));
|
||||
}
|
||||
|
||||
TEST(UtilTest, UTF16ToUTF8Convert) {
|
||||
fmt::internal::utf16_to_utf8 u;
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
||||
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
typedef void (*FormatErrorMessage)(
|
||||
fmt::internal::buffer &out, int error_code, string_view message);
|
||||
|
||||
template <typename Error>
|
||||
void check_throw_error(int error_code, FormatErrorMessage format) {
|
||||
fmt::system_error error(0, "");
|
||||
try {
|
||||
throw Error(error_code, "test {}", "error");
|
||||
} catch (const fmt::system_error &e) {
|
||||
error = e;
|
||||
}
|
||||
fmt::memory_buffer message;
|
||||
format(message, error_code, "test error");
|
||||
EXPECT_EQ(to_string(message), error.what());
|
||||
EXPECT_EQ(error_code, error.error_code());
|
||||
}
|
||||
|
||||
TEST(UtilTest, FormatSystemError) {
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_system_error(message, EDOM, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)),
|
||||
to_string(message));
|
||||
message = fmt::memory_buffer();
|
||||
|
||||
// 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::string_view(0, max_size));
|
||||
EXPECT_EQ(fmt::format("error {}", EDOM), to_string(message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, SystemError) {
|
||||
fmt::system_error e(EDOM, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), e.what());
|
||||
EXPECT_EQ(EDOM, e.error_code());
|
||||
check_throw_error<fmt::system_error>(EDOM, fmt::format_system_error);
|
||||
}
|
||||
|
||||
TEST(UtilTest, ReportSystemError) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::format_system_error(out, EDOM, "test error");
|
||||
out.push_back('\n');
|
||||
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"),
|
||||
to_string(out));
|
||||
}
|
||||
|
||||
#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::utf16_to_utf8 utf8_message(message);
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::internal::format_windows_error(
|
||||
actual_message, ERROR_FILE_EXISTS, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
actual_message.resize(0);
|
||||
fmt::internal::format_windows_error(
|
||||
actual_message, ERROR_FILE_EXISTS,
|
||||
fmt::string_view(0, std::numeric_limits<size_t>::max()));
|
||||
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
|
||||
fmt::to_string(actual_message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, FormatLongWindowsError) {
|
||||
LPWSTR message = 0;
|
||||
// this error code is not available on all Windows platforms and
|
||||
// Windows SDKs, so do not fail the test if the error string cannot
|
||||
// be retrieved.
|
||||
const int provisioning_not_allowed = 0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
|
||||
provisioning_not_allowed, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
|
||||
return;
|
||||
}
|
||||
fmt::internal::utf16_to_utf8 utf8_message(message);
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::internal::format_windows_error(
|
||||
actual_message, provisioning_not_allowed, "test");
|
||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
}
|
||||
|
||||
TEST(UtilTest, WindowsError) {
|
||||
check_throw_error<fmt::windows_error>(
|
||||
ERROR_FILE_EXISTS, fmt::internal::format_windows_error);
|
||||
}
|
||||
|
||||
TEST(UtilTest, ReportWindowsError) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
|
||||
out.push_back('\n');
|
||||
EXPECT_WRITE(stderr,
|
||||
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
|
||||
fmt::to_string(out));
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
enum TestEnum2 {};
|
||||
|
||||
TEST(UtilTest, ConvertToInt) {
|
||||
EXPECT_FALSE((fmt::internal::convert_to_int<char, char>::value));
|
||||
EXPECT_FALSE((fmt::internal::convert_to_int<const char *, char>::value));
|
||||
EXPECT_TRUE((fmt::internal::convert_to_int<TestEnum2, char>::value));
|
||||
}
|
||||
|
||||
#if FMT_USE_ENUM_BASE
|
||||
enum TestEnum : char {TestValue};
|
||||
TEST(UtilTest, IsEnumConvertibleToInt) {
|
||||
EXPECT_TRUE((fmt::internal::convert_to_int<TestEnum, char>::value));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(UtilTest, ParseNonnegativeInt) {
|
||||
if (std::numeric_limits<int>::max() != (1 << 31)) {
|
||||
fmt::print("Skipping parse_nonnegative_int test\n");
|
||||
return;
|
||||
}
|
||||
const char *s = "10000000000";
|
||||
EXPECT_THROW_MSG(
|
||||
parse_nonnegative_int(s, fmt::internal::error_handler()),
|
||||
fmt::format_error, "number is too big");
|
||||
s = "2147483649";
|
||||
EXPECT_THROW_MSG(
|
||||
parse_nonnegative_int(s, fmt::internal::error_handler()),
|
||||
fmt::format_error, "number is too big");
|
||||
}
|
||||
|
||||
TEST(UtilTest, FPSubtract) {
|
||||
auto r = fp(123, 1) - fp(102, 1);
|
||||
EXPECT_EQ(r.f, 21u);
|
||||
EXPECT_EQ(r.e, 1);
|
||||
}
|
||||
|
||||
TEST(UtilTest, FPMultiply) {
|
||||
auto r = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
||||
EXPECT_EQ(r.f, 123u * 56u);
|
||||
EXPECT_EQ(r.e, 4 + 7 + 64);
|
||||
r = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
|
||||
EXPECT_EQ(r.f, (123 * 567 + 1u) / 2);
|
||||
EXPECT_EQ(r.e, 4 + 8 + 64);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ std::string get_system_error(int error_code);
|
||||
extern const char *const FILE_CONTENT;
|
||||
|
||||
// Opens a buffered file for reading.
|
||||
fmt::buffered_file 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) {
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
|
||||
Reference in New Issue
Block a user