Compare commits

..

208 Commits
6.0.0 ... 6.1.0

Author SHA1 Message Date
Victor Zverovich
4ca6821e8f Update version 2019-12-01 16:58:40 -08:00
Victor Zverovich
7111a1eb9f Bump version 2019-12-01 16:50:07 -08:00
Victor Zverovich
ae00bbdc91 Update changelog 2019-12-01 16:39:58 -08:00
Victor Zverovich
e71e07d9fb Update changlog 2019-12-01 16:26:07 -08:00
Victor Zverovich
0184df7020 Update docs 2019-12-01 14:49:56 -08:00
Victor Zverovich
1cbae6e9ba Put vprint declarations in one place 2019-12-01 14:43:51 -08:00
Robert Franke
159f89e2b8 Fixing installation directory of '*.dll' files on Windows 2019-12-01 14:36:21 -08:00
Victor Zverovich
4b120b68ae Clean up includes 2019-12-01 10:19:07 -08:00
Victor Zverovich
186b225d9d Update changlog 2019-12-01 10:16:01 -08:00
Victor Zverovich
4cbf4888ea Update changelog 2019-12-01 10:04:38 -08:00
Victor Zverovich
e31f2b3d03 Update changelog 2019-12-01 09:27:28 -08:00
Victor Zverovich
62da1db62a Avoid wchar_t instantiations 2019-12-01 07:34:09 -08:00
Chris Martin
3bc28fcc6b Squelch MSVC warning exporting subclasses of runtime_error
When compiling {fmt} as a DLL, MSVC complains that we are exporting
classes that inherit from "std::runtime_error", which we are not
exporting.

In this case, it's not really a problem because that symbol is already
exported via the C++ stdlib. So we just add a pragma to silence the
warning.
2019-11-30 12:00:25 -08:00
Victor Zverovich
3c05fa46c6 Update changelog 2019-11-30 09:18:16 -08:00
Victor Zverovich
ba6e330fd3 digits -> num_bits 2019-11-30 08:41:07 -08:00
Victor Zverovich
6037b3cae9 Fix dangling else problem in FMT_ASSERT 2019-11-30 07:52:33 -08:00
Victor Zverovich
fafb03fa6d Fix handling of fallback_uintptr 2019-11-30 07:31:38 -08:00
Victor Zverovich
2f9acd1838 Remove dependency on <cassert> 2019-11-29 09:37:18 -08:00
Victor Zverovich
aaf829bfb1 Fix fallback pointer formatting on big endian, take 2 2019-11-29 07:07:08 -08:00
Victor Zverovich
b994a0ab13 Fix handling of missing fraction in snprintf_float 2019-11-29 06:17:29 -08:00
Victor Zverovich
bb205d940d Fix fallback pointer formatting on big endian 2019-11-29 05:15:59 -08:00
Victor Zverovich
ef7369ce90 Update docs 2019-11-28 08:09:46 -08:00
Victor Zverovich
40e4c227db Update changelog 2019-11-28 07:43:22 -08:00
Florin Iucha
ea54b21e78 Remove invalid noexcept annotation
buffered_file& operator=(buffered_file&& other) calls close which can
throw.
2019-11-28 06:43:08 -08:00
Florin Iucha
9cbf4b087c Fix -Wconversion warnings 2019-11-28 06:43:08 -08:00
Victor Zverovich
1200a34e10 Update changelog 2019-11-27 16:15:42 -08:00
Victor Zverovich
9c7e2a6c6f Add missing newline 2019-11-27 11:06:40 -08:00
Victor Zverovich
34e921f6fe Update docs 2019-11-27 09:26:32 -08:00
Victor Zverovich
c3be0f593d Refactor floating-point formatting 2019-11-27 08:08:22 -08:00
Victor Zverovich
c68703c9f4 float_spec -> float_specs 2019-11-26 15:53:24 -08:00
Victor Zverovich
9a21728b0a Remove gen_digits_params 2019-11-26 11:27:53 -08:00
Victor Zverovich
3de36e9348 Enable -Wswitch-enum in CI 2019-11-26 10:17:48 -08:00
Victor Zverovich
4afb39bc24 Update README.rst 2019-11-26 08:33:16 -08:00
Victor Zverovich
7ffa62db18 Fix precision handling in snprintf_float 2019-11-25 20:00:10 -08:00
Victor Zverovich
0d07db1234 Fix handling of streamable and convertible to string types 2019-11-25 16:46:33 -08:00
Victor Zverovich
d19ed6716d Fix hexfloat buffer reallocation 2019-11-25 15:54:04 -08:00
Victor Zverovich
99b6e928d4 Fix handling of types with deleted rvalue conversion to string (#1421) 2019-11-25 08:30:47 -08:00
Victor Zverovich
57cd3f72e9 Update comment 2019-11-24 15:34:33 -08:00
Victor Zverovich
111fc127fe Remove fp::operator- 2019-11-24 13:28:15 -08:00
Victor Zverovich
6003ec3f25 Simplify Grisu implementation 2019-11-24 13:24:28 -08:00
Victor Zverovich
8877a67724 Instantiate snprintf_float 2019-11-24 08:57:39 -08:00
Victor Zverovich
75fff1db64 Minor cleanup 2019-11-24 08:43:56 -08:00
Victor Zverovich
28d7191c27 Don't print trailing zero with fixed, precision=0, and showpoint (#1417) 2019-11-24 08:23:10 -08:00
Victor Zverovich
43271ba8e8 Handle null terminator at the end of the buffer 2019-11-24 07:15:25 -08:00
Paul Dreik
63a9f3fcd4 fix bad oss fuzz link in the oss-fuzz badge 2019-11-23 12:37:59 -08:00
Victor Zverovich
4cf59ce734 Integrate Grisu and sprintf digit generators 2019-11-23 06:56:29 -08:00
Victor Zverovich
7395472dde Refactor floating-point formatting 2019-11-22 18:49:19 -08:00
Victor Zverovich
9108b25da9 Merge branch 'float' 2019-11-22 08:54:28 -08:00
Victor Zverovich
4d366c68b7 Merge branch 'master' of github.com:fmtlib/fmt 2019-11-22 08:53:58 -08:00
Victor Zverovich
ded1e7679e Refactor floating point formatting 2019-11-22 08:22:22 -08:00
Victor Zverovich
c7edd8e570 Cleanup FP formatting 2019-11-20 13:17:03 -08:00
Victor Zverovich
75108a56f6 Don't print % for nan and inf 2019-11-20 12:26:48 -08:00
Victor Zverovich
3e1f70fe02 Merge write_fp into write 2019-11-20 12:20:17 -08:00
Victor Zverovich
125fc5e520 Update comment 2019-11-20 11:45:25 -08:00
Victor Zverovich
6793ffc1d6 Update README.rst 2019-11-20 09:33:59 -08:00
Victor Zverovich
f4fcc5fd28 Update README.rst 2019-11-20 09:31:11 -08:00
Victor Zverovich
4de41aa655 Move basic_writer::write_fp to where it belongs 2019-11-20 08:22:34 -08:00
Victor Zverovich
404a880bd4 Make parse_arg_id more readable 2019-11-20 08:12:23 -08:00
Victor Zverovich
092d2dc7b2 Merge safe-duration-cast.h into chrono.h 2019-11-20 08:05:18 -08:00
Victor Zverovich
093e554211 Remove redundant qualification 2019-11-20 07:44:34 -08:00
Ivan Shynkarenka
d0696b0aa6 warning C4456: declaration of 'num_digits' hides previous local declaration 2019-11-20 06:43:46 -08:00
Victor Zverovich
66d7746bb3 Use grisu for exponent notation 2019-11-19 16:55:11 -08:00
Victor Zverovich
e9bff78814 Don't parse % unless FMT_DEPRECATED_PERCENT is set 2019-11-19 11:54:18 -08:00
Victor Zverovich
57b6f2966d Deprecate the fmt macro 2019-11-19 11:28:17 -08:00
Victor Zverovich
d79493e5ee Remove Grisu2 2019-11-19 09:50:59 -08:00
Leon Klingele
78842ce0d6 test: add default constructor for a const value
This fixed a compilation error of the OS X 10.11.6 C++ compiler:

    ./fmt/test/format-test.cc:1861:16: error: default initialization of an object of const type 'const Answer' without a user-provided default constructor
      const Answer const_answer;
                   ^
                               {}
2019-11-19 08:32:35 -08:00
Victor Zverovich
5420bcce2d Make % an opt-in to improve compatibility with std::format 2019-11-18 08:04:43 -08:00
Victor Zverovich
56a2e2075c Refactor float spec parsing 2019-11-18 07:37:36 -08:00
Victor Zverovich
ed117baa4f Replace bool with float_format and add exponential 2019-11-17 15:52:13 -08:00
Victor Zverovich
f26446290b Move float_spec_handler to internal namespace and update asserts 2019-11-17 13:47:00 -08:00
Victor Zverovich
7e1cb3237a Fix indentation 2019-11-17 13:17:43 -08:00
Victor Zverovich
f67783d7e6 Clarify that numeric alignment is deprecated 2019-11-17 10:24:55 -08:00
Victor Zverovich
1c6d85f7bb Apply coding conventions to examples 2019-11-17 10:15:16 -08:00
Victor Zverovich
4a1da44f91 Apply coding conventions to examples 2019-11-17 10:14:06 -08:00
Victor Zverovich
080b6899d2 Tweak the docs 2019-11-17 10:10:32 -08:00
Victor Zverovich
c01ec54fde Document and clean basic_format_parse_context 2019-11-17 09:28:26 -08:00
Victor Zverovich
b0c2ab93fa Bump version 2019-11-17 08:35:26 -08:00
Victor Zverovich
9b7fe2a4a1 Don't use POSIX API on UWP 2019-11-17 08:29:08 -08:00
Daniel Laügt
c58b7d9c2f Use overridden locale in ostream 2019-11-17 06:22:22 -08:00
Victor Zverovich
ceff9b0b2e Tweak the docs 2019-11-16 07:14:57 -08:00
Tom de Geus
3dc8639f8a [docs] Added conda 2019-11-16 06:19:33 -08:00
Victor Zverovich
dcde089b4e Improve POSIX API detection 2019-11-15 07:28:02 -08:00
David P. Sicilia
2145a7bdcc Move has_formatter into the public fmt namespace. (#1407)
* Move has_formatter into the public fmt namespace.

This will allow users to do SFINAE-friendly checks for
the formattability of a type.

Fixes #1369
2019-11-14 07:08:24 -08:00
Victor Zverovich
52ae134f84 Remove broken CI config 2019-11-14 07:06:38 -08:00
Victor Zverovich
0d6dd0cc6a Correct basic_string_view from string ctor 2019-11-14 05:57:23 -08:00
Rosen Penev
1f918159ed [clang-tidy] Replace deprecated C headers
Found with modernize-deprecated-headers

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
6868f888b2 [clang-tidy] Add missing override
Found with hicpp-use-override

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
87cd545a1e [clang-tidy] Replace {} with = default
Found with hicpp-use-equals-default

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
12f9437e22 [clang-tidy] Use auto
Found with hicpp-use-auto

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
bb0c8bfea8 [clang-tidy] Add noexcept where move is used
Found with performance-noexcept-move-constructor

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
e6e8298904 [clang-tidy] Add parentheses to macro arguments
Found with bugprone-macro-parentheses

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
0f0848e4f4 [clang-tidy] Use braced init list
Found with modernize-return-braced-init-list

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
a1fb5c7337 [clang-tidy] Changes suffixes to uppercase
Found with hicpp-uppercase-literal-suffix

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Rosen Penev
8a411c2bca [clang-tidy] Turn deleted function to public
Found with modernize-use-equals-delete

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
Victor Zverovich
0047dc10a2 Mark apidoc as rst 2019-11-06 15:16:02 +00:00
Victor Zverovich
263cdef8a0 Merge branch 'master' of github.com:fmtlib/fmt 2019-11-06 13:19:56 +00:00
Victor Zverovich
d4ca54253a Update docs 2019-11-06 12:48:21 +00:00
Victor Zverovich
5bb7b28e15 Document members 2019-11-05 10:43:18 +00:00
Victor Zverovich
1409dfe76b Try fix CI 2019-11-05 10:39:14 +00:00
daniel
f1559e1d56 Use grouping() from locale for specifier 'n' 2019-11-05 07:22:12 +00:00
Victor Zverovich
ffd05e65ed basic_parse_context -> basic_format_parse_context per standard and document 2019-11-05 07:13:58 +00:00
Attila Tajti
0889856d61 Fix UTF-8 truncation 2019-11-03 11:53:15 +00:00
Victor Zverovich
d6eede9e08 Remove redundant ctor 2019-11-01 12:09:04 -07:00
Victor Zverovich
213e09644f Workaround X11 madness (#1388) 2019-11-01 08:47:11 -07:00
Victor Zverovich
6bfc9af8c9 Add double support to compile 2019-10-30 20:45:29 -07:00
Victor Zverovich
3487f1b9cd Always inline grisu_gen_digits and disable grisu2 by default 2019-10-30 16:52:00 -07:00
Victor Zverovich
791294d17b Apply get_cached_power optimization by jk-jeon 2019-10-30 08:07:01 -07:00
Victor Zverovich
8e700619b7 Simplify format_handler 2019-10-30 07:02:38 -07:00
Victor Zverovich
58c6f8c7f5 Make unsigned-integer-overflow sanitizer happy (#1377) 2019-10-28 14:41:09 -07:00
Victor Zverovich
40414b3446 Don't emit trailing zeros in exponential notation (#1376) 2019-10-28 12:31:00 -07:00
Victor Zverovich
b7a157401e Simplify grisu_writer 2019-10-23 13:52:09 -07:00
Victor Zverovich
7aa58c30bf Simplify NVCC checks 2019-10-23 11:32:35 -07:00
Victor Zverovich
8e9bffa986 clang-format 2019-10-23 11:20:21 -07:00
Victor Zverovich
ce4d87acd4 Remove obsolete comment and clang-format 2019-10-23 11:15:43 -07:00
Ivan Shynkarenka
21acc2af43 Fix more Visual Studio 2019 pedantic warnings (#1371)
* format-inl.h(444,1): warning C4804: '>>': unsafe use of type 'bool' in operation
format.h(2808,1): warning C4127: conditional expression is constant

* More fixes for VS2019 pedantic warnings

* Fix "conditional expression is constant" VS2019 warning in more specific way

* Use const_check to silence constexpr warning
2019-10-22 17:13:03 -07:00
kent-tri
00669427df Patch compiler error when building using nvcc
If you compile using `nvcc` and pass the option `--expt-relaxed-constexpr` it will crash with an internal compiler error. This modification prevents using `constexpr` in `fmtlib` when compiling using `nvcc` and prevents the crash.
2019-10-22 08:34:52 -07:00
Victor Zverovich
d39ebf3ff2 Optimize counting 2019-10-21 06:57:42 -07:00
Victor Zverovich
6498bc6d31 Simplify grisu_writer 2019-10-20 19:29:24 -07:00
Victor Zverovich
a967dcbe20 Improve handling of signs 2019-10-20 19:05:38 -07:00
Victor Zverovich
8498bc97dd Initialize all the things 2019-10-20 17:53:18 -07:00
Victor Zverovich
e2ea940673 Handle assymetric boundaries 2019-10-20 07:55:05 -07:00
Victor Zverovich
2bc5585ff0 Fix computing lower boundaries for smallest normalized double 2019-10-18 17:56:52 -07:00
Victor Zverovich
bb728a572a packed_arg_bitsize -> packed_arg_bits and remove packed_arg_mask 2019-10-18 10:06:57 -07:00
Victor Zverovich
36d1390e67 Implement round half to even 2019-10-18 07:21:12 -07:00
Orivej Desh
599e0aef45 Support single precision floats in grisu formatting
Fixes #1336
2019-10-18 07:08:41 -07:00
Ivan Shynkarenka
91f7619cc9 Fix Visual Studio 2019 pedantic warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) 2019-10-14 08:55:18 -07:00
Victor Zverovich
c4dc6bef24 Apply clang-format 2019-10-13 18:31:09 -07:00
Victor Zverovich
646966e973 Reduce bigint capacity 2019-10-13 14:06:38 -07:00
Victor Zverovich
a5abe5d95c Handle negative exponent and nonnegative power 2019-10-13 13:16:09 -07:00
Victor Zverovich
1cbc5fa6cb Handle negative exponent and rename value/pow10 to numerator/denominator 2019-10-13 12:50:48 -07:00
Victor Zverovich
f7a5748fd3 Partially implement (FPP)^2 2019-10-13 09:28:35 -07:00
Victor Zverovich
0e94b931a2 Fix a linkage error introduced by #1360 (#1362) 2019-10-13 08:23:47 -07:00
Victor Zverovich
5e58eb97b1 Implement add_compare 2019-10-13 08:05:06 -07:00
Orivej Desh
3a15ea3ea5 Rename write_double to write_fp
It handles all floating point types, not just doubles.
2019-10-12 11:41:24 -07:00
Orivej Desh
b87ac4d840 Distinguish float from double 2019-10-12 11:41:24 -07:00
Orivej Desh
a927dda9bb Use words for packed constants 2019-10-12 11:41:24 -07:00
Orivej Desh
dd11d45847 Encode types using 5 bits
This is needed to support more than the current 16 types.
2019-10-12 11:41:24 -07:00
Victor Zverovich
b55551f900 Implement more comparison operators 2019-10-12 09:22:24 -07:00
Deniz Evrenci
96f91428c6 Add defaulted copy and move operations to format_error and system_error (#1347)
* Avoid weak vtables by providing a private virtual member function

* Add warning Wweak-vtables to clang when FMT_PEDANTIC is on

* Add defaulted copy and move operations to format_error and system_error

Compiler generated copy operations are deprecated and move operations
are not generated altogether.

* Add warning Wdeprecated to clang when FMT_PEDANTIC is on
2019-10-11 10:44:20 -07:00
Orivej Desh
b732f28c00 Deduplicate color vformat and vprint
After #1351 they became essentially the same.
2019-10-11 10:42:11 -07:00
Tanki Zhang
a82c1dc6d9 use memory_buffer to make color print behave atomic #1348 (#1351) 2019-10-10 08:28:56 -07:00
Victor Zverovich
2730e90186 Fix compile error in printf with gcc9 (#1354) 2019-10-09 18:58:40 -07:00
Victor Zverovich
e4d6d9d7c8 Implement divmod 2019-10-09 13:40:50 -07:00
Orivej Desh
a1079e9fd6 Fix undefined in format-test (#1349)
When `MoveCtor` performs `check_move_buffer`, the buffer allocator becomes null,
but then `MoveCtor` attempts to use it to allocate a dynamic buffer. This
succeeds nevertheless because a typical `std::allocator<char>::allocate` does
not use `this`, so it does not crash when `this` is null.

Fixes #1344
2019-10-08 15:42:51 -07:00
Orivej Desh
b66bb6b71f Fix undefined in core-test and printf-test (#1345)
* Fix undefined in core-test

Fixes "reference binding to null pointer" in BufferTest.Ctor

buffer.operator[] attempts to return a reference to `buffer.ptr_[0]` when `ptr_`
in `mock_buffer<int> buffer` is null.

* Fix undefined in printf-test

Fixes "signed integer overflow" in PrintfTest.Length

This occurs in `TestLength<long long>("ll")`, since its minimum value minus one
does not fit in long long.

* Fix undefined in printf %0$

Printf counts arguments from 1.

Fixes "shift exponent -4 is negative" in PrintfTest.InvalidArgIndex.

`do_get` is called with index -1 when `basic_printf_context.arg` is called with
id 4294967295 when basic_printf_context::get_arg subtracts 1 from arg_index 0 in
the format string "%0$d".
2019-10-08 06:28:39 -07:00
Victor Zverovich
b60114533f Implement more bigint operations 2019-10-06 12:49:23 -07:00
Victor Zverovich
c41cea8b18 Initial implementation of square 2019-10-05 16:37:52 -07:00
Victor Zverovich
0c7650373c Fix handling of types convertible to std::string_view 2019-10-05 06:58:37 -07:00
Jack Andersen
0571013709 Repoint one more Python 2 link to Python 3
One more python reference, this one acknowledging the `str.format` function.
2019-10-01 11:44:36 -07:00
Paul Dreik
d2c9276fcd let README point to python 3 instead of 2 2019-09-29 08:30:47 -07:00
Victor Zverovich
0fc7bd1573 Fix ambiguity for types with dodgy conversions 2019-09-28 11:35:20 -07:00
Jeremy Ong
b4f1988c4b Provide overload for fmt::join that handles std::tuples
Address enhancement request #1322.

The overload is provided in `ranges` (original `fmt::join` exists
currently in `format.h` for historical reasons.

Tests for prvalue and lvalue tuple arguments as well as the empty
tuple are provided in `ranges-test.cc`.
2019-09-27 14:05:35 -07:00
Victor Zverovich
4b8f8fac96 Update README.rst 2019-09-25 07:14:45 -07:00
Richard Musil
3b2fc033d1 Making CUDA test work with CMAKE_MSVC_RUNTIME_LIBRARY
CMake 3.15 introduced a new way of handling MSVC CRT type definition for
the build: CMAKE_MSVC_RUNTIME_LIBRARY variable.
(https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html)

This is supposed to be the way to go with MSVC CRT selection in new
projects. Using this method however breaks the current CMake script for
CUDA test. The reason is the CUDA test uses "FindCUDA" CMake module to
detect and set up CUDA support in CMake, which is deprecated since CMake
version 3.10, and which does not support CMAKE_MSVC_RUNTIME_LIBRARY
selector correctly (i.e. it does not propagate the compiler option
related to the CRT).

I did not find a way to "patch" in the correct compiler options, so
(while knowing this feature is only available from CMake 3.15 on) I
decided to change also the way CUDA is handled and instead of using
FindCUDA, used enable_language. Apart from having some nice additional
side-effects, it also fixed the problem with CRT selection.

However, the propagation of the compiler options (and in particular the
options related to C++ standard selection) is still a bit flaky on
Windows+MSVC platform, so it had to be done manually.

The patch makes two things in parallel:

1) Introduces MSVC_BUILD_STATIC, which, together with CMake version >=
3.15, allows building static version of the 'fmt' lib (and all the
tests).

2) At the same time, for CMake >= 3.15 it switches handling of CUDA
support from (old) FindCUDA to (new) enable_language, to fix the
problems which the old method has with the new CRT selector for MSVC in
a new CMake.

Added a check for CUDA before enabling it.

Using VERSION_LESS instead of VERSION_GREATER_EQUAL

Since apparently VERSION_GREATER_EQUAL exists only from CMake 3.7, while
Android is using CMake 3.6.

Removed MSVC_RUNTIME_LIBRARY logic from the CMake file.

The static build can be set on the command line with CMake >= 3.15
by defining the policy and the CMAKE_MSVC_RUNTIME_LIBARY this way:

cmake -G <gen> <options>
    -DCMAKE_POLICY_DEFAULT_CMP0091=NEW
    -DMSVC_BUILD_STATIC=ON
    -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$<CONFIG:Debug>:Debug>"

When MSVC_BUILD_DEBUG is set the test 'posix-mock-test' is skipped as it
does not build with the static runtime.
2019-09-25 06:46:11 -07:00
mwinterb
ac59d9f3a4 workaround VS2019 Update 3 compiler bug. (#1328) 2019-09-24 11:29:29 -07:00
Daumantas Kavolis
8f27ce4d8b add test for multiple compilation types 2019-09-24 06:25:21 -07:00
Daumantas Kavolis
89b0c71fa9 fix name clash in header-only mode 2019-09-24 06:25:21 -07:00
Daumantas Kavolis
f6a783ad2e fix get 2019-09-24 06:25:21 -07:00
Victor Zverovich
ccc8f5db02 Disable integral operator<< (#1316) 2019-09-23 20:34:08 -07:00
Victor Zverovich
20fdb88a1c Remove redundant and nonportable test (#1313) 2019-09-23 13:36:12 -07:00
Victor Zverovich
f29901097f Don't use const char* overload of operator<< (#1309) 2019-09-23 12:35:08 -07:00
Victor Zverovich
758446c80d Fix a warning (#1319) and simplify code 2019-09-19 16:00:40 -07:00
Vedran Miletić
f7aedc5fc4 Fix shared build on Solaris 2019-09-19 14:12:03 -07:00
Paul Dreik
840a817ed2 add oss fuzz badge 2019-09-19 08:23:05 -07:00
Rosen Penev
79c923ba2c fmt.pc.in: Fix for cross compilation
These variables get set to /usr/lib and /usr/include , which is totally wrong in a cross compilation
environment.

Changed to standard pkgconfig values consistent with most other ones.
2019-09-16 17:18:21 -07:00
Victor Zverovich
5dc577c064 Update ChangeLog.rst 2019-09-12 18:20:48 -07:00
Victor Zverovich
c6d1a94a9f Fix fallback_format (#1306) 2019-09-09 17:28:53 -07:00
Victor Zverovich
0656045d02 Fix format overload that takes text_style (#1305) 2019-09-08 18:41:02 -07:00
Victor Zverovich
c85ae23c73 Add max_value 2019-09-08 09:21:30 -07:00
Victor Zverovich
b3bf665764 Implement multiplication and part of assignment from pow of 10 2019-09-08 08:27:00 -07:00
Victor Zverovich
0887887e23 Implement left shift 2019-09-07 18:10:36 -07:00
Victor Zverovich
ac71d853be Refactor normalize and clean up 2019-09-07 17:28:59 -07:00
Victor Zverovich
6649b8e0ca value -> bigit 2019-09-07 14:23:19 -07:00
Victor Zverovich
56b5c192a0 Add a bigint stub and reenable grisu 2019-09-07 13:50:22 -07:00
Victor Zverovich
b2f0b6e44e Tweak comment 2019-09-07 08:43:35 -07:00
Victor Zverovich
3d9f3c163b Integrate new format string compilation 2019-09-07 07:01:51 -07:00
Victor Zverovich
19547d5148 Update index.rst 2019-09-06 13:27:28 -07:00
Victor Zverovich
972ffd3151 Update README.rst 2019-09-06 13:13:30 -07:00
Victor Zverovich
2ed412fa38 Update README.rst 2019-09-06 13:03:14 -07:00
gabime
df4dcf2ece Fixed vs 2015 warning about unused variable when grisu is off 2019-09-06 11:29:56 -07:00
Victor Zverovich
f7a4b4ab91 Make numeric alignment optional 2019-09-06 09:43:53 -07:00
Victor Zverovich
611cf0b3c6 Format octal 0 as 0 2019-09-06 07:03:47 -07:00
Victor Zverovich
58a8f2f539 Add ccache to the list of projects 2019-09-06 06:26:09 -07:00
Victor Zverovich
1882b9687b Reduce the numer of ifdefs with an empty (u)int128_t fallback 2019-09-05 18:03:47 -07:00
Deniz Evrenci
6de0454b42 Add support for built-in __int128 when available 2019-09-04 07:05:08 -07:00
Deniz Evrenci
16e3c48bb0 Move definition of FMT_USE_INT128 to core.h 2019-09-04 07:05:08 -07:00
Deniz Evrenci
8ce5f680f2 Rename internal::is_integral and internal::is_arithmetic
To reserve space for the type traits sharing the same name as the ones
from standard library.
2019-09-04 07:05:08 -07:00
Victor Zverovich
2fd8f9ec8a Initial implementation of optimal compile-time formatter generation 2019-09-02 19:50:30 -07:00
Victor Zverovich
fe642d7648 Clarify use of the core API in header-only mode (#1296) 2019-09-02 17:14:30 -07:00
Victor Zverovich
a128b5b2cb Simplify format string compilation 2019-09-02 06:48:19 -07:00
Victor Zverovich
466128de00 Remove unused code and refactor 2019-09-01 13:08:53 -07:00
Victor Zverovich
22e98a5b6a Make compile work with user-defined types 2019-09-01 11:49:27 -07:00
Victor Zverovich
f18a3f36a7 Remove string_view_metadata
string_view_metadata was introduced to make compiled format relocatable.
However, format string compilation is an optimization and therefore
adding overhead and extra complexity for relocation is undesirable.
In most cases the string will be either static or outlive compilation
and formatting and if it doesn't, it's possible to make compiled
representation relocatale by other means.
2019-09-01 11:10:49 -07:00
Victor Zverovich
7cad33563c Refactor format string compilation 2019-09-01 10:28:41 -07:00
Victor Zverovich
e1ab6bc006 Simplify format string compilation 2019-09-01 08:31:32 -07:00
Victor Zverovich
24a88545d9 Add -Werror to tests 2019-08-31 09:26:45 -07:00
Victor Zverovich
422e7b9d70 Fix compile-time checks for user-defined types (#1292) 2019-08-31 08:35:38 -07:00
Victor Zverovich
d1dd9d5327 Document floating-point n specifier (#1291) 2019-08-31 08:16:47 -07:00
Victor Zverovich
9a56a608ee Fix warnings (#1288) 2019-08-31 08:16:47 -07:00
Victor Zverovich
c76957565c FixedEnum -> StrongEnum and make it a regression test 2019-08-31 08:16:47 -07:00
Rosen Penev
200ee6f108 Fix minor clang-tidy warnings
using instead of typedef.

climits instead of limits.h

Added missing cast to size_t.
2019-08-31 07:02:21 -07:00
Egor Seredin
bcd9b9331a Map not int enum to correct underlying_type (#1286)
* Map not int enum to correct underlying_type

* Use non-zero constant in TestFixedEnum
2019-08-31 06:25:38 -07:00
luncliff
345ba07f1d Add a CUDA test
test cuda: import fmt in CUDA source code

Current test is only for Windows(cl.exe).
Need to test more with the other host compilers...

* Activate the test when `find_package(CUDA)` worked
* The test runs with C++14

Detailed comments in 'test/cuda-test'

test cuda: add more comment / macro check

* checks both `__NVCC__` and `__CUDACC__`

More comments for CMake and CUDA source file.

test cuda: <fmt/core.h> checks NVCC and CUDA

The header file checks 2 things.

* __NVCC__: if the compiler is from NVIDIA
* __CUDACC__: if the source code is CUDA(.cu) file

Since we can't sure all users prefer latest, Version for
`find_pacakge(CUDA)` is downgraded to 9.0.
This is the minimum version for C++14 in CUDA
2019-08-30 17:45:50 -07:00
hhb
9e2490be4c Rename precision parameter
To avoid clang warning:

fmtlib/include/fmt/chrono.h:753:32: error: declaration shadows a field of 'formatter<duration<type-parameter-0-0, type-parameter-0-1>, type-parameter-0-2, void>' [-Werror,-Wshadow]
2019-08-28 07:29:04 -07:00
Victor Zverovich
3f75e2b69e Make buffer_range public and update custom formatting docs (#1281) 2019-08-28 06:50:20 -07:00
Victor Zverovich
744302add0 Workaround broken [[deprecated]] in Intel compiler (#1273) 2019-08-28 05:58:09 -07:00
Greg Sjaardema
f5556225a4 Eliminate shadow variable warning
gcc-9 gives the following shadow warning:
```
In file included from /Users/libraries/ioss/src/fmt/ostream.h:12,
                 from /Users/libraries/ioss/src/Ioss_DatabaseIO.C:59:
/Users/libraries/ioss/src/fmt/format.h: In function 'void fmt::v6::internal::parse_format_string(fmt::v6::basic_string_view<Char>, Handler&&)':
/Users/libraries/ioss/src/fmt/format.h:2442:10: warning: declaration of 'struct fmt::v6::internal::parse_format_string(fmt::v6::basic_string_view<Char>, Handler&&)::writer' shadows a global declaration [-Wshadow]
 2442 |   struct writer {
      |          ^~~~~~
/Users/libraries/ioss/src/fmt/format.h:1703:7: note: shadowed declaration is here
 1703 | using writer = basic_writer<buffer_range<char>>;
      |       ^~~~~~
```

Since the `writer` struct is only used internally in the `parse_format_string` function, its name can be changed somewhat aribtrarily to avoid conflicts with names in an outer scope.

Note that this warning is also present in the 6.0.0 release.
2019-08-27 14:29:39 -07:00
Bruce Mitchener
ad3c7855e2 Fix typos. 2019-08-27 06:26:17 -07:00
51 changed files with 3633 additions and 2319 deletions

View File

@@ -92,45 +92,6 @@ matrix:
# g++ 4.8 on Linux with C++11
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
compiler: gcc
- name: Android NDK (Gradle)
language: android
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- ninja-build
- curl
- tree
android:
components:
- tools
- platform-tools
- android-25 # 7.0
- android-27 # 8.1
- android-28 # 9.0
- build-tools-28.0.3
before_install:
# Install Gradle from https://sdkman.io/
- curl -s "https://get.sdkman.io" | bash > /dev/null
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- sdk version
- sdk install gradle
- sdk use gradle
- gradle --version
install:
# Accept SDK Licenses + Install NDK
- yes | sdkmanager --update > /dev/null 2>&1
- sdkmanager ndk-bundle > /dev/null 2>&1
before_script:
- pushd ./support
script:
- gradle clean
- gradle assemble
after_success:
- popd;
- tree ./libs
before_script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi

View File

@@ -78,7 +78,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion
-Wconversion -Wswitch-enum
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
@@ -98,7 +98,7 @@ endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion)
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@@ -135,10 +135,8 @@ endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(open fcntl.h HAVE_OPEN)
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
@@ -152,13 +150,8 @@ endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
locale.h ostream.h printf.h ranges.h
safe-duration-cast.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
locale.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc src/posix.cc)
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
@@ -185,7 +178,7 @@ set_target_properties(fmt PROPERTIES
DEBUG_POSTFIX d)
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed)
@@ -259,7 +252,9 @@ if (FMT_INSTALL)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)

View File

@@ -1,3 +1,254 @@
6.1.0 - 2019-12-01
------------------
* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal
representation with correct rounding by default:
.. code:: c++
#include <cmath>
#include <fmt/core.h>
int main() {
fmt::print("{}", M_PI);
}
prints ``3.141592653589793``.
* Made the fast binary to decimal floating-point formatter the default,
simplified it and improved performance. {fmt} is now 15 times faster than
libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10%
faster than double-conversion on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_:
================== ========= =======
Function Time (ns) Speedup
================== ========= =======
ostringstream 1,346.30 1.00x
ostrstream 1,195.74 1.13x
sprintf 995.08 1.35x
doubleconv 99.10 13.59x
fmt 88.34 15.24x
================== ========= =======
.. image:: https://user-images.githubusercontent.com/576385/
69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
* {fmt} no longer converts ``float`` arguments to ``double``. In particular this
improves the default (shortest) representation of floats and makes
``fmt::format`` consistent with ``std::format`` specs
(`#1336 <https://github.com/fmtlib/fmt/issues/1336>`_,
`#1353 <https://github.com/fmtlib/fmt/issues/1353>`_,
`#1360 <https://github.com/fmtlib/fmt/pull/1360>`_,
`#1361 <https://github.com/fmtlib/fmt/pull/1361>`_):
.. code:: c++
fmt::print("{}", 0.1f);
prints ``0.1`` instead of ``0.10000000149011612``.
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_.
* Made floating-point formatting output consistent with ``printf``/iostreams
(`#1376 <https://github.com/fmtlib/fmt/issues/1376>`_,
`#1417 <https://github.com/fmtlib/fmt/issues/1417>`_).
* Added support for 128-bit integers
(`#1287 <https://github.com/fmtlib/fmt/pull/1287>`_):
.. code:: c++
fmt::print("{}", std::numeric_limits<__int128_t>::max());
prints ``170141183460469231731687303715884105727``.
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the
output from different threads doesn't interleave
(`#1351 <https://github.com/fmtlib/fmt/pull/1351>`_).
Thanks `@tankiJong (Tanki Zhang) <https://github.com/tankiJong>`_.
* Made compile time in the header-only mode ~20% faster by reducing the number
of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from
``fmt/core.h`` to ``fmt/format.h``.
* Added an overload of ``fmt::join`` that works with tuples
(`#1322 <https://github.com/fmtlib/fmt/issues/1322>`_,
`#1330 <https://github.com/fmtlib/fmt/pull/1330>`_):
.. code:: c++
#include <tuple>
#include <fmt/ranges.h>
int main() {
std::tuple<char, int, float> t{'a', 1, 2.0f};
fmt::print("{}", t);
}
prints ``('a', 1, 2.0)``.
Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_.
* Changed formatting of octal zero with prefix from "0o0" to "0":
.. code:: c++
fmt::print("{:#o}", 0);
prints ``0``.
* The locale is now passed to ostream insertion (``<<``) operators
(`#1406 <https://github.com/fmtlib/fmt/pull/1406>`_):
.. code:: c++
#include <fmt/locale.h>
#include <fmt/ostream.h>
struct S {
double value;
};
std::ostream& operator<<(std::ostream& os, S s) {
return os << s.value;
}
int main() {
auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42});
// s == "0,42"
}
Thanks `@dlaugt (Daniel Laügt) <https://github.com/dlaugt>`_.
* Locale-specific number formatting now uses grouping
(`#1393 <https://github.com/fmtlib/fmt/issues/1393>`_
`#1394 <https://github.com/fmtlib/fmt/pull/1394>`_).
Thanks `@skrdaniel <https://github.com/skrdaniel>`_.
* Fixed handling of types with deleted implicit rvalue conversion to
``const char**`` (`#1421 <https://github.com/fmtlib/fmt/issues/1421>`_):
.. code:: c++
struct mystring {
operator const char*() const&;
operator const char*() &;
operator const char*() const&& = delete;
operator const char*() && = delete;
};
mystring str;
fmt::print("{}", str); // now compiles
* Enums are now mapped to correct underlying types instead of ``int``
(`#1286 <https://github.com/fmtlib/fmt/pull/1286>`_).
Thanks `@agmt (Egor Seredin) <https://github.com/agmt>`_.
* Enum classes are no longer implicitly converted to ``int``
(`#1424 <https://github.com/fmtlib/fmt/issues/1424>`_).
* Added ``basic_format_parse_context`` for consistency with C++20
``std::format`` and deprecated ``basic_parse_context``.
* Fixed handling of UTF-8 in precision
(`#1389 <https://github.com/fmtlib/fmt/issues/1389>`_,
`#1390 <https://github.com/fmtlib/fmt/pull/1390>`_).
Thanks `@tajtiattila (Attila Tajti) <https://github.com/tajtiattila>`_.
* {fmt} can now be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__ using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__
(`#1410 <https://github.com/fmtlib/fmt/pull/1410>`_)::
conda install -c conda-forge fmt
Thanks `@tdegeus (Tom de Geus) <https://github.com/tdegeus>`_.
* Added a CUDA test (`#1285 <https://github.com/fmtlib/fmt/pull/1285>`_,
`#1317 <https://github.com/fmtlib/fmt/pull/1317>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and
`@risa2000 <https://github.com/risa2000>`_.
* Improved documentation (`#1276 <https://github.com/fmtlib/fmt/pull/1276>`_,
`#1291 <https://github.com/fmtlib/fmt/issues/1291>`_,
`#1296 <https://github.com/fmtlib/fmt/issues/1296>`_,
`#1315 <https://github.com/fmtlib/fmt/pull/1315>`_,
`#1332 <https://github.com/fmtlib/fmt/pull/1332>`_,
`#1337 <https://github.com/fmtlib/fmt/pull/1337>`_,
`#1395 <https://github.com/fmtlib/fmt/issues/1395>`_
`#1418 <https://github.com/fmtlib/fmt/pull/1418>`_).
Thanks
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Various code improvements
(`#1358 <https://github.com/fmtlib/fmt/pull/1358>`_,
`#1407 <https://github.com/fmtlib/fmt/pull/1407>`_).
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@dpacbach (David P. Sicilia) <https://github.com/dpacbach>`_,
* Fixed compile-time format string checks for user-defined types
(`#1292 <https://github.com/fmtlib/fmt/issues/1292>`_).
* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer
(`#1377 <https://github.com/fmtlib/fmt/issues/1377>`_).
* Fixed various warnings and compilation issues
(`#1273 <https://github.com/fmtlib/fmt/issues/1273>`_,
`#1278 <https://github.com/fmtlib/fmt/pull/1278>`_,
`#1280 <https://github.com/fmtlib/fmt/pull/1280>`_,
`#1281 <https://github.com/fmtlib/fmt/issues/1281>`_,
`#1288 <https://github.com/fmtlib/fmt/issues/1288>`_,
`#1290 <https://github.com/fmtlib/fmt/pull/1290>`_,
`#1301 <https://github.com/fmtlib/fmt/pull/1301>`_,
`#1305 <https://github.com/fmtlib/fmt/issues/1305>`_,
`#1306 <https://github.com/fmtlib/fmt/issues/1306>`_,
`#1309 <https://github.com/fmtlib/fmt/issues/1309>`_,
`#1312 <https://github.com/fmtlib/fmt/pull/1312>`_,
`#1313 <https://github.com/fmtlib/fmt/issues/1313>`_,
`#1316 <https://github.com/fmtlib/fmt/issues/1316>`_,
`#1319 <https://github.com/fmtlib/fmt/issues/1319>`_,
`#1320 <https://github.com/fmtlib/fmt/pull/1320>`_,
`#1326 <https://github.com/fmtlib/fmt/pull/1326>`_,
`#1328 <https://github.com/fmtlib/fmt/pull/1328>`_,
`#1344 <https://github.com/fmtlib/fmt/issues/1344>`_,
`#1345 <https://github.com/fmtlib/fmt/pull/1345>`_,
`#1347 <https://github.com/fmtlib/fmt/pull/1347>`_,
`#1349 <https://github.com/fmtlib/fmt/pull/1349>`_,
`#1354 <https://github.com/fmtlib/fmt/issues/1354>`_,
`#1362 <https://github.com/fmtlib/fmt/issues/1362>`_,
`#1366 <https://github.com/fmtlib/fmt/issues/1366>`_,
`#1364 <https://github.com/fmtlib/fmt/pull/1364>`_,
`#1370 <https://github.com/fmtlib/fmt/pull/1370>`_,
`#1371 <https://github.com/fmtlib/fmt/pull/1371>`_,
`#1385 <https://github.com/fmtlib/fmt/issues/1385>`_,
`#1388 <https://github.com/fmtlib/fmt/issues/1388>`_,
`#1397 <https://github.com/fmtlib/fmt/pull/1397>`_,
`#1414 <https://github.com/fmtlib/fmt/pull/1414>`_,
`#1416 <https://github.com/fmtlib/fmt/pull/1416>`_,
`#1422 <https://github.com/fmtlib/fmt/issues/1422>`_
`#1427 <https://github.com/fmtlib/fmt/pull/1427>`_,
`#1431 <https://github.com/fmtlib/fmt/issues/1431>`_,
`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_).
Thanks `@hhb <https://github.com/hhb>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@neheb (Rosen Penev) <https://github.com/neheb>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_
`@leonklingele <https://github.com/leonklingele>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@kent-tri <https://github.com/kent-tri>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_,
`@marti4d (Chris Martin) <https://github.com/marti4d>`_.
6.0.0 - 2019-08-26
------------------
@@ -183,6 +434,7 @@
* Modernized the codebase using more C++11 features and removing workarounds.
Most importantly, ``buffer_context`` is now an alias template, so
use ``buffer_context<T>`` instead of ``buffer_context<T>::type``.
These features require GCC 4.8 or later.
* ``formatter`` specializations now always take precedence over implicit
conversions to ``int`` and the undocumented ``convert_to_int`` trait
@@ -298,7 +550,7 @@
`#1222 <https://github.com/fmtlib/fmt/issues/1222>`_).
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* Fixed bugs discovered by fuzzing or during fuzzing integation
* Fixed bugs discovered by fuzzing or during fuzzing integration
(`#1124 <https://github.com/fmtlib/fmt/issues/1124>`_,
`#1127 <https://github.com/fmtlib/fmt/issues/1127>`_,
`#1132 <https://github.com/fmtlib/fmt/issues/1132>`_,

View File

@@ -7,6 +7,10 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: http://stackoverflow.com/questions/tagged/fmt
@@ -24,7 +28,7 @@ Features
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization.
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python.
* Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
@@ -39,12 +43,12 @@ Features
consists of just three header files, ``core.h``, ``format.h`` and
``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests
<https://github.com/fmtlib/fmt/tree/master/test>`_.
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed.
* Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow
errors.
* Ease of use: small self-contained code base, no external dependencies,
permissive BSD `license
permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers.
@@ -77,16 +81,15 @@ Check a format string at compile time:
.. code:: c++
// test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{2}"), 42);
.. code::
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message);
@@ -118,11 +121,10 @@ Format objects of user-defined types via a simple `extension API
template <>
struct fmt::formatter<date> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) {
auto format(const date& d, FormatContext& ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
}
};
@@ -138,12 +140,12 @@ which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++
// Prints formatted error message.
void vreport_error(const char *format, fmt::format_args args) {
void vreport_error(const char* format, fmt::format_args args) {
fmt::print("Error: ");
fmt::vprint(format, args);
}
template <typename... Args>
void report_error(const char *format, const Args & ... args) {
void report_error(const char* format, const Args & ... args) {
vreport_error(format, fmt::make_format_args(args...));
}
@@ -162,15 +164,14 @@ Speed tests
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.01
libc++ std::ostream 3.04
{fmt} 1632f72 fmt::print 0.86
tinyformat 2.0.1 tfm::printf 3.23
Boost Format 1.67 boost::format 7.98
libc printf 1.03
libc++ std::ostream 2.98
{fmt} 4de41a fmt::print 0.76
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~17% faster than ``printf``.
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.3 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
@@ -183,7 +184,7 @@ further details refer to the `source
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
@@ -207,7 +208,6 @@ 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
============= =============== ==================== ==================
@@ -228,14 +228,13 @@ 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 libraries to
compare formatting function overhead only. Boost Format and tinyformat are
header-only libraries so they don't provide any linkage options.
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
@@ -278,6 +277,8 @@ Projects using this library
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
* `ccache <https://ccache.dev/>`_: A compiler cache
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
@@ -343,7 +344,7 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework
`More... <https://github.com/search?q=cppformat&type=Code>`_
`More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
@@ -419,20 +420,6 @@ arguments. However it has significant limitations, citing its author:
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses ``printf``-like format strings and
is type safe. It doesn't support user-defined types or positional arguments and
makes unconventional use of ``operator()`` for passing format arguments.
Tinyformat
~~~~~~~~~~
This library supports ``printf``-like format strings and is very small .
It doesn't support positional arguments and wrapping it in C++98 is somewhat
difficult. Tinyformat relies on iostreams which limits its performance.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
@@ -469,7 +456,7 @@ A: use ``std::tuple``:
License
-------
{fmt} is distributed under the BSD `license
{fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax
@@ -502,7 +489,7 @@ Some ideas used in the implementation are borrowed from `Loki
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary

View File

@@ -84,7 +84,7 @@
<div class="jumbotron">
<div class="tb-container">
<h1>{fmt}</h1>
<p class="lead">Small, safe and fast formatting library</p>
<p class="lead">A modern formatting library</p>
<div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success"

View File

@@ -10,12 +10,14 @@ The {fmt} library API consists of the following parts:
facilities and a lightweight subset of formatting functions
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
format string checks, output iterator and user-defined type support
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
All functions and types provided by the library reside in namespace ``fmt`` and
macros have prefix ``FMT_`` or ``fmt``.
macros have prefix ``FMT_``.
.. _core-api:
@@ -23,7 +25,8 @@ Core API
========
``fmt/core.h`` defines the core API which provides argument handling facilities
and a lightweight subset of formatting functions.
and a lightweight subset of formatting functions. In the header-only mode
include ``fmt/format.h`` instead of ``fmt/core.h``.
The following functions use :ref:`format string syntax <syntax>`
similar to that of Python's `str.format
@@ -49,7 +52,6 @@ participate in an overload resolution if the latter is not a string.
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
Named Arguments
---------------
@@ -94,8 +96,11 @@ string checks, output iterator and user-defined type support.
Compile-time Format String Checks
---------------------------------
Compile-time checks are supported for built-in and string types as well as
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
specializations.
.. doxygendefine:: FMT_STRING
.. doxygendefine:: fmt
Formatting User-defined Types
-----------------------------
@@ -107,32 +112,56 @@ template and implement ``parse`` and ``format`` methods::
struct point { double x, y; };
namespace fmt {
template <>
struct formatter<point> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) {
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". The formatter should
// parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'.
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range:
if (it != end && *it != '}')
throw format_error("invalid format");
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point &p, FormatContext &ctx) {
return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
auto format(const point& p, FormatContext& ctx) {
// ctx.out() is an output iterator to write to.
return format_to(
ctx.out(),
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
p.x, p.y);
}
};
}
Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2};
std::string s = fmt::format("{}", p);
std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)"
In the example above the ``formatter<point>::parse`` function ignores the
contents of the format string referred to by ``ctx.begin()`` so the object will
always be formatted in the same way. See ``formatter<tm>::parse`` in
:file:`fmt/chrono.h` for an advanced example of how to parse the format string and
customize the formatted output.
You can also reuse existing formatters, for example::
You can also reuse existing formatters via inheritance or composition, for
example::
enum class color {red, green, blue};
@@ -140,7 +169,7 @@ You can also reuse existing formatters, for example::
struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext &ctx) {
auto format(color c, FormatContext& ctx) {
string_view name = "unknown";
switch (c) {
case color::red: name = "red"; break;
@@ -180,6 +209,9 @@ You can also write a formatter for a hierarchy of classes::
fmt::print("{}", a); // prints "B"
}
.. doxygenclass:: fmt::basic_format_parse_context
:members:
Output Iterator Support
-----------------------
@@ -263,7 +295,7 @@ allocator::
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args & ... args) {
const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_args(args...));
}
@@ -278,21 +310,21 @@ Custom Formatting of Built-in Types
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
using arg_formatter =
fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>>;
using arg_formatter = fmt::arg_formatter<fmt::buffer_range<char>>;
// A custom argument formatter that formats negative integers as unsigned
// with the ``x`` format specifier.
class custom_arg_formatter : public arg_formatter {
public:
custom_arg_formatter(fmt::format_context &ctx,
fmt::format_specs *spec = nullptr)
: arg_formatter(ctx, spec) {}
custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx = nullptr,
fmt::format_specs* spec = nullptr)
: arg_formatter(ctx, parse_ctx, spec) {}
using arg_formatter::operator();
auto operator()(int value) {
if (spec().type() == 'x')
if (specs() && specs()->type == 'x')
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value);
}
@@ -307,7 +339,7 @@ custom argument formatter class::
template <typename ...Args>
inline std::string custom_format(
fmt::string_view format_str, const Args &... args) {
fmt::string_view format_str, const Args&... args) {
return custom_vformat(format_str, fmt::make_format_args(args...));
}
@@ -316,6 +348,31 @@ custom argument formatter class::
.. doxygenclass:: fmt::arg_formatter
:members:
.. _ranges-api:
Ranges and Tuple Formatting
===========================
The library also supports convenient formatting of ranges and tuples::
#include <fmt/ranges.h>
std::tuple<char, int, float> t{'a', 1, 2.0f};
// Prints "('a', 1, 2.0)"
fmt::print("{}", t);
NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main
``format.h`` header, but expect this to change in the future.
Using ``fmt::join``, you can separate tuple elements with a custom separator::
#include <fmt/ranges.h>
std::tuple<int, char> t = {1, 'a'};
// Prints "1, a"
fmt::print("{}", fmt::join(t, ", "));
.. _chrono-api:
Date and Time Formatting
@@ -349,7 +406,7 @@ user-defined types that have overloaded ``operator<<``::
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const date &d) {
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};

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', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.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', '5.3.0', '6.0.0', '6.1.0']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."

View File

@@ -1,17 +1,18 @@
Overview
========
**fmt** (formerly cppformat) is an open-source formatting library.
It can be used as a fast and safe alternative to printf and IOStreams.
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
.. raw:: html
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for a long
time. Ive used both boost::format and loki::SPrintf, and neither felt
like the right answer. This does.
Thanks for creating this library. Its been a hole in C++ for
aa long time. Ive used both <code>boost::format</code> and
<code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
</div>
</div>
@@ -20,12 +21,13 @@ It can be used as a fast and safe alternative to printf and IOStreams.
Format API
----------
The replacement-based Format API provides a safe alternative to ``printf``,
``sprintf`` and friends with comparable or `better performance
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
The format API is similar in spirit to the C ``printf`` family of function but
is safer, simpler and serveral times `faster
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
than common standard library implementations.
The `format string syntax <syntax.html>`_ is similar to the one used by
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python:
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
Python:
.. code:: c++
@@ -60,7 +62,7 @@ The Format API also supports positional arguments useful for localization:
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
Named arguments can be created with ``fmt::arg``. This makes it easier to track
what goes where when multiple values are being inserted:
what goes where when multiple arguments are being formatted:
.. code:: c++
@@ -72,21 +74,10 @@ an alternative, slightly terser syntax for named arguments:
.. code:: c++
using namespace fmt::literals;
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
"name"_a="World", "number"_a=42);
The ``_format`` suffix may be used to format string literals similar to Python:
.. code:: c++
std::string message = "{0}{1}{0}"_format("abra", "cad");
Other than the placement of the format string on the left of the operator,
``_format`` is functionally identical to ``fmt::format``. In order to use the
literal operators, they must be made visible with the directive
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
``_format`` but nothing else from the ``fmt`` namespace.
.. _safety:
Safety
@@ -106,11 +97,10 @@ string", because the argument ``"forty-two"`` is a string while the format code
.. code:: c++
format(fmt("The answer is {:d}"), "forty-two");
format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error for the same reason on compilers that support
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for how to enable
compile-time checks.
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
The following code
@@ -178,13 +168,13 @@ The library is highly portable and relies only on a small set of C++11 features:
* alias templates
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
compilers use fmt `version 4.x
compilers use {fmt} `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98.
The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the
output of ``printf`` is platform-dependent in this case. For example,
output of ``printf`` is platform-dependent. For example,
.. code::
@@ -197,9 +187,9 @@ always prints ``inf``.
Ease of Use
-----------
fmt has a small self-contained code base with the core library consisting of
{fmt} has a small self-contained code base with the core library consisting of
just three header files and no external dependencies.
A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects.
.. raw:: html

View File

@@ -101,11 +101,6 @@ The meaning of the various alignment options is as follows:
| ``'>'`` | Forces the field to be right-aligned within the |
| | available space (this is the default for numbers). |
+---------+----------------------------------------------------------+
| ``'='`` | Forces the padding to be placed after the sign (if any) |
| | but before the digits. This is used for printing fields |
| | in the form '+000000120'. This alignment option is only |
| | valid for numeric types. |
+---------+----------------------------------------------------------+
| ``'^'`` | Forces the field to be centered within the available |
| | space. |
+---------+----------------------------------------------------------+
@@ -154,9 +149,11 @@ conversions, trailing zeros are not removed from the result.
*width* is a decimal integer defining the minimum field width. If not
specified, then the field width will be determined by the content.
Preceding the *width* field by a zero (``'0'``) character enables
sign-aware zero-padding for numeric types. This is equivalent to a *fill*
character of ``'0'`` with an *alignment* type of ``'='``.
Preceding the *width* field by a zero (``'0'``) character enables sign-aware
zero-padding for numeric types. It forces the padding to be placed after the
sign or base (if any) but before the digits. This is used for printing fields in
the form '+000000120'. This option is only valid for numeric types and it has no
effect on formatting of infinity and NaN.
The *precision* is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted with
@@ -264,6 +261,10 @@ The available presentation types for floating-point values are:
| | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
+---------+----------------------------------------------------------+
| ``'%'`` | Fixed point as a percentage. This is similar to ``'f'``, |
| | but the argument is multiplied by 100 and a percent sign |
| | ``%`` is appended. |

View File

@@ -103,7 +103,17 @@ the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documentation in ``doc/html``.
Conda
=====
fmt can be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__, using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows::
conda install -c conda-forge fmt
Android NDK
===========

View File

@@ -16,16 +16,291 @@
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
@@ -403,7 +678,7 @@ inline bool isfinite(T value) {
return std::isfinite(value);
}
// Convers value to int and checks that it's in the range [0, upper).
// Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
@@ -582,8 +857,8 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_t<int> n = to_unsigned(
to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
@@ -728,7 +1003,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@@ -738,8 +1013,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id);
return arg_ref_type(str_val);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
@@ -750,7 +1024,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; }
void on_precision(unsigned _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
@@ -762,13 +1036,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
using iterator = typename basic_parse_context<Char>::iterator;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
@@ -789,7 +1063,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
@@ -806,10 +1080,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
specs.width, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin());
precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);

View File

@@ -299,15 +299,15 @@ class text_style {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@@ -470,58 +470,41 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
auto background =
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buffer, format_str, args);
vformat_to(buf, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
internal::reset_color<Char>(buf);
}
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/**
@@ -536,7 +519,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >;
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
@@ -554,11 +537,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
@@ -573,11 +558,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S> >
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return internal::vformat(ts, to_string_view(format_str),
{internal::make_args_checked(format_str, args...)});
return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE

View File

@@ -14,250 +14,44 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
public:
struct named_argument_id {
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
: id(id) {}
internal::string_view_metadata id;
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
struct argument_id {
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
FMT_CONSTEXPR argument_id(unsigned id)
: which(which_arg_id::index), val(id) {}
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
: which(which_arg_id::named_index), val(id) {}
enum class which_arg_id { index, named_index };
which_arg_id which;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(internal::string_view_metadata id)
: named_index(id) {}
unsigned index;
internal::string_view_metadata named_index;
} val;
};
struct specification {
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
argument_id arg_id;
internal::dynamic_format_specs<Char> parsed_specs;
};
FMT_CONSTEXPR format_part()
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
: which(kind::text), end_of_argument_id(0u), val(text) {}
FMT_CONSTEXPR format_part(unsigned id)
: which(kind::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
FMT_CONSTEXPR format_part(specification spec)
: which(kind::specification), end_of_argument_id(0u), val(spec) {}
enum class kind { argument_id, named_argument_id, text, specification };
kind which;
std::size_t end_of_argument_id;
kind part_kind;
union value {
FMT_CONSTEXPR value() : arg_id(0u) {}
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR value(named_argument_id named_id)
: named_arg_id(named_id.id) {}
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
FMT_CONSTEXPR value(specification s) : spec(s) {}
unsigned arg_id;
internal::string_view_metadata named_arg_id;
internal::string_view_metadata text;
specification spec;
unsigned arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
};
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
template <typename Char, typename PartsContainer>
class format_preparation_handler : public internal::error_handler {
private:
using part = format_part<Char>;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
public:
using iterator = typename basic_string_view<Char>::iterator;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin == end) return;
const auto offset = begin - format_.data();
const auto size = end - begin;
parts_.push_back(part(string_view_metadata(offset, size)));
static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
return format_part(kind::arg_index, index);
}
FMT_CONSTEXPR void on_arg_id() {
parts_.push_back(part(parse_context_.next_arg_id()));
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
parts_.push_back(part(id));
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
const auto view = string_view_metadata(format_, id);
const auto arg_id = typename part::named_argument_id(view);
parts_.push_back(part(arg_id));
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
parts_.back().end_of_argument_id = ptr - format_.begin();
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
const auto specs_offset = to_unsigned(begin - format_.begin());
using parse_context = basic_parse_context<Char>;
internal::dynamic_format_specs<Char> parsed_specs;
dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler);
if (*begin != '}') on_error("missing '}' in format string");
auto& last_part = parts_.back();
auto specs = last_part.which == part::kind::argument_id
? typename part::specification(last_part.val.arg_id)
: typename part::specification(last_part.val.named_arg_id);
specs.parsed_specs = parsed_specs;
last_part = part(specs);
last_part.end_of_argument_id = specs_offset;
return begin;
}
private:
PartsContainer& parts_;
basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_;
};
template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format {
public:
using char_type = char_t<Format>;
using format_part_t = format_part<char_type>;
constexpr prepared_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete;
using context = buffer_context<char_type>;
template <typename Range, typename Context>
auto vformat_to(Range out, basic_format_args<Context> args) const ->
typename Context::iterator {
const auto format_view = internal::to_string_view(format_);
basic_parse_context<char_type> parse_ctx(format_view);
Context ctx(out.begin(), args);
const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
switch (part.which) {
case format_part_t::kind::text: {
const auto text = value.text.to_view(format_view.data());
auto output = ctx.out();
auto&& it = internal::reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
} break;
case format_part_t::kind::argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(parse_ctx, ctx, value.arg_id);
} break;
case format_part_t::kind::named_argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id =
value.named_arg_id.to_view(format_view.data());
format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break;
case format_part_t::kind::specification: {
const auto& arg_id_value = value.spec.arg_id.val;
const auto arg = value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.named_index.to_view(
to_string_view(format_).data()));
auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(
specs.width, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx, format_view.begin());
check_prepared_specs(specs, arg.type());
advance_parse_context_to_specification(parse_ctx, part);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
} break;
}
}
return ctx.out();
}
private:
void advance_parse_context_to_specification(
basic_parse_context<char_type>& parse_ctx,
const format_part_t& part) const {
const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id;
advance_to(parse_ctx, specification_begin);
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
Id arg_id) const {
parse_ctx.check_arg_id(arg_id);
const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
ctx.advance_to(stopped_at);
}
template <typename Char>
void check_prepared_specs(const basic_format_specs<Char>& specs,
internal::type arg_type) const {
internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
}
private:
Format format_;
PreparedPartsProvider parts_provider_;
};
template <typename Char> struct part_counter {
@@ -276,13 +70,13 @@ template <typename Char> struct part_counter {
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned braces_counter = 0;
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++braces_counter;
++brace_counter;
} else if (*begin == '}') {
if (braces_counter == 0u) break;
--braces_counter;
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
@@ -291,156 +85,486 @@ template <typename Char> struct part_counter {
FMT_CONSTEXPR void on_error(const char*) {}
};
template <typename Format> class compiletime_prepared_parts_type_provider {
private:
using char_type = char_t<Format>;
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
static FMT_CONSTEXPR unsigned count_parts() {
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
part_counter<char_type> counter;
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, counter);
return counter.num_parts;
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
// Workaround for old compilers. Compiletime parts preparation will not be
// performed with them anyway.
FMT_CONSTEXPR void on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
compiletime_prepared_parts_type_provider::count_parts();
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned number_of_format_parts = 0u;
static const unsigned num_format_parts = 1;
#endif
public:
template <unsigned N> struct format_parts_array {
using value_type = format_part<char_type>;
using parts_container = format_part<char_type>[num_format_parts];
FMT_CONSTEXPR format_parts_array() : arr{} {}
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
FMT_CONSTEXPR const value_type* begin() const { return arr; }
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
private:
value_type arr[N];
};
struct empty {
// Parts preparator will search for it
using value_type = format_part<char_type>;
};
using type = conditional_t<number_of_format_parts != 0,
format_parts_array<number_of_format_parts>, empty>;
};
template <typename Parts> class compiletime_prepared_parts_collector {
private:
using format_part = typename Parts::value_type;
public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
: parts_{parts}, counter_{0u} {}
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
private:
Parts& parts_;
unsigned counter_;
};
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) {
using collector = compiletime_prepared_parts_collector<PartsContainer>;
PartsContainer parts;
collector c(parts);
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
format, format_preparation_handler<Char, collector>(format, c));
return parts;
}
template <typename PartsContainer> class runtime_parts_provider {
public:
runtime_parts_provider() = delete;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {}
const PartsContainer& parts() const { return parts_; }
private:
PartsContainer parts_;
};
template <typename Format, typename PartsContainer>
struct compiletime_parts_provider {
compiletime_parts_provider() = delete;
template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{}));
return prepared_parts;
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format<
S,
internal::compiletime_parts_provider<
S,
typename internal::compiletime_prepared_parts_type_provider<S>::type>,
Args...> {
return format_str;
}
#endif
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) -> internal::prepared_format<
std::basic_string<Char>,
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
Args...> {
return std::basic_string<Char>(format_str, N - 1);
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type>
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = internal::buffer_range<Char>;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
cf.template vformat_to<range, context>(range(buffer),
{make_format_args<context>(args...)});
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return cf.template vformat_to<range, context>(
range(out), {make_format_args<context>(args...)});
return internal::cf::vformat_to<context>(
range(out), cf, {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
@@ -455,10 +579,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return fmt::format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
return format_to(internal::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE

View File

@@ -8,7 +8,6 @@
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
#include <cassert>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
@@ -16,7 +15,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60000
#define FMT_VERSION 60100
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@@ -49,6 +48,12 @@
# define FMT_HAS_GXX_CXX11 0
#endif
#ifdef __NVCC__
# define FMT_NVCC __NVCC__
#else
# define FMT_NVCC 0
#endif
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
#else
@@ -60,7 +65,8 @@
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
@@ -133,6 +139,13 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
@@ -154,9 +167,9 @@
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
# define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport)
# define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllimport)
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
#endif
@@ -173,10 +186,6 @@
# define FMT_EXTERN
#endif
#ifndef FMT_ASSERT
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@@ -200,6 +209,8 @@ template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
struct monostate {};
@@ -213,6 +224,19 @@ namespace internal {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
void assert_fail(const char* file, int line, const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
@@ -222,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
template <typename T> struct std_string_view {};
#endif
// Casts nonnegative integer to unsigned.
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
#else
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
struct int128_t {};
struct uint128_t {};
#endif
// Casts a 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");
@@ -266,10 +304,11 @@ template <typename Char> class basic_string_view {
: data_(s), size_(std::char_traits<Char>::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.data()),
size_(s.size()) {}
template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view(
const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
: data_(s.data()),
size_(s.size()) {}
template <
typename S,
@@ -286,6 +325,8 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR iterator begin() const { return data_; }
FMT_CONSTEXPR iterator end() const { return data_ + size_; }
FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
FMT_CONSTEXPR void remove_prefix(size_t n) {
data_ += n;
size_ -= n;
@@ -357,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
return s;
}
template <typename Char, typename Traits, typename Allocator>
template <typename Char, typename Traits, typename Alloc>
inline basic_string_view<Char> to_string_view(
const std::basic_string<Char, Traits, Allocator>& s) {
return {s.data(), s.size()};
const std::basic_string<Char, Traits, Alloc>& s) {
return s;
}
template <typename Char>
@@ -405,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
};
struct error_handler {
FMT_CONSTEXPR error_handler() {}
FMT_CONSTEXPR error_handler(const error_handler&) {}
FMT_CONSTEXPR error_handler() = default;
FMT_CONSTEXPR error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void on_error(const char* message);
@@ -416,10 +457,24 @@ struct error_handler {
/** String's character type. */
template <typename S> using char_t = typename internal::char_t_impl<S>::type;
// Parsing context consisting of a format string range being parsed and an
// argument counter for automatic indexing.
/**
\rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
You can use one of the following type aliases for common character types:
+-----------------------+-------------------------------------+
| Type | Definition |
+=======================+=====================================+
| format_parse_context | basic_format_parse_context<char> |
+-----------------------+-------------------------------------+
| wformat_parse_context | basic_format_parse_context<wchar_t> |
+-----------------------+-------------------------------------+
\endrst
*/
template <typename Char, typename ErrorHandler = internal::error_handler>
class basic_parse_context : private ErrorHandler {
class basic_format_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
int next_arg_id_;
@@ -428,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
ErrorHandler eh = ErrorHandler())
explicit FMT_CONSTEXPR basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
// Returns an iterator to the beginning of the format string range being
// parsed.
/**
Returns an iterator to the beginning of the format string range being
parsed.
*/
FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
return format_str_.begin();
}
// Returns an iterator past the end of the format string range being parsed.
/**
Returns an iterator past the end of the format string range being parsed.
*/
FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
// Advances the begin iterator to ``it``.
/** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
format_str_.remove_prefix(internal::to_unsigned(it - begin()));
}
// Returns the next argument index.
/**
Reports an error if using the manual argument indexing; otherwise returns
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR int next_arg_id() {
if (next_arg_id_ >= 0) return next_arg_id_++;
on_error("cannot switch from manual to automatic argument indexing");
return 0;
}
FMT_CONSTEXPR bool check_arg_id(int) {
if (next_arg_id_ > 0) {
/**
Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
FMT_CONSTEXPR void check_arg_id(int) {
if (next_arg_id_ > 0)
on_error("cannot switch from automatic to manual argument indexing");
return false;
}
next_arg_id_ = -1;
return true;
else
next_arg_id_ = -1;
}
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
@@ -471,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
};
using format_parse_context = basic_parse_context<char>;
using wformat_parse_context = basic_parse_context<wchar_t>;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using parse_context FMT_DEPRECATED = basic_parse_context<char>;
using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>;
template <typename Char, typename ErrorHandler = internal::error_handler>
using basic_parse_context FMT_DEPRECATED_ALIAS =
basic_format_parse_context<Char, ErrorHandler>;
using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
@@ -492,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
: bool_constant<!std::is_arithmetic<T>::value &&
std::is_convertible<T, int>::value> {};
namespace internal {
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
namespace internal {
/** A contiguous memory buffer with an optional growing ability. */
template <typename T> class buffer {
private:
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
T* ptr_;
std::size_t size_;
std::size_t capacity_;
@@ -532,7 +596,9 @@ template <typename T> class buffer {
using value_type = T;
using const_reference = const T&;
virtual ~buffer() {}
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@@ -626,10 +692,13 @@ enum type {
uint_type,
long_long_type,
ulong_long_type,
int128_type,
uint128_type,
bool_type,
char_type,
last_integer_type = char_type,
// followed by floating-point types.
float_type,
double_type,
long_double_type,
last_numeric_type = long_double_type,
@@ -652,20 +721,23 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(int128_t, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral(type t) {
FMT_CONSTEXPR bool is_integral_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_integer_type;
}
FMT_CONSTEXPR bool is_arithmetic(type t) {
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_numeric_type;
}
@@ -676,7 +748,7 @@ template <typename Char> struct string_value {
};
template <typename Context> struct custom_value {
using parse_context = basic_parse_context<typename Context::char_type>;
using parse_context = basic_format_parse_context<typename Context::char_type>;
const void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
};
@@ -691,8 +763,11 @@ template <typename Context> class value {
unsigned uint_value;
long long long_long_value;
unsigned long long ulong_long_value;
int128_t int128_value;
uint128_t uint128_value;
bool bool_value;
char_type char_value;
float float_value;
double double_value;
long double long_double_value;
const void* pointer;
@@ -705,6 +780,9 @@ template <typename Context> class value {
FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) : long_long_value(val) {}
value(unsigned long long val) : ulong_long_value(val) {}
value(int128_t val) : int128_value(val) {}
value(uint128_t val) : uint128_value(val) {}
value(float val) : float_value(val) {}
value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {}
value(bool val) : bool_value(val) {}
@@ -732,9 +810,9 @@ template <typename Context> class value {
private:
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
static void format_custom_arg(const void* arg,
basic_parse_context<char_type>& parse_ctx,
Context& ctx) {
static void format_custom_arg(
const void* arg, basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) {
Formatter f;
parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
@@ -764,6 +842,8 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
FMT_CONSTEXPR long long map(long long val) { return val; }
FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
FMT_CONSTEXPR bool map(bool val) { return val; }
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
@@ -774,7 +854,7 @@ template <typename Context> struct arg_mapper {
return val;
}
FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); }
FMT_CONSTEXPR float map(float val) { return val; }
FMT_CONSTEXPR double map(double val) { return val; }
FMT_CONSTEXPR long double map(long double val) { return val; }
@@ -793,6 +873,15 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
template <
typename T,
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
FMT_CONSTEXPR const char* map(const signed char* val) {
static_assert(std::is_same<char_type, char>::value, "invalid string type");
return reinterpret_cast<const char*>(val);
@@ -818,11 +907,14 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR int map(const T& val) {
return static_cast<int>(val);
FMT_CONSTEXPR auto map(const T& val) -> decltype(
map(static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>,
T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
@@ -841,12 +933,13 @@ template <typename Context> struct arg_mapper {
// A type constant after applying arg_mapper<Context>.
template <typename T, typename Context>
using mapped_type_constant =
type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())),
type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>;
enum { packed_arg_bits = 5 };
// Maximum number of arguments with packed types.
enum { max_packed_args = 15 };
enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
enum { max_packed_args = 63 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
template <typename Context> class arg_map;
} // namespace internal
@@ -877,7 +970,8 @@ template <typename Context> class basic_format_arg {
public:
explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const {
void format(basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx);
}
@@ -893,8 +987,8 @@ template <typename Context> class basic_format_arg {
internal::type type() const { return type_; }
bool is_integral() const { return internal::is_integral(type_); }
bool is_arithmetic() const { return internal::is_arithmetic(type_); }
bool is_integral() const { return internal::is_integral_type(type_); }
bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
};
/**
@@ -923,10 +1017,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(arg.value_.long_long_value);
case internal::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
case internal::int128_type:
return vis(arg.value_.int128_value);
case internal::uint128_type:
return vis(arg.value_.uint128_value);
#else
case internal::int128_type:
case internal::uint128_type:
break;
#endif
case internal::bool_type:
return vis(arg.value_.bool_value);
case internal::char_type:
return vis(arg.value_.char_value);
case internal::float_type:
return vis(arg.value_.float_value);
case internal::double_type:
return vis(arg.value_.double_value);
case internal::long_double_type:
@@ -948,9 +1054,6 @@ namespace internal {
// A map from argument names to their values for named arguments.
template <typename Context> class arg_map {
private:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
using char_type = typename Context::char_type;
struct entry {
@@ -968,6 +1071,8 @@ template <typename Context> class arg_map {
}
public:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
arg_map() : map_(nullptr), size_(0) {}
void init(const basic_format_args<Context>& args);
~arg_map() { delete[] map_; }
@@ -990,6 +1095,8 @@ class locale_ref {
locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
template <typename Locale> Locale get() const;
};
@@ -998,7 +1105,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value |
(encode_types<Context, Args...>() << 4);
(encode_types<Context, Args...>() << packed_arg_bits);
}
template <typename Context, typename T>
@@ -1034,14 +1141,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal::arg_map<basic_format_context> map_;
internal::locale_ref loc_;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
public:
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>;
template <typename T> using formatter_type = formatter<T, char_type>;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
/**
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
@@ -1143,8 +1249,9 @@ template <typename Context> class basic_format_args {
bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
internal::type type(int index) const {
int shift = index * 4;
return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
int shift = index * internal::packed_arg_bits;
unsigned int mask = (1 << internal::packed_arg_bits) - 1;
return static_cast<internal::type>((types_ >> shift) & mask);
}
friend class internal::arg_map<Context>;
@@ -1371,7 +1478,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
}
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
FMT_API void vprint(string_view format_str, format_args args);
/**
\rst
@@ -1391,9 +1498,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_API void vprint(string_view format_str, format_args args);
FMT_API void vprint(wstring_view format_str, wformat_args args);
/**
\rst
Prints formatted data to ``stdout``.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
@@ -56,9 +60,9 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype((void)(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
std::true_type())
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
@@ -75,8 +79,7 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size =
to_unsigned((std::numeric_limits<std::streamsize>::max)());
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
@@ -86,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
@@ -101,7 +106,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}

View File

@@ -13,11 +13,10 @@
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
@@ -27,6 +26,18 @@
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
@@ -54,8 +65,8 @@
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
@@ -132,16 +143,15 @@ class buffered_file {
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
@@ -177,6 +187,7 @@ class buffered_file {
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
@@ -204,14 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
public:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -260,6 +270,7 @@ class file {
// Returns the memory page size.
long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
@@ -283,11 +294,10 @@ class Locale {
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));

View File

@@ -1,4 +1,4 @@
// Formatting library for C++
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
@@ -16,15 +16,11 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max();
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
@@ -33,7 +29,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
@@ -158,12 +154,12 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value);
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
@@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@@ -332,14 +328,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
@@ -361,15 +357,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
format_arg arg(unsigned id) const { return args_.get(id); }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter =
printf_arg_formatter<internal::buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format();
};
@@ -403,7 +398,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max())
if (arg_index == internal::max_value<unsigned>())
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
@@ -413,7 +408,7 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
unsigned arg_index = internal::max_value<unsigned>();
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
@@ -470,6 +465,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
if (it != end && *it == '.') {

View File

@@ -246,7 +246,8 @@ template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value;
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
@@ -283,6 +284,82 @@ struct formatter<RangeT, Char,
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@@ -1,293 +0,0 @@
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (from < 0) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 =
std::numeric_limits<IntermediateRep>::max() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 =
std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
FMT_END_NAMESPACE

View File

@@ -10,9 +10,10 @@
FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned,
int&) = internal::grisu_format;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
@@ -21,6 +22,7 @@ template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
@@ -35,23 +37,27 @@ template FMT_API std::string internal::vformat<char>(
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context>&);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

View File

@@ -12,7 +12,9 @@
#include "fmt/posix.h"
#include <limits.h>
#include <climits>
#if FMT_USE_FCNTL
#include <sys/stat.h>
#include <sys/types.h>
@@ -39,8 +41,8 @@
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef fileno
# undef fileno
@@ -49,7 +51,7 @@
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
@@ -58,7 +60,7 @@ inline unsigned convert_rwcount(std::size_t count) {
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
@@ -94,6 +96,7 @@ int buffered_file::fileno() const {
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
@@ -138,7 +141,7 @@ long long file::size() const {
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
@@ -230,4 +233,5 @@ long getpagesize() {
return size;
#endif
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

2
support/Vagrantfile vendored
View File

@@ -3,7 +3,7 @@
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.box = "ubuntu/xenial64"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"

View File

@@ -1,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: fmt
Description: A modern formatting library

View File

@@ -47,8 +47,6 @@ target_compile_definitions(gmock
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)
@@ -84,6 +82,9 @@ function(add_fmt_test name)
if (FMT_PEDANTIC)
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (FMT_WERROR)
target_compile_options(${name} PRIVATE ${WERROR_FLAG})
endif ()
target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
add_test(NAME ${name} COMMAND ${name})
endfunction()
@@ -96,6 +97,9 @@ add_fmt_test(grisu-test)
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
add_fmt_test(gtest-extra-test)
add_fmt_test(format-test mock-allocator.h)
if (MSVC)
target_compile_options(format-test PRIVATE /bigobj)
endif ()
if (NOT (MSVC AND BUILD_SHARED_LIBS))
add_fmt_test(format-impl-test)
endif ()
@@ -107,12 +111,11 @@ add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
if (HAVE_OPEN)
if (NOT MSVC_BUILD_STATIC)
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock)
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
if (FMT_PEDANTIC)
@@ -225,3 +228,21 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()
# Activate optional CUDA tests if CUDA is found. For version selection, see
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()

View File

@@ -20,3 +20,14 @@ TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"),
"don't panic!");
}
bool test_condition = false;
TEST(AssertTest, DanglingElse) {
bool executed_else = false;
if (test_condition)
FMT_ASSERT(true, "");
else
executed_else = true;
EXPECT_TRUE(executed_else);
}

View File

@@ -47,7 +47,7 @@ TEST(ColorsTest, ColorsPrint) {
"\x1b[105mtbmagenta\x1b[0m");
}
TEST(ColorsTest, ColorsFormat) {
TEST(ColorsTest, Format) {
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
@@ -78,4 +78,6 @@ TEST(ColorsTest, ColorsFormat) {
"\x1b[92mtbgreen\x1b[0m");
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
"\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m");
}

View File

@@ -34,205 +34,17 @@
using testing::Return;
using testing::StrictMock;
FMT_BEGIN_NAMESPACE
namespace internal {
bool operator==(const internal::string_view_metadata& lhs,
const internal::string_view_metadata& rhs) {
return std::tie(lhs.offset_, lhs.size_) == std::tie(rhs.offset_, rhs.size_);
}
bool operator!=(const internal::string_view_metadata& lhs,
const internal::string_view_metadata& rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char>::specification& lhs,
const format_part<char>::specification& rhs) {
if (lhs.arg_id.which != rhs.arg_id.which) {
return false;
}
typedef format_part<char>::argument_id::which_arg_id which_arg_id;
switch (lhs.arg_id.which) {
case which_arg_id::index: {
if (lhs.arg_id.val.index != rhs.arg_id.val.index) {
return false;
}
} break;
case which_arg_id::named_index: {
if (lhs.arg_id.val.named_index != rhs.arg_id.val.named_index) {
return false;
}
} break;
}
return std::tie(lhs.parsed_specs.width, lhs.parsed_specs.fill[0],
lhs.parsed_specs.align, lhs.parsed_specs.precision,
lhs.parsed_specs.sign, lhs.parsed_specs.type) ==
std::tie(rhs.parsed_specs.width, rhs.parsed_specs.fill[0],
rhs.parsed_specs.align, rhs.parsed_specs.precision,
rhs.parsed_specs.sign, rhs.parsed_specs.type);
}
bool operator!=(const format_part<char>::specification& lhs,
const format_part<char>::specification& rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char>& lhs,
const fmt::internal::format_part<char>& rhs) {
typedef format_part<char>::kind kind;
if (lhs.which != rhs.which ||
lhs.end_of_argument_id != rhs.end_of_argument_id) {
return false;
}
switch (lhs.which) {
case kind::argument_id: {
return lhs.val.arg_id == rhs.val.arg_id;
}
case kind::named_argument_id: {
return lhs.val.named_arg_id == rhs.val.named_arg_id;
}
case kind::text: {
return lhs.val.text == rhs.val.text;
}
case kind::specification: {
return lhs.val.spec == rhs.val.spec;
}
}
return false;
}
bool operator!=(const fmt::internal::format_part<char>& lhs,
const fmt::internal::format_part<char>& rhs) {
return !(lhs == rhs);
}
}
FMT_END_NAMESPACE
TEST(CompileTest, FormatPart_ComparisonOperators) {
typedef fmt::internal::format_part<char> format_part;
typedef fmt::internal::dynamic_format_specs<char> prepared_specs;
{
const auto part = format_part(0u);
const auto other = format_part(0u);
EXPECT_EQ(part, other);
}
{
const auto lhs = format_part(0u);
const auto rhs = format_part(1u);
EXPECT_NE(lhs, rhs);
}
{
const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42));
const auto rhs = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_EQ(lhs, rhs);
}
{
const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42));
const auto rhs = format_part(fmt::internal::string_view_metadata(0, 4422));
EXPECT_NE(lhs, rhs);
}
{
auto lhs = format_part(0u);
auto rhs = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_NE(lhs, rhs);
rhs = format_part(fmt::internal::string_view_metadata(0, 0));
EXPECT_NE(lhs, rhs);
}
{
auto lhs = format_part(0u);
lhs.end_of_argument_id = 42;
auto rhs = format_part(0u);
rhs.end_of_argument_id = 42;
EXPECT_EQ(lhs, rhs);
rhs.end_of_argument_id = 13;
EXPECT_NE(lhs, rhs);
}
{
const auto specs_argument_id = 0u;
const auto specs_named_argument_id =
fmt::internal::string_view_metadata(0, 42);
auto specs = format_part::specification(0u);
auto lhs = format_part(specs);
auto rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs = format_part::specification(specs_named_argument_id);
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
auto lhs_spec = format_part::specification(specs_argument_id);
auto rhs_spec = format_part::specification(specs_named_argument_id);
lhs = format_part(lhs_spec);
rhs = format_part(rhs_spec);
EXPECT_NE(lhs, rhs);
lhs_spec = format_part::specification(specs_argument_id);
rhs_spec = format_part::specification(specs_argument_id);
lhs_spec.parsed_specs.precision = 1;
rhs_spec.parsed_specs.precision = 2;
lhs = format_part(lhs_spec);
rhs = format_part(rhs_spec);
EXPECT_NE(lhs, rhs);
}
{
const auto specs_argument_id = 0u;
const auto specs_named_argument_id =
fmt::internal::string_view_metadata(0, 42);
auto specs = format_part::specification(specs_argument_id);
auto lhs = format_part(specs);
auto rhs = format_part(0u);
auto rhs2 = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs.parsed_specs = prepared_specs();
lhs = format_part{specs};
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs = format_part::specification(specs_named_argument_id);
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
}
}
// compiletime_prepared_parts_type_provider is useful only with relaxed
// constexpr.
#if FMT_USE_CONSTEXPR
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
void check_prepared_parts_type(Format format) {
typedef fmt::internal::compiletime_prepared_parts_type_provider<decltype(
format)>
provider;
typedef typename provider::template format_parts_array<EXPECTED_PARTS_COUNT>
expected_parts_type;
static_assert(
std::is_same<typename provider::type, expected_parts_type>::value,
"CompileTimePreparedPartsTypeProvider test failed");
typedef fmt::internal::compiled_format_base<decltype(format)> provider;
typedef fmt::internal::format_part<char>
expected_parts_type[EXPECTED_PARTS_COUNT];
static_assert(std::is_same<typename provider::parts_container,
expected_parts_type>::value,
"CompileTimePreparedPartsTypeProvider test failed");
}
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
@@ -253,40 +65,6 @@ TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
}
#endif
class custom_parts_container {
public:
typedef fmt::internal::format_part<char> format_part_type;
private:
typedef std::deque<format_part_type> parts;
public:
void add(format_part_type part) { parts_.push_back(std::move(part)); }
void substitute_last(format_part_type part) {
parts_.back() = std::move(part);
}
format_part_type last() { return parts_.back(); }
auto begin() -> decltype(std::declval<parts>().begin()) {
return parts_.begin();
}
auto begin() const -> decltype(std::declval<const parts>().begin()) {
return parts_.begin();
}
auto end() -> decltype(std::declval<parts>().begin()) { return parts_.end(); }
auto end() const -> decltype(std::declval<const parts>().begin()) {
return parts_.end();
}
private:
parts parts_;
};
TEST(CompileTest, PassStringLiteralFormat) {
const auto prepared = fmt::compile<int>("test {}");
EXPECT_EQ("test 42", fmt::format(prepared, 42));
@@ -338,3 +116,28 @@ TEST(CompileTest, FormattedSize) {
auto f = fmt::compile<int>("{:10}");
EXPECT_EQ(fmt::formatted_size(f, 42), 10);
}
TEST(CompileTest, MultipleTypes) {
auto f = fmt::compile<int, int>("{} {}");
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
}
struct formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<formattable> : formatter<const char*> {
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
return formatter<const char*>::format("foo", ctx);
}
};
FMT_END_NAMESPACE
TEST(CompileTest, FormatUserDefinedType) {
auto f = fmt::compile<formattable>("{}");
EXPECT_EQ(fmt::format(f, formattable()), "foo");
}
TEST(CompileTest, EmptyFormatString) {
auto f = fmt::compile<>("");
EXPECT_EQ(fmt::format(f), "");
}

View File

@@ -103,7 +103,7 @@ template <typename T> struct mock_buffer : buffer<T> {
TEST(BufferTest, Ctor) {
{
mock_buffer<int> buffer;
EXPECT_EQ(nullptr, &buffer[0]);
EXPECT_EQ(nullptr, buffer.data());
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
@@ -127,8 +127,13 @@ TEST(BufferTest, Ctor) {
struct dying_buffer : test_buffer<int> {
MOCK_METHOD0(die, void());
~dying_buffer() { die(); }
private:
virtual void avoid_weak_vtable();
};
void dying_buffer::avoid_weak_vtable() {}
TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer;
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
@@ -285,8 +290,6 @@ 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; \
@@ -450,11 +453,11 @@ template <> struct formatter<enabled_formatter> {
FMT_END_NAMESPACE
TEST(CoreTest, HasFormatter) {
using fmt::internal::has_formatter;
using fmt::has_formatter;
using context = fmt::format_context;
EXPECT_TRUE((has_formatter<enabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter_convertible, context>::value));
static_assert(has_formatter<enabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter_convertible, context>::value, "");
}
struct convertible_to_int {
@@ -613,6 +616,17 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
#endif
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
@@ -621,17 +635,15 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
#endif
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
struct disabled_rvalue_conversion {
operator const char*() const& { return "foo"; }
operator const char*()& { return "foo"; }
operator const char*() const&& = delete;
operator const char*()&& = delete;
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
TEST(FormatterTest, DisabledRValueConversion) {
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
}
#endif

View File

@@ -0,0 +1,73 @@
# We can find some usecases which follow the guide of CMake which uses
# `enable_language(CUDA)` instead of `find_package(CUDA)` and let the CMake
# built-in functions use NVCC.
# See: https://cmake.org/cmake/help/latest/module/FindCUDA.html#replacement
#
# However, this requires CMake version 3.10 or higher and we can't be sure most
# of the CUDA projects are using those.
#
# This test relies on `find_package(CUDA)` in the parent CMake config.
# These can be updated when NVCC becomes ready for C++ 17 features
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
set(CMAKE_CUDA_STANDARD 14)
set(CMAKE_CUDA_STANDARD_REQUIRED 14)
# In this test, we assume that the user is going to compile CUDA source code
# with some libraries (fmt in this case).
#
# In addition to that, this test invokes both the C++ host compiler and NVCC
# by providing another (non-CUDA) C++ source code.
if (${CMAKE_VERSION} VERSION_LESS 3.15)
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html
list(APPEND CUDA_NVCC_FLAGS "-std=c++14")
if (MSVC)
# This is the solution of pytorch:
# https://github.com/pytorch/pytorch/pull/7118
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/std:c++14")
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/Zc:__cplusplus")
# for the reason of this -Xcompiler options, see below.
endif ()
cuda_add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
if (MSVC)
# This part is for (non-CUDA) C++ code. MSVC can define incorrect
# `__cplusplus` macro. Fix for the issue is to use additional compiler flag.
#
# See Also:
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
# https://github.com/Microsoft/vscode-cpptools/issues/2595
target_compile_options(fmt-in-cuda-test PRIVATE /Zc:__cplusplus /permissive-)
endif ()
else()
# now using a "new" way of handling CUDA
add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
set_target_properties(fmt-in-cuda-test PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
if (MSVC)
# with MSVC, 'cxx_std_14' will only propagate to the host code (MSVC), but will
# not set __cplusplus correctly anyway, while nvcc will ignore it.
# If specified for nvcc on the command line as '-std=c++14' nvcc will emit this
# message instead:
# nvcc warning : The -std=c++14 flag is not supported with the configured host
# compiler. Flag will be ignored.
set_property(SOURCE cuda-cpp14.cu APPEND PROPERTY
COMPILE_OPTIONS -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus)
set_property(SOURCE cpp14.cc APPEND PROPERTY
COMPILE_OPTIONS /std:c++14 /Zc:__cplusplus)
endif()
endif()
get_target_property(IN_USE_CUDA_STANDARD fmt-in-cuda-test CUDA_STANDARD)
message(STATUS "cuda_standard: ${IN_USE_CUDA_STANDARD}")
get_target_property(IN_USE_CUDA_STANDARD_REQUIRED
fmt-in-cuda-test CUDA_STANDARD_REQUIRED)
message(STATUS "cuda_standard_required: ${IN_USE_CUDA_STANDARD_REQUIRED}")
# We don't use PUBLIC or other keyword for reasons explained in the
# CUDA_LINK_LIBRARIES_KEYWORD section in
# https://cmake.org/cmake/help/latest/module/FindCUDA.html
target_link_libraries(fmt-in-cuda-test fmt::fmt)

11
test/cuda-test/cpp14.cc Normal file
View File

@@ -0,0 +1,11 @@
#include <fmt/core.h>
// The purpose of this part is to ensure NVCC's host compiler also supports
// the standard version. See 'cuda-cpp14.cu'.
//
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for host compiler");
auto make_message_cpp() -> std::string {
return fmt::format("host compiler \t: __cplusplus == {}", __cplusplus);
}

View File

@@ -0,0 +1,28 @@
// Direct NVCC command line example:
//
// nvcc ./cuda-cpp14.cu -x cu -I"../include" -l"fmtd" -L"../build/Debug" \
// -std=c++14 -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus
// Ensure that we are using the latest C++ standard for NVCC
// The version is C++14
//
// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-cplusplus-language-support
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for nvcc");
#include <fmt/core.h>
#include <cuda.h>
#include <iostream>
extern auto make_message_cpp() -> std::string;
extern auto make_message_cuda() -> std::string;
int main() {
std::cout << make_message_cuda() << std::endl;
std::cout << make_message_cpp() << std::endl;
}
auto make_message_cuda() -> std::string {
return fmt::format("nvcc compiler \t: __cplusplus == {}", __cplusplus);
}

View File

@@ -18,9 +18,9 @@
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class custom_arg_formatter
: public fmt::arg_formatter<fmt::internal::buffer_range<char>> {
: public fmt::arg_formatter<fmt::buffer_range<char>> {
public:
using range = fmt::internal::buffer_range<char>;
using range = fmt::buffer_range<char>;
typedef fmt::arg_formatter<range> base;
custom_arg_formatter(fmt::format_context& ctx,

View File

@@ -8,6 +8,7 @@
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <cassert>
#include <variant>
#include "fmt/format.h"
@@ -666,8 +667,7 @@ struct formatter {
break;
case internal::double_type:
case internal::long_double_type:
handle_float_type_spec(type_spec,
internal::float_type_checker<decltype(eh)>(eh));
internal::parse_float_type_spec(specs_, eh);
break;
case internal::cstring_type:
internal::handle_cstring_type_spec(
@@ -690,9 +690,9 @@ struct formatter {
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
specs_.width, specs_.width_ref, ctx, nullptr);
specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, nullptr);
specs_.precision, specs_.precision_ref, ctx);
using range_type = fmt::internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
@@ -718,7 +718,7 @@ template<class... Args>
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::internal::buffer<char>& buf = mbuf;
using range = fmt::internal::buffer_range<char>;
using range = fmt::buffer_range<char>;
detail::format_handler<detail::arg_formatter<range>, char, format_context>
h(range(std::back_inserter(buf)), fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);

View File

@@ -22,53 +22,232 @@
#undef max
using fmt::internal::bigint;
using fmt::internal::fp;
using fmt::internal::max_value;
template <bool is_iec559> void test_construct_from_double() {
static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
TEST(BigIntTest, Construct) {
EXPECT_EQ("", fmt::format("{}", bigint()));
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
}
TEST(BigIntTest, Compare) {
bigint n1(42);
bigint n2(42);
EXPECT_EQ(compare(n1, n2), 0);
n2 <<= 32;
EXPECT_LT(compare(n1, n2), 0);
bigint n3(43);
EXPECT_LT(compare(n1, n3), 0);
EXPECT_GT(compare(n3, n1), 0);
bigint n4(42 * 0x100000001);
EXPECT_LT(compare(n2, n4), 0);
EXPECT_GT(compare(n4, n2), 0);
}
TEST(BigIntTest, AddCompare) {
EXPECT_LT(
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
bigint(0x300000010)),
0);
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000000)),
0);
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000001)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000002)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000003)),
0);
}
TEST(BigIntTest, ShiftLeft) {
bigint n(0x42);
n <<= 0;
EXPECT_EQ("42", fmt::format("{}", n));
n <<= 1;
EXPECT_EQ("84", fmt::format("{}", n));
n <<= 25;
EXPECT_EQ("108000000", fmt::format("{}", n));
}
TEST(BigIntTest, Multiply) {
bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
TEST(BigIntTest, Accumulator) {
fmt::internal::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += fmt::internal::max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(BigIntTest, Square) {
bigint n0(0);
n0.square();
EXPECT_EQ("0", fmt::format("{}", n0));
bigint n1(0x100);
n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1));
bigint n2(0xfffffffff);
n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
bigint n3(max_value<uint64_t>());
n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
bigint n4;
n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4));
}
TEST(BigIntTest, DivModAssignZeroDivisor) {
bigint zero(0);
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
}
TEST(BigIntTest, DivModAssignSelf) {
bigint n(100);
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
}
TEST(BigIntTest, DivModAssignUnaligned) {
// (42 << 340) / pow(10, 100):
bigint n1(42);
n1 <<= 340;
bigint n2;
n2.assign_pow10(100);
int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
fmt::format("{}", n1));
}
TEST(BigIntTest, DivModAssign) {
// 100 / 10:
bigint n1(100);
int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1));
// pow(10, 100) / (42 << 320):
n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
fmt::format("{}", n1));
// 42 / 100:
bigint n2(42);
n1.assign_pow10(2);
result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0);
EXPECT_EQ("2a", fmt::format("{}", n2));
}
template <bool is_iec559> void run_double_tests() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <> void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
template <> void run_double_tests<true>() {
// Construct from double.
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
// Compute boundaries:
fp value;
// Normalized & not power of 2 - equidistant boundaries:
auto b = value.assign_with_boundaries(1.23);
EXPECT_EQ(value, fp(0x0013ae147ae147ae, -52));
EXPECT_EQ(b.lower, 0x9d70a3d70a3d6c00);
EXPECT_EQ(b.upper, 0x9d70a3d70a3d7400);
// Normalized power of 2 - lower boundary is closer:
b = value.assign_with_boundaries(1.9807040628566084e+28); // 2**94
EXPECT_EQ(value, fp(0x0010000000000000, 42));
EXPECT_EQ(b.lower, 0x7ffffffffffffe00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Smallest normalized double - equidistant boundaries:
b = value.assign_with_boundaries(2.2250738585072014e-308);
EXPECT_EQ(value, fp(0x0010000000000000, -1074));
EXPECT_EQ(b.lower, 0x7ffffffffffffc00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Subnormal - equidistant boundaries:
b = value.assign_with_boundaries(4.9406564584124654e-324);
EXPECT_EQ(value, fp(0x0000000000000001, -1074));
EXPECT_EQ(b.lower, 0x4000000000000000);
EXPECT_EQ(b.upper, 0xc000000000000000);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
TEST(FPTest, DoubleTests) {
run_double_tests<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
auto v = fp(0xbeef, 42);
v.normalize();
EXPECT_EQ(0xbeef000000000000, v.f);
EXPECT_EQ(-6, v.e);
const auto v = fp(0xbeef, 42);
auto normalized = normalize(v);
EXPECT_EQ(0xbeef000000000000, normalized.f);
EXPECT_EQ(-6, normalized.e);
}
TEST(FPTest, ComputeBoundariesSubnormal) {
auto v = fp(0xbeef, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0xbeee800000000000, lower.f);
EXPECT_EQ(-6, lower.e);
EXPECT_EQ(0xbeef800000000000, upper.f);
EXPECT_EQ(-6, upper.e);
}
TEST(FPTest, ComputeBoundaries) {
auto v = fp(0x10000000000000, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
EXPECT_EQ(31, lower.e);
EXPECT_EQ(0x8000000000000400, upper.f);
EXPECT_EQ(31, upper.e);
}
TEST(FPTest, Subtract) {
auto v = fp(123, 1) - fp(102, 1);
EXPECT_EQ(v.f, 21u);
EXPECT_EQ(v.e, 1);
TEST(FPTest, ComputeFloatBoundaries) {
struct {
double x, lower, upper;
} tests[] = {
// regular
{1.5f, 1.4999999403953552, 1.5000000596046448},
// boundary
{1.0f, 0.9999999701976776, 1.0000000596046448},
// min normal
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
// max subnormal
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
// min subnormal
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
};
for (auto test : tests) {
fp vlower = normalize(fp(test.lower));
fp vupper = normalize(fp(test.upper));
vlower.f >>= vupper.e - vlower.e;
vlower.e = vupper.e;
fp value;
auto b = value.assign_float_with_boundaries(test.x);
EXPECT_EQ(vlower.f, b.lower);
EXPECT_EQ(vupper.f, b.upper);
}
}
TEST(FPTest, Multiply) {
@@ -100,7 +279,7 @@ TEST(FPTest, GetRoundDirection) {
EXPECT_EQ(fmt::internal::up, get_round_direction(100, 60, 10));
for (int i = 41; i < 60; ++i)
EXPECT_EQ(fmt::internal::unknown, get_round_direction(100, i, 10));
uint64_t max = std::numeric_limits<uint64_t>::max();
uint64_t max = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
@@ -132,15 +311,14 @@ TEST(FPTest, FixedHandler) {
// Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
// Check that 2 * error doesn't overflow.
uint64_t max = std::numeric_limits<uint64_t>::max();
uint64_t max = max_value<uint64_t>();
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false),
digits::error);
}
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
int exp = 0;
grisu_format(4.2f, buf, -1, false, exp);
format_float(0.42, -1, fmt::internal::float_specs(), buf);
}
template <typename T> struct value_extractor {
@@ -149,10 +327,21 @@ template <typename T> struct value_extractor {
template <typename U> FMT_NORETURN T operator()(U) {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
}
#ifdef __apple_build_version__
// Apple Clang does not define typeid for __int128_t and __uint128_t.
FMT_NORETURN T operator()(__int128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__int128_t"));
}
FMT_NORETURN T operator()(__uint128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__uint128_t"));
}
#endif
};
TEST(FormatTest, ArgConverter) {
long long value = std::numeric_limits<long long>::max();
long long value = max_value<long long>();
auto arg = fmt::internal::make_arg<fmt::format_context>(value);
fmt::visit_format_arg(
fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
@@ -243,8 +432,7 @@ TEST(FormatTest, CountCodePoints) {
// 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) {
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10;
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
@@ -259,7 +447,7 @@ TEST(UtilTest, CountDigits) {
TEST(UtilTest, WriteUIntPtr) {
fmt::memory_buffer buf;
fmt::internal::writer writer(buf);
writer.write_pointer(fmt::internal::bit_cast<fmt::internal::fallback_uintptr>(
writer.write_pointer(fmt::internal::fallback_uintptr(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ("0xface", to_string(buf));

View File

@@ -20,8 +20,14 @@
# include <windows.h>
#endif
// Check if fmt/format.h compiles with the X11 index macro defined.
#define index(x, y) no nice things
#include "fmt/color.h"
#include "fmt/format.h"
#undef index
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
@@ -34,12 +40,13 @@
using std::size_t;
using fmt::basic_memory_buffer;
using fmt::internal::basic_writer;
using fmt::format;
using fmt::format_error;
using fmt::memory_buffer;
using fmt::string_view;
using fmt::wmemory_buffer;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using testing::Return;
using testing::StrictMock;
@@ -99,7 +106,7 @@ void std_format(long double value, std::wstring& result) {
template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::internal::buffer_range<Char>;
using range = fmt::buffer_range<Char>;
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
@@ -162,8 +169,7 @@ TEST(UtilTest, Increment) {
}
TEST(UtilTest, ParseNonnegativeInt) {
if (std::numeric_limits<int>::max() !=
static_cast<int>(static_cast<unsigned>(1) << 31)) {
if (max_value<int>() != static_cast<int>(static_cast<unsigned>(1) << 31)) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
}
@@ -181,7 +187,7 @@ TEST(UtilTest, ParseNonnegativeInt) {
}
TEST(IteratorTest, CountingIterator) {
fmt::internal::counting_iterator<char> it;
fmt::internal::counting_iterator it;
auto prev = it++;
EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1);
@@ -270,7 +276,7 @@ static void check_move_buffer(
EXPECT_EQ(alloc, buffer2.get_allocator().get());
}
TEST(MemoryBufferTest, MoveCtor) {
TEST(MemoryBufferTest, MoveCtorInlineBuffer) {
std::allocator<char> alloc;
basic_memory_buffer<char, 5, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
@@ -280,15 +286,22 @@ TEST(MemoryBufferTest, MoveCtor) {
// dynamic allocation.
buffer.push_back('a');
check_move_buffer("testa", buffer);
}
TEST(MemoryBufferTest, MoveCtorDynamicBuffer) {
std::allocator<char> alloc;
basic_memory_buffer<char, 4, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
buffer.append(test, test + 4);
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));
buffer.push_back('a');
basic_memory_buffer<char, 4, 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);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 4u);
}
static void check_move_assign_buffer(const char* str,
@@ -477,7 +490,7 @@ TEST(UtilTest, FormatSystemError) {
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;
size_t max_size = max_value<size_t>() / 2;
bool throws_on_alloc = false;
try {
std::allocator<char> alloc;
@@ -525,9 +538,8 @@ TEST(UtilTest, FormatWindowsError) {
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()));
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_value<size_t>()));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message));
}
@@ -593,31 +605,31 @@ TEST(WriterTest, WriteInt) {
CHECK_WRITE(static_cast<short>(12));
CHECK_WRITE(34u);
CHECK_WRITE(std::numeric_limits<int>::min());
CHECK_WRITE(std::numeric_limits<int>::max());
CHECK_WRITE(std::numeric_limits<unsigned>::max());
CHECK_WRITE(max_value<int>());
CHECK_WRITE(max_value<unsigned>());
}
TEST(WriterTest, WriteLong) {
CHECK_WRITE(56l);
CHECK_WRITE(78ul);
CHECK_WRITE(std::numeric_limits<long>::min());
CHECK_WRITE(std::numeric_limits<long>::max());
CHECK_WRITE(std::numeric_limits<unsigned long>::max());
CHECK_WRITE(max_value<long>());
CHECK_WRITE(max_value<unsigned long>());
}
TEST(WriterTest, WriteLongLong) {
CHECK_WRITE(56ll);
CHECK_WRITE(78ull);
CHECK_WRITE(std::numeric_limits<long long>::min());
CHECK_WRITE(std::numeric_limits<long long>::max());
CHECK_WRITE(std::numeric_limits<unsigned long long>::max());
CHECK_WRITE(max_value<long long>());
CHECK_WRITE(max_value<unsigned long long>());
}
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
auto min = std::numeric_limits<double>::min();
auto max = std::numeric_limits<double>::max();
auto max = max_value<double>();
if (fmt::internal::use_grisu<double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
@@ -637,7 +649,7 @@ TEST(WriterTest, WriteLongDouble) {
else
fmt::print("warning: long double formatting with std::swprintf is broken");
auto min = std::numeric_limits<long double>::min();
auto max = std::numeric_limits<long double>::max();
auto max = max_value<long double>();
if (fmt::internal::use_grisu<long double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
@@ -673,9 +685,7 @@ TEST(WriterTest, WriteString) {
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) {
CHECK_WRITE_WCHAR(L"abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) {
std::string s;
@@ -697,12 +707,6 @@ TEST(FormatToTest, WideString) {
EXPECT_STREQ(buf.data(), L"42");
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42.0", buffer);
}
TEST(FormatToTest, FormatToMemoryBuffer) {
fmt::basic_memory_buffer<char, 100> buffer;
fmt::format_to(buffer, "{}", "foo");
@@ -856,6 +860,7 @@ TEST(FormatterTest, RightAlign) {
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
}
#if FMT_NUMERIC_ALIGN
TEST(FormatterTest, NumericAlign) {
EXPECT_EQ(" 42", format("{0:=4}", 42));
EXPECT_EQ("+ 42", format("{0:=+4}", 42));
@@ -882,6 +887,13 @@ TEST(FormatterTest, NumericAlign) {
EXPECT_EQ(" 1.0", fmt::format("{:= }", 1.0));
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42.0", buffer);
}
#endif
TEST(FormatterTest, CenterAlign) {
EXPECT_EQ(" 42 ", format("{0:^5}", 42));
EXPECT_EQ(" 42 ", format("{0:^5o}", 042));
@@ -1001,6 +1013,7 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("0x42", format("{0:#x}", 0x42));
EXPECT_EQ("0X42", format("{0:#X}", 0x42));
EXPECT_EQ("-0x42", format("{0:#x}", -0x42));
EXPECT_EQ("0", format("{0:#o}", 0));
EXPECT_EQ("042", format("{0:#o}", 042));
EXPECT_EQ("-042", format("{0:#o}", -042));
EXPECT_EQ("42", format("{0:#}", 42u));
@@ -1185,12 +1198,36 @@ TEST(FormatterTest, Precision) {
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2%}", 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:3.0}", 'x'), format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", format("{0:.2}", 1.2345));
EXPECT_EQ("1.2", format("{0:.2}", 1.2345l));
EXPECT_EQ("1.2e+56", format("{:.2}", 1.234e56));
EXPECT_EQ("1e+00", format("{:.0e}", 1.0L));
EXPECT_EQ(
"4.9406564584124654417656879286822137236505980261432476442558568250067550"
"727020875186529983636163599237979656469544571773092665671035593979639877"
"479601078187812630071319031140452784581716784898210368871863605699873072"
"305000638740915356498438731247339727316961514003171538539807412623856559"
"117102665855668676818703956031062493194527159149245532930545654440112748"
"012970999954193198940908041656332452475714786901472678015935523861155013"
"480352649347201937902681071074917033322268447533357208324319361e-324",
format("{:.494}", 4.9406564584124654E-324));
EXPECT_EQ(
"-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000P+127",
format("{:.838A}", -2.14001164E+38));
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
format_error,
@@ -1330,9 +1367,17 @@ TEST(FormatterTest, FormatBin) {
EXPECT_EQ("10010001101000101011001111000", format("{0:b}", 0x12345678));
EXPECT_EQ("10010000101010111100110111101111", format("{0:b}", 0x90ABCDEF));
EXPECT_EQ("11111111111111111111111111111111",
format("{0:b}", std::numeric_limits<uint32_t>::max()));
format("{0:b}", max_value<uint32_t>()));
}
#if FMT_USE_INT128
constexpr auto int128_max = static_cast<__int128_t>(
(static_cast<__uint128_t>(1) << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1);
constexpr auto int128_min = -int128_max - 1;
constexpr auto uint128_max = ~static_cast<__uint128_t>(0);
#endif
TEST(FormatterTest, FormatDec) {
EXPECT_EQ("0", format("{0}", 0));
EXPECT_EQ("42", format("{0}", 42));
@@ -1341,6 +1386,23 @@ TEST(FormatterTest, FormatDec) {
EXPECT_EQ("-42", format("{0}", -42));
EXPECT_EQ("12345", format("{0}", 12345));
EXPECT_EQ("67890", format("{0}", 67890));
#if FMT_USE_INT128
EXPECT_EQ("0", format("{0}", static_cast<__int128_t>(0)));
EXPECT_EQ("0", format("{0}", static_cast<__uint128_t>(0)));
EXPECT_EQ("9223372036854775808",
format("{0}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-9223372036854775809",
format("{0}", static_cast<__int128_t>(INT64_MIN) - 1));
EXPECT_EQ("18446744073709551616",
format("{0}", static_cast<__int128_t>(UINT64_MAX) + 1));
EXPECT_EQ("170141183460469231731687303715884105727",
format("{0}", int128_max));
EXPECT_EQ("-170141183460469231731687303715884105728",
format("{0}", int128_min));
EXPECT_EQ("340282366920938463463374607431768211455",
format("{0}", uint128_max));
#endif
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%d", INT_MIN);
EXPECT_EQ(buffer, format("{0}", INT_MIN));
@@ -1365,6 +1427,19 @@ TEST(FormatterTest, FormatHex) {
EXPECT_EQ("90abcdef", format("{0:x}", 0x90abcdef));
EXPECT_EQ("12345678", format("{0:X}", 0x12345678));
EXPECT_EQ("90ABCDEF", format("{0:X}", 0x90ABCDEF));
#if FMT_USE_INT128
EXPECT_EQ("0", format("{0:x}", static_cast<__int128_t>(0)));
EXPECT_EQ("0", format("{0:x}", static_cast<__uint128_t>(0)));
EXPECT_EQ("8000000000000000",
format("{0:x}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-8000000000000001",
format("{0:x}", static_cast<__int128_t>(INT64_MIN) - 1));
EXPECT_EQ("10000000000000000",
format("{0:x}", static_cast<__int128_t>(UINT64_MAX) + 1));
EXPECT_EQ("7fffffffffffffffffffffffffffffff", format("{0:x}", int128_max));
EXPECT_EQ("-80000000000000000000000000000000", format("{0:x}", int128_min));
EXPECT_EQ("ffffffffffffffffffffffffffffffff", format("{0:x}", uint128_max));
#endif
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "-%x", 0 - static_cast<unsigned>(INT_MIN));
@@ -1387,6 +1462,23 @@ TEST(FormatterTest, FormatOct) {
EXPECT_EQ("42", format("{0:o}", 042u));
EXPECT_EQ("-42", format("{0:o}", -042));
EXPECT_EQ("12345670", format("{0:o}", 012345670));
#if FMT_USE_INT128
EXPECT_EQ("0", format("{0:o}", static_cast<__int128_t>(0)));
EXPECT_EQ("0", format("{0:o}", static_cast<__uint128_t>(0)));
EXPECT_EQ("1000000000000000000000",
format("{0:o}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-1000000000000000000001",
format("{0:o}", static_cast<__int128_t>(INT64_MIN) - 1));
EXPECT_EQ("2000000000000000000000",
format("{0:o}", static_cast<__int128_t>(UINT64_MAX) + 1));
EXPECT_EQ("1777777777777777777777777777777777777777777",
format("{0:o}", int128_max));
EXPECT_EQ("-2000000000000000000000000000000000000000000",
format("{0:o}", int128_min));
EXPECT_EQ("3777777777777777777777777777777777777777777",
format("{0:o}", uint128_max));
#endif
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "-%o", 0 - static_cast<unsigned>(INT_MIN));
EXPECT_EQ(buffer, format("{0:o}", INT_MIN));
@@ -1403,11 +1495,7 @@ TEST(FormatterTest, FormatOct) {
}
TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("123", format("{:n}", 123));
EXPECT_EQ("1,234", format("{:n}", 1234));
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
EXPECT_EQ("4,294,967,295",
format("{:n}", std::numeric_limits<uint32_t>::max()));
EXPECT_EQ("1234", format("{:n}", 1234));
}
struct ConvertibleToLongLong {
@@ -1420,7 +1508,6 @@ TEST(FormatterTest, FormatConvertibleToLongLong) {
TEST(FormatterTest, FormatFloat) {
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
EXPECT_EQ("12.500000%", format("{0:%}", 0.125f));
}
TEST(FormatterTest, FormatDouble) {
@@ -1433,8 +1520,6 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ("392.65", format("{:G}", 392.65));
EXPECT_EQ("392.650000", format("{:f}", 392.65));
EXPECT_EQ("392.650000", format("{:F}", 392.65));
EXPECT_EQ("12.500000%", format("{:%}", 0.125));
EXPECT_EQ("12.34%", format("{:.2%}", 0.1234432));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, format("{0:e}", 392.65));
@@ -1474,7 +1559,6 @@ TEST(FormatterTest, FormatNaN) {
EXPECT_EQ("nan ", format("{:<7}", nan));
EXPECT_EQ(" nan ", format("{:^7}", nan));
EXPECT_EQ(" nan", format("{:>7}", nan));
EXPECT_EQ("nan%", format("{:%}", nan));
}
TEST(FormatterTest, FormatInfinity) {
@@ -1487,7 +1571,6 @@ TEST(FormatterTest, FormatInfinity) {
EXPECT_EQ("inf ", format("{:<7}", inf));
EXPECT_EQ(" inf ", format("{:^7}", inf));
EXPECT_EQ(" inf", format("{:>7}", inf));
EXPECT_EQ("inf%", format("{:%}", inf));
}
TEST(FormatterTest, FormatLongDouble) {
@@ -1498,8 +1581,6 @@ TEST(FormatterTest, FormatLongDouble) {
EXPECT_EQ("392.65", format("{0:G}", 392.65l));
EXPECT_EQ("392.650000", format("{0:f}", 392.65l));
EXPECT_EQ("392.650000", format("{0:F}", 392.65l));
EXPECT_EQ("12.500000%", format("{:%}", 0.125l));
EXPECT_EQ("12.34%", format("{:.2%}", 0.1234432l));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%Le", 392.65l);
EXPECT_EQ(buffer, format("{0:e}", 392.65l));
@@ -1745,13 +1826,12 @@ TEST(FormatIntTest, FormatInt) {
EXPECT_EQ("42", fmt::format_int(42ull).str());
EXPECT_EQ("-42", fmt::format_int(-42ll).str());
std::ostringstream os;
os << std::numeric_limits<int64_t>::max();
EXPECT_EQ(os.str(),
fmt::format_int(std::numeric_limits<int64_t>::max()).str());
os << max_value<int64_t>();
EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str());
}
TEST(FormatTest, Print) {
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!");
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
"Don't panic!");
@@ -1858,7 +1938,7 @@ TEST(FormatTest, CustomFormatCompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer()));
Answer answer;
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer));
const Answer const_answer;
const Answer const_answer = Answer();
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer));
}
@@ -1894,6 +1974,11 @@ TEST(FormatTest, UdlTemplate) {
EXPECT_EQ("foo", "foo"_format());
EXPECT_EQ(" 42", "{0:10}"_format(42));
}
TEST(FormatTest, UdlPassUserDefinedObjectAsLvalue) {
Date date(2015, 10, 21);
EXPECT_EQ("2015-10-21", "{}"_format(date));
}
#endif // FMT_USE_USER_DEFINED_LITERALS
enum TestEnum { A };
@@ -1901,22 +1986,29 @@ enum TestEnum { A };
TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); }
TEST(FormatTest, FormatterNotSpecialized) {
EXPECT_FALSE((fmt::internal::has_formatter<fmt::formatter<TestEnum>,
fmt::format_context>::value));
static_assert(
!fmt::has_formatter<fmt::formatter<TestEnum>, fmt::format_context>::value,
"");
}
#if FMT_HAS_FEATURE(cxx_strong_enums)
enum TestFixedEnum : short { B };
enum big_enum : unsigned long long { big_enum_value = 5000000000ULL };
TEST(FormatTest, FixedEnum) { EXPECT_EQ("0", fmt::format("{}", B)); }
TEST(FormatTest, StrongEnum) {
EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value));
}
#endif
using buffer_range = fmt::internal::buffer_range<char>;
using buffer_range = fmt::buffer_range<char>;
class mock_arg_formatter
: public fmt::internal::arg_formatter_base<buffer_range> {
private:
#if FMT_USE_INT128
MOCK_METHOD1(call, void(__int128_t value));
#else
MOCK_METHOD1(call, void(long long value));
#endif
public:
typedef fmt::internal::arg_formatter_base<buffer_range> base;
@@ -1929,14 +2021,14 @@ class mock_arg_formatter
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type
typename std::enable_if<fmt::internal::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
typename std::enable_if<!fmt::internal::is_integral<T>::value, iterator>::type
operator()(T value) {
return base::operator()(value);
}
@@ -1988,8 +2080,10 @@ TEST(FormatTest, DynamicFormatter) {
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:{0}}", num), format_error,
"cannot switch from automatic to manual argument indexing");
#if FMT_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{:=}", str), format_error,
"format specifier requires numeric argument");
#endif
EXPECT_THROW_MSG(format("{:+}", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{:-}", str), format_error,
@@ -2214,9 +2308,9 @@ struct test_context {
template <size_t N>
FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
fmt::format_specs specs;
test_parse_context parse_ctx;
test_context ctx{};
auto specs = fmt::format_specs();
auto parse_ctx = test_parse_context();
auto ctx = test_context();
fmt::internal::specs_handler<test_parse_context, test_context> h(
specs, parse_ctx, ctx);
parse_format_specs(s, s + N, h);
@@ -2372,13 +2466,15 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
# if FMT_MSC_VER >= 1916
// This causes an internal compiler error in MSVC2017.
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
EXPECT_ERROR("{:10000000000}", "number is too big", int);
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
# if FMT_NUMERIC_ALIGN
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
const char*);
# endif
EXPECT_ERROR("{:+}", "format specifier requires numeric argument",
const char*);
EXPECT_ERROR("{:-}", "format specifier requires numeric argument",
@@ -2534,3 +2630,13 @@ TEST(FormatTest, FormatCustomChar) {
EXPECT_EQ(result.size(), 1);
EXPECT_EQ(result[0], mychar('x'));
}
TEST(FormatTest, FormatUTF8Precision) {
using str_type = std::basic_string<char8_t>;
str_type format(reinterpret_cast<const char8_t*>(u8"{:.4}"));
str_type str(reinterpret_cast<const char8_t*>(u8"caf\u00e9s")); // cafés
auto result = fmt::format(format, str);
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
EXPECT_EQ(result.size(), 5);
EXPECT_EQ(result, str.substr(0, 5));
}

View File

@@ -48,10 +48,28 @@ TEST(GrisuTest, Prettify) {
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
EXPECT_EQ("1e+11", fmt::format("{}", 1e11));
EXPECT_EQ("100000000000.0", fmt::format("{}", 1e11));
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f)));
}
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }
TEST(GrisuTest, Fallback) {
EXPECT_EQ("1e+23", fmt::format("{}", 1e23));
EXPECT_EQ("9e-265", fmt::format("{}", 9e-265));
EXPECT_EQ("5.423717798060526e+125",
fmt::format("{}", 5.423717798060526e+125));
EXPECT_EQ("1.372371880954233e-288",
fmt::format("{}", 1.372371880954233e-288));
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
EXPECT_EQ("2.2506787569811123e-253",
fmt::format("{}", 2.2506787569811123e-253));
EXPECT_EQ("1103618912042992.8", fmt::format("{}", 1103618912042992.8));
// pow(2, -25) - assymetric boundaries:
EXPECT_EQ("2.9802322387695312e-08",
fmt::format("{}", 2.9802322387695312e-08));
}

View File

@@ -74,14 +74,6 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
EXPECT_EQ(s_ + 1, p_);
}
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) {
// successful EXPECT_THROW_MSG
@@ -163,6 +155,15 @@ TEST_F(SingleEvaluationTest, SystemErrorTests) {
EXPECT_EQ(4, b_);
}
#if FMT_USE_FCNTL
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE
@@ -187,6 +188,24 @@ TEST_F(SingleEvaluationTest, WriteTests) {
EXPECT_EQ(2, b_);
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
#endif // FMT_USE_FCNTL
// Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
@@ -280,16 +299,6 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
format_system_error(EDOM, "test")));
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
<< "unexpected failure";
@@ -308,20 +317,13 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
"expected failure");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
TEST(UtilTest, FormatSystemError) {
fmt::memory_buffer out;
fmt::format_system_error(out, EDOM, "test message");
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
}
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::buffered_file;
using fmt::error_code;

View File

@@ -7,7 +7,7 @@
#include "gtest-extra.h"
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::file;
@@ -78,7 +78,7 @@ std::string read(file& f, std::size_t count) {
return buffer;
}
#endif // FMT_USE_FILE_DESCRIPTORS
#endif // FMT_USE_FCNTL
std::string format_system_error(int error_code, fmt::string_view message) {
fmt::memory_buffer out;

View File

@@ -10,16 +10,7 @@
#include <string>
#include "gmock.h"
#include "fmt/core.h"
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
# include "fmt/posix.h"
#endif
#include "fmt/posix.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
@@ -65,7 +56,7 @@ std::string format_system_error(int error_code, fmt::string_view message);
EXPECT_THROW_MSG(statement, fmt::system_error, \
format_system_error(error_code, message))
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
// Captures file output by redirecting it to a pipe.
// The output it can handle is limited by the pipe capacity.
@@ -151,7 +142,9 @@ std::string read(fmt::file& f, std::size_t count);
# define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
#endif // FMT_USE_FILE_DESCRIPTORS
#else
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
#endif // FMT_USE_FCNTL
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; }

View File

@@ -8,45 +8,82 @@
#include "fmt/locale.h"
#include "gmock.h"
using fmt::internal::max_value;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '?'; }
std::string do_grouping() const FMT_OVERRIDE { return "\03"; }
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
};
template <typename Char> struct no_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return ""; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
template <typename Char> struct special_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return "\03\02"; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
template <typename Char> struct small_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return "\01"; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
TEST(LocaleTest, DoubleDecimalPoint) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23));
// Test with Grisu disabled.
fmt::memory_buffer buf;
fmt::internal::writer w(buf, fmt::internal::locale_ref(loc));
auto specs = fmt::format_specs();
specs.type = 'n';
w.write_double<double, false>(1.23, specs);
EXPECT_EQ(fmt::to_string(buf), "1?23");
}
TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567));
fmt::format_arg_store<fmt::format_context, int> as{1234567};
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as)));
std::string s;
fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567);
EXPECT_EQ("1~234~567", s);
std::locale no_grouping_loc(std::locale(), new no_grouping<char>());
EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:n}", 1234567));
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:n}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<char>());
EXPECT_EQ("4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, "{:n}", max_value<uint32_t>()));
}
TEST(LocaleTest, WFormat) {
std::locale loc(std::locale(), new numpunct<wchar_t>());
EXPECT_EQ(L"1,234,567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567));
fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:n}", fmt::wformat_args(as)));
auto sep =
std::use_facet<std::numpunct<wchar_t>>(std::locale("C")).thousands_sep();
auto result = sep == ',' ? L"1,234,567" : L"1234567";
EXPECT_EQ(result, fmt::format(std::locale("C"), L"{:n}", 1234567));
EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:n}", 1234567));
std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>());
EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:n}", 1234567));
std::locale special_grouping_loc(std::locale(),
new special_grouping<wchar_t>());
EXPECT_EQ(L"1,23,45,678",
fmt::format(special_grouping_loc, L"{:n}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<wchar_t>());
EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, L"{:n}", max_value<uint32_t>()));
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@@ -64,7 +64,7 @@ TEST(OStreamTest, Enum) {
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
}
using range = fmt::internal::buffer_range<char>;
using range = fmt::buffer_range<char>;
struct test_arg_formatter : fmt::arg_formatter<range> {
fmt::format_parse_context parse_ctx;
@@ -95,8 +95,10 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
#if FMT_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
"format specifier requires numeric argument");
#endif
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
@@ -143,8 +145,8 @@ TEST(OStreamTest, WriteToOStream) {
}
TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
std::size_t max_size = fmt::internal::max_value<std::size_t>();
std::streamsize max_streamsize = fmt::internal::max_value<std::streamsize>();
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return;
struct test_buffer : fmt::internal::buffer<char> {
@@ -185,8 +187,8 @@ TEST(OStreamTest, Join) {
#if FMT_USE_CONSTEXPR
TEST(OStreamTest, ConstexprString) {
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
EXPECT_EQ("a string", format(fmt("{0}"), TestString("a string")));
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
}
#endif
@@ -240,3 +242,37 @@ TEST(FormatTest, UDL) {
EXPECT_EQ("{}"_format("test"), "test");
}
#endif
template <typename T> struct convertible {
T value;
explicit convertible(const T& val) : value(val) {}
operator T() const { return value; }
};
TEST(OStreamTest, DisableBuiltinOStreamOperators) {
EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42)));
EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLikeIgnoreInserter) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}

View File

@@ -30,7 +30,6 @@
using fmt::buffered_file;
using fmt::error_code;
using fmt::file;
using testing::_;
using testing::Return;
@@ -198,6 +197,9 @@ static void write_file(fmt::cstring_view filename, fmt::string_view content) {
f.print("{}", content);
}
#if FMT_USE_FCNTL
using fmt::file;
TEST(UtilTest, GetPageSize) {
#ifdef _WIN32
SYSTEM_INFO si = {};
@@ -429,6 +431,7 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
#endif // FMT_USE_FCNTL
struct TestMock {
static TestMock* instance;

View File

@@ -19,6 +19,9 @@
using fmt::buffered_file;
using fmt::error_code;
#if FMT_USE_FCNTL
using fmt::file;
// Checks if the file is open by reading one character from it.
@@ -368,12 +371,6 @@ TEST(FileTest, Fdopen) {
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
}
TEST(FileTest, FdopenError) {
file f;
EXPECT_SYSTEM_ERROR_NOASSERT(f.fdopen("r"), EBADF,
"cannot associate stream with file descriptor");
}
#ifdef FMT_LOCALE
TEST(LocaleTest, Strtod) {
fmt::Locale locale;
@@ -382,3 +379,4 @@ TEST(LocaleTest, Strtod) {
EXPECT_EQ(start + 3, ptr);
}
#endif
#endif // FMT_USE_FCNTL

View File

@@ -16,6 +16,7 @@
using fmt::format;
using fmt::format_error;
using fmt::internal::max_value;
const unsigned BIG_NUM = INT_MAX + 1u;
@@ -295,13 +296,12 @@ void TestLength(const char* length_spec, U value) {
long long signed_value = 0;
unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument.
using std::numeric_limits;
unsigned long long max = numeric_limits<U>::max();
unsigned long long max = max_value<U>();
using fmt::internal::const_check;
if (const_check(max <= static_cast<unsigned>(numeric_limits<int>::max()))) {
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned>(value);
} else if (const_check(max <= numeric_limits<unsigned>::max())) {
} else if (const_check(max <= max_value<unsigned>())) {
signed_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned>(value);
}
@@ -332,25 +332,27 @@ void TestLength(const char* length_spec, U value) {
}
template <typename T> void TestLength(const char* length_spec) {
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
T min = std::numeric_limits<T>::min(), max = max_value<T>();
TestLength<T>(length_spec, 42);
TestLength<T>(length_spec, -42);
TestLength<T>(length_spec, min);
TestLength<T>(length_spec, max);
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
unsigned long long long_long_max = std::numeric_limits<long long>::max();
long long long_long_min = std::numeric_limits<long long>::min();
if (static_cast<long long>(min) > long_long_min)
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
unsigned long long long_long_max = max_value<long long>();
if (static_cast<unsigned long long>(max) < long_long_max)
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
TestLength<T>(length_spec, std::numeric_limits<short>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned short>::max());
TestLength<T>(length_spec, max_value<unsigned short>());
TestLength<T>(length_spec, std::numeric_limits<int>::min());
TestLength<T>(length_spec, std::numeric_limits<int>::max());
TestLength<T>(length_spec, max_value<int>());
TestLength<T>(length_spec, std::numeric_limits<unsigned>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned>::max());
TestLength<T>(length_spec, max_value<unsigned>());
TestLength<T>(length_spec, std::numeric_limits<long long>::min());
TestLength<T>(length_spec, std::numeric_limits<long long>::max());
TestLength<T>(length_spec, max_value<long long>());
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::max());
TestLength<T>(length_spec, max_value<unsigned long long>());
}
TEST(PrintfTest, Length) {
@@ -366,7 +368,7 @@ TEST(PrintfTest, Length) {
TestLength<intmax_t>("j");
TestLength<std::size_t>("z");
TestLength<std::ptrdiff_t>("t");
long double max = std::numeric_limits<long double>::max();
long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
}
@@ -389,7 +391,7 @@ TEST(PrintfTest, Int) {
TEST(PrintfTest, long_long) {
// fmt::printf allows passing long long arguments to %d without length
// specifiers.
long long max = std::numeric_limits<long long>::max();
long long max = max_value<long long>();
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
}
@@ -408,6 +410,7 @@ TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.65", "%G", 392.65);
EXPECT_PRINTF("392", "%g", 392.0);
EXPECT_PRINTF("392", "%G", 392.0);
EXPECT_PRINTF("4.56e-07", "%g", 0.000000456);
safe_sprintf(buffer, "%a", -392.65);
EXPECT_EQ(buffer, format("{:a}", -392.65));
safe_sprintf(buffer, "%A", -392.65);
@@ -425,7 +428,7 @@ TEST(PrintfTest, Inf) {
TEST(PrintfTest, Char) {
EXPECT_PRINTF("x", "%c", 'x');
int max = std::numeric_limits<int>::max();
int max = max_value<int>();
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
// EXPECT_PRINTF("x", "%lc", L'x');
EXPECT_PRINTF(L"x", L"%c", L'x');
@@ -474,7 +477,7 @@ enum E { A = 42 };
TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
TEST(PrintfTest, Examples) {
const char* weekday = "Thursday";
const char* month = "August";
@@ -561,8 +564,7 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
#endif
}
typedef fmt::printf_arg_formatter<fmt::internal::buffer_range<char>>
formatter_t;
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t;
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
// A custom printf argument formatter that doesn't print `-` for floating-point
@@ -587,8 +589,7 @@ class custom_printf_arg_formatter : public formatter_t {
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
return formatter_t::operator()(value);
}
}
;
};
typedef fmt::basic_format_args<context_t> format_args_t;

View File

@@ -9,13 +9,13 @@
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
/// Check if 'if constexpr' is supported.
#include "fmt/ranges.h"
#include "gtest.h"
// Check if 'if constexpr' is supported.
#if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
# include "fmt/ranges.h"
# include "gtest.h"
# include <array>
# include <map>
# include <string>
@@ -49,6 +49,25 @@ TEST(RangesTest, FormatTuple) {
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", tu1));
}
TEST(RangesTest, JoinTuple) {
// Value tuple args
std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
EXPECT_EQ("(a, 1, 2.0)", fmt::format("({})", fmt::join(t1, ", ")));
// Testing lvalue tuple args
int x = 4;
std::tuple<char, int&> t2{'b', x};
EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + ")));
// Empty tuple
std::tuple<> t3;
EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|")));
// Single element tuple
std::tuple<float> t4{4.0f};
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
}
struct my_struct {
int32_t i;
std::string str; // can throw
@@ -100,3 +119,16 @@ TEST(RangesTest, PathLike) {
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
// 201402L && _MSC_VER >= 1910)
#ifdef FMT_USE_STRING_VIEW
struct string_like {
const char* begin();
const char* end();
explicit operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; }
};
TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like()));
}
#endif // FMT_USE_STRING_VIEW

View File

@@ -201,7 +201,8 @@ struct scan_handler : error_handler {
scan_ctx_.advance_to(it);
break;
}
default:
case scan_type::none_type:
case scan_type::custom_type:
assert(false);
}
}

View File

@@ -30,18 +30,14 @@ TEST(StdFormatTest, Alignment) {
// Error: '=' with charT and no integer presentation type
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
string s6 = format("{:6d}", c); // s6 == " 120"
string s7 = format("{:=+06d}", c); // s7 == "+00120"
string s8 = format("{:0=#6x}", 0xa); // s8 == "0x000a"
string s9 = format("{:6}", true); // s9 == "true "
string s7 = format("{:6}", true); // s9 == "true "
EXPECT_EQ(s0, " 42");
EXPECT_EQ(s1, "x ");
EXPECT_EQ(s2, "x*****");
EXPECT_EQ(s3, "*****x");
EXPECT_EQ(s4, "**x***");
EXPECT_EQ(s6, " 120");
EXPECT_EQ(s7, "+00120");
EXPECT_EQ(s8, "0x000a");
EXPECT_EQ(s9, "true ");
EXPECT_EQ(s7, "true ");
}
TEST(StdFormatTest, Float) {
@@ -65,11 +61,11 @@ TEST(StdFormatTest, Int) {
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:n}", 1234); // s3 == "1,234" (depends on the locale)
string s3 = format("{:n}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A");
EXPECT_EQ(s3, "1,234");
EXPECT_EQ(s3, "1234");
}
#include <format>

View File

@@ -14,8 +14,13 @@
class assertion_failure : public std::logic_error {
public:
explicit assertion_failure(const char* message) : std::logic_error(message) {}
private:
virtual void avoid_weak_vtable();
};
void assertion_failure::avoid_weak_vtable() {}
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw assertion_failure(message);

View File

@@ -33,11 +33,17 @@ std::string get_system_error(int error_code) {
const char* const FILE_CONTENT = "Don't panic!";
fmt::buffered_file open_buffered_file(FILE** fp) {
#if FMT_USE_FCNTL
fmt::file read_end, write_end;
fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close();
fmt::buffered_file f = read_end.fdopen("r");
if (fp) *fp = f.get();
#else
fmt::buffered_file f("test-file", "w");
fputs(FILE_CONTENT, f.get());
if (fp) *fp = f.get();
#endif
return f;
}