Compare commits

..

160 Commits
5.0.0 ... 5.2.1

Author SHA1 Message Date
Victor Zverovich
3e75ad9822 Update version 2018-09-21 10:18:51 -07:00
Victor Zverovich
4f043f8e00 Bump version 2018-09-21 10:07:56 -07:00
Victor Zverovich
cc02cbc455 Fix formatting 2018-09-21 09:48:21 -07:00
Victor Zverovich
73c0238e3b Update changelog 2018-09-21 09:48:21 -07:00
Victor Zverovich
cb122a4d03 Fix format_to formatting to wmemory_buffer 2018-09-21 09:48:21 -07:00
Victor Zverovich
dc69cc45d2 Clean tests 2018-09-21 09:48:21 -07:00
Candy Gumdrop
9d8021f0d6 Add checks for NVIDIA's CUDA compiler
- Disable templated user-defined literals when compiled under nvcc

Similarly to Intel's C++ compiler, nvcc fails to compile templated UDLs
as they both share the same parsing engine (EDG C++ Front End).  This
fixes building CUDA programs which make use of fmt.
2018-09-21 06:55:45 -07:00
Jonathan Müller
9d2221b954 Improve error message when formatting unknown types 2018-09-20 07:01:39 -07:00
hhggit
70a6a4bb01 prevent ""fmt/range.h"" from specializing fmt::basic_string_view (#865) 2018-09-19 20:13:05 -07:00
Victor Zverovich
e4fc856c2f Disable android build due to gradle issues 2018-09-19 19:53:47 -07:00
Victor Zverovich
3f4984fb36 Clean core-test and fix linkage errors on older gcc 2018-09-19 12:24:38 -07:00
medithe
d43665056d Workaround visit lookup issues in printf.h on gcc 2018-09-19 09:16:26 -07:00
Daniel Marshall
894b6fac8e Changed to use scoped enum
Changed "reusing existing formatters example" to use scoped enum instead.
2018-09-16 17:28:40 -07:00
Victor Zverovich
59f555ad8f Workaround more visit lookup issues on gcc 2018-09-15 08:46:12 -07:00
Victor Zverovich
a7e356cc80 Update README.rst 2018-09-14 07:52:30 -07:00
Victor Zverovich
e758bfbae1 Merge branch 'release' of github.com:fmtlib/fmt 2018-09-13 07:38:23 -07:00
Victor Zverovich
66381e308d Minor cleanup 2018-09-13 07:37:50 -07:00
Victor Zverovich
295a0d84d9 Update version 2018-09-13 07:28:20 -07:00
Victor Zverovich
1fb1c4c912 Update docs 2018-09-13 07:20:43 -07:00
Victor Zverovich
465a593536 Add table support to rst2md 2018-09-13 07:15:18 -07:00
Victor Zverovich
d62f4c3bc1 Formatting 2018-09-12 19:00:26 -07:00
Victor Zverovich
a243490ad7 Add more methods to benchmark results 2018-09-12 18:28:26 -07:00
Victor Zverovich
9e12ca6069 Update changelog 2018-09-12 18:07:29 -07:00
Victor Zverovich
fbca830dd1 Update changelog, readme and improve compat 2018-09-12 12:44:04 -07:00
Victor Zverovich
6146248cf4 Update changelog 2018-09-12 10:42:50 -07:00
Victor Zverovich
bc26fbf1b0 Move experimental color API to fmt/color.h 2018-09-12 09:04:26 -07:00
Victor Zverovich
97cc889374 Workaround a visit lookup issue in gcc 8 (#851) 2018-09-12 08:45:14 -07:00
Victor Zverovich
7110b46076 Optimize default formatting 2018-09-12 08:45:14 -07:00
Victor Zverovich
c8a8464f7d Optimize buffer construction 2018-09-12 08:45:14 -07:00
medithe
8cbfb6e727 Get rid of conversion warning in gcc-4.8 (#854)
Get rid of conversion warning in gcc-4.8 

Get rid of the following warning:
conversion to ‘int’ from ‘long unsigned int’ may alter its value [-Werror=conversion]
2018-09-11 06:14:39 -07:00
Victor Zverovich
6ffc828a79 Phasing out null_terminating_iterator 2018-09-10 06:51:38 -07:00
Victor Zverovich
aeb6add336 Skip strchr for the common case 2018-09-09 09:08:41 -07:00
Victor Zverovich
5614289dd8 Optimize and simplify format string parsing 2018-09-09 08:41:27 -07:00
Victor Zverovich
10c7f89351 Optimize format string processing on dumb compilers 2018-09-09 08:13:06 -07:00
Victor Zverovich
59c268a5f8 Use strlen when possible since it's constexpr on gcc 2018-09-08 16:17:22 -07:00
Victor Zverovich
918bb1ce8f Optimize argument capture 2018-09-08 15:29:32 -07:00
Victor Zverovich
a3ba6b4f62 Disable the fmt(...) macro by default (#853) 2018-09-08 09:55:41 -07:00
Victor Zverovich
8671689449 Update docs and formatting 2018-09-08 09:06:54 -07:00
Victor Zverovich
cc10b4607f Make format_to faster on older gcc 2018-09-08 07:22:29 -07:00
medithe
981797f059 Get rid of implicit-fallthrough warn. in GCC 7 and 8
This change enables FMT_FALLLTHROUGH also for GCC-Compilers in the versions 7 and 8.
2018-09-07 12:43:32 -07:00
Victor Zverovich
2117775747 Micro-optimize parsing 2018-09-05 07:10:50 -07:00
Victor Zverovich
be0e268468 Optimize processing of trailing '}' 2018-08-31 15:34:31 -07:00
Victor Zverovich
fbc38b9083 Pass heavy arguments by ref 2018-08-31 15:17:18 -07:00
Victor Zverovich
8dc69b9da9 Workaround a bug in Intellisense 2018-08-29 20:40:54 -07:00
Victor Zverovich
1489d3b7fa Implement exponential notation 2018-08-29 18:55:57 -07:00
Victor Zverovich
dd8c5ce442 Implement more FP formatting options 2018-08-29 09:34:57 -07:00
Victor Zverovich
46484da711 Fix a warning 2018-08-29 07:00:24 -07:00
Victor Zverovich
802ff8866e Fix compilation of time.h when localtime_t is a macro (#843) 2018-08-29 06:40:54 -07:00
medithe
95a718992c Remove conversion compiler warnings (#844)
* Remove conversion compiler warning

When compiling with g++8, I get the following two errors:
include/fmt/format-inl.h:400:29: error: conversion from ‘int’ to ‘char’ may change value [-Werror=conversion]
       buffer[size++] = zero + static_cast<char>(digit);
                        ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
include/fmt/format-inl.h:416:28: error: conversion from ‘int’ to ‘char’ may change value [-Werror=conversion]
       buffer[size++] = '0' + digit;
                        ~~~~^~~~~~~

With this change, the errors are gone.
2018-08-29 06:38:56 -07:00
Victor Zverovich
e483a01a0f Implement some formatting options in Grisu 2018-08-27 17:51:49 -07:00
Victor Zverovich
f51080916e Revert "Implement some formatting options in Grisu"
This reverts commit 2a952dd0b2.
2018-08-27 17:51:27 -07:00
Victor Zverovich
2a952dd0b2 Implement some formatting options in Grisu 2018-08-26 09:51:49 -07:00
Victor Zverovich
0de44a469a Implement exponent formatting 2018-08-26 08:12:35 -07:00
Victor Zverovich
f0d0a1ebd7 Implement Grisu2 digit generation 2018-08-25 20:45:50 -07:00
Victor Zverovich
569ac91e0b Implement Grisu boundary computation 2018-08-25 11:39:38 -07:00
Victor Zverovich
a11eb3a090 Workaround various icc bugs (#822) 2018-08-22 09:07:17 -07:00
Victor Zverovich
62010520ed Disable gnu-string-literal-operator-template warning 2018-08-22 08:12:59 -07:00
Victor Zverovich
987514761e Make convert_to_int public (#818) 2018-08-22 07:40:06 -07:00
Victor Zverovich
ba95e36a58 Clarify that '\0' cannot be used as fill (#832) 2018-08-19 08:37:40 -07:00
Thomas Bernard
abde38b4fb Add compilation support with Newlib nano for embedded targets
Remove the dependency on std::locale to reduce executable sizes

For embedded target the usage of locale bring a huge amount of code to the executable, just to get hold of the thousands separator. Using a static defined thousands separator makes libfmt embedded target friendly.
2018-08-19 07:40:34 -07:00
Elias Daler
18400503da Fix C4127 warning in basic_writer<Range>::write_double 2018-08-17 07:36:38 -07:00
Victor Zverovich
9de312112a Reformat and add a comment 2018-08-15 06:54:43 -07:00
Victor Zverovich
8bbb0b48b4 Update README.rst 2018-08-15 06:34:34 -07:00
Victor Zverovich
5c0101ab2d Use the correct function signature in the docs 2018-08-12 11:34:48 -07:00
Victor Zverovich
fbe6410e53 Fix docs 2018-08-12 08:52:47 -07:00
Victor Zverovich
8b9fb9fb7e Fix ambiguous instantiation with formatter in fmt/ostream.h (#830) 2018-08-11 09:13:54 -07:00
Victor Zverovich
0f04ec68a9 Fix package upload (#828) 2018-08-11 07:57:45 -07:00
Victor Zverovich
809073851f Update changelog 2018-08-11 07:22:41 -07:00
Victor Zverovich
5d02041c59 Update changelog 2018-08-11 07:12:52 -07:00
medithe
4b868b8922 Re-enable compile-time format-string checking
Since the commit 691a7a9 compile-time string checking was accidently not possible anymore.
With this change, it should be possible again.
2018-08-07 07:43:47 -07:00
Victor Zverovich
4061a0d35d Parameterize vformat to support custom char types 2018-08-05 10:44:59 -07:00
Victor Zverovich
c68bab7014 Remove broken fmt::internal::format_enum (#818) 2018-08-01 07:11:53 -07:00
Victor Zverovich
0c63d15ee9 Improve wording 2018-07-27 07:37:45 -07:00
Victor Zverovich
ce19309d09 Workaround a bug in icc 15 2018-07-26 07:59:41 -07:00
Victor Zverovich
c684349195 Move contiguous version of format_to to fmt/core.h 2018-07-22 18:09:22 -07:00
Victor Zverovich
8db14efa84 util-test -> core-test and minor cleanup 2018-07-22 17:05:23 -07:00
Victor Zverovich
ffe414cad1 Add compile-time format string checks to format_to (#783) 2018-07-22 15:30:51 -07:00
Victor Zverovich
c178ab440f Remove FMT_USE_RVALUE_REFERENCES 2018-07-22 15:07:53 -07:00
Victor Zverovich
5befe6584d Remove fmt/folly.h and clean up core API 2018-07-22 14:42:25 -07:00
Victor Zverovich
35538ca66c Merge more format overloads 2018-07-22 12:24:47 -07:00
Victor Zverovich
4f16409730 Merge format overloads using SFINAE 2018-07-22 08:06:42 -07:00
Victor Zverovich
2a4e948864 Add UTF-8 types 2018-07-22 06:36:21 -07:00
MikePopoloski
d778bded95 Make line in tests fit within 80chars 2018-07-20 17:07:23 -07:00
MikePopoloski
7b4f170c94 Fix warning about using old-style cast 2018-07-20 17:07:23 -07:00
MikePopoloski
b1d10a2884 Add support for dynamic arg sets
This allows construction of basic_format_args from a dynamic set of arguments. The syntax is a little clunky and could probably be improved but this at least enables the functionality.
2018-07-20 17:07:23 -07:00
Victor Zverovich
cf2719bd12 Add support for types explicitly convertible to wstring_view 2018-07-18 19:12:10 -07:00
Victor Zverovich
50584f42b4 Test formatting of an object with templated conversion to string-like 2018-07-18 16:24:46 -07:00
Victor Zverovich
73bed45b7a Add support for types explicitly convertible to fmt::string_view 2018-07-18 14:42:26 -07:00
Victor Zverovich
6eaa507473 Fix global initialization issue (#807) 2018-07-18 09:49:21 -07:00
Victor Zverovich
48dff9f3c5 Update docs 2018-07-18 06:44:46 -07:00
Victor Zverovich
a9e261599b Minor cleanup 2018-07-17 08:54:22 -07:00
Henry Fredrick Schreiner
efd8ee8a7f Reduce warnings, support #809 2018-07-16 12:37:54 +02:00
Victor Zverovich
8615ff2acc Micro-optimize argument retrieval 2018-07-15 07:28:31 -07:00
Victor Zverovich
916ed99dab Micro-optimize argument retrieval 2018-07-14 15:28:55 -07:00
Victor Zverovich
e7e9578ed4 Optimize format string parsing 2018-07-14 14:27:56 -07:00
Victor Zverovich
c99a259739 Mark new functions with FMT_API (#808) 2018-07-14 07:43:16 -07:00
Victor Zverovich
e0f6a2f8be Add a formatter for folly::StringPiece 2018-07-14 07:31:47 -07:00
Victor Zverovich
ae4a3945f5 Revert "Better support for newer CMake's"
This reverts commit 0eb01b832c.
2018-07-14 06:34:20 -07:00
Alexej Harm
a317448bd4 Keep noexcept specifier when exceptions are disabled. 2018-07-13 12:46:15 -07:00
Henry Schreiner
0eb01b832c Better support for newer CMake's 2018-07-11 07:08:17 -07:00
Marek Kurdej
2a4cd6d05e Fix the returned value of format_to_n with user-defined types having operator<<. 2018-07-10 14:01:12 -07:00
Henry Schreiner
9c32e73abf Fixing return unreachable warning on NVCC 2018-07-09 21:33:10 -07:00
gabime
e5c93108e6 Added clear() to basic_buffer 2018-07-09 20:25:49 -07:00
Victor Zverovich
60c662b3a7 Add an example of reusing formatters 2018-07-08 15:00:44 -07:00
Victor Zverovich
f66ba6508a Optimize format string parsing 2018-07-08 10:15:24 -07:00
Victor Zverovich
f21268aa72 Revert "Optimize format string parsing" because of a bug in MSVC
https://godbolt.org/g/rpiDgh

This reverts commit f9e9bf0231.
2018-07-08 08:08:50 -07:00
Victor Zverovich
07b690a679 Update README.rst 2018-07-07 18:18:56 -07:00
Victor Zverovich
f9e9bf0231 Optimize format string parsing 2018-07-07 16:50:22 -07:00
Victor Zverovich
c2ce7e4f07 Update version 2018-07-05 06:22:54 -07:00
Victor Zverovich
434eb9167e Update README.rst 2018-07-04 18:07:17 -07:00
Victor Zverovich
09d94162f9 Update changelog 2018-07-04 17:47:46 -07:00
Florin Iucha
e6362642cf Fix pedantic conversion warning 2018-07-04 17:45:28 -07:00
Victor Zverovich
f0110e8125 Update changelog and CI 2018-07-04 17:43:07 -07:00
Victor Zverovich
479ee2a8c6 Fix MSVC build, take 2 2018-07-04 14:24:07 -07:00
Victor Zverovich
e928b6724c Fix MSVC 2013 build 2018-07-04 12:33:15 -07:00
Dhruv Paranjape
ec218a3ad1 Fix redefinition warning for RESET_COLOR 2018-07-04 07:52:19 -07:00
Victor Zverovich
c04fb91b03 Fix handling of user-defined types in format_to (#793) 2018-07-04 07:40:56 -07:00
Victor Zverovich
323b92bffe Force linking of inline functions into the library (#795) 2018-07-03 19:10:23 -07:00
Jonathan Müller
c6d9730ddb Fix sign conversion warnings (#790) 2018-06-27 14:31:20 +02:00
Victor Zverovich
2e95823ef7 Move new color support to format.h and mark old as deprecated 2018-06-24 06:39:22 -07:00
Victor Zverovich
ab2d88ca8e Make format_to work with basic_memory_buffer (#776) 2018-06-23 08:29:26 -07:00
Victor Zverovich
3abd036c47 Fix compilation on gcc 4 2018-06-23 07:18:59 -07:00
Victor Zverovich
c2f3805488 Add vformat_to_n (#769) 2018-06-23 07:03:00 -07:00
Remotion
ce500635c7 Renamed enum color to colors.
Added enum colors conversion to rgb struct.
Added colors_test.cpp.

Removed print_colored.
Renamed enum colors back to color.

Removed unnecessary inline keyword.
Removed print_rgb.
2018-06-23 06:14:44 -07:00
Victor Zverovich
0508bbc7ae Add wchar_t overload of format_to_n (#764) 2018-06-13 08:24:32 +02:00
gabime
c2fbadb9cf Fixed issue #779 2018-06-12 07:39:44 +02:00
Remotion
47268ecd80 Fixed GCC version test 2018-06-10 20:35:38 +02:00
Victor Zverovich
9ff3b6af2e Fix handling of compile-time strings when including ostream.h (#768) 2018-06-10 19:05:27 +02:00
Victor Zverovich
e3707ef14b Document that file should be in wide-oriented mode for wide print 2018-06-09 09:12:49 +02:00
Victor Zverovich
45fa4ee949 Merge branch 'master' of github.com:fmtlib/fmt 2018-06-08 21:43:56 +02:00
Remotion
9c07b37fff Using enum class now.
Renamed from hex to color.
Changed colr names to snake case.
2018-06-08 21:40:35 +02:00
Remotion
5b5886a993 Fixed line length. 2018-06-08 19:48:41 +02:00
Remotion
d2bfee13e2 Added quotes for strings in ranges and tuple likes. 2018-06-08 19:48:41 +02:00
Remotion
aff6e45e14 Added support for rgb color output. 2018-06-07 22:49:16 +02:00
Victor Zverovich
1b8a7f8fa0 Fix postincrement in truncating and counting iterators 2018-06-07 20:20:36 +02:00
Victor Zverovich
4bc26f0a7b Merge branch 'master' of github.com:fmtlib/fmt 2018-06-07 18:42:30 +02:00
Victor Zverovich
fc6e0fe992 Fix FP formatting to a non-back_insert_iterator with sign & numeric alignment (#756) 2018-06-07 18:41:40 +02:00
Dror Levin
cd5b5670ba Make is_range and is_tuple_like public API, fix #751 2018-06-06 21:49:14 +02:00
Victor Zverovich
6322b47e60 Minor cleanup 2018-06-06 16:51:35 +02:00
Elias Kosunen
691a7a91a1 Add more compilers to CI and increase FMT_PEDANTIC warning levels (#736)
* Add a _lot_ more warnings to FMT_PEDANTIC
Fix these warnings

* Add more compilers to CI
Fix (some) of the compiler errors with them

* Enable -Werror on CI
Increase warning level on MSVC when compiling with FMT_PEDANTIC

* Add VS 2013 and 2015 to Appveyor

* Fix Appveyor tests
Formatting

* Implement requested changes
Fix some of the MSVC warnings
Implement C++11 integer_sequence

* Reintroduce appveyor-build.py

* Remove ranges-test from tests

* Remove (some) explicit warning suppressions
Fix C++ standard setting in CI

* Remove (some) explicit warning suppressions
Fix C++ standard setting in CI

* Fix test builds with C++11

* Enable pedantic warnings on tests

* Fix warnings from edits to master

* Cleanups

* Add C++11 support to ranges.h
Re-enable ranges-test
Fix a Visual Studio error about function not returning a value in printf.h
Fix a bug in .travis.yml
2018-06-06 15:57:59 +02:00
Victor Zverovich
dd1a5ef7f9 Let requests close the file 2018-06-05 16:58:02 +02:00
Victor Zverovich
d5c462596b Fix formatting of more than 15 named arguments (#754) 2018-06-05 08:32:28 +02:00
Victor Zverovich
47d147b65d Simplify the nvcc warning fix 2018-06-04 21:27:20 +02:00
Victor Zverovich
911a75114b Fix nvcc warnings (#752) 2018-06-04 21:00:28 +02:00
Victor Zverovich
94b476283c Fix docs 2018-06-04 19:56:22 +02:00
Victor Zverovich
252f11f85d Fix a bogus MSVC warning about unreachable code, take 2 2018-06-04 18:17:55 +02:00
Victor Zverovich
81d5663825 Fix more bogus MSVC warnings about unreachable code (#748) 2018-06-04 06:10:52 +02:00
Victor Zverovich
68f0ac8271 Fix a bogus MSVC warning about unreachable code 2018-06-03 08:03:56 +02:00
Victor Zverovich
b60a5c5d49 Improve floating-point formatting 2018-05-28 20:16:30 -07:00
Victor Zverovich
8dc2360b11 Fix a comment 2018-05-28 17:21:39 -07:00
Victor Zverovich
4e4b8570e5 Implement simple version of Grisu 2018-05-28 11:25:07 -07:00
Victor Zverovich
4027557958 Fix tests on 64-bit MSVC 2018-05-27 11:38:53 -07:00
Victor Zverovich
5c32aa411c Workaround a bug in MSVC 2018-05-27 11:18:27 -07:00
Victor Zverovich
468c243ca8 Add a function to get cached power of 10 2018-05-27 10:57:26 -07:00
Victor Zverovich
2f257b7291 Implement normalization and simplify power table 2018-05-27 08:04:30 -07:00
Victor Zverovich
6a5bb6e268 Move Android.mk to support and update 2018-05-27 06:20:54 -07:00
Victor Zverovich
e282d963fe Bump version 2018-05-26 09:46:59 -07:00
Victor Zverovich
e2cd521b8f Fix incorrect call to on_align in '{:}=' (#750) 2018-05-26 09:23:09 -07:00
Victor Zverovich
fba352a92a Don't use UDL templates on Intel C++ compiler (#742) 2018-05-26 08:07:45 -07:00
Victor Zverovich
6dcc526d5b Update release script 2018-05-26 07:02:49 -07:00
51 changed files with 3838 additions and 2288 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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:]))

View File

@@ -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__':

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 &copy = 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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