Compare commits

...

788 Commits
5.1.0 ... text

Author SHA1 Message Date
48f76dbb52 Remove unneeded stuff 2019-09-29 09:51:22 -07:00
ca3dacba47 Remove algorithm.hpp 2019-09-29 09:43:23 -07:00
6d47e093c5 Remove utility.hpp 2019-09-29 09:42:03 -07:00
d1626e96ef Remove unused overloads 2019-09-29 09:29:19 -07:00
160bcb723c Remove lazy_segment_range 2019-09-29 09:25:29 -07:00
c7ea093c27 Handle Supplemental Symbols and Pictographs 2019-09-28 11:47:22 -07:00
00434c93ef Implement correct width computation 2019-09-28 11:47:22 -07:00
b12033fd68 Handle grapheme clusters when computing width 2019-09-28 11:47:22 -07:00
e5ab813ffb Add a subset of the text library as an optional component 2019-09-28 11:47:22 -07:00
0fc7bd1573 Fix ambiguity for types with dodgy conversions 2019-09-28 11:35:20 -07:00
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
4b8f8fac96 Update README.rst 2019-09-25 07:14:45 -07:00
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
ac59d9f3a4 workaround VS2019 Update 3 compiler bug. (#1328) 2019-09-24 11:29:29 -07:00
8f27ce4d8b add test for multiple compilation types 2019-09-24 06:25:21 -07:00
89b0c71fa9 fix name clash in header-only mode 2019-09-24 06:25:21 -07:00
f6a783ad2e fix get 2019-09-24 06:25:21 -07:00
ccc8f5db02 Disable integral operator<< (#1316) 2019-09-23 20:34:08 -07:00
20fdb88a1c Remove redundant and nonportable test (#1313) 2019-09-23 13:36:12 -07:00
f29901097f Don't use const char* overload of operator<< (#1309) 2019-09-23 12:35:08 -07:00
758446c80d Fix a warning (#1319) and simplify code 2019-09-19 16:00:40 -07:00
f7aedc5fc4 Fix shared build on Solaris 2019-09-19 14:12:03 -07:00
840a817ed2 add oss fuzz badge 2019-09-19 08:23:05 -07:00
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
5dc577c064 Update ChangeLog.rst 2019-09-12 18:20:48 -07:00
c6d1a94a9f Fix fallback_format (#1306) 2019-09-09 17:28:53 -07:00
0656045d02 Fix format overload that takes text_style (#1305) 2019-09-08 18:41:02 -07:00
c85ae23c73 Add max_value 2019-09-08 09:21:30 -07:00
b3bf665764 Implement multiplication and part of assignment from pow of 10 2019-09-08 08:27:00 -07:00
0887887e23 Implement left shift 2019-09-07 18:10:36 -07:00
ac71d853be Refactor normalize and clean up 2019-09-07 17:28:59 -07:00
6649b8e0ca value -> bigit 2019-09-07 14:23:19 -07:00
56b5c192a0 Add a bigint stub and reenable grisu 2019-09-07 13:50:22 -07:00
b2f0b6e44e Tweak comment 2019-09-07 08:43:35 -07:00
3d9f3c163b Integrate new format string compilation 2019-09-07 07:01:51 -07:00
19547d5148 Update index.rst 2019-09-06 13:27:28 -07:00
972ffd3151 Update README.rst 2019-09-06 13:13:30 -07:00
2ed412fa38 Update README.rst 2019-09-06 13:03:14 -07:00
df4dcf2ece Fixed vs 2015 warning about unused variable when grisu is off 2019-09-06 11:29:56 -07:00
f7a4b4ab91 Make numeric alignment optional 2019-09-06 09:43:53 -07:00
611cf0b3c6 Format octal 0 as 0 2019-09-06 07:03:47 -07:00
58a8f2f539 Add ccache to the list of projects 2019-09-06 06:26:09 -07:00
1882b9687b Reduce the numer of ifdefs with an empty (u)int128_t fallback 2019-09-05 18:03:47 -07:00
6de0454b42 Add support for built-in __int128 when available 2019-09-04 07:05:08 -07:00
16e3c48bb0 Move definition of FMT_USE_INT128 to core.h 2019-09-04 07:05:08 -07:00
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
2fd8f9ec8a Initial implementation of optimal compile-time formatter generation 2019-09-02 19:50:30 -07:00
fe642d7648 Clarify use of the core API in header-only mode (#1296) 2019-09-02 17:14:30 -07:00
a128b5b2cb Simplify format string compilation 2019-09-02 06:48:19 -07:00
466128de00 Remove unused code and refactor 2019-09-01 13:08:53 -07:00
22e98a5b6a Make compile work with user-defined types 2019-09-01 11:49:27 -07:00
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
7cad33563c Refactor format string compilation 2019-09-01 10:28:41 -07:00
e1ab6bc006 Simplify format string compilation 2019-09-01 08:31:32 -07:00
24a88545d9 Add -Werror to tests 2019-08-31 09:26:45 -07:00
422e7b9d70 Fix compile-time checks for user-defined types (#1292) 2019-08-31 08:35:38 -07:00
d1dd9d5327 Document floating-point n specifier (#1291) 2019-08-31 08:16:47 -07:00
9a56a608ee Fix warnings (#1288) 2019-08-31 08:16:47 -07:00
c76957565c FixedEnum -> StrongEnum and make it a regression test 2019-08-31 08:16:47 -07:00
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
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
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
3f75e2b69e Make buffer_range public and update custom formatting docs (#1281) 2019-08-28 06:50:20 -07:00
744302add0 Workaround broken [[deprecated]] in Intel compiler (#1273) 2019-08-28 05:58:09 -07:00
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
ad3c7855e2 Fix typos. 2019-08-27 06:26:17 -07:00
7512a55aa3 Update version 2019-08-26 06:23:37 -07:00
9bd2f1f9af Temporarily disable Grisu 2019-08-26 06:11:44 -07:00
b9815cf048 Simplify part_counter 2019-08-25 08:30:59 -07:00
fe00cddde2 Move part counter to the namespace scope
to reduce the number of instantiations
2019-08-25 08:22:43 -07:00
1a7d172dc7 which_value -> kind 2019-08-25 08:08:46 -07:00
006c2546f2 Get rid of basic_prepared_format 2019-08-25 07:57:56 -07:00
4ce006fb6e Simplify format string compilation 2019-08-25 07:48:09 -07:00
e2e557e273 Remove preparator 2019-08-25 06:47:15 -07:00
a5f470eb10 Test and fix compiled format_to_n and formatted_size 2019-08-25 06:38:41 -07:00
4070c1d80b PrepareTest -> CompileTest 2019-08-25 06:22:13 -07:00
f8b59251c4 Remove unused preparator 2019-08-24 12:48:24 -07:00
643aa4c8c4 Simplify format string compilation 2019-08-24 09:39:40 -07:00
ffe29a92f4 Get rid of internal::compile 2019-08-24 09:04:03 -07:00
e94d723667 Fix compiled format string version of format_to_n 2019-08-24 09:00:30 -07:00
a9337395a2 Remove unused variable 2019-08-24 08:46:15 -07:00
562a9f4991 Remove parts_container_concept_check 2019-08-24 08:41:43 -07:00
b257c56e51 Fix size types 2019-08-24 08:36:14 -07:00
1488df3395 Bump version 2019-08-24 07:31:25 -07:00
b918e3ff81 Fix docopt usage 2019-08-24 07:24:42 -07:00
23b04ca6d5 Update usage instructions 2019-08-24 07:16:45 -07:00
5d5918a74d Update usage instructions 2019-08-24 07:04:24 -07:00
413d97b33a Fix formatting in changelog 2019-08-24 06:46:43 -07:00
790fd90f40 Update changelog 2019-08-24 06:34:19 -07:00
c0890be77d Update changelog 2019-08-24 06:32:47 -07:00
5a4b24613b Update the default floating-point format docs (#1270) 2019-08-22 15:21:14 -07:00
45dc3486fe Update license 2019-08-22 08:37:36 -07:00
5a353fa8cf Draft license 2019-08-21 14:31:22 -07:00
c84d227da9 Update changelog 2019-08-21 13:06:21 -07:00
b2d4ca1546 Workaround an issue with std::filesystem::path being an infinitely deep range (#1268) 2019-08-21 11:22:59 -07:00
2aae6b120c Fix doc for importing header only target with CMake 2019-08-14 13:59:15 -07:00
9f09b8eed1 Fix a warning 2019-08-11 11:38:16 -07:00
2bb8120d98 Fix a warning 2019-08-11 09:27:59 -07:00
c1e97392be Fix warnings 2019-08-11 08:44:12 -07:00
4e99e09bb3 Fix warnings 2019-08-10 16:56:57 -07:00
1607a01870 Suppress a warning 2019-08-10 14:15:55 -07:00
e8219952c6 Restrict fmt::compile to literal strings
to make sure we don't break the API too much when making all of this
compile-time only.
2019-08-10 13:18:11 -07:00
544b537334 Update changelog 2019-08-10 10:01:23 -07:00
83c13a1fe3 Update changelog 2019-08-10 09:56:13 -07:00
2546dafece Update changelog 2019-08-10 09:25:16 -07:00
02c04b1736 Update changelog 2019-08-10 07:59:33 -07:00
5360ab0b59 Fix iteration over named arguments (#1168) 2019-08-09 11:53:07 -07:00
b615eca964 Update changelog 2019-08-05 05:37:44 -07:00
22a42c0b01 Cleanup compile implementation 2019-08-04 11:15:43 -07:00
c63624ed45 Update changelog 2019-08-04 10:38:33 -07:00
211d312406 Don't terminate on I/O errors in report_error 2019-08-04 09:38:47 -07:00
ef55e74e08 Update changelog 2019-08-04 09:20:49 -07:00
612669d343 Update changelog 2019-08-04 07:28:48 -07:00
ba63ac8c18 Make format_to a non-member 2019-08-04 06:47:49 -07:00
3fe49163bd Revert "Make format_to a non-member"
This reverts commit a5bd3ddb28.
2019-08-03 16:12:34 -07:00
a5bd3ddb28 Make format_to a non-member 2019-08-03 08:36:53 -07:00
3df0ea34e5 Remove unnecessary overloads 2019-08-03 08:26:51 -07:00
436acf3489 Make formatted_size & format_to_n non-members 2019-08-03 07:54:49 -07:00
823128049b Make format a non-member 2019-08-03 06:31:24 -07:00
1dfa2591b3 Make operator _a constexpr 2019-08-01 16:56:49 -07:00
6f2dd30e38 Simplify udl_formatter return type 2019-08-01 16:56:49 -07:00
431d0f85cf Use basic_string_view in non-template UDL 2019-08-01 16:56:49 -07:00
ad71f5a706 Use forwarding references in UDL template 2019-08-01 16:56:49 -07:00
d0f2f3b816 Update usage.rst 2019-07-27 20:25:40 +03:00
eac2796ce3 Fix locale detection 2019-07-26 12:13:05 +03:00
ee36257469 Internalize format_part 2019-07-26 09:02:45 +03:00
bbf0bada33 Simplify compile 2019-07-26 07:58:19 +03:00
3730b4f039 Cleanup compile implementation 2019-07-25 21:26:22 +03:00
25ff2efc0a Update changelog 2019-07-25 19:59:59 +03:00
9393fe26f6 prepare -> compile 2019-07-25 19:39:41 +03:00
b48ffc14a4 Update changelog 2019-07-25 18:44:40 +03:00
3268531bc9 Workaround a broken C locale on Windows 2019-07-25 12:55:23 +03:00
8bd59ec936 Use fputws for outputting wide strings
Also adds fwide byte/wide orientation checking to verify streams are
able to receive the character type in question.

On Windows, the fwide calls are no-ops that pass through the second
arg and optimize out the if statement entirely.
2019-07-25 10:29:32 +03:00
1235f0a24a Fix typo in usage.rst 2019-07-24 15:54:02 +02:00
4fb73d1145 Update README.rst 2019-07-21 15:28:48 +02:00
f9ceefb0fe Fix a warning and is_negative usage 2019-07-19 14:18:10 +02:00
6bcc3fd216 Fix warnings 2019-07-19 13:27:41 +02:00
6a497e1d06 Fix warnings 2019-07-19 06:18:46 +02:00
e9b9b0aefa clang-format 2019-07-18 06:28:53 +02:00
ec24342b2b Fix more warnings 2019-07-18 06:28:17 +02:00
230b24944c Fix sign conversion warnings 2019-07-17 21:18:55 +02:00
cadd92d69f Fix #1232. 2019-07-17 11:52:10 +02:00
41076d1de3 Use FMT_THROW in fmt/color.h.
This allows fmt to be compiled with `-fno-exceptions`.
2019-07-17 10:15:31 +02:00
d561cb47a7 Opt-in macro to enforce use of compile-time format strings 2019-07-16 07:08:59 +02:00
699fe8e711 Remove const qualification in compile-time checks 2019-07-15 09:20:16 +02:00
da2d33f1f1 Update changelog 2019-07-12 16:21:13 -07:00
af4734fd15 Fix warnings 2019-07-12 16:03:12 -07:00
a3a74672a0 Update signatures 2019-07-09 17:05:34 -07:00
c11e68305f Update signatures 2019-07-09 17:01:13 -07:00
dd8cc8b0ba Disallow passing views as lvalues 2019-07-09 12:18:48 -07:00
f6f0415b83 typedef -> using 2019-07-07 17:14:26 -07:00
c92dc37464 typedef -> using 2019-07-07 15:40:34 -07:00
e3f20d3e13 Remove gcc 4.4 workaround and use proper alias templates (#940) 2019-07-07 14:35:56 -07:00
b438812324 Clean up is_output_iterator 2019-07-07 12:59:55 -07:00
bc15e3700e typedef -> using 2019-07-07 10:44:06 -07:00
cf5ebf2059 Cleanup 2019-07-07 10:09:22 -07:00
9c20e72de3 Cleanup 2019-07-07 07:58:30 -07:00
79209598f5 core_format_specs -> sprintf_specs 2019-07-07 06:54:25 -07:00
5488d0b53a spec -> specs 2019-07-07 06:39:20 -07:00
e4f84ee1c6 Refactor format_specs for #1109 and #940
Refactor `format_specs` and related APIs to support variable-width fill
(#1109), improve naming consistency, remove legacy setters (#940), and
optimize layout.
2019-07-06 20:57:40 -07:00
8e0dcd20b3 Remove old deprecated functions 2019-07-06 07:55:52 -07:00
1d3e3d8c04 Make the 'n' format specifier work with grisu disabled 2019-07-04 15:15:14 -07:00
bc628f8d49 Fix EXPECT_DEBUG_DEATH_IF_SUPPORTED (#1214) 2019-07-04 07:55:36 -07:00
edd13fcc1c Fix small number rounding with fixed precision in grisu 2019-07-04 07:11:40 -07:00
6a031347e5 Remove unnecessary qualification 2019-07-03 21:14:45 -07:00
bc14c6ee20 Use the decimal point from locale 2019-07-03 17:55:00 -07:00
476f25cd81 Remove ancient gcc workaround 2019-07-03 16:37:02 -07:00
c9d5a08ed8 Add the 'n' float format specifier 2019-07-03 16:36:23 -07:00
f487ddbdfa thousands_sep -> add_thousands_sep 2019-07-03 16:18:12 -07:00
d8fd1699b1 Make data names follow naming conventions 2019-07-03 16:14:14 -07:00
c286ffc88d int_traits -> uint32_or_64_t 2019-07-03 16:01:21 -07:00
1289782f06 Get rid of add_thousands_sep 2019-07-03 15:50:05 -07:00
2249f5571e Simplify thousands separator handling and cleanup 2019-07-03 15:30:45 -07:00
e76446958c Cleanup and remove deprecated visit 2019-07-03 14:31:01 -07:00
de37de912b Remove deprecated color API 2019-07-03 14:19:24 -07:00
df1a3a141b Remove null_terminating_iterator 2019-07-03 14:00:55 -07:00
78dec87a46 typedef -> using 2019-07-03 11:05:20 -07:00
ded0a3bb3d Internalize undocumented basic_writer 2019-07-03 10:01:03 -07:00
83174f2a1f Refactor ranges 2019-07-03 07:23:08 -07:00
c2e84ee9cc Fix FormatTest.StrError on Solaris 2019-07-03 06:09:24 -07:00
2711cb1672 Apply clang format and other minor formatting tweaks 2019-06-30 08:52:15 -07:00
ab0ba8a9d0 Don't emit decimal point if there are no trailing digits (#1210) 2019-06-30 08:44:59 -07:00
bd3fd3bfda clang-format 2019-06-30 06:58:49 -07:00
260c115908 Fix formatting of 0.0 with (#1210) 2019-06-30 06:54:41 -07:00
9d97201ede add oss-fuzz support
see https://github.com/google/oss-fuzz/pull/2381

the history of the fuzz branch is long and messy and
is difficult to rebase on top of the current master.

Squashed commit of the following:

commit b9d6db50010e185d0af2590a35472e9334102248
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Jun 29 21:50:34 2019 +0200

    update exception with a more accurate description

commit f3fbaf60cc80c7f57fa95962dc0069b10c3d3e61
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Jun 29 21:34:55 2019 +0200

    fix missing flags in reproduce build

commit 40a17bec7a1ad724203577842a254ca9c42da388
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Jun 29 21:22:48 2019 +0200

    move check for large precision values closer to where needed

commit ef6e23e1f52d639c5aec1a1e713157cec380a8c3
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 28 20:55:34 2019 +0200

    simplify the fuzzer build script

commit eadee6e0557be6df695e80f0f2717046a29846e0
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 28 20:47:54 2019 +0200

    minimize source code pollution

commit 1ece6416438f199c164ee9aa89f42ad1f21a4985
Merge: f404079b 037b84f2
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 28 20:22:52 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	CMakeLists.txt

commit f404079b4e00e51b0d5a4c9218cbe7afb350b777
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 28 20:20:52 2019 +0200

    make named_arg write into a string or a memory_buffer

    this makes the fuzzer consistent with the others.

commit 545dbe136817eef9e734c32991a324874a51bb4a
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu Jun 20 06:34:17 2019 +0200

    tidy up extra newlines, missing std:: etc.

commit 2d816ef2b13fc2a46c0df76a91f7912bd7196087
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:49:08 2019 +0200

    update unit test to handle expected result following review comment

commit a5b9a26808d0165acd2edc4c3baabf9bff40d8bd
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:40:06 2019 +0200

    update build script to reflect changes after review

commit 8411cb78984f76c74bca273c0bb18918e084e711
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:39:39 2019 +0200

    review comment: clarify what the .gitignore is for

commit 18d9e7bb43d98568fe491e076106e4fa29070b33
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:36:56 2019 +0200

    review comment: don't touch root .gitignore

commit 7683d7faa116a6e261da824ec6c1a6a75689841b
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:35:40 2019 +0200

    review comment: condiionally include main.cpp

commit be0bdaeb27b0c1914cc0b0fd85c2b3bcc6fd2245
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:31:28 2019 +0200

    review comment: drop commented out code not working on travis

commit 013429812d7fb745eec146296623ea245c4848b4
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:30:26 2019 +0200

    review comment: renumber case labels

    the old ones were to be able to reuse the corpus, let's drop it

commit f66fe7bead4a71978f21d9e47a8f3f9e4935fccd
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:06:47 2019 +0200

    review comment: libfmt->fmt

commit 4a4ddb654dd5b90646cd7e6ff45318c17b66dc9f
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:05:32 2019 +0200

    reuse fmt_safe_duration_cast

commit 0a1679411a8bd77c2ff34e1cd572307c92e12040
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:02:48 2019 +0200

    review comment: name convention, better name

    C was for "chrono duration"

commit 63084cac00b798c636e0dc13207df46a5c4539f6
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 20:00:50 2019 +0200

    reuse earlier extract fmt_safe_... function

commit b23388d4d7f919163ead9a9e9bdd50d14daf80b7
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 19:58:55 2019 +0200

    review comment: don't output inf.inf

commit 6f861f1d89d2127bbe2446d176d59a354665cc15
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 19:57:00 2019 +0200

    review comment: extract function for invoking safe_duration_cast

commit df19bc87ccff77d4bedf54fa3d3992f78ef699bd
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 19:47:30 2019 +0200

    review comment: leftover garbage

commit 84eea802efb1164c4f2a83b2e480a7c5bdf4e921
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 19:45:22 2019 +0200

    review comment: turn cmake option into macro for SAFE_DURATION_CAST

    decided to have it on by default

commit c3a159498c2544a52662cd03d23d5a1d00537bba
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 19:40:14 2019 +0200

    review comment: extra newline

commit aa556875c5161d817c347ff984dad171c7a35df9
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 19:38:49 2019 +0200

    review comment: file name convention

commit 4102d82c455324bda4ccd64072eab86b3f0ecebf
Merge: 28add37d 4912cff6
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 17 16:29:29 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 28add37df3944cbaa00f614e8063210a6d83c17c
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 21:44:06 2019 +0200

    disable check to pass travis

commit 4119378aedfd3e4063058e8f1f03c29d9f44d5e8
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 21:32:39 2019 +0200

    add missing include

commit ba2efb82f20d6ecb5e49a8c6ced96a7febedc175
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 21:04:41 2019 +0200

    try working around build issue on travis

commit 380671a2cb6e52f2b7d5eaad409d491baba5b7e6
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 20:22:41 2019 +0200

    write positive infinity without sign

commit fd72b9adace17e00c46aae24e061bf14c3af6bb1
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 20:20:50 2019 +0200

    remove leftover from merge

commit 1ae3128be2c53914e4c840d12e1b02c59758c378
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 20:14:45 2019 +0200

    format to buffer instead of string

commit 1d83a561244c2fe81231d17e911c6eb24c87cac4
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 19:50:19 2019 +0200

    fix warnings

commit a33b45a7bb5cf70eb3ef1cd95908282621195f1f
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 19:43:46 2019 +0200

    refactor and fix warnings

commit 02afb12dd5b05804a1a8b55e1b9fb7e3de593e84
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 19:17:27 2019 +0200

    use fixed size input

commit 35f84c8cf20efb18a137efecd10dcdc6bcebf7b5
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 19:07:43 2019 +0200

    factor out main into a separate file

commit a23b7a198ba739dd813897901855c98441e6f29b
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 18:56:40 2019 +0200

    refactor

commit 9a3f4cfb3bc32a304a1a49b8ff24fbc2f924266c
Merge: 7842582a 12f46838
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri Jun 14 18:20:03 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	include/fmt/chrono.h

commit 7842582a0089c24a5d44bbb2d156beb732bb7b58
Merge: 90cab5aa cbbee1b3
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu Jun 13 10:41:34 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 90cab5aa023271a3a746cf9c60dd613b4546ca10
Merge: 8feb8a3f e5422db4
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed Jun 12 18:49:08 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 8feb8a3fe20da043a8303e59b1580b4b2862cc57
Merge: e9fabac1 87fbc6f7
Author: Paul Dreik <github@pauldreik.se>
Date:   Tue Jun 11 19:18:35 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit e9fabac1dd6d710fec1b30ca51dc953f57f2f9f5
Merge: eaff9316 e1a67b52
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 10 22:38:36 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit eaff93166402ff9a16a9ab0fab1081104f4f06b8
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 22:29:01 2019 +0200

    drop old crashes

commit 7f861e481abb7367bd187c92fe202e25d36d0dd0
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 21:47:30 2019 +0200

    build fuzzers as part of the linux clang 6 build

commit 42c339066dce148723f452d4a94487a6ac80637f
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 21:42:12 2019 +0200

    travis has old libs

commit 9264e3ac82582a941eba3501301dee1468175c08
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 21:33:26 2019 +0200

    more travis workarounds

commit c6eed3adaf6cf65d440dd58c88a034f61c55114f
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 21:27:49 2019 +0200

    travis workaround

commit 5e230d6240841dbc67f0c08ace3a1c24defea54f
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 21:15:36 2019 +0200

    fix constexpr issues

commit cc5fc033479c769a8ac19115aef48020c532c943
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 21:11:13 2019 +0200

    add a fuzzer build

commit 3997375296eca0d0455e0935ef3aed8b010ecf2d
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:53:04 2019 +0200

    fix minor documentation errors

commit 1572411261abd5c0756ff2998ab707f1131d4fdf
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:49:25 2019 +0200

    polish the documentation

commit 9e5274437cfc3e9c82131c14cf7f3a0abdb10025
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:32:45 2019 +0200

    remove unused headers

commit 4b2492a5e037d3153de342ef7f2729b69e8f5dce
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:28:12 2019 +0200

    clang format

commit a0004ebb417bce5a24c15ea65a0f3741e45b8480
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:27:02 2019 +0200

    format also void*

commit 820142ee2076ae17fea25857c41a1ecdca4a8521
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:12:21 2019 +0200

    improve two_args with lessons learnt from the others

commit 7b8fd7f5123fccf78b600c69ca25d025582c095e
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:11:18 2019 +0200

    improve function names

commit 641bf36a7a061abf6a02d5e31f3adeb33b079f43
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:07:29 2019 +0200

    clang format

commit 7975c0c3cbe19a7159336ad8fcb170fa6259b1cb
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 20:06:02 2019 +0200

    apply lessons learned from chrono fuzzer on sprintf

commit 972124c9f921f8ef786f99c294f556bb7dd9b9ee
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:55:49 2019 +0200

    format to buffer instead of string

commit 7b015c692364d1e087a59dfe83dda1b8f8fd2991
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:50:25 2019 +0200

    apply lessons learned from the chrono fuzzer at one_arg

commit daa8ea95dd71704e367b760fa6605f5a7cac6890
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:41:17 2019 +0200

    renumber cases

commit a667365d0e0f3eb0bc6d6f2cfee5e128a6574aee
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:38:06 2019 +0200

    clang format

commit e0e361b8a3594c43e131d661a28381613a186c2b
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:36:35 2019 +0200

    disable fuzzing by default

commit ccb4274ab246ee1fe3becb2b73432179a8a5fe6f
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:36:25 2019 +0200

    refresh named_arg with lessons learnt from the chrono fuzzer

commit 60da706d4ef35c18eb967bf4ba8d395ef05a9c61
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:36:00 2019 +0200

    fix build error

commit e361bfc24246d7c7e91f5e45209eda2f22689d98
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 19:00:11 2019 +0200

    add comment about formatting to string vs. memory buffer

commit 74c0ed062d34eae1786ca699886ad4f3bccc7fd1
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 18:51:23 2019 +0200

    try to use better names

commit 4efea36f77020eecf3c7826f891417f94aedf6f4
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 18:46:07 2019 +0200

    fix clang build error

commit 03cdd2e4631ad302dde3e1048671d3bb08956096
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 18:44:21 2019 +0200

    drop workarounds fixed upstream

commit e936829ebbd97ed2e6f8c5f595b414c0982f2e4d
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 18:41:02 2019 +0200

    move the fuzzers into the test/ subdirectory

commit 2967765698259764c1f06966a7cdefbc5365e5f2
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 18:30:03 2019 +0200

    revert temporary tests handled upstream

commit 749c5027b0eab3d90d8fbfb6e55ee313a7f7dfe4
Merge: dee69088 5d9100fa
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 17:49:00 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit dee690881bd33bb77bee1d5ffce643e8bef84a33
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 17:40:37 2019 +0200

    keep documentation comment formatted properly

commit 87d2c99487eef586ce54d432697f384cbe7a50e1
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 17:36:12 2019 +0200

    switch to fmt constexpr macros

commit c23fa59139c425a3dfa2e5eeaeb3269c251c90d1
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 17:30:22 2019 +0200

    clang format

commit 9e58207e9b24a8cc90c721277405b409dd61740d
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 17:27:03 2019 +0200

    get rid of safe_duration_cast submodule

    replaced with an embedded miniature version

commit a4d36eac46e5db45ded96f80f84986f4f76ea0ec
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Jun 9 17:25:00 2019 +0200

    add safe_duration_cast into fmt

commit 7d5b0ecef37722c40952251c88d74a2552221d84
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Jun 8 22:39:33 2019 +0200

    mark #1194 as fixed

commit ee91514ecf7a8788f2081996db85eef50d7cd57b
Merge: 60569117 4faadff0
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Jun 8 22:25:37 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 6056911784bc86e6caf56a61fd64142f113d531e
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed Jun 5 09:58:30 2019 +0200

    format to small size buffer instead of string

commit 9f006097255c239188840b589f6e39cbb4476481
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed Jun 5 09:45:23 2019 +0200

    switch to fmt::string_view and workaround reported bug

commit 387de0d9440852fac974ff165b2555d54e2380da
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed Jun 5 06:26:15 2019 +0200

    ignore build directories

commit 55da271c5bb3c11a239c4e46570b6741803ce329
Merge: 3716491e c264e641
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed Jun 5 06:12:36 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 3716491ec51c34a918834857c67300eea180ba02
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 3 07:03:29 2019 +0200

    fix UB in on_second

commit 2740241b13b7417a4dae655f825ea5a551ffe7ba
Merge: 1c258402 d54e64b3
Author: Paul Dreik <github@pauldreik.se>
Date:   Mon Jun 3 06:37:18 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	include/fmt/chrono.h

commit 1c258402a4bd03f390e0256fa9475cc5187d37f9
Merge: ca9596d1 f57227a1
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Jun 1 08:01:58 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit ca9596d1c91b0315b407ce2c4b3e9e5ba1aeb640
Merge: 1c274cfd d07cc202
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 30 19:42:33 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 1c274cfd4112138bfc59dd16f58022016128fe85
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 30 08:12:10 2019 +0200

    make it easier for the chrono fuzzer to explore

    using a fixed size makes the cases cross pollinate
    each other better.

    the execution speed is much higher as well

commit f0d7cccdc70c98576b7129428c416e7c9e68a8aa
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 30 07:36:21 2019 +0200

    add a build adapted for analysis of fuzzing performance

commit 56f7cf3fa979de415174a10b18221727e3138b7b
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 30 06:25:10 2019 +0200

    allow negative values again

commit a77a5fc505bbeab1cfa36be16d40f7799689317a
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 30 05:45:55 2019 +0200

    fix UB on signed int overflow in chrono_formatter constructor

    see https://github.com/fmtlib/fmt/issues/1179

commit b6a592720be520b58ed2f2d8668ffc6c8b71f0f7
Merge: 492a2046 30bce6c1
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 30 05:26:30 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	include/fmt/chrono.h

commit 492a204623c3c4bbf04c9d47d69979d3a484959c
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 21:36:00 2019 +0200

    fix bad assert

commit 0ae68b03fbb0e80e292a01f529d5cd7e76349907
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 21:35:49 2019 +0200

    add unsigned types for chrono fuzzing

commit 2753d7db76645e8847ff2110c5e98f5c8de4a6b9
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 20:25:21 2019 +0200

    use C++17

commit bc12742f098ec8b513985daedc57faa518203eb0
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 19:44:59 2019 +0200

    add symlink for safe_duration_cast

commit 67201d2639b93736768e109d73b3e9ccc9401c48
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 19:40:40 2019 +0200

    turn on safe duration cast for the fuzzer builds

commit 31a70080a63a5213594e4b4e6a33e7e315cf756e
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 19:32:52 2019 +0200

    clang format

commit 981e30c5782d04453ece1b31e887da4f29268370
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 19:30:19 2019 +0200

    reduce maximum allocation size

commit 7ba51da81de7ecbc5498a22dc29de5b0648bcad2
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 19:30:01 2019 +0200

    make nan unit test pass

commit 95b4b9c28a589c30727826dd4e1367bebfad5894
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 18:39:08 2019 +0200

    special case nan and inf

commit 2673c965506e51d150c005340698a6e15d98aaba
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 16:40:11 2019 +0200

    build a fast fuzzer, for making coverage fast

commit db52b62612fd7ea3ceeaee05584fd8cb83e54a35
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 16:39:48 2019 +0200

    add safe duration cast as submodule

commit c8a028faec5d91728472f5de01ea8b1766fb929d
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 16:00:26 2019 +0200

    enable chrono fuzzing for non-negative values

commit de3555cc573e561691858ca16586f8b45a3ae703
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 13:06:35 2019 +0200

    try start using safe duration cast

commit 5c3245118c3debcb3f6f69c04c2c32d48449ee16
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 09:59:34 2019 +0200

    add failing test

commit 3a565d3b091c29210e24042f86869fecafb70914
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 09:59:09 2019 +0200

    fix cmake option type (should be string, not bool)

commit 61c67564207a13992b1c69d95614b2c4aec5df86
Merge: 63e7b9e3 bb254d14
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 29 07:03:42 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 63e7b9e32c714c594d019ef463c9c40a3510a2f2
Merge: 7dd1d80f 5e7bdf1b
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri May 17 19:17:20 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 7dd1d80f3a32465d0fa13ce733bff8686a5b0bad
Merge: 2c9aa5a3 2a9e8b52
Author: Paul Dreik <github@pauldreik.se>
Date:   Tue May 14 19:38:32 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 2c9aa5a31e64af25f8bb4afa8134258822532d3e
Merge: 16a442c8 2c77562b
Author: Paul Dreik <github@pauldreik.se>
Date:   Tue May 14 06:33:16 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 16a442c864dbdce70c22b4a859dba5e3b5edaf35
Merge: b1d70b61 f4dfd6e3
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 12 15:24:31 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	include/fmt/chrono.h

commit b1d70b6144c7a61e580eb44ec7d1bdd2368f5531
Author: Paul Dreik <github@pauldreik.se>
Date:   Fri May 10 08:52:57 2019 +0200

    prevent excessive time (found by oss-fuzz)

    the following triggered this:
    std::string message =
          fmt::format("\377{:.214718908}\377", fmt::arg("/\0", 0.f));

    there are probably more places with calls to fill_n which could be checked

commit 9a91093a6b20fd22afd6739f5dcba3b00f6f8eaf
Merge: 7de0fdec e9bab6d0
Author: Paul Dreik <github@pauldreik.se>
Date:   Thu May 9 06:06:32 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	include/fmt/chrono.h

commit 7de0fdec38270f2d0302413904a5ef1b13d47177
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 20:08:53 2019 +0200

    clang format

commit bb375e1ca10eb3cc2c6684bf698ad4738ab7eb10
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 19:47:10 2019 +0200

    seems to pass the unit test now (except for the nan stuff from victor)

commit 786b4b7351bc8e305ad7e68d11ca6b542f66d456
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 19:25:23 2019 +0200

    add assert triggering data, and unit test

commit 2790e480b81ec83d00315aa69407fe71b8c4c637
Merge: fa859a05 ca978b3d
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 18:42:51 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit fa859a05c2c3abef263166f3a44cdbaa3122d642
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 18:18:54 2019 +0200

    add crash

commit 1f6e341b1c4bc966a44c7a98b63f22bd65958d0b
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 18:18:27 2019 +0200

    assert floating point is finite internally

commit 50877748d08a0f4433af4f1213c5bc9021e76e7a
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 17:32:03 2019 +0200

    invoke undefined behaviour inside chrono

commit bac7ac4149f2d001f7b36236e1710484674d029b
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 17:31:35 2019 +0200

    refactor the fuzzer build script

commit b19c4cd84a0c8b6d4a7beb281ad881156173ce78
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 16:25:11 2019 +0200

    add one more crash

commit 7607592e06ebaa189dc180441fa1863430e0938e
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 16:24:05 2019 +0200

    add crashing input

commit b059a98b27b40cd284e08a54493c25363d743557
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 16:21:25 2019 +0200

    trigger undefined behaviour with NaN durations

commit 7cce33250282b397c00159e6809125f5fc1c0190
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 16:20:51 2019 +0200

    add asan only fuzzer

commit 757319a4e30978d8661b3be8f75937266071b413
Merge: a574b21c c1d430e6
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun May 5 06:34:59 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	test/chrono-test.cc

commit a574b21c840339abef5e4ad33612b6efac6ad54b
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat May 4 12:54:13 2019 +0200

    disable chrono fuzzing for now

    it triggers integer overflow and is not trivial to solve.

commit ff17322bceba53e0c2d9ebcf3756115ad148195e
Merge: d6a59851 29c10fbf
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat May 4 07:29:39 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit d6a598511c7dc0c208c2d688b2943b0d7c092029
Merge: 663b1592 4a4d72f9
Author: Paul Dreik <github@pauldreik.se>
Date:   Wed May 1 20:44:16 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

    # Conflicts:
    #	test/chrono-test.cc

commit 663b159235f8ae5f58fe80bb02d49bfa392056b0
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 22:36:07 2019 +0200

    add crash case (triggers assertion)

commit 082a5cb226142ea30b415d4231cea9425748741a
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 21:44:19 2019 +0200

    add const

commit b8d70919ea6be0d2e4c58ef82887496f55125ba9
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 21:29:43 2019 +0200

    provoke assertion

    fmt/include/fmt/core.h:246:
    typename std::make_unsigned<_Tp>::type fmt::v5::internal::to_unsigned(Int)
    [with Int = long int;
     typename std::make_unsigned<_Tp>::type = long unsigned int]:
    Assertion `(value >= 0) && "negative value"' failed.

commit e1966013af4eb7febf047d4629cc6236a6aae0e3
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 20:46:16 2019 +0200

    add more crashes

commit 1394ae3fe915319ce7dc63d6a9dc820a29c9539e
Merge: 89338cad 4c721e3a
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 17:16:14 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 89338cad4eed9441644ec8c5f1687b511c829ea4
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 09:40:32 2019 +0200

    add notes on how to reproduce crashes

commit 7dc3e4c7223617da274c4cccb9cf5459d0510e0b
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 09:28:20 2019 +0200

    add crashes from chrono duration

commit b62e8bc783134c2d15ebf0372c8a61b41624e6b1
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 09:26:53 2019 +0200

    rename fuzzer

commit 7f4ab2b80d072fe3ad96e37e45f3fa807a85c99f
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 09:15:38 2019 +0200

    clang format

commit a6cc2a35a9799e88b9ed89e578b7aefd9b09ad09
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 09:12:04 2019 +0200

    add chrono duration fuzzer

commit 682713c9a61d52b46e95fdb7d970a8733f77ce88
Merge: 8b934b37 8d8ea21c
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 28 08:07:56 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 8b934b37161d1389de603ced6560982507bb7ae5
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 10:23:44 2019 +0200

    clang format

commit 793d97b9af33269f5628094f547f9771e968e3f2
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 10:23:17 2019 +0200

    tighten memory allocation

commit e2301f2430b15c9817433206597ef82c990f49a0
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 10:19:56 2019 +0200

    clean up and set license (BSD 2-clause simplified, same as fmt)

commit e64c3fb35719afa644dee1f9f17829cace6e17ff
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 10:04:23 2019 +0200

    clean up and add afl

commit ab46241206aaf46759fd3f292ee4a1088b652d15
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 09:54:48 2019 +0200

    drop c++17 requirement

commit 20c01e1acf330c8a28192f55b16efeebddb72ab0
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 09:25:19 2019 +0200

    initial oss-fuzz compatible version

commit 6cbd91a37cf36a1d0e994bb16cf44a12622f7dca
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 08:42:51 2019 +0200

    initial commit of fuzzers from https://github.com/pauldreik/fuzzfmt

commit eaddfb16d86ef1c259b737e2aab40145b0c956a6
Merge: e37d7db3 134904c8
Author: Paul Dreik <github@pauldreik.se>
Date:   Sat Apr 27 08:38:19 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit e37d7db3b938c82f569d71e6bb00bd1bf8394db7
Merge: 99b2e08b bd516e34
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 21 17:28:06 2019 +0200

    Merge remote-tracking branch 'upstream/master' into fuzz

commit 99b2e08b6bef25b793df5ef07621c9c4402587de
Author: Paul Dreik <github@pauldreik.se>
Date:   Sun Apr 21 10:30:56 2019 +0200

    stop high memory use when fuzzing
2019-06-30 06:10:07 -07:00
037b84f214 Fix warnings 2019-06-24 16:46:48 -07:00
e37ee419c6 Export exceptions destructors
Reenabling Compiling and Linking as DLL.
2019-06-24 08:53:35 -07:00
e3488fcae2 Fix Warning
QtCreator (4.9) Clang Code Model (Clang 7) gives the
warning:core.h:404:11: error: an attribute list cannot appear here
core.h:116:24: note: expanded from macro 'FMT_NORETURN'
2019-06-24 08:53:35 -07:00
d5d5865615 Move strtod_l check to CMake since it's very system-specific 2019-06-23 17:10:57 -07:00
29ef7d31e4 Fix a warning 2019-06-23 11:09:15 -07:00
af83192d79 Fix warnings 2019-06-23 08:14:15 -07:00
6952732b6c Remove misplaced checks 2019-06-23 06:33:07 -07:00
b97e5d8c2a Remove deprecated fmt/time.h 2019-06-23 06:13:17 -07:00
572b077dbf Fix warnings 2019-06-22 20:52:33 -07:00
72e519a4bd Add formatter<std::string_view> 2019-06-22 20:26:28 -07:00
635e01fe74 Update <format> to the current wording 2019-06-22 13:44:48 -07:00
0e72c98043 Make undocumented output_range internal 2019-06-19 20:08:11 -07:00
f13906f408 back_insert_range<internal::buffer> -> buffer_range 2019-06-19 14:12:29 -07:00
22ddd4b989 Simplify feature checks 2019-06-19 07:46:09 -07:00
bb827341ed Add FMT_FUNC where necessary 2019-06-18 09:48:23 -07:00
64c54703d4 Cleanup 2019-06-17 11:02:18 -07:00
1c3197b8d8 Fix warnings 2019-06-17 09:21:29 -07:00
fd2292f13a Cleanup checked iterators 2019-06-17 08:30:37 -07:00
4912cff65d Fix handling of mapped types in compile checks (#1200) 2019-06-16 16:06:06 -07:00
4639843839 Cleanup 2019-06-15 09:44:51 -07:00
e29708ee57 uintptr -> fallback_uintptr to avoid confusion with uintptr_t 2019-06-14 18:47:36 -07:00
f03a6c5325 Cleanup 2019-06-14 13:38:44 -07:00
6c3d584e67 Simplify warning suppression 2019-06-14 13:03:34 -07:00
12f4683883 Make iterator_t an alias template 2019-06-13 21:32:58 -07:00
874d6727e4 Remove workarounds for pre-C++11 compilers 2019-06-13 20:56:08 -07:00
a9940192fb Fix warnings 2019-06-13 08:56:35 -07:00
34b5418359 Fix warnings 2019-06-13 07:29:10 -07:00
cbbee1b385 Fix handling of hexfloat 2019-06-12 20:53:01 -07:00
92a44db11c Fix warnings 2019-06-12 19:44:08 -07:00
d05d42751c Remove old msvc workaround from arg_formatter_base and fix warning 2019-06-12 19:27:23 -07:00
d32fe0f3f6 Fix hadling of nullptr 2019-06-12 09:22:36 -07:00
e5422db4b2 Fix handling of uintptr_t 2019-06-12 08:16:41 -07:00
9d7b64a259 More cleanup 2019-06-11 22:11:47 -07:00
5e293bd97a Remove unnecessary qualification 2019-06-11 20:36:39 -07:00
4a502d9802 Add remove_reference_t 2019-06-11 20:28:05 -07:00
d384cdd397 Make is_contiguous_back_insert_iterator internal 2019-06-11 20:16:57 -07:00
32544b6108 More cleanup 2019-06-11 19:22:19 -07:00
39f522a13a get_types -> encode_types 2019-06-11 18:50:14 -07:00
d7d2bebf99 Remove redundant typename 2019-06-11 18:29:47 -07:00
9427f15bef Fixed issue with formatting to an array of chars 2019-06-11 18:09:18 -07:00
87fbc6f756 get_type -> mapped_type_constant 2019-06-11 07:23:39 -07:00
a48daa60e5 Remove remove_volatile 2019-06-10 20:25:04 -07:00
afdbbac75c Cleanup 2019-06-10 18:10:26 -07:00
e33fe14f5b Simplify visit 2019-06-10 08:28:39 -07:00
e895da2ec3 Clean up value 2019-06-10 07:58:00 -07:00
e1a67b5285 Remove TODO 2019-06-10 06:54:09 -07:00
a291f07e1a Clean up argument mapping 2019-06-09 22:27:40 -07:00
5d9100fa2f Move char8_t to fmt/core.h 2019-06-08 09:42:11 -07:00
4faadff0a0 Add preliminary user-defined type support 2019-06-08 09:04:46 -07:00
5d48733596 Clean up value construction 2019-06-07 19:27:20 -07:00
0f0b42861b Cleanup 2019-06-07 17:53:25 -07:00
209db68b28 Get rid of FMT_CONSTEXPR11 2019-06-07 14:00:57 -07:00
cb4c59495e Deprecate convert_to_int 2019-06-07 13:38:08 -07:00
40779749ac is_formattable -> has_formatter 2019-06-07 06:51:21 -07:00
b3cf8613b1 Make formatter specializations override implicit conversions 2019-06-07 06:25:46 -07:00
3fdba04924 Reduce the number of nontrivial formatter instantiations 2019-06-06 22:02:53 -07:00
f5f3ffac59 Merge string make_value overloads 2019-06-06 08:29:16 -07:00
a38b99a188 Fix a regression in named argument handling introduced by prepare 2019-06-05 16:50:04 -07:00
aa31028b2c char_t_impl -> char_t 2019-06-05 10:13:34 -07:00
0787d69747 Simplify SFINAE 2019-06-05 09:02:33 -07:00
bae00aa8d7 Simplify is_string 2019-06-05 08:41:00 -07:00
388bb389e6 integral_constant -> bool_constant 2019-06-05 08:14:47 -07:00
7e39c7e6f4 Put stringy stuff together 2019-06-05 07:43:20 -07:00
3eff8f94d2 Update docs 2019-06-05 06:24:18 -07:00
469a4bbd35 Use enable_if_t 2019-06-04 21:38:18 -07:00
d2ee5f2407 Merge tests 2019-06-04 21:31:23 -07:00
c264e641ea Add conditional_t for pre-C++14 2019-06-04 18:50:30 -07:00
4aa0dc578b Fix docs 2019-06-04 18:02:55 -07:00
064ce6b6c0 Specialize is_char for character types 2019-06-04 17:08:58 -07:00
7893d85394 Clarify why we cannot have nice things 2019-06-04 08:47:25 -07:00
5bafcb437b Add comments about things broken in MSVC 2019-06-03 19:14:16 -07:00
1530242551 Fix signature in the docs 2019-06-03 17:35:15 -07:00
eddb84cfc0 Fix formatting of exotic characters 2019-06-03 17:08:00 -07:00
7e42c65bb6 Document a more useful to_string_view overload 2019-06-03 11:57:33 -07:00
1e6e87cb74 Update docs 2019-06-03 11:35:12 -07:00
0c6a6e0250 Get rid of the FILE* hack and reword apidocs 2019-06-03 10:27:00 -07:00
1653244c69 Fix compilation issue on VS2019 (#1186) (#1191) 2019-06-03 08:34:27 -07:00
d54e64b3c8 Make buffer_context an alias template 2019-06-02 18:28:49 -07:00
ec6651087d Remove old is_constructible workarounds and replace typedefs with using 2019-06-02 16:46:45 -07:00
4d4b8c238d FMT_CHAR -> char_t 2019-06-02 15:03:37 -07:00
56d2b91108 Install git 2019-06-02 13:57:19 -07:00
89d6c959b2 Fix cmake link 2019-06-02 13:24:32 -07:00
76ef39fc50 Update vagrant config 2019-06-02 13:19:33 -07:00
af2c73772c Implement parsing of string_views 2019-06-02 11:11:28 -07:00
9df0e2d1f1 Implement string parsing 2019-06-02 07:39:38 -07:00
5b7bbf8853 Revert "FMT_CHAR -> char_t"
This reverts commit 4c650057a0.
2019-06-01 15:01:04 -07:00
4c650057a0 FMT_CHAR -> char_t 2019-06-01 12:32:24 -07:00
2833c76f22 Move char_t to template param to reduce symbol size 2019-06-01 12:14:27 -07:00
67feef5589 Make enable_if_t more std-like and move to fmt namespace 2019-06-01 10:05:16 -07:00
78daa50ffc Fix handling of chrono durations with minimal signed rep 2019-06-01 09:36:37 -07:00
87e4ea2906 Fix a warning 2019-06-01 08:24:36 -07:00
c56b17029a Add msvc2019 to CI 2019-06-01 07:50:15 -07:00
c929684e33 inlines count_code_points(basic_string_view<char8_t)
count_code_points(basic_string_view<char8_t>) was defined in
fmt/format-inl.h, and only declared in fmt/format.h, but not defined
in src/format.cc. This commit moves the overload to fmt/format.h and
inlines it.
2019-06-01 07:17:22 -07:00
f57227a148 FMT_ENABLE_IF -> enable_if_t 2019-05-31 20:09:19 -07:00
634f707f2b Simplify char_t and remove msvc2013 from CI 2019-05-31 19:28:37 -07:00
406e632bd1 result_of -> invoke_result_t 2019-05-31 08:54:32 -07:00
49f78a427b Demacrify 2019-05-30 21:30:29 -07:00
637bf3c6d9 Workaround a bug in clang-format 2019-05-30 11:46:31 -07:00
8302c2f33b fmt::internal::declval -> std::declval 2019-05-30 09:46:10 -07:00
d07cc2026b FMT_EXPLICIT -> explicit, FMT_NULL -> nullptr 2019-05-30 07:42:36 -07:00
4a7966c773 Drop gcc 4.4 from CI 2019-05-30 06:51:59 -07:00
9b3c24b99b One weird trick to simplify docs; doxygen hates it 2019-05-29 21:09:43 -07:00
30bce6c14c Fix a few chrono formatting corner cases (#1178) 2019-05-29 18:02:26 -07:00
e5512c5d57 Use static_assert instead of SFINAE in arg(...) 2019-05-29 13:21:24 -07:00
afc571aedc Document join and relax its compiler requirements 2019-05-29 11:21:05 -07:00
ad360a62b0 add gcc 8 c++17 release build 2019-05-29 06:50:43 -07:00
3cf12d7b11 add gcc 8 c++17 build 2019-05-29 06:50:43 -07:00
bb254d146b Disable std-format-test by default 2019-05-27 20:02:08 -07:00
291ba837f2 Remove wrong compile-time checks from printf (#1173) 2019-05-24 07:50:13 -07:00
01c631af95 Implement unsigned and long long parsing 2019-05-19 07:34:54 -07:00
4fcd4a4bd0 Reuse parse_format_string in scan 2019-05-18 14:18:06 -07:00
2346779d6b More documentation fixes 2019-05-18 09:30:36 -07:00
afc1a74a6c Minor documentation fixes 2019-05-18 09:10:17 -07:00
ad0eade471 Remove MSVC nonsense 2019-05-18 09:05:19 -07:00
28c187bcd7 Capitalize titles 2019-05-18 08:56:49 -07:00
91bb3aaf06 fmtlib.net -> fmt.dev 2019-05-17 15:42:00 -07:00
5e7bdf1b97 Clean up vagrant config 2019-05-16 12:22:04 -07:00
570453f271 Add a vagrant config for testing gcc 4.4 2019-05-16 06:26:16 -07:00
ef6282fc45 Fix gcc 4.4 build 2019-05-15 22:37:00 -07:00
e3e470bb69 Remove deprecated format_decimal 2019-05-15 10:34:45 -07:00
67179dbc23 Remove deprecated format_decimal 2019-05-15 10:34:38 -07:00
a5ffa735db Fix gcc 4.4 build 2019-05-15 10:20:51 -07:00
5ee0804631 Experiment with scan API 2019-05-15 10:02:40 -07:00
25b72fc4cd Move <format> to tests not to confuse users 2019-05-15 09:08:42 -07:00
d179ec5f8b Simplify Grisu 2019-05-15 08:40:21 -07:00
2a9e8b52de Fix advance_to() and begin() using iterator (#1159) 2019-05-14 08:41:11 -07:00
2c77562b13 Fix ambiguous formatter specialization in fmt/ranges.h (#1123) 2019-05-12 13:03:20 -07:00
98b3775297 Add support for exotic string_view iterators (#1156) 2019-05-12 11:07:49 -07:00
b488df6cfe Fix Grisu3 stopping conditions 2019-05-12 08:56:50 -07:00
f4dfd6e30f Suppress all clang-target-msvc test warning in CMake and other misc fixes (#1151)
* Fix conditional `char8_t` from `format.h` and fix `-Wunused-result` of [[no_discard]] begin() when in c++17

* Suppress `-Winconsistent-dllimport` when in clang-target-msvc

* Suppress warning _CRT_SECURE_NO_WARNINGS in MSVC and -Wdeprecated-declarations

Suppress warning _CRT_SECURE_NO_WARNINGS in MSVC and -Wdeprecated-declarations of POSIX functions in Clang target MSVC.
Those functions are used by gtest.

* Remove FMT_FUNC, mark FMT_API to export
2019-05-11 11:48:27 -07:00
a6e8ed15c4 Disable UDL templates on GCC 9 by default (#1148) 2019-05-11 08:58:39 -07:00
de5da50910 Fix formatting of extreme durations (#1154) 2019-05-11 08:42:02 -07:00
ea2976e6d5 Move internal::uintptr_t test to format-impl-test (#1152) 2019-05-11 06:58:22 -07:00
77d6036cd5 Fix unexpected trailing decimal point (#1153) 2019-05-11 06:34:47 -07:00
ccc318e807 Update README.rst 2019-05-10 10:04:44 -07:00
4c8efd694c Update README.rst 2019-05-10 09:56:40 -07:00
d22d11b5ff Update README.rst 2019-05-10 09:54:42 -07:00
e9bab6d028 Improve handling of large durations 2019-05-08 12:40:33 -07:00
f52c09f924 Fix format_to_n docs in 5.1.0 2019-05-08 12:40:25 -07:00
118d8bccc2 Fix compilation error under MSVC 19.21 (#1140) 2019-05-08 11:20:55 -07:00
6828d549e8 Add FMT_ENABLE_IF_T
for definitions of functions which declared using FMT_ENABLE_IF
2019-05-08 11:20:55 -07:00
3fd134be03 Move test_count_digits to format-impl-test and disable gtest warnings (#1147)
* This translation unit includes `gtest.h` in which using something like `strncpy`

Suppress MSVC warnings from `gtest.h` included.

Warning sample:
fmt-master\test\gtest\gtest.h(2873,10): warning: 'strncpy' is deprecated: This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. [-Wdeprecated-declarations]

* Moving `test_count_digits` to format-impl-test then count_digits<4>(internal::uintptr_t) shall not be exported

* retaining _CRT_SECURE_NO_WARNINGS always defined should be no harms.
2019-05-07 12:09:11 -07:00
ca7c1f89dc Fixed a compile error under MSVC.
The #warning directive is not supported by MSVC.
To still get a message to the user we use #pragma message() under MSVC now.
2019-05-05 14:04:04 -07:00
ca978b3d21 Fix handling of nan durations 2019-05-05 08:53:23 -07:00
c1d430e61a Improve handling of negative durations 2019-05-04 17:52:20 -07:00
38a85502ed Use the same rep type for seconds to prevent overflow 2019-05-04 09:22:09 -07:00
241414028d Eliminate shadowed variable warnings from gcc-7.2
The gcc-7.2.0 compiler (and others) were giving shadowed variable warnings for this include file.  A simple renaming of a couple local variables eliminates the warnings.
2019-05-04 08:00:31 -07:00
29c10fbf6e Fix DLL visibility of explicit instantiation "declaration" of internal::basic_data<void> in header format.h and the explicit instantiation "definition" in format.cc (#1134)
* Update format.cc

As the explicit instantiation *declaration* of `internal::basic_data<void>` in format.h, this explicit instantiation *definition* should mirror FMT_API also.

* Mirror visibility of explicit instantiation declaration 

explicit instantiation declaration of internal::basic_data<void> should mirror visibility of FMT_API

* Eliminate `__declspec(dllexport)` designation on extern template internal::basic_data<> when `extern` affected during exporting phase.

* Add `FMT_EXTERN_TEMPLATE_API` for designate DLL export `extern template`

When exporting DLL, do not designate `__declspec(dllexport)` any template that has any explicit class template declaration a.k.a. `extern template`. Instead, designate `__declspec(dllexport)` at single point where we have explicit class template definition a.k.a. normal instantiation without `extern`

Note: this is a c++11 feature.

* Delete whole `FMT_USE_EXTERN_TEMPLATES` block and its condition

1. Remove whole `FMT_USE_EXTERN_TEMPLATES` block
(trailing `FMT_UDL_TEMPLATE` block)
````
#ifndef FMT_USE_EXTERN_TEMPLATES
#  ifndef FMT_HEADER_ONLY
#    define FMT_USE_EXTERN_TEMPLATES                           \
      ((FMT_CLANG_VERSION >= 209 && __cplusplus >= 201103L) || \
       (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11))
#  else
#    define FMT_USE_EXTERN_TEMPLATES 0
#  endif
#endif
````

2. Delete `FMT_USE_EXTERN_TEMPLATES` condition, only condition, that trailing basic_data class template definition.
````
#if FMT_USE_EXTERN_TEMPLATES
extern template struct basic_data<void>;
#endif
````

3. Replace `FMT_API` with new `FMT_EXTERN_TEMPLATE_API` added in `core.h` for sake of extern template of `basic_data<void>`

* Add `#define FMT_EXTERN extern` only when not `FMT_HEADER_ONLY`

* Replace `extern` on basic_data<void> with the `FMT_EXTERN` condition in core.h

* replace misspelled if !define() with ifndef
2019-05-02 07:49:01 -07:00
4a4d72f917 Fix handling of invalid string in chrono formatting 2019-05-01 10:06:42 -07:00
b3cc9c0567 Merge remote-tracking branch 'upstream/master' into invalidcolons
# Conflicts:
#	test/chrono-test.cc
2019-04-28 21:35:21 +02:00
2e3352fd05 provoke assertion
fmt/include/fmt/core.h:246:
typename std::make_unsigned<_Tp>::type fmt::v5::internal::to_unsigned(Int)
[with Int = long int;
 typename std::make_unsigned<_Tp>::type = long unsigned int]:
Assertion `(value >= 0) && "negative value"' failed.
2019-04-28 21:34:09 +02:00
4c721e3a2f Fix chrono formatting with invalid argument id (#1132) 2019-04-28 07:08:41 -07:00
8d8ea21c69 Partially implement Grisu3 2019-04-27 12:17:21 -07:00
40a7975640 Remove trailing zeros 2019-04-27 07:42:27 -07:00
cb46397dfb Fix typo 2019-04-27 07:13:35 -07:00
134904c886 Re-enabled constexpr tests 2019-04-26 17:07:37 -07:00
bd516e3429 Convert negative precision to zero in printf (#1127)
and remove redundant check in grisu2_prettify.
2019-04-21 07:44:04 -07:00
5efb24dd2b Add specialization test 2019-04-19 17:15:46 -07:00
946498cfbc Fix handling of zero precision 2019-04-19 15:10:12 -07:00
6b20863918 fmt::ptr: support unique_ptr and shared_ptr. 2019-04-18 10:12:11 -07:00
d306585a3f Don't inject internal names into std (#1120) 2019-04-17 13:25:31 -07:00
544b927933 Don't detect C compiler 2019-04-17 06:18:53 -07:00
dc94010fa5 Remove char_traits (#1117) 2019-04-16 17:08:24 -07:00
397e8dd9d5 clang-format 2019-04-15 11:39:19 -07:00
2b415b7af7 Restructure printf_arg_formatter to make it customizable 2019-04-15 11:10:31 -07:00
5d755d0a4e Fix handling of volatile char (#1115) 2019-04-14 12:34:56 -07:00
bade46aae5 Optimize grisu using uint128_t 2019-04-13 13:44:41 -07:00
41fbaeb3b1 Add <format> test 2019-04-13 11:37:52 -07:00
8bc0adb9ba Get rid of obsolete cmake stuff 2019-04-13 11:37:52 -07:00
1763d0e7a2 Add MongoDB to "Projects using this library" 2019-04-12 15:52:04 -07:00
f569c1ba28 doc fix: time -> chrono
Change refs to fmt/time.h -> fmt/chrono.h
RST label rename time-api -> chrono-api
2019-04-12 14:51:32 -07:00
ccd70f59ee Workaround bogus unreachable warnings in MSVC 2019-04-11 17:59:35 -07:00
a4969ebe06 Link to fmt(...) docs from index 2019-04-10 19:22:10 -07:00
a6ad29aa34 Update <format> 2019-04-10 09:09:34 -07:00
52eb3fe274 Update <format> 2019-04-10 08:29:07 -07:00
09e2ac5e46 Update <format> 2019-04-10 07:56:50 -07:00
df4ea0c76c Update <format> 2019-04-10 06:25:42 -07:00
718f60accb Fix shadowing warning (#1105) and clang format 2019-04-08 19:14:32 -07:00
aeb5ad3ce1 Enable [[noreturn]] some. 2019-04-08 07:53:59 -07:00
2808395481 basic_buffer -> buffer
This reduces symbol sizes and gets rid of shadowing warnings.
2019-04-07 20:32:28 -07:00
6e37c20030 Use compile features cxx_auto_type cxx_variadic_templates instead of cxx_std_11 2019-04-07 08:47:17 -07:00
3de3d76a36 Add compile features for cmake 3.8+ 2019-04-07 08:47:17 -07:00
07d5a86a7c Fix warnings 2019-04-06 22:10:10 -07:00
ab1474ef66 Workaround segfault in doxygen and apply clang-format 2019-04-06 15:55:14 -07:00
918ab77baa Try fixing doc build 2019-04-06 10:04:56 -07:00
735b1fadc3 Only update key on Linux 2019-04-06 09:49:35 -07:00
3c531b735b Fix expired key error when installing with apt
Error:
W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: https://packagecloud.io/github/git-lfs/ubuntu trusty InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 6B05F25D762E3157
W: http://ppa.launchpad.net/couchdb/stable/ubuntu/dists/trusty/Release.gpg: Signature by key 15866BAFD9BCC4F3C1E0DFC7D69548E1C17EAB57 uses weak digest algorithm (SHA1)
W: Failed to fetch https://packagecloud.io/github/git-lfs/ubuntu/dists/trusty/InRelease  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 6B05F25D762E3157
W: Some index files failed to download. They have been ignored, or old ones used instead.
2019-04-06 09:32:38 -07:00
f10a7e2e46 Test exotic pointer formatting 2019-04-06 09:04:44 -07:00
bd8177177a Add support for platforms without uintptr_t 2019-04-06 08:11:03 -07:00
0302927f56 Optimize pointer formatting 2019-04-05 20:36:37 -07:00
bb6842ba35 Simplify to_string 2019-04-05 19:15:22 -07:00
b23c8633fa Detect presence of uintptr_t 2019-04-05 07:43:00 -07:00
b588d7f35f Fix a couple of deprecated things. 2019-04-04 16:26:22 -07:00
018d8b57f6 Remove broken snprintf 2019-03-30 20:27:52 -07:00
1987db663b clang-format 2019-03-30 07:48:53 -07:00
a6d1ad741d handle fwrite results #1098 2019-03-30 07:48:53 -07:00
e979c782d3 Extend basic_writer with write() method for doubles with optional format_specs argument 2019-03-25 19:16:41 -07:00
9e1531c1e1 install pkg-config file into libdir
Most pkgconfig files contain a Libs: variable, which is either /usr/lib
or /usr/lib64. If a 32bit and a 64bit variant of fmt libraries is
installed, the last one wins. As a result compiling for the other
bitsize will fail.

Instead of sharedir use libdir as install target.

Fixes commit 9d0c9c4bb1
Fixes commit 287342dab1

Signed-off-by: Olaf Hering <olaf@aepfle.de>
2019-03-25 19:15:35 -07:00
0a66e4cbbb Update README.rst 2019-03-24 11:37:07 -07:00
91acfe6852 Fix UBSAN warning 2019-03-22 07:56:02 -07:00
b7e6bf9671 clang format 2019-03-21 20:51:37 -07:00
da0ea4161a Make compile-time checks work with fallback formatter (#1088) 2019-03-21 19:26:53 -07:00
7ad3015f5b Added missing typename to FMT_STRING. (#1089)
* Added missing typename to FMT_STRING.

This is so that FMT_STRING can be used in a template.
2019-03-20 20:37:00 -07:00
f0b572da05 Update wording test 2019-03-20 06:53:31 -07:00
6d416cf674 Forward declare is_string<FILE*> specializations 2019-03-19 05:20:18 -07:00
b742f622ab Create PR template to help agree to the licensing terms (#1083) 2019-03-18 14:41:17 +01:00
02a6f16b52 Update LICENSE.rst 2019-03-17 18:49:12 -07:00
294fd7df96 Remove isinf workaround 2019-03-17 14:49:19 -07:00
17c6900f83 Update docs 2019-03-17 14:15:48 -07:00
0faa968cc3 Make floating-point formatting locale-independent 2019-03-17 13:26:56 -07:00
bc784d3625 Remove isnan workaround 2019-03-17 12:23:36 -07:00
53379dfd0c Don't set CMAKE_BUILD_TYPE if fmt is a subproject (#1081) 2019-03-17 10:50:40 -07:00
76d326a2a2 Enable grisu for general format 2019-03-17 09:48:50 -07:00
c21c6b8c4b Move enable_if to template params 2019-03-16 15:15:38 -07:00
ec645ca262 Update readme and doxygen config 2019-03-16 10:47:50 -07:00
ae0b0dab9f Remove obsolete TODO 2019-03-16 10:04:16 -07:00
5466a5b41f Document inherited members 2019-03-16 08:22:16 -07:00
9b392a683d Update readme 2019-03-16 07:59:58 -07:00
0fa65cf329 Add example 2019-03-16 07:36:27 -07:00
fdd0149e71 Update readme 2019-03-16 07:27:53 -07:00
e19a95b271 Update readme 2019-03-16 06:51:35 -07:00
ef39274973 Update readme 2019-03-16 06:47:18 -07:00
a7f68dcc01 Update readme 2019-03-16 06:46:19 -07:00
1428b34299 Update readme 2019-03-16 06:30:50 -07:00
97619e27a2 More fixed precision tests 2019-03-15 10:20:20 -07:00
dd6cc0e6ac Merge time-test into chrono-test 2019-03-14 18:10:56 -07:00
a939c75956 Merge fmt/time.h into fmt/chrono.h 2019-03-14 18:07:42 -07:00
17e4b53926 Make chrono formatting work without exceptions (#1062) 2019-03-14 18:01:03 -07:00
a82b3680dc More tests and fixed precision fixes 2019-03-13 19:47:07 -07:00
287342dab1 cmake: default FMT_PKGCONFIG_DIR to a relative path 2019-03-13 11:28:50 -07:00
e28429ee73 Prevent overflow with zero precision 2019-03-13 10:46:25 -07:00
49bbf3c876 Simplify shadowing warning fix 2019-03-13 08:29:37 -07:00
5e5506f833 Update readme 2019-03-12 06:08:18 -07:00
e06523361d Visual Studio 2017: warning C4456: declaration of 'result' hides previous local declaration 2019-03-11 14:15:23 -07:00
ebec00138b Reword licensing part 2019-03-10 17:42:36 -07:00
8daa3c6834 Clarify contribution licensing
This is similar to https://github.com/apple/swift/blob/master/CONTRIBUTING.md
with an explicit clause that allows changing license for #1073.
2019-03-10 17:38:26 -07:00
0d418a8d58 Update and rename CONTRIBUTING.rst to CONTRIBUTING.md 2019-03-10 17:25:06 -07:00
4c66dad8c1 Refactor digit generation 2019-03-10 15:45:06 -07:00
b1f7cca89e stop -> handler and swap args 2019-03-10 14:43:26 -07:00
f90d33ca12 error_ulp -> error 2019-03-10 14:29:28 -07:00
78c755dc48 Update readme 2019-03-10 12:17:35 -07:00
f230170157 Update readme 2019-03-10 12:13:14 -07:00
ced8aa8c9e Update readme 2019-03-10 12:07:57 -07:00
77d54251f0 Update benchmark results 2019-03-10 11:21:24 -07:00
1632f72cbe Test get_round_direction 2019-03-10 11:14:50 -07:00
8129b9bc46 Test that the library can be compiled witout locales 2019-03-10 07:57:29 -07:00
835087dd04 Add color-test 2019-03-10 07:57:29 -07:00
4523053e6f Fix typos. 2019-03-10 07:49:33 -07:00
8407f4cb24 Round close to zero fixed precision 2019-03-09 15:18:34 -08:00
49d244c065 Don't emit more than precision digits (#1072) 2019-03-09 13:53:23 -08:00
3466d9c845 Don't override fixed formatting depending on exponent (#1072) 2019-03-09 13:22:07 -08:00
93d22dec35 Implement rounding up and clean up FP formatting 2019-03-09 10:40:36 -08:00
d560ddac25 Temporarily disable Grisu for fixed formatting 2019-03-08 19:09:05 -08:00
2d981bb133 Add documentation for '%' format type. (#1071) 2019-03-06 11:50:32 -10:00
d8434baa03 Declare the size of RESET_COLOR.
This is so that the format with a text_tyle will compile even if
header-only mode isn't enabled. Addresses #1063.
2019-03-06 09:41:45 -10:00
8f7780a4f6 Correct comment 2019-03-06 09:23:16 -08:00
2e526a664a Fix handling of output iterator in ranges 2019-03-06 08:10:46 -08:00
79b79f329e Add support for '%' type to output floating point values as a
percentage.

This helps with compatibility with Python's format strings.
2019-03-05 17:36:20 -08:00
287eaab3b2 Increment output iterator in basic_writer::write for character types (#1056) 2019-02-26 12:16:34 -10:00
a97757736b Use grisu for fixed precision 2019-02-23 15:50:50 -10:00
327d4b6e9e Fixes for some pedantic warnings (#1054)
* Fix order of initializers

* Remove unused parameter

* Disable warning "-Wswitch-enum"
2019-02-23 09:40:08 -10:00
8af651be39 Implement fixed precision 2019-02-22 10:47:27 -10:00
187bd1b8b2 Clarify lifetimes of named_arg parameters (#1051)
* Clarify usage of fmt::arg

Document that fmt::arg takes a non-owning
reference, even if that reference is to
a temporary. As such, users should make sure
the lifetime of the reference lasts as long
as the named argument.

* Clean up language

Remove mentions of `std::reference_wrapper` and rvalues
in favor of more common terminology like dangling references.
2019-02-22 06:37:19 -10:00
4e5694fd05 Update arg signature 2019-02-21 17:38:30 -10:00
82c24edcf6 Workaround a bogus Qt Creator warning 2019-02-19 06:33:55 -10:00
ed138d794a test: assert-test: fix typo in else-branch of EXPECT_DEBUG_DEATH_IF_SUPPORTED
Patch by Ievgen Liubymkin (jackyf).
2019-02-18 14:02:02 -10:00
0476a51cba Add Sublime syntax (#1037) 2019-02-15 15:08:58 -08:00
c5aad69f2a Restore deprecated begin() 2019-02-15 15:00:02 -08:00
5b0006476a Make stopping condition configurable in grisu 2019-02-14 18:25:13 -08:00
a44238f2ef Improve grisu 2019-02-13 20:03:27 -08:00
83808076e3 Minor cleanup 2019-02-13 19:28:02 -08:00
9660ea1bff Simplify format string checks 2019-02-10 18:00:19 -08:00
4a9d676362 Gradle 4.10 >> Gradle 5.2
TravisCI
- replaced from manual Gradle download to install with 'sdkman'
- changed build name

Gradle
- updated build tool version
2019-02-10 16:15:32 -08:00
f041f128f5 Minor cleanup 2019-02-10 15:20:40 -08:00
e4572e5def Update std implementation 2019-02-10 07:56:33 -08:00
442fa1bd46 Decouple format and parse contexts 2019-02-09 20:39:37 -08:00
744e66bb08 Deprecate format_context::parse_context() 2019-02-09 16:15:20 -08:00
d231d68a8b Fix handling of custom context 2019-02-09 07:36:09 -08:00
01f34d0b0b Fix library deprecation warning
Since C++17, using template specialization 'std::allocator<void>' in any shape or form (even just mentioning it) is deprecated! A simple workaround is replacing 'void' by another (preferably empty) type, e.g. 'fmt::monostate'.

Found by Clang 9 in Visual Studio.
2019-02-07 12:20:46 -08:00
9a0a24f90b Test is_streamable with overloaded comma operators 2019-02-06 15:44:59 -08:00
430e6ac9b6 Protect against overloaded comma operators in decltype 2019-02-06 15:44:59 -08:00
467520e7a4 Remove unused macro 2019-02-06 11:34:59 -08:00
5a314a5288 Eliminate extra copy on floating-point formatting 2019-02-06 10:17:50 -08:00
9989e7f4e3 Update benchmark results 2019-02-04 17:56:33 -08:00
31510cb437 Fix warnings from Visual Studio
Name hiding.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-02-04 11:58:56 -08:00
61c9b563c2 Replace 'std::result_of' by 'std::invoke_result' where possible (#1025)
C++17 deprecated 'std::result_of' in favour of 'std::invoke_result' and will ban it outright in C++20. Therefore
- implement 'internal::result_of' in terms of 'std::invoke_result' when compiling C++17 mode.
- implement 'internal::result_of' in terms of 'std::result_of' when compiling in modes C++11 or C++14.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-02-04 11:25:22 -08:00
864b9a2202 Correct the comment 2019-02-04 08:29:39 -08:00
1538336836 Remove unnecessary checks 2019-02-04 08:25:41 -08:00
22de5a755f Fix warnings from Travis 2019-02-03 15:25:09 -08:00
355eb6d29a Enable grisu for shortest roundtrip (default) formatting 2019-02-03 10:38:28 -08:00
b8d34e0db3 Fix rounding 2019-02-02 09:19:46 -08:00
e61cac687d Minor grisu improvements 2019-02-02 07:41:52 -08:00
7fbbfed8c6 Fix warnings caused by usage of deprecated functionality 2019-02-02 05:48:39 -08:00
c3268f4e50 Remove use cases of deprecated functionality 2019-02-02 05:48:39 -08:00
34951f1999 Replace comments regarding deprecation with attributes 2019-02-02 05:48:39 -08:00
3f52336e6c Simplify formatter selection 2019-01-30 19:39:42 -08:00
7ca8fc3b17 Fix a bogus warning 2019-01-30 07:06:40 -08:00
5289dd600b Test formatting of special numbers 2019-01-30 06:56:46 -08:00
bf6529f2ce Update docs 2019-01-30 06:07:59 -08:00
0700612249 Implement 'chrono' formatting specifiers '%Q' and '%q'
Howard Hinnant's 'date' library recently gained these two new formatting specifiers. This implementation in {fmt} includes support for 'std::chrono::duration' specializations with floating-point representation types and user-definable precision.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-01-27 11:10:08 -08:00
06c005b7b0 Clarify that compile-time checks don't support named arguments 2019-01-27 10:10:13 -08:00
4f6fda558c Add a grisu test stub 2019-01-27 10:10:13 -08:00
9f70b034e1 Implement precision for floating-point durations.
The formatting syntax follows p1361r0, augmented by a precision field as proposed in #1004.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-01-23 07:17:00 -08:00
7cdb1e5e40 Workaround broken is_default_constructible in MSVC 2019-01-21 10:54:49 -08:00
5f1ceebc7f Make formatter<T> override ostream<< for templates (#952) 2019-01-21 09:42:55 -08:00
1b11b000c7 Update readme 2019-01-20 15:33:30 -08:00
83f052930a Add code from p0645 2019-01-20 15:12:01 -08:00
fdd8e333c1 Fix compilation with locales disabled (#1011) 2019-01-19 07:16:05 -08:00
dad1eec842 Workaround unimplemented T... on gcc 4.6.x (#1008) 2019-01-16 11:41:20 -08:00
b0cde860ae Implement 'snprintf(OutputIt it, size_t n, const S &format, const Args & ... args)' (#917)
Mostly equivalent to 'sprintf(const S &format, const Args & ... args)' but generates at most 'n' characters through output iterator 'it'. The output type is the same as with 'format_to_n'.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-01-13 13:08:01 -05:00
e05dfb0887 Fix compile errors due to name-hiding and an unused function argument.
These problems are pointed out by clang-tidy and fail compilation on msvc at /W4 /WX.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-01-13 12:18:05 -05:00
dde095fab8 Revert "Reorder defines"
This reverts commit 4a059914a9.
2019-01-13 08:13:38 -08:00
4a059914a9 Reorder defines 2019-01-13 07:40:37 -08:00
58b6f8db48 Format the code using clang-format 2019-01-13 06:58:20 -08:00
9a777b9e1c Implemented fmt::prepare()
Implementation of fmt::prepare() function and features around it.
2019-01-12 10:53:31 -08:00
da55e96f53 Install ninja to fix android build 2019-01-09 15:55:29 -08:00
dc8f8ce4c0 Fix handling of dynamic width in chrono formatter
Thanks to Howard Hinnant.
2019-01-09 15:23:21 -08:00
f5cc77cea0 Get rid of 'null_terminating_iterator' in printf.h (#980)
The iterator class itself and its helpers 'to_pointer' are removed after reformulating the formatting loop in terms of a 'basic_string_view<Char>::iterator'.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2019-01-04 12:18:14 -05:00
39623a7400 Replaced usage of gtest's internal scoped_ptr with unique_ptr.
scoped_ptr was removed in with gtest google/googletest@e857f9cdd9.
2019-01-02 19:05:51 -05:00
ae1de3a8d3 Add support for using text_style in format and vformat directly (#993)
* Closes #993
2019-01-01 09:26:54 -05:00
1b8a216ddf Improve docs 2018-12-28 15:05:42 -08:00
b3ad759a0a Bump version and correct changelog 2018-12-28 13:09:31 -08:00
9e554999ce Update version 2018-12-28 12:31:41 -08:00
b34d92b051 Bump version 2018-12-28 12:23:29 -08:00
d39ece1870 Make rst2md runnable and update changelog 2018-12-28 12:04:34 -08:00
fe2d715ff1 Update changelog 2018-12-24 11:56:48 -08:00
27b306701f Update changelog 2018-12-24 11:51:20 -08:00
68837079aa Update changelog 2018-12-24 11:48:41 -08:00
c98b202eb4 Update changelog 2018-12-24 11:47:06 -08:00
587a7f6634 Update changelog and docs 2018-12-24 10:56:13 -08:00
84e5170c9c Update changelog and deprecate visit 2018-12-24 10:02:41 -08:00
130e412b64 Update changelog and docs 2018-12-24 09:37:52 -08:00
0bbdca5b8b Fix conversion warnings (#989)
* Fix sign conversion warnings

* Ignore unused local typedefs

* Remove cast reported as useless on GCC

* Remove warning on unused variable

* Enable conversion warning checking for pedantic build

* Fix sign-conversion warnings in headers
2018-12-20 06:24:19 -08:00
77a7244804 Implement fill/align/width for strftime-like formatting 2018-12-19 07:03:02 -08:00
3e01376e08 Implement fill/align/width parsing in chrono formatter 2018-12-16 09:08:11 -08:00
1f92f8a9d8 Remove noexcept 2018-12-15 06:41:32 -08:00
8668639ae2 Get rid of null_terminating_iterator in format 2018-12-15 06:17:25 -08:00
93fd473b8f Add support for builtin terminal colors. (#974) 2018-12-15 06:16:00 -08:00
61ad543c3e Windows .sln filename changed from FORMAT to FMT 2018-12-14 07:50:54 -08:00
7f7504b3f5 Clean up docs 2018-12-12 18:21:13 -08:00
37f599b1a1 Fix docs 2018-12-12 17:50:50 -08:00
8c2e15aed5 Make printf work in search (#164) 2018-12-12 16:07:54 -08:00
de71db6d42 Fix asan error (#977) 2018-12-12 11:29:39 -08:00
b180b39152 Fix default formatting 2018-12-12 06:39:47 -08:00
24594c747e Disable printing the reset escape code when no style modifiers where applied. (#973) 2018-12-09 15:57:20 -08:00
b0f2224719 Implement default chrono formatting 2018-12-09 15:56:15 -08:00
749276072f Add file stream support for stylized text printing. (#967) 2018-12-09 09:28:48 -08:00
f54f3d0fb7 Move chrono-specific code to a separate header 2018-12-09 06:45:20 -08:00
bf1f1c73e3 Fix time test 2018-12-07 08:19:13 -08:00
b6bc6ec246 Add default ctor and fix use of constexpr macros in text_style 2018-12-07 07:22:18 -08:00
acfa95d4a8 Workaround a bug in MSVC's strftime (#965) 2018-12-07 07:07:21 -08:00
628f830583 More chrono formatting 2018-12-07 06:36:08 -08:00
aa3b5aba41 Implement locale-specific minute formatting 2018-12-06 16:12:10 -08:00
639de21757 Workaround more MSVC bugs 2018-12-06 12:16:04 -08:00
3242ddf7b5 Fix warnings 2018-12-06 08:16:12 -08:00
bd1104046b Workaround a bug in MSVC 2018-12-06 08:16:01 -08:00
81b5c4a5fd Add experimental emphasis support (#961) 2018-12-06 07:56:01 -08:00
7c4eb0fbeb Fix warnings in time.h 2018-12-05 14:38:08 -08:00
2d624218bf Fix another warning 2018-12-05 10:19:53 -08:00
b31680990e Fix a warning 2018-12-05 09:49:39 -08:00
b10ccb83e1 Add rpclib to projects 2018-12-05 06:42:33 -08:00
0497875ff3 Stop the orgy of casts 2018-12-05 06:27:31 -08:00
37dc495b9d Simplify MSVC workaround 2018-12-05 06:22:18 -08:00
2ff4996d05 Fix ambiguous complier error C2666 in vs2017.The '+' opeator may cause ambiguity.Avoid implicit conversion. 2018-12-05 05:55:43 -08:00
77656c672c Fix sign-conversion warnings reported by Clang7 2018-12-03 16:22:59 -08:00
ea5e4790bd Fix formatting 2018-12-03 08:58:48 -08:00
86681c4bb0 Update README.rst 2018-12-03 08:04:21 -08:00
e867768ee6 Do not override user provided compile flag 2018-12-03 07:31:19 -08:00
0c7f5c3ca4 Update README.rst 2018-12-02 08:22:51 -08:00
e7e2ab1070 Make return type of basic_format_args::max_size() consistent.
This squashes nasty narrowing conversion warnings.
2018-12-01 17:07:21 -08:00
29352af369 Update README.rst 2018-12-01 08:19:20 -08:00
68214bd904 More time formatting 2018-11-30 20:12:01 -08:00
bcf3fcd673 Clean up bit fiddling for argument packing 2018-11-30 17:04:49 -08:00
9dcf127fa5 Workaround a bogus MSVC warning 2018-11-30 13:47:04 -08:00
b8b06e3e10 Fix conversion warnings in Grisu 2018-11-30 10:37:40 -08:00
322b2594e0 Implement more time specifiers 2018-11-30 09:25:18 -08:00
0835f1ba3b Use full paths for fmt.pc.in 2018-11-30 06:17:33 -08:00
a084495d7e Add Ceph to projects 2018-11-28 17:28:27 -08:00
fa1d4dbcfd Fix warnings 2018-11-28 16:15:15 -08:00
2b2cfdac19 Update docs 2018-11-28 12:53:09 -08:00
99744f8f8f Suppress unfixable warning 2018-11-28 12:52:41 -08:00
f5fe849238 Specialize formatter for chrono durations 2018-11-28 11:39:21 -08:00
a5a9805a91 First stub at the datetime format parser 2018-11-25 17:25:22 -08:00
645c76a9a5 Fix dummy warnings 2018-11-25 15:33:28 -08:00
fecb2d6f0d Eliminate msvc compiler warnings (#931)
The sheer presence of 'std::gmtime' or 'std::localtime' gives rise to C4996 warnings in Microsofts compilers. Alas, the 'fallback(internal::null<>)'functions containing these library calls are never ADL-picked in the respective 'handle(internal::null<>)' selectors. Therefore hiding the fallbacks from msvc is perfectly fine.
2018-11-25 13:48:52 -08:00
64690d3a97 Add context_base::arg() 2018-11-24 10:49:48 -08:00
01640f44cf Fully qualify dummy_int (#941) 2018-11-24 06:22:59 -08:00
e37d6a9840 add make_printf_args and make_wprintf_args functions (#934)
* add make_printf_args and make_wprintf_args to printf.h
add minimal test for make_printf_args and make_wprintf_args to printf-test.cc

renamed the old printf_context template to basic_printf_context_t. the original wish was to rename it basic_printf_context, but that clashed with the name of the inner typedef. this style matches the format_context_t struct.
2018-11-22 13:57:07 -08:00
982ee5c699 parse_context -> format_parse_context 2018-11-21 07:10:22 -08:00
b7b8548559 thousands_sep -> thousands_sep_impl (#939) 2018-11-20 15:43:17 -08:00
00a8cc8325 Fix formatting 2018-11-20 07:43:40 -08:00
33fbb3a7eb Fix remaining linker errors. 2018-11-20 07:01:21 -08:00
bd6121596f Disable fmt-impl-test in windows + shared lib. 2018-11-20 07:01:21 -08:00
702b3d1618 Fix link error in windows with shared library. 2018-11-20 07:01:21 -08:00
9d4ef94350 Install pdb files. 2018-11-20 07:01:21 -08:00
6c95fb3562 Default Context to format_context 2018-11-18 07:19:25 -08:00
16b78ee629 fix incompatibilities with c++2a mode in clang 2018-11-17 08:38:04 -08:00
19e008876b More locale support 2018-11-14 16:25:49 -08:00
f2ee988105 Improve locale support 2018-11-14 12:07:39 -08:00
1385050e26 More formatter tests 2018-11-08 16:26:55 -08:00
03c1b110a5 Fix gcc 4.4 build 2018-11-08 12:41:55 -08:00
cc805c6162 Test enabled formatters 2018-11-08 10:59:10 -08:00
e01579231d Disallow leading zeros in arg-id 2018-11-08 08:48:56 -08:00
34030deca9 Cleanup warning flags 2018-11-06 17:22:30 -08:00
6b26e3f2d7 Manifest & Gradle comment
- #926
- The version of android build results follows latest release(5.2.1)
- Comment for Gradle and its plugin for android
 - `net.fmtlib` package name in the manifest
2018-11-05 17:31:09 -08:00
d286c97753 Update for Gradle build
Recovered Gradle build with latest release (ver. 4.10.2)

TravisCI
- Changed several apt configurations & build steps for Android

AndroidManifest:
- package name: fmt -> org.fmtlib

Gradle:
- Plugin version 3.1.3. Requires Gradle 4.4+
- Removed `x86` (duplicated with `x86_64`)
- `-std=c++17`. Latest NDK(clang7) should support this
- Missing `repositories` configuration

For some reason, the `repositories` config affects to the error:
"Could not find com.android.tools.lint:lint-gradle:26.1.3."
2018-11-05 17:31:09 -08:00
d951f6dfe7 Get latest Gradle (ver. 4.10.2)
Recovery of Gradle build. 1 of N
Modified several apt configurationss and order of the build
2018-11-05 17:31:09 -08:00
a23d592472 Fix check_format_string (#925) 2018-11-03 16:51:15 -07:00
36161284e2 Update docs 2018-10-28 09:28:51 -07:00
38f355d87b Revert "find sphinx-build before calling build.py"
This reverts commit 07200f445a because
the latter doesn't work with virtualenv correctly.
2018-10-28 09:26:21 -07:00
324eac1aa5 Make locales work with any character type 2018-10-28 09:03:27 -07:00
bdda4d6030 Simplify compile-time strings 2018-10-28 09:02:03 -07:00
5ee1a4bc8a check for property 'mutable iterator' and SFINAE on it 2018-10-28 08:57:43 -07:00
2dea780fbd change type naming and fix sfinae bug 2018-10-28 06:00:55 -07:00
b98e8301d5 add non-char support for compile-time format check 2018-10-28 06:00:55 -07:00
ccd3e8bbf3 Make is_constructible public (#918) 2018-10-27 17:10:19 -07:00
4373153800 Update usage.rst 2018-10-27 12:59:51 -07:00
73cfd8f325 Fix colored print 2018-10-25 17:18:46 -07:00
ec384302db additional test for print with background color
checks for clashes with other function overloads in fmt with templated parameters on the first or second position in the parameter list
2018-10-25 16:54:54 -07:00
0a96c032b9 Parameterize v*printf on string type (#920) 2018-10-25 13:52:08 -07:00
61e6d2e38c Fix core version of vformat_to 2018-10-25 06:21:41 -07:00
ea4010d704 Merge has_to_string_view into is_string 2018-10-24 18:16:00 -07:00
486fff597c Add sprintf_format instantiations and remove syntactic noise 2018-10-24 16:46:32 -07:00
1e3dcbba81 fix: 'format_to_n' compiles 'std::back_inserter' arguments
std::back_insert_iterators model the OutputIterator concept but differ considerably in their traits and behavior. In particular the former made compilation to fail when format_to_n is given a back_inserter as first argument. The emulation of an OutputIterator is not perfect due to the behavioural differences of back_insert_iterators (e.g. assignment always implies increment) but good enough to be used within fmt's machinery.
2018-10-24 16:45:18 -07:00
f0328f8e36 Use char_traits::length in string_view ctor (#914) 2018-10-24 15:21:21 -07:00
895fb98456 Disallow gcc 4.4 failures 2018-10-24 10:52:13 -07:00
20c708bf6d Fix build on gcc 4.4 2018-10-24 10:52:02 -07:00
9d0c9c4bb1 cmake: output share/fmt.pc 2018-10-24 07:14:08 -07:00
2d2326a76d Fix compilation with older gcc 2018-10-23 07:31:24 -07:00
1ec0272303 Get rid of FMT_UNION 2018-10-22 20:19:31 -07:00
2c81c851b2 Adapt any string-like type to be used by {fmt} just like the standard string types already supported. The adaption is totally non-intrusive.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-10-22 18:25:52 -07:00
846c644e84 Workeround broken sprintf in MSVC 2018-10-19 07:30:59 -07:00
13d472bd8c Compute output size for grisu 2018-10-18 17:16:50 -07:00
b71d3fe7ab Remove use_grisu 2018-10-17 17:01:45 -07:00
847abb6f8f Fix test 2018-10-17 11:57:06 -07:00
dda47c9466 Merge min_digits and max_digits 2018-10-17 10:49:30 -07:00
2924622157 Fix naming of basic_format_specs members 2018-10-17 10:13:10 -07:00
bda5f9a556 Replace grisu2_specs with core_format_specs 2018-10-17 08:55:45 -07:00
b1ca608bac Remove unused empty_spec 2018-10-17 08:39:58 -07:00
e8efdef8de Avoid extra copy 2018-10-17 08:19:46 -07:00
98f1c1fe87 Remove unused code 2018-10-14 15:36:45 -07:00
50b18a3c10 Integrate Grisu 2018-10-14 14:33:25 -07:00
699297520a Implement Grisu rounding 2018-10-13 11:18:54 -07:00
4bb76ef0c7 Remove redundant definition of print 2018-10-13 11:15:46 -07:00
ddd7caf38e Fix locale-dependent formatting (#905) 2018-10-13 11:15:41 -07:00
10e03e695b use found python executable for launching sphinx-build
there is chance that the distro install python as python3, in that case
the shebang of
```
```
in build.py won't work.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
2018-10-13 09:14:53 -07:00
07200f445a find sphinx-build before calling build.py
this allows us to use sphinx-build-3, as user might already in a python3
world, and distros, like fedora 28, tend to add a "-3" suffix to sphinx-build.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
2018-10-13 09:14:53 -07:00
08a65c2282 Workaround broken constexpr in MSVC2017 2018-10-11 06:38:25 -07:00
167f8fe325 Fix a typo in api.rst 2018-10-10 10:51:43 -07:00
57983423c3 Remove signbit workaround 2018-10-10 09:41:46 -07:00
7bebb3e128 Clarify overload resolution in docs 2018-10-10 09:41:46 -07:00
939fbe5567 Remove basic_fixed_buffer.
Issue #873 indicates that this class is no longer required,
as it has been superseded by a new API.

Fixes #873
2018-10-09 06:51:01 -07:00
61f81a0719 minor documentation corrections
In the Format API section, it says fmt:print writes to a file, but it
writes to a stream as referenced from fopen(3), "stream open
functions". Also in the Safety section a typo; tim should be time.
2018-10-08 06:27:25 -07:00
f27defc639 Parameterize printf functions on the type of the format string.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-10-08 06:22:04 -07:00
6a685571df Make 'std::*::basic_string_view' a valid argument type for 'format_str' parameters.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-10-08 06:12:02 -07:00
87a0408c64 Fix ostream.h build 2018-10-07 12:58:20 -07:00
2b5acad4a9 Remove redundant size argument to write_padded 2018-10-07 12:45:34 -07:00
655ce53383 is_format_string -> is_string 2018-10-07 12:45:22 -07:00
fea712abb1 Parameterize ostream functions on the type of the format string.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-10-07 09:21:14 -07:00
f16a118e88 Fix non-matching char types.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-10-07 05:56:21 -07:00
041bf83d9b Improve fmt::format readability 2018-10-05 18:42:02 -07:00
2299032392 Document how to write a formatter for a type hierarchy 2018-10-05 13:21:29 -07:00
f5480635c0 visit -> visit_format_arg 2018-10-05 07:15:41 -07:00
cdf3fa08dc Put related code together in fmt/core.h 2018-10-04 19:44:09 -07:00
38325248e5 Count width in code points (#628) 2018-10-03 20:09:46 -07:00
deb901b9e4 Parameterize core functions on the type of the format string.
Take #2 of n

Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-10-03 14:19:08 -07:00
0f98de3011 Update docs 2018-10-03 12:07:55 -07:00
c797708fcc Workaround strlen being non-constexpr in ARM toolchain 2018-10-03 09:23:49 -07:00
49b4c1e9db Update docs 2018-10-02 06:21:19 -07:00
63a87beba4 Add to_string_view 2018-10-01 18:48:18 -07:00
4e0c314654 checked_format_args -> checked_args 2018-10-01 07:14:59 -07:00
c3538a1eee Simplify variadic functions further 2018-09-30 22:19:32 -07:00
2d7d0835d7 Simplify variadic functions 2018-09-30 21:52:47 -07:00
3f4cfa6c60 Implement UTF-8 string support 2018-09-30 15:45:58 -07:00
f8027414f5 Impelement char8_t support 2018-09-30 12:31:25 -07:00
76a47d41c8 Cleanup the use of FMT_CHAR 2018-09-30 08:45:57 -07:00
267fdc7a1b Parameterize core functions on the type of the format string.
Take #1 of n

Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-09-30 08:40:58 -07:00
5bced12421 Parameterize more functions on string type 2018-09-30 08:02:19 -07:00
674999c527 fix vs2017 warning fmt::v5::localtime 'not all control paths return a value'. 2018-09-30 07:23:35 -07:00
e4fea22d15 Make char8_t a strongly-typed enum 2018-09-30 07:11:37 -07:00
66992e90d2 Clarify that writing to memory_buffer appends (#877) 2018-09-28 06:22:12 -07:00
e864acfdbd Fix compilation with intel compilers (ICC/ICPC) v14.0 2018-09-27 07:11:23 -07:00
4cf21f58b2 constrain templated format_to on proper format string type.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-09-26 21:05:47 -07:00
d7f17613f5 Fix compilation on platforms with exotic double (#878) 2018-09-26 08:45:34 -07:00
e4ca37ccf6 Parameterize format_to on string type (#880) 2018-09-25 07:47:15 -07:00
d66fa2216e Reduce syntactic noise 2018-09-23 14:31:14 -07:00
48e6dcd0f2 Implement workarounds for gcc 4.4 2018-09-23 12:29:48 -07:00
0ea3221d34 Remove is_named_arg and add FMT_CHAR 2018-09-23 09:44:01 -07:00
73c53d7833 Parameterize 'printf(rgb color, ...)' and 'vprint_rgb(rgb color, ...)' on the type of the format string.
Signed-off-by: Daniela Engert <dani@ngrt.de>
2018-09-23 09:15:27 -07:00
d41be23acd Simplify string_view detection 2018-09-23 07:29:35 -07:00
2def9e4c82 Remove FMT_DTOR_NOEXCEPT 2018-09-22 18:51:32 -07:00
ff6e46ed97 More cleanup 2018-09-22 16:00:34 -07:00
715f2b4c00 Remove require_wchar and internalize no_formatter_error 2018-09-21 18:25:24 -07:00
ec0cdc46f6 Workaround Windows slowness 2018-09-21 17:11:57 -07:00
3e75ad9822 Update version 2018-09-21 10:18:51 -07:00
4f043f8e00 Bump version 2018-09-21 10:07:56 -07:00
cc02cbc455 Fix formatting 2018-09-21 09:48:21 -07:00
73c0238e3b Update changelog 2018-09-21 09:48:21 -07:00
cb122a4d03 Fix format_to formatting to wmemory_buffer 2018-09-21 09:48:21 -07:00
dc69cc45d2 Clean tests 2018-09-21 09:48:21 -07:00
9d8021f0d6 Add checks for NVIDIA's CUDA compiler
- Disable templated user-defined literals when compiled under nvcc

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

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

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

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

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

This reverts commit f9e9bf0231.
2018-07-08 08:08:50 -07:00
07b690a679 Update README.rst 2018-07-07 18:18:56 -07:00
f9e9bf0231 Optimize format string parsing 2018-07-07 16:50:22 -07:00
103 changed files with 24998 additions and 9008 deletions

8
.clang-format Normal file
View File

@ -0,0 +1,8 @@
# Run manually to reformat a file:
# clang-format -i --style=file <file>
Language: Cpp
BasedOnStyle: Google
IndentPPDirectives: AfterHash
IndentCaseLabels: false
AlwaysBreakTemplateDeclarations: false
DerivePointerAlignment: false

6
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,6 @@
<!-- Please read the contribution guidelines before submitting a pull request. -->
<!-- By submitting this pull request, you agree that your contributions are licensed under the {fmt} license,
and agree to future changes to the licensing. -->
<!-- If you're a first-time contributor, please acknowledge it by leaving the statement below. -->
I agree that my contributions are licensed under the {fmt} license, and agree to future changes to the licensing.

1
.gitignore vendored
View File

@ -31,3 +31,4 @@ CMakeFiles
FMT.build
Makefile
run-msbuild.bat
fmt.pc

View File

@ -7,7 +7,6 @@ os: linux
git:
depth: 1
env:
global:
- secure: |-
@ -39,6 +38,26 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-6
# g++ 8 on Linux with C++17
- env: COMPILER=g++-8 BUILD=Debug STANDARD=17
compiler: gcc
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
- env: COMPILER=g++-8 BUILD=Release STANDARD=17
compiler: gcc
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
# Apple clang on OS X with C++14
- env: BUILD=Debug STANDARD=14
compiler: clang
@ -46,8 +65,8 @@ matrix:
- env: BUILD=Release STANDARD=14
compiler: clang
os: osx
# clang 6.0 on Linux with C++14
- env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14
# clang 6.0 on Linux with C++14 (builds the fuzzers as well)
- env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14 ENABLE_FUZZING=1
compiler: clang
addons:
apt:
@ -73,53 +92,45 @@ matrix:
# g++ 4.8 on Linux with C++11
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
compiler: gcc
# g++ 4.4 on Linux with C++11
- env: COMPILER=g++-4.4 BUILD=Debug STANDARD=11
compiler: gcc
- name: Android NDK (Gradle)
language: android
addons:
apt:
update: true
packages:
- g++-4.4
sources:
- ubuntu-toolchain-r-test
# Android
- language: android
packages:
- ninja-build
- curl
- tree
android:
addons:
apt:
update: true
components:
- tools
- platform-tools
- android-21
- sys-img-armeabi-v7a-android-21
env:
- ANDROID=true
components:
- tools
- platform-tools
- android-25 # 7.0
- android-27 # 8.1
- android-28 # 9.0
- build-tools-28.0.3
before_install:
- git submodule update --init --recursive
- sudo apt-get install wget unzip tree
# 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
# Download Gradle 4.3.1
- wget https://services.gradle.org/distributions/gradle-4.3.1-bin.zip
- mkdir -p gradle
- unzip -q -d ./gradle gradle-4.3.1-bin.zip
- export GRADLE=${TRAVIS_BUILD_DIR}/gradle/gradle-4.3.1/bin/gradle
before_script:
- bash $GRADLE --version
- cd ./support
- pushd ./support
script:
- bash $GRADLE clean assemble
- gradle clean
- gradle assemble
after_success:
- cd ${TRAVIS_BUILD_DIR}
- popd;
- tree ./libs
allow_failures:
# Errors
- env: COMPILER=g++-4.4 BUILD=Debug STANDARD=11
compiler: gcc
before_script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi

View File

@ -1,12 +1,18 @@
message(STATUS "CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 3.1.0)
# Use newer policies if available, up to most recent tested version of CMake.
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
message(STATUS "CMake version: ${CMAKE_VERSION}")
endif ()
# Joins arguments and places the results in ${result_var}.
@ -21,21 +27,25 @@ endfunction()
# Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake).
if (NOT CMAKE_BUILD_TYPE)
if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
endif ()
option(FMT_USE_TEXT "Use the text library." OFF)
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
project(FMT)
project(FMT CXX)
# Get version from core.h
file(READ include/fmt/core.h core_h)
@ -60,18 +70,21 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14)
include(CheckCXXCompilerFlag)
set(FMT_REQUIRED_FEATURES cxx_auto_type cxx_variadic_templates)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
-Wold-style-cast -Wfloat-equal -Wlogical-op -Wundef
-Wredundant-decls -Wshadow -Wwrite-strings -Wpointer-arith
-Wold-style-cast -Wundef
-Wredundant-decls -Wwrite-strings -Wpointer-arith
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
-Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Wmissing-declarations -Woverloaded-virtual
-Wno-ctor-dtor-privacy -Wno-dangling-else -Wno-float-equal
-Wno-format-nonliteral -Wno-sign-conversion -Wno-shadow)
-Winvalid-pch -Woverloaded-virtual
-Wconversion
-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)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
-Wno-dangling-else -Wno-unused-local-typedefs)
endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
@ -82,33 +95,18 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
-Wnull-dereference -Wduplicated-cond)
endif ()
set(WERROR_FLAG -Werror)
endif ()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Weverything -Wpedantic
-Wno-weak-vtables -Wno-padded -Wno-gnu-statement-expression
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
-Wno-global-constructors -Wno-disabled-macro-expansion
-Wno-switch-enum -Wno-documentation-unknown-command
-Wno-gnu-string-literal-operator-template -Wno-unused-member-function
-Wno-format-nonliteral -Wno-missing-noreturn -Wno-undefined-func-template
-Wno-shadow -Wno-sign-conversion -Wno-used-but-marked-unused
-Wno-covered-switch-default -Wno-missing-variable-declarations
-Wno-double-promotion)
set(WERROR_FLAG -Werror)
check_cxx_compiler_flag(-Wno-zero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-zero-as-null-pointer-constant)
endif ()
check_cxx_compiler_flag(-Wno-gnu-string-literal-operator-template HAS_GNU_UDL_WARNING)
if (HAS_GNU_UDL_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-gnu-string-literal-operator-template)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
-Wzero-as-null-pointer-constant)
endif ()
set(WERROR_FLAG -Werror)
endif ()
if (MSVC)
@ -132,11 +130,18 @@ if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
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 ()
function(add_headers VAR)
@ -148,17 +153,26 @@ function(add_headers VAR)
endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS core.h format.h format-inl.h ostream.h printf.h time.h
ranges.h)
add_headers(FMT_HEADERS 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 ()
if (FMT_USE_TEXT)
set(FMT_SOURCES ${FMT_SOURCES} src/text/grapheme_break.cpp)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif ()
@ -166,28 +180,40 @@ if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
if (FMT_USE_TEXT)
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/text>)
endif ()
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX d)
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE)
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)
endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif ()
if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
endif()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
@ -197,9 +223,10 @@ if (FMT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets)
set (INSTALL_TARGETS fmt)
@ -213,11 +240,18 @@ if (FMT_INSTALL)
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
${version_config}
VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion)
configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}"
@ONLY)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
@ -237,7 +271,11 @@ if (FMT_INSTALL)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif ()
if (FMT_DOC)
@ -249,6 +287,11 @@ if (FMT_TEST)
add_subdirectory(test)
endif ()
# Control fuzzing independent of the unit tests.
if (FMT_FUZZ)
add_subdirectory(test/fuzzing)
endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore.

17
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,17 @@
Contributing to {fmt}
=====================
By submitting a pull request or a patch, you represent that you have the right
to license your contribution to the {fmt} project owners and the community,
agree that your contributions are licensed under the {fmt} license, and agree
to future changes to the licensing.
All C++ code must adhere to [Google C++ Style Guide](
https://google.github.io/styleguide/cppguide.html) with the following
exceptions:
* Exceptions are permitted
* snake_case should be used instead of UpperCamelCase for function and type
names
Thanks for contributing!

View File

@ -1,12 +0,0 @@
Contributing to fmt
===================
All C++ code must adhere to `Google C++ Style Guide
<https://google.github.io/styleguide/cppguide.html>`_ with the following
exceptions:
* Exceptions are permitted
* snake_case should be used instead of UpperCamelCase for function and type
names
Thanks for contributing!

View File

@ -1,3 +1,770 @@
6.0.0 - 2019-08-26
------------------
* Switched to the `MIT license
<https://github.com/fmtlib/fmt/blob/5a4b24613ba16cc689977c3b5bd8274a3ba1dd1f/LICENSE.rst>`_
with an optional exception that allows distributing binary code without
attribution.
* Floating-point formatting is now locale-independent by default:
.. code:: c++
#include <locale>
#include <fmt/core.h>
int main() {
std::locale::global(std::locale("ru_RU.UTF-8"));
fmt::print("value = {}", 4.2);
}
prints "value = 4.2" regardless of the locale.
For locale-specific formatting use the ``n`` specifier:
.. code:: c++
std::locale::global(std::locale("ru_RU.UTF-8"));
fmt::print("value = {:n}", 4.2);
prints "value = 4,2".
* Added an experimental Grisu floating-point formatting algorithm
implementation (disabled by default). To enable it compile with the
``FMT_USE_GRISU`` macro defined to 1:
.. code:: c++
#define FMT_USE_GRISU 1
#include <fmt/format.h>
auto s = fmt::format("{}", 4.2); // formats 4.2 using Grisu
With Grisu enabled, {fmt} is 13x faster than ``std::ostringstream`` (libc++)
and 10x faster than ``sprintf`` on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_ (`full results
<https://fmt.dev/unknown_mac64_clang10.0.html>`_):
.. image:: https://user-images.githubusercontent.com/576385/
54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
* Separated formatting and parsing contexts for consistency with
`C++20 std::format <http://eel.is/c++draft/format>`_, removing the
undocumented ``basic_format_context::parse_context()`` function.
* Added `oss-fuzz <https://github.com/google/oss-fuzz>`_ support
(`#1199 <https://github.com/fmtlib/fmt/pull/1199>`_).
Thanks `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_.
* ``formatter`` specializations now always take precedence over ``operator<<``
(`#952 <https://github.com/fmtlib/fmt/issues/952>`_):
.. code:: c++
#include <iostream>
#include <fmt/ostream.h>
struct S {};
std::ostream& operator<<(std::ostream& os, S) {
return os << 1;
}
template <>
struct fmt::formatter<S> : fmt::formatter<int> {
auto format(S, format_context& ctx) {
return formatter<int>::format(2, ctx);
}
};
int main() {
std::cout << S() << "\n"; // prints 1 using operator<<
fmt::print("{}\n", S()); // prints 2 using formatter
}
* Introduced the experimental ``fmt::compile`` function that does format string
compilation (`#618 <https://github.com/fmtlib/fmt/issues/618>`_,
`#1169 <https://github.com/fmtlib/fmt/issues/1169>`_,
`#1171 <https://github.com/fmtlib/fmt/pull/1171>`_):
.. code:: c++
#include <fmt/compile.h>
auto f = fmt::compile<int>("{}");
std::string s = fmt::format(f, 42); // can be called multiple times to format
// different values
// s == "42"
It moves the cost of parsing a format string outside of the format function
which can be beneficial when identically formatting many objects of the same
types. Thanks `@stryku (Mateusz Janek) <https://github.com/stryku>`_.
* Added the ``%`` format specifier that formats floating-point values as
percentages (`#1060 <https://github.com/fmtlib/fmt/pull/1060>`_,
`#1069 <https://github.com/fmtlib/fmt/pull/1069>`_,
`#1071 <https://github.com/fmtlib/fmt/pull/1071>`_):
.. code:: c++
auto s = fmt::format("{:.1%}", 0.42); // s == "42.0%"
Thanks `@gawain-bolton (Gawain Bolton) <https://github.com/gawain-bolton>`_.
* Implemented precision for floating-point durations
(`#1004 <https://github.com/fmtlib/fmt/issues/1004>`_,
`#1012 <https://github.com/fmtlib/fmt/pull/1012>`_):
.. code:: c++
auto s = fmt::format("{:.1}", std::chrono::duration<double>(1.234));
// s == 1.2s
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Implemented ``chrono`` format specifiers ``%Q`` and ``%q`` that give the value
and the unit respectively (`#1019 <https://github.com/fmtlib/fmt/pull/1019>`_):
.. code:: c++
auto value = fmt::format("{:%Q}", 42s); // value == "42"
auto unit = fmt::format("{:%q}", 42s); // unit == "s"
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Fixed handling of dynamic width in chrono formatter:
.. code:: c++
auto s = fmt::format("{0:{1}%H:%M:%S}", std::chrono::seconds(12345), 12);
// ^ width argument index ^ width
// s == "03:25:45 "
Thanks Howard Hinnant.
* Removed deprecated ``fmt/time.h``. Use ``fmt/chrono.h`` instead.
* Added ``fmt::format`` and ``fmt::vformat`` overloads that take ``text_style``
(`#993 <https://github.com/fmtlib/fmt/issues/993>`_,
`#994 <https://github.com/fmtlib/fmt/pull/994>`_):
.. code:: c++
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}.", 42);
Thanks `@Naios (Denis Blank) <https://github.com/Naios>`_.
* Removed the deprecated color API (``print_colored``). Use the new API, namely
``print`` overloads that take ``text_style`` instead.
* Made ``std::unique_ptr`` and ``std::shared_ptr`` formattable as pointers via
``fmt::ptr`` (`#1121 <https://github.com/fmtlib/fmt/pull/1121>`_):
.. code:: c++
std::unique_ptr<int> p = ...;
fmt::print("{}", fmt::ptr(p)); // prints p as a pointer
Thanks `@sighingnow (Tao He) <https://github.com/sighingnow>`_.
* Made ``print`` and ``vprint`` report I/O errors
(`#1098 <https://github.com/fmtlib/fmt/issues/1098>`_,
`#1099 <https://github.com/fmtlib/fmt/pull/1099>`_).
Thanks `@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_.
* Marked deprecated APIs with the ``[[deprecated]]`` attribute and removed
internal uses of deprecated APIs
(`#1022 <https://github.com/fmtlib/fmt/pull/1022>`_).
Thanks `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_.
* 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
is now deprecated.
* Moved the undocumented ``basic_writer``, ``writer``, and ``wwriter`` types
to the ``internal`` namespace.
* Removed deprecated ``basic_format_context::begin()``. Use ``out()`` instead.
* Disallowed passing the result of ``join`` as an lvalue to prevent misuse.
* Refactored the undocumented structs that represent parsed format specifiers
to simplify the API and allow multibyte fill.
* Moved SFINAE to template parameters to reduce symbol sizes.
* Switched to ``fputws`` for writing wide strings so that it's no longer
required to call ``_setmode`` on Windows
(`#1229 <https://github.com/fmtlib/fmt/issues/1229>`_,
`#1243 <https://github.com/fmtlib/fmt/pull/1243>`_).
Thanks `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Improved literal-based API
(`#1254 <https://github.com/fmtlib/fmt/pull/1254>`_).
Thanks `@sylveon (Charles Milette) <https://github.com/sylveon>`_.
* Added support for exotic platforms without ``uintptr_t`` such as IBM i
(AS/400) which has 128-bit pointers and only 64-bit integers
(`#1059 <https://github.com/fmtlib/fmt/issues/1059>`_).
* Added `Sublime Text syntax highlighting config
<https://github.com/fmtlib/fmt/blob/master/support/C%2B%2B.sublime-syntax>`_
(`#1037 <https://github.com/fmtlib/fmt/issues/1037>`_).
Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_.
* Added the ``FMT_ENFORCE_COMPILE_STRING`` macro to enforce the use of
compile-time format strings
(`#1231 <https://github.com/fmtlib/fmt/pull/1231>`_).
Thanks `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Stopped setting ``CMAKE_BUILD_TYPE`` if {fmt} is a subproject
(`#1081 <https://github.com/fmtlib/fmt/issues/1081>`_).
* Various build improvements
(`#1039 <https://github.com/fmtlib/fmt/pull/1039>`_,
`#1078 <https://github.com/fmtlib/fmt/pull/1078>`_,
`#1091 <https://github.com/fmtlib/fmt/pull/1091>`_,
`#1103 <https://github.com/fmtlib/fmt/pull/1103>`_,
`#1177 <https://github.com/fmtlib/fmt/pull/1177>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_,
`@jasonszang (Jason Shuo Zang) <https://github.com/jasonszang>`_,
`@olafhering (Olaf Hering) <https://github.com/olafhering>`_,
`@Lecetem <https://github.com/Lectem>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_.
* Improved documentation
(`#1049 <https://github.com/fmtlib/fmt/issues/1049>`_,
`#1051 <https://github.com/fmtlib/fmt/pull/1051>`_,
`#1083 <https://github.com/fmtlib/fmt/pull/1083>`_,
`#1113 <https://github.com/fmtlib/fmt/pull/1113>`_,
`#1114 <https://github.com/fmtlib/fmt/pull/1114>`_,
`#1146 <https://github.com/fmtlib/fmt/issues/1146>`_,
`#1180 <https://github.com/fmtlib/fmt/issues/1180>`_,
`#1250 <https://github.com/fmtlib/fmt/pull/1250>`_,
`#1252 <https://github.com/fmtlib/fmt/pull/1252>`_,
`#1265 <https://github.com/fmtlib/fmt/pull/1265>`_).
Thanks `@mikelui (Michael Lui) <https://github.com/mikelui>`_,
`@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
`@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_,
`@jwakely (Jonathan Wakely) <https://github.com/jwakely>`_,
`@kaisbe (Kais Ben Salah) <https://github.com/kaisbe>`_,
`@sdebionne (Samuel Debionne) <https://github.com/sdebionne>`_.
* Fixed ambiguous formatter specialization in ``fmt/ranges.h``
(`#1123 <https://github.com/fmtlib/fmt/issues/1123>`_).
* Fixed formatting of a non-empty ``std::filesystem::path`` which is an
infinitely deep range of its components
(`#1268 <https://github.com/fmtlib/fmt/issues/1268>`_).
* Fixed handling of general output iterators when formatting characters
(`#1056 <https://github.com/fmtlib/fmt/issues/1056>`_,
`#1058 <https://github.com/fmtlib/fmt/pull/1058>`_).
Thanks `@abolz (Alexander Bolz) <https://github.com/abolz>`_.
* Fixed handling of output iterators in ``formatter`` specialization for
ranges (`#1064 <https://github.com/fmtlib/fmt/issues/1064>`_).
* Fixed handling of exotic character types
(`#1188 <https://github.com/fmtlib/fmt/issues/1188>`_).
* Made chrono formatting work with exceptions disabled
(`#1062 <https://github.com/fmtlib/fmt/issues/1062>`_).
* Fixed DLL visibility issues
(`#1134 <https://github.com/fmtlib/fmt/pull/1134>`_,
`#1147 <https://github.com/fmtlib/fmt/pull/1147>`_).
Thanks `@denchat <https://github.com/denchat>`_.
* Disabled the use of UDL template extension on GCC 9
(`#1148 <https://github.com/fmtlib/fmt/issues/1148>`_).
* Removed misplaced ``format`` compile-time checks from ``printf``
(`#1173 <https://github.com/fmtlib/fmt/issues/1173>`_).
* Fixed issues in the experimental floating-point formatter
(`#1072 <https://github.com/fmtlib/fmt/issues/1072>`_,
`#1129 <https://github.com/fmtlib/fmt/issues/1129>`_,
`#1153 <https://github.com/fmtlib/fmt/issues/1153>`_,
`#1155 <https://github.com/fmtlib/fmt/pull/1155>`_,
`#1210 <https://github.com/fmtlib/fmt/issues/1210>`_,
`#1222 <https://github.com/fmtlib/fmt/issues/1222>`_).
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* 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>`_,
`#1135 <https://github.com/fmtlib/fmt/pull/1135>`_,
`#1136 <https://github.com/fmtlib/fmt/issues/1136>`_,
`#1141 <https://github.com/fmtlib/fmt/issues/1141>`_,
`#1142 <https://github.com/fmtlib/fmt/issues/1142>`_,
`#1178 <https://github.com/fmtlib/fmt/issues/1178>`_,
`#1179 <https://github.com/fmtlib/fmt/issues/1179>`_,
`#1194 <https://github.com/fmtlib/fmt/issues/1194>`_).
Thanks `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_.
* Fixed building tests on FreeBSD and Hurd
(`#1043 <https://github.com/fmtlib/fmt/issues/1043>`_).
Thanks `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_.
* Fixed various warnings and compilation issues
(`#998 <https://github.com/fmtlib/fmt/pull/998>`_,
`#1006 <https://github.com/fmtlib/fmt/pull/1006>`_,
`#1008 <https://github.com/fmtlib/fmt/issues/1008>`_,
`#1011 <https://github.com/fmtlib/fmt/issues/1011>`_,
`#1025 <https://github.com/fmtlib/fmt/issues/1025>`_,
`#1027 <https://github.com/fmtlib/fmt/pull/1027>`_,
`#1028 <https://github.com/fmtlib/fmt/pull/1028>`_,
`#1029 <https://github.com/fmtlib/fmt/pull/1029>`_,
`#1030 <https://github.com/fmtlib/fmt/pull/1030>`_,
`#1031 <https://github.com/fmtlib/fmt/pull/1031>`_,
`#1054 <https://github.com/fmtlib/fmt/pull/1054>`_,
`#1063 <https://github.com/fmtlib/fmt/issues/1063>`_,
`#1068 <https://github.com/fmtlib/fmt/pull/1068>`_,
`#1074 <https://github.com/fmtlib/fmt/pull/1074>`_,
`#1075 <https://github.com/fmtlib/fmt/pull/1075>`_,
`#1079 <https://github.com/fmtlib/fmt/pull/1079>`_,
`#1086 <https://github.com/fmtlib/fmt/pull/1086>`_,
`#1088 <https://github.com/fmtlib/fmt/issues/1088>`_,
`#1089 <https://github.com/fmtlib/fmt/pull/1089>`_,
`#1094 <https://github.com/fmtlib/fmt/pull/1094>`_,
`#1101 <https://github.com/fmtlib/fmt/issues/1101>`_,
`#1102 <https://github.com/fmtlib/fmt/pull/1102>`_,
`#1105 <https://github.com/fmtlib/fmt/issues/1105>`_,
`#1107 <https://github.com/fmtlib/fmt/pull/1107>`_,
`#1115 <https://github.com/fmtlib/fmt/issues/1115>`_,
`#1117 <https://github.com/fmtlib/fmt/issues/1117>`_,
`#1118 <https://github.com/fmtlib/fmt/issues/1118>`_,
`#1120 <https://github.com/fmtlib/fmt/issues/1120>`_,
`#1123 <https://github.com/fmtlib/fmt/issues/1123>`_,
`#1139 <https://github.com/fmtlib/fmt/pull/1139>`_,
`#1140 <https://github.com/fmtlib/fmt/issues/1140>`_,
`#1143 <https://github.com/fmtlib/fmt/issues/1143>`_,
`#1144 <https://github.com/fmtlib/fmt/pull/1144>`_,
`#1150 <https://github.com/fmtlib/fmt/pull/1150>`_,
`#1151 <https://github.com/fmtlib/fmt/pull/1151>`_,
`#1152 <https://github.com/fmtlib/fmt/issues/1152>`_,
`#1154 <https://github.com/fmtlib/fmt/issues/1154>`_,
`#1156 <https://github.com/fmtlib/fmt/issues/1156>`_,
`#1159 <https://github.com/fmtlib/fmt/pull/1159>`_,
`#1175 <https://github.com/fmtlib/fmt/issues/1175>`_,
`#1181 <https://github.com/fmtlib/fmt/issues/1181>`_,
`#1186 <https://github.com/fmtlib/fmt/issues/1186>`_,
`#1187 <https://github.com/fmtlib/fmt/pull/1187>`_,
`#1191 <https://github.com/fmtlib/fmt/pull/1191>`_,
`#1197 <https://github.com/fmtlib/fmt/issues/1197>`_,
`#1200 <https://github.com/fmtlib/fmt/issues/1200>`_,
`#1203 <https://github.com/fmtlib/fmt/issues/1203>`_,
`#1205 <https://github.com/fmtlib/fmt/issues/1205>`_,
`#1206 <https://github.com/fmtlib/fmt/pull/1206>`_,
`#1213 <https://github.com/fmtlib/fmt/issues/1213>`_,
`#1214 <https://github.com/fmtlib/fmt/issues/1214>`_,
`#1217 <https://github.com/fmtlib/fmt/pull/1217>`_,
`#1228 <https://github.com/fmtlib/fmt/issues/1228>`_,
`#1230 <https://github.com/fmtlib/fmt/pull/1230>`_,
`#1232 <https://github.com/fmtlib/fmt/issues/1232>`_,
`#1235 <https://github.com/fmtlib/fmt/pull/1235>`_,
`#1236 <https://github.com/fmtlib/fmt/pull/1236>`_,
`#1240 <https://github.com/fmtlib/fmt/issues/1240>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_,
`@morinmorin <https://github.com/morinmorin>`_,
`@ricco19 (Brian Ricciardelli) <https://github.com/ricco19>`_,
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@remyabel <https://github.com/remyabel>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@rcane (Ronny Krüger) <https://github.com/rcane>`_,
`@mocabe <https://github.com/mocabe>`_,
`@denchat <https://github.com/denchat>`_,
`@cjdb (Christopher Di Bella) <https://github.com/cjdb>`_,
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_,
`@DaanDeMeyer (Daan De Meyer) <https://github.com/DaanDeMeyer>`_,
`@starkmapper (Mark Stapper) <https://github.com/starkmapper>`_.
5.3.0 - 2018-12-28
------------------
* Introduced experimental chrono formatting support:
.. code:: c++
#include <fmt/chrono.h>
int main() {
using namespace std::literals::chrono_literals;
fmt::print("Default format: {} {}\n", 42s, 100ms);
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}
prints::
Default format: 42s 100ms
strftime-like format: 03:15:30
* Added experimental support for emphasis (bold, italic, underline,
strikethrough), colored output to a file stream, and improved colored
formatting API
(`#961 <https://github.com/fmtlib/fmt/pull/961>`_,
`#967 <https://github.com/fmtlib/fmt/pull/967>`_,
`#973 <https://github.com/fmtlib/fmt/pull/973>`_):
.. code:: c++
#include <fmt/color.h>
int main() {
print(fg(fmt::color::crimson) | fmt::emphasis::bold,
"Hello, {}!\n", "world");
print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
fmt::emphasis::underline, "Hello, {}!\n", "мир");
print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
"Hello, {}!\n", "世界");
}
prints the following on modern terminals with RGB color support:
.. image:: https://user-images.githubusercontent.com/576385/
50405788-b66e7500-076e-11e9-9592-7324d1f951d8.png
Thanks `@Rakete1111 (Nicolas) <https://github.com/Rakete1111>`_.
* Added support for 4-bit terminal colors
(`#968 <https://github.com/fmtlib/fmt/issues/968>`_,
`#974 <https://github.com/fmtlib/fmt/pull/974>`_)
.. code:: c++
#include <fmt/color.h>
int main() {
print(fg(fmt::terminal_color::red), "stop\n");
}
Note that these colors vary by terminal:
.. image:: https://user-images.githubusercontent.com/576385/
50405925-dbfc7e00-0770-11e9-9b85-333fab0af9ac.png
Thanks `@Rakete1111 (Nicolas) <https://github.com/Rakete1111>`_.
* Parameterized formatting functions on the type of the format string
(`#880 <https://github.com/fmtlib/fmt/issues/880>`_,
`#881 <https://github.com/fmtlib/fmt/pull/881>`_,
`#883 <https://github.com/fmtlib/fmt/pull/883>`_,
`#885 <https://github.com/fmtlib/fmt/pull/885>`_,
`#897 <https://github.com/fmtlib/fmt/pull/897>`_,
`#920 <https://github.com/fmtlib/fmt/issues/920>`_).
Any object of type ``S`` that has an overloaded ``to_string_view(const S&)``
returning ``fmt::string_view`` can be used as a format string:
.. code:: c++
namespace my_ns {
inline string_view to_string_view(const my_string& s) {
return {s.data(), s.length()};
}
}
std::string message = fmt::format(my_string("The answer is {}."), 42);
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Made ``std::string_view`` work as a format string
(`#898 <https://github.com/fmtlib/fmt/pull/898>`_):
.. code:: c++
auto message = fmt::format(std::string_view("The answer is {}."), 42);
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Added wide string support to compile-time format string checks
(`#924 <https://github.com/fmtlib/fmt/pull/924>`_):
.. code:: c++
print(fmt(L"{:f}"), 42); // compile-time error: invalid type specifier
Thanks `@XZiar <https://github.com/XZiar>`_.
* Made colored print functions work with wide strings
(`#867 <https://github.com/fmtlib/fmt/pull/867>`_):
.. code:: c++
#include <fmt/color.h>
int main() {
print(fg(fmt::color::red), L"{}\n", 42);
}
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Introduced experimental Unicode support
(`#628 <https://github.com/fmtlib/fmt/issues/628>`_,
`#891 <https://github.com/fmtlib/fmt/pull/891>`_):
.. code:: c++
using namespace fmt::literals;
auto s = fmt::format("{:*^5}"_u, "🤡"_u); // s == "**🤡**"_u
* Improved locale support:
.. code:: c++
#include <fmt/locale.h>
struct numpunct : std::numpunct<char> {
protected:
char do_thousands_sep() const override { return '~'; }
};
std::locale loc;
auto s = fmt::format(std::locale(loc, new numpunct()), "{:n}", 1234567);
// s == "1~234~567"
* Constrained formatting functions on proper iterator types
(`#921 <https://github.com/fmtlib/fmt/pull/921>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Added ``make_printf_args`` and ``make_wprintf_args`` functions
(`#934 <https://github.com/fmtlib/fmt/pull/934>`_).
Thanks `@tnovotny <https://github.com/tnovotny>`_.
* Deprecated ``fmt::visit``, ``parse_context``, and ``wparse_context``.
Use ``fmt::visit_format_arg``, ``format_parse_context``, and
``wformat_parse_context`` instead.
* Removed undocumented ``basic_fixed_buffer`` which has been superseded by the
iterator-based API
(`#873 <https://github.com/fmtlib/fmt/issues/873>`_,
`#902 <https://github.com/fmtlib/fmt/pull/902>`_).
Thanks `@superfunc (hollywood programmer) <https://github.com/superfunc>`_.
* Disallowed repeated leading zeros in an argument ID:
.. code:: c++
fmt::print("{000}", 42); // error
* Reintroduced support for gcc 4.4.
* Fixed compilation on platforms with exotic ``double``
(`#878 <https://github.com/fmtlib/fmt/issues/878>`_).
* Improved documentation
(`#164 <https://github.com/fmtlib/fmt/issues/164>`_,
`#877 <https://github.com/fmtlib/fmt/issues/877>`_,
`#901 <https://github.com/fmtlib/fmt/pull/901>`_,
`#906 <https://github.com/fmtlib/fmt/pull/906>`_,
`#979 <https://github.com/fmtlib/fmt/pull/979>`_).
Thanks `@kookjr (Mathew Cucuzella) <https://github.com/kookjr>`_,
`@DarkDimius (Dmitry Petrashko) <https://github.com/DarkDimius>`_,
`@HecticSerenity <https://github.com/HecticSerenity>`_.
* Added pkgconfig support which makes it easier to consume the library from
meson and other build systems
(`#916 <https://github.com/fmtlib/fmt/pull/916>`_).
Thanks `@colemickens (Cole Mickens) <https://github.com/colemickens>`_.
* Various build improvements
(`#909 <https://github.com/fmtlib/fmt/pull/909>`_,
`#926 <https://github.com/fmtlib/fmt/pull/926>`_,
`#937 <https://github.com/fmtlib/fmt/pull/937>`_,
`#953 <https://github.com/fmtlib/fmt/pull/953>`_,
`#959 <https://github.com/fmtlib/fmt/pull/959>`_).
Thanks `@tchaikov (Kefu Chai) <https://github.com/tchaikov>`_,
`@luncliff (Park DongHa) <https://github.com/luncliff>`_,
`@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_,
`@hotwatermorning <https://github.com/hotwatermorning>`_,
`@Zefz (JohanJansen) <https://github.com/Zefz>`_.
* Improved ``string_view`` construction performance
(`#914 <https://github.com/fmtlib/fmt/pull/914>`_).
Thanks `@gabime (Gabi Melman) <https://github.com/gabime>`_.
* Fixed non-matching char types
(`#895 <https://github.com/fmtlib/fmt/pull/895>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Fixed ``format_to_n`` with ``std::back_insert_iterator``
(`#913 <https://github.com/fmtlib/fmt/pull/913>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Fixed locale-dependent formatting
(`#905 <https://github.com/fmtlib/fmt/issues/905>`_).
* Fixed various compiler warnings and errors
(`#882 <https://github.com/fmtlib/fmt/pull/882>`_,
`#886 <https://github.com/fmtlib/fmt/pull/886>`_,
`#933 <https://github.com/fmtlib/fmt/pull/933>`_,
`#941 <https://github.com/fmtlib/fmt/pull/941>`_,
`#931 <https://github.com/fmtlib/fmt/issues/931>`_,
`#943 <https://github.com/fmtlib/fmt/pull/943>`_,
`#954 <https://github.com/fmtlib/fmt/pull/954>`_,
`#956 <https://github.com/fmtlib/fmt/pull/956>`_,
`#962 <https://github.com/fmtlib/fmt/pull/962>`_,
`#965 <https://github.com/fmtlib/fmt/issues/965>`_,
`#977 <https://github.com/fmtlib/fmt/issues/977>`_,
`#983 <https://github.com/fmtlib/fmt/pull/983>`_,
`#989 <https://github.com/fmtlib/fmt/pull/989>`_).
Thanks `@Luthaf (Guillaume Fraux) <https://github.com/Luthaf>`_,
`@stevenhoving (Steven Hoving) <https://github.com/stevenhoving>`_,
`@christinaa (Kristina Brooks) <https://github.com/christinaa>`_,
`@lgritz (Larry Gritz) <https://github.com/lgritz>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@0x8000-0000 (Sign Bit) <https://github.com/0x8000-0000>`_,
`@liuping1997 <https://github.com/liuping1997>`_.
5.2.1 - 2018-09-21
------------------
* Fixed ``visit`` lookup issues on gcc 7 & 8
(`#870 <https://github.com/fmtlib/fmt/pull/870>`_).
Thanks `@medithe <https://github.com/medithe>`_.
* Fixed linkage errors on older gcc.
* Prevented ``fmt/range.h`` from specializing ``fmt::basic_string_view``
(`#865 <https://github.com/fmtlib/fmt/issues/865>`_,
`#868 <https://github.com/fmtlib/fmt/pull/868>`_).
Thanks `@hhggit (dual) <https://github.com/hhggit>`_.
* Improved error message when formatting unknown types
(`#872 <https://github.com/fmtlib/fmt/pull/872>`_).
Thanks `@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
* Disabled templated user-defined literals when compiled under nvcc
(`#875 <https://github.com/fmtlib/fmt/pull/875>`_).
Thanks `@CandyGumdrop (Candy Gumdrop) <https://github.com/CandyGumdrop>`_,
* Fixed ``format_to`` formatting to ``wmemory_buffer``
(`#874 <https://github.com/fmtlib/fmt/issues/874>`_).
5.2.0 - 2018-09-13
------------------
* Optimized format string parsing and argument processing which resulted in up
to 5x speed up on long format strings and significant performance boost on
various benchmarks. For example, version 5.2 is 2.22x faster than 5.1 on
decimal integer formatting with ``format_to`` (macOS, clang-902.0.39.2):
================== ======= =======
Method Time, s Speedup
================== ======= =======
fmt::format 5.1 0.58
fmt::format 5.2 0.35 1.66x
fmt::format_to 5.1 0.51
fmt::format_to 5.2 0.23 2.22x
sprintf 0.71
std::to_string 1.01
std::stringstream 1.73
================== ======= =======
* Changed the ``fmt`` macro from opt-out to opt-in to prevent name collisions.
To enable it define the ``FMT_STRING_ALIAS`` macro to 1 before including
``fmt/format.h``:
.. code:: c++
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string answer = format(fmt("{}"), 42);
* Added compile-time format string checks to ``format_to`` overload that takes
``fmt::memory_buffer`` (`#783 <https://github.com/fmtlib/fmt/issues/783>`_):
.. code:: c++
fmt::memory_buffer buf;
// Compile-time error: invalid type specifier.
fmt::format_to(buf, fmt("{:d}"), "foo");
* Moved experimental color support to ``fmt/color.h`` and enabled the
new API by default. The old API can be enabled by defining the
``FMT_DEPRECATED_COLORS`` macro.
* Added formatting support for types explicitly convertible to
``fmt::string_view``:
.. code:: c++
struct foo {
explicit operator fmt::string_view() const { return "foo"; }
};
auto s = format("{}", foo());
In particular, this makes formatting function work with
``folly::StringPiece``.
* Implemented preliminary support for ``char*_t`` by replacing the ``format``
function overloads with a single function template parameterized on the string
type.
* Added support for dynamic argument lists
(`#814 <https://github.com/fmtlib/fmt/issues/814>`_,
`#819 <https://github.com/fmtlib/fmt/pull/819>`_).
Thanks `@MikePopoloski (Michael Popoloski)
<https://github.com/MikePopoloski>`_.
* Reduced executable size overhead for embedded targets using newlib nano by
making locale dependency optional
(`#839 <https://github.com/fmtlib/fmt/pull/839>`_).
Thanks `@teajay-fr (Thomas Benard) <https://github.com/teajay-fr>`_.
* Keep ``noexcept`` specifier when exceptions are disabled
(`#801 <https://github.com/fmtlib/fmt/issues/801>`_,
`#810 <https://github.com/fmtlib/fmt/pull/810>`_).
Thanks `@qis (Alexej Harm) <https://github.com/qis>`_.
* Fixed formatting of user-defined types providing ``operator<<`` with
``format_to_n``
(`#806 <https://github.com/fmtlib/fmt/pull/806>`_).
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
* Fixed dynamic linkage of new symbols
(`#808 <https://github.com/fmtlib/fmt/issues/808>`_).
* Fixed global initialization issue
(`#807 <https://github.com/fmtlib/fmt/issues/807>`_):
.. code:: c++
// This works on compilers with constexpr support.
static const std::string answer = fmt::format("{}", 42);
* Fixed various compiler warnings and errors
(`#804 <https://github.com/fmtlib/fmt/pull/804>`_,
`#809 <https://github.com/fmtlib/fmt/issues/809>`_,
`#811 <https://github.com/fmtlib/fmt/pull/811>`_,
`#822 <https://github.com/fmtlib/fmt/issues/822>`_,
`#827 <https://github.com/fmtlib/fmt/pull/827>`_,
`#830 <https://github.com/fmtlib/fmt/issues/830>`_,
`#838 <https://github.com/fmtlib/fmt/pull/838>`_,
`#843 <https://github.com/fmtlib/fmt/issues/843>`_,
`#844 <https://github.com/fmtlib/fmt/pull/844>`_,
`#851 <https://github.com/fmtlib/fmt/issues/851>`_,
`#852 <https://github.com/fmtlib/fmt/pull/852>`_,
`#854 <https://github.com/fmtlib/fmt/pull/854>`_).
Thanks `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_,
`@medithe <https://github.com/medithe>`_, and
`@eliasdaler (Elias Daler) <https://github.com/eliasdaler>`_.
5.1.0 - 2018-07-05
------------------
@ -16,7 +783,7 @@
``color`` enum) is now deprecated.
(`#762 <https://github.com/fmtlib/fmt/issues/762>`_
`#767 <https://github.com/fmtlib/fmt/pull/767>`_).
Thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
* Added quotes to strings in ranges and tuples
(`#766 <https://github.com/fmtlib/fmt/pull/766>`_).
@ -97,7 +864,7 @@
* Implemented ``constexpr`` parsing of format strings and `compile-time format
string checks
<http://fmtlib.net/dev/api.html#compile-time-format-string-checks>`_. For
<https://fmt.dev/dev/api.html#compile-time-format-string-checks>`_. For
example
.. code:: c++
@ -158,7 +925,7 @@
throw format_error("invalid specifier");
* Added `iterator support
<http://fmtlib.net/dev/api.html#output-iterator-support>`_:
<https://fmt.dev/dev/api.html#output-iterator-support>`_:
.. code:: c++
@ -169,7 +936,7 @@
fmt::format_to(std::back_inserter(out), "{}", 42);
* Added the `format_to_n
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
<https://fmt.dev/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
function that restricts the output to the specified number of characters
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
@ -180,7 +947,7 @@
// out == "1234" (without terminating '\0')
* Added the `formatted_size
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
<https://fmt.dev/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
function for computing the output size:
.. code:: c++
@ -190,7 +957,7 @@
auto size = fmt::formatted_size("{}", 12345); // size == 5
* Improved compile times by reducing dependencies on standard headers and
providing a lightweight `core API <http://fmtlib.net/dev/api.html#core-api>`_:
providing a lightweight `core API <https://fmt.dev/dev/api.html#core-api>`_:
.. code:: c++
@ -202,7 +969,7 @@
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_.
* Added the `make_format_args
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_
<https://fmt.dev/dev/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_
function for capturing formatting arguments:
.. code:: c++
@ -284,7 +1051,7 @@
fmt::format("{} {two}", 1, fmt::arg("two", 2));
* Removed the write API in favor of the `format API
<http://fmtlib.net/dev/api.html#format-api>`_ with compile-time handling of
<https://fmt.dev/dev/api.html#format-api>`_ with compile-time handling of
format strings.
* Disallowed formatting of multibyte strings into a wide character target
@ -687,7 +1454,7 @@
3.0.1 - 2016-11-01
------------------
* Fixed handling of thousands seperator
* Fixed handling of thousands separator
(`#353 <https://github.com/fmtlib/fmt/issues/353>`_).
* Fixed handling of ``unsigned char`` strings
@ -745,10 +1512,10 @@
Including ``format.h`` from the ``cppformat`` directory is deprecated
but works via a proxy header which will be removed in the next major version.
The documentation is now available at http://fmtlib.net.
The documentation is now available at https://fmt.dev.
* Added support for `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like
`date and time formatting <http://fmtlib.net/3.0.0/api.html#date-and-time-formatting>`_
`date and time formatting <https://fmt.dev/3.0.0/api.html#date-and-time-formatting>`_
(`#283 <https://github.com/fmtlib/fmt/issues/283>`_):
.. code:: c++
@ -780,7 +1547,7 @@
// s == "The date is 2012-12-9"
* Added support for `custom argument formatters
<http://fmtlib.net/3.0.0/api.html#argument-formatters>`_
<https://fmt.dev/3.0.0/api.html#argument-formatters>`_
(`#235 <https://github.com/fmtlib/fmt/issues/235>`_).
* Added support for locale-specific integer formatting with the ``n`` specifier
@ -1160,7 +1927,7 @@ Documentation
* Added `Building the documentation
<http://fmtlib.net/2.0.0/usage.html#building-the-documentation>`_
<https://fmt.dev/2.0.0/usage.html#building-the-documentation>`_
section to the documentation.
* Documentation build script is now compatible with Python 3 and newer pip versions.

View File

@ -1,23 +1,27 @@
Copyright (c) 2012 - 2016, Victor Zverovich
Copyright (c) 2012 - present, Victor Zverovich
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

View File

@ -6,80 +6,82 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://badges.gitter.im/Join%20Chat.svg
:alt: Join the chat at https://gitter.im/fmtlib/fmt
:target: https://gitter.im/fmtlib/fmt
.. 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
.. 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%3Dfmt&can=1
**{fmt}** is an open-source formatting library for C++.
It can be used as a safe and fast alternative to (s)printf and IOStreams.
It can be used as a safe and fast alternative to (s)printf and iostreams.
`Documentation <http://fmtlib.net/latest/>`__
`Documentation <https://fmt.dev/latest/>`__
This is a development branch that implements the C++ standards proposal `P0645
Text Formatting <http://fmtlib.net/Text%20Formatting.html>`__.
Released versions are available from the `Releases page
<https://github.com/fmtlib/fmt/releases>`__.
Q&A: ask questions on `StackOverflow with the tag fmt <http://stackoverflow.com/questions/tagged/fmt>`_.
Features
--------
* Replacement-based `format API <http://fmtlib.net/dev/api.html>`_ with
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization.
* `Format string syntax <http://fmtlib.net/dev/syntax.html>`_ similar to the one
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
in Python.
* Safe `printf implementation
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_ including
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
the POSIX extension for positional arguments.
* Implementation of `C++20 std::format <https://fmt.dev/Text%20Formatting.html>`__.
* Support for user-defined types.
* High speed: performance of the format API is close to that of glibc's `printf
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the
performance of IOStreams. See `Speed tests`_ and
`Fast integer to string conversion in C++
* High performance: faster than common standard library implementations of
`printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and
iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
* Small code size both in terms of source code (the minimum configuration
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 <http://fmtlib.net/latest/index.html#portability>`_ with
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers.
* Clean warning-free codebase even on high warning levels
(``-Wall -Wextra -pedantic``).
* Support for wide strings.
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
See the `documentation <http://fmtlib.net/latest/>`_ for more details.
See the `documentation <https://fmt.dev/latest/>`_ for more details.
Examples
--------
This prints ``Hello, world!`` to stdout:
Print ``Hello, world!`` to ``stdout``:
.. code:: c++
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // uses printf format string syntax
fmt::print("Hello, {}!", "world"); // Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // printf format string syntax
Arguments can be accessed by position and arguments' indices can be repeated:
Format a string and use positional arguments:
.. code:: c++
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
// s == "abracadabra"
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."
Format strings can be checked at compile time:
Check a format string at compile time:
.. code:: c++
// test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string s = format(fmt("{2}"), 42);
@ -87,7 +89,7 @@ Format strings can be checked at compile time:
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:3:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
@ -97,7 +99,7 @@ Format strings can be checked at compile time:
context_.on_error("argument index out of range");
^
{fmt} can be used as a safe portable replacement for ``itoa``
Use {fmt} as a safe portable replacement for ``itoa``
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
.. code:: c++
@ -105,31 +107,36 @@ Format strings can be checked at compile time:
fmt::memory_buffer buf;
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
// access the string using to_string(buf) or buf.data()
// access the string with to_string(buf) or buf.data()
An object of any user-defined type for which there is an overloaded
:code:`std::ostream` insertion operator (``operator<<``) can be formatted:
Format objects of user-defined types via a simple `extension API
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
.. code:: c++
#include "fmt/ostream.h"
#include "fmt/format.h"
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
struct date {
int year, month, day;
};
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
template <>
struct fmt::formatter<date> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
}
};
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
// s == "The date is 2012-12-9"
You can create your own functions similar to `format
<http://fmtlib.net/latest/api.html#format>`_ and
`print <http://fmtlib.net/latest/api.html#print>`_
Create your own functions similar to `format
<https://fmt.dev/latest/api.html#format>`_ and
`print <https://fmt.dev/latest/api.html#print>`_
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++
@ -147,19 +154,138 @@ which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
report_error("file not found: {}", path);
Note that ``vreport_error`` is not parameterized on argument types which can
improve compile times and reduce code size compared to fully parameterized version.
improve compile times and reduce code size compared to a fully parameterized
version.
Benchmarks
----------
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
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~17% 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
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
{fmt} is 10x faster than ``std::ostringstream`` and ``sprintf`` on floating-point
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
size compared to iostreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
**Non-optimized build**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
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.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ https://fmt.dev/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
Then you can run the speed test::
$ make speed-test
or the bloat test::
$ make bloat-test
Projects using this library
---------------------------
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time strategy game
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time
strategy game
* `AMPL/MP <https://github.com/ampl/mp>`_:
An open-source library for mathematical programming
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft operations suite
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
operations suite
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
* `ccache <https://ccache.dev/>`_: A compiler cache
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
@ -180,6 +306,8 @@ Projects using this library
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `MongoDB <https://mongodb.com/>`_: Distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets
@ -194,7 +322,11 @@ Projects using this library
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster proxy
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
proxy
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
library
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
Small crossplatform 2D graphic engine
@ -202,11 +334,11 @@ Projects using this library
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store that can handle
1 million transactions per second on a single server
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
that can handle 1 million transactions per second on a single server
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++ framework for
high-performance server applications on modern hardware
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
framework for high-performance server applications on modern hardware
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
@ -214,9 +346,10 @@ Projects using this library
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source MMORPG framework
* `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
@ -228,28 +361,28 @@ Motivation
So why yet another formatting library?
There are plenty of methods for doing this task, from standard ones like
the printf family of function and IOStreams to Boost Format library and
FastFormat. The reason for creating a new library is that every existing
the printf family of function and iostreams to Boost Format and FastFormat
libraries. The reason for creating a new library is that every existing
solution that I found either had serious issues or didn't provide
all the features I needed.
Printf
printf
~~~~~~
The good thing about printf is that it is pretty fast and readily available
The good thing about ``printf`` is that it is pretty fast and readily available
being a part of the C standard library. The main drawback is that it
doesn't support user-defined types. Printf also has safety issues although
they are mostly solved with `__attribute__ ((format (printf, ...))
doesn't support user-defined types. ``printf`` also has safety issues although
they are somewhat mitigated with `__attribute__ ((format (printf, ...))
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
There is a POSIX extension that adds positional arguments required for
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
to printf but it is not a part of C99 and may not be available on some
to ``printf`` but it is not a part of C99 and may not be available on some
platforms.
IOStreams
iostreams
~~~~~~~~~
The main issue with IOStreams is best illustrated with an example:
The main issue with iostreams is best illustrated with an example:
.. code:: c++
@ -261,21 +394,20 @@ which is a lot of typing compared to printf:
printf("%.2f\n", 1.23456);
Matthew Wilson, the author of FastFormat, referred to this situation with
IOStreams as "chevron hell". IOStreams doesn't support positional arguments
by design.
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
don't support positional arguments by design.
The good part is that IOStreams supports user-defined types and is safe
although error reporting is awkward.
The good part is that iostreams support user-defined types and are safe although
error handling is awkward.
Boost Format library
~~~~~~~~~~~~~~~~~~~~
Boost Format
~~~~~~~~~~~~
This is a very powerful library which supports both printf-like format
strings and positional arguments. The main its drawback is performance.
According to various benchmarks it is much slower than other methods
considered here. Boost Format also has excessive build times and severe
code bloat issues (see `Benchmarks`_).
This is a very powerful library which supports both ``printf``-like format
strings and positional arguments. Its main drawback is performance. According to
various benchmarks it is much slower than other methods considered here. Boost
Format also has excessive build times and severe code bloat issues (see
`Benchmarks`_).
FastFormat
~~~~~~~~~~
@ -296,147 +428,58 @@ too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses printf-like format strings
and is type safe. It doesn't support user-defined types or positional
arguments. It makes unconventional use of ``operator()`` for passing
format arguments.
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 and
fast. Unfortunately it doesn't support positional arguments and wrapping
it in C++98 is somewhat difficult. Also its performance and code compactness
are limited by IOStreams.
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
~~~~~~~~~~~~~~~~~~
This is not really a formatting library but I decided to include it here
for completeness. As IOStreams it suffers from the problem of mixing
verbatim text with arguments. The library is pretty fast, but slower
on integer formatting than ``fmt::Writer`` on Karma's own benchmark,
This is not really a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing verbatim text
with arguments. The library is pretty fast, but slower on integer formatting
than ``fmt::format_int`` on Karma's own benchmark,
see `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
Benchmarks
----------
FAQ
---
Speed tests
~~~~~~~~~~~
Q: how can I capture formatting arguments and format them later?
The following speed tests results were generated by building
``tinyformat_test.cpp`` on Ubuntu GNU/Linux 14.04.1 with
``g++-4.8.2 -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of three
runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` or
equivalent is filled 2000000 times with output sent to ``/dev/null``; for
further details see the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
A: use ``std::tuple``:
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.35
libc++ std::ostream 3.42
fmt 534bff7 fmt::print 1.56
tinyformat 2.0.1 tfm::printf 3.73
Boost Format 1.54 boost::format 8.44
Folly Format folly::format 2.54
================= ============= ===========
.. code:: c++
As you can see ``boost::format`` is much slower than the alternative methods; this
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat
cannot be faster than the IOStreams because it uses them internally.
Performance of fmt is close to that of printf, being `faster than printf on integer
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_,
but slower on floating-point formatting which dominates this benchmark.
template <typename... Args>
auto capture(const Args&... args) {
return std::make_tuple(args...);
}
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
auto print_message = [](const auto&... args) {
fmt::print(args...);
};
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.7 29 26
printf+string 18.4 29 26
IOStreams 34.6 59 55
fmt 22.0 37 34
tinyformat 51.8 103 97
Boost Format 120.5 762 739
Folly Format 158.7 102 87
============= =============== ==================== ==================
As you can see, fmt has 60% less overhead in terms of resulting binary code
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
has by far the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
**Non-optimized build**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.4 33 30
printf+string 18.5 33 30
IOStreams 31.9 56 52
fmt 20.9 56 51
tinyformat 38.9 88 82
Boost Format 64.8 366 304
Folly Format 113.5 442 428
============= =============== ==================== ==================
``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.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ http://fmtlib.net/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
Then you can run the speed test::
$ make speed-test
or the bloat test::
$ make bloat-test
// Capture and store arguments:
auto args = capture("{} {}", 42, "foo");
// Do formatting:
std::apply(print_message, args);
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
<http://fmtlib.net/latest/syntax.html>`_
<https://fmt.dev/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. For this reason the documentation is
@ -448,7 +491,7 @@ It only applies if you distribute the documentation of fmt.
Acknowledgments
---------------
The fmt library is maintained by Victor Zverovich (`vitaut
The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
<https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and

View File

@ -6,7 +6,7 @@ endif ()
add_custom_target(doc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
SOURCES api.rst syntax.rst build.py conf.py _templates/layout.html)
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION share/doc/fmt OPTIONAL)

View File

@ -58,7 +58,7 @@
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
{% for v in versions.split(',') %}
<li><a href="http://fmtlib.net/{{v}}">{{v}}</a></li>
<li><a href="https://fmt.dev/{{v}}">{{v}}</a></li>
{% endfor %}
</ul>
</li>

View File

@ -10,7 +10,9 @@ 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/time.h <time-api>`: date and time formatting
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
@ -23,41 +25,43 @@ 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>`
imilar to that of Python's `str.format
similar to that of Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#str.format>`_.
They take *format_str* and *args* as arguments.
*format_str* is a format string that contains literal text and replacement
fields surrounded by braces ``{}``. The fields are replaced with formatted
arguments in the resulting string.
arguments in the resulting string. A function taking *format_str* doesn't
participate in an overload resolution if the latter is not a string.
*args* is an argument list representing objects to be formatted.
.. _format:
.. doxygenfunction:: format(string_view, const Args&...)
.. doxygenfunction:: vformat(string_view, format_args)
.. doxygenfunction:: format(const S&, Args&&...)
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>)
.. _print:
.. doxygenfunction:: print(string_view, const Args&...)
.. doxygenfunction:: print(const S&, Args&&...)
.. doxygenfunction:: vprint(string_view, format_args)
.. doxygenfunction:: print(std::FILE *, string_view, const Args&...)
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
.. doxygenfunction:: print(std::FILE *, wstring_view, const Args&...)
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
Named arguments
Named Arguments
---------------
.. doxygenfunction:: fmt::arg(string_view, const T&)
.. doxygenfunction:: fmt::arg(const S&, const T&)
Argument lists
Named arguments are not supported in compile-time checks at the moment.
Argument Lists
--------------
.. doxygenfunction:: fmt::make_format_args(const Args&...)
@ -90,12 +94,13 @@ Format API
``fmt/format.h`` defines the full format API providing compile-time format
string checks, output iterator and user-defined type support.
Compile-time format string checks
Compile-time Format String Checks
---------------------------------
.. doxygendefine:: FMT_STRING
.. doxygendefine:: fmt
Formatting user-defined types
Formatting User-defined Types
-----------------------------
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
@ -113,7 +118,7 @@ template and implement ``parse`` and ``format`` methods::
template <typename FormatContext>
auto format(const point &p, FormatContext &ctx) {
return format_to(ctx.begin(), "({:.1f}, {:.1f})", p.x, p.y);
return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
}
};
}
@ -127,18 +132,62 @@ Then you can pass objects of type ``point`` to any formatting function::
In the example above the ``formatter<point>::parse`` function ignores the
contents of the format string referred to by ``ctx.begin()`` so the object will
always be formatted in the same way. See ``formatter<tm>::parse`` in
:file:`fmt/time.h` for an advanced example of how to parse the format string and
:file:`fmt/chrono.h` for an advanced example of how to parse the format string and
customize the formatted output.
This section shows how to define a custom format function for a user-defined
type. The next section describes how to get ``fmt`` to use a conventional stream
output ``operator<<`` when one is defined for a user-defined type.
You can also reuse existing formatters, for example::
Output iterator support
enum class color {red, green, blue};
template <>
struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext &ctx) {
string_view name = "unknown";
switch (c) {
case color::red: name = "red"; break;
case color::green: name = "green"; break;
case color::blue: name = "blue"; break;
}
return formatter<string_view>::format(name, ctx);
}
};
You can also write a formatter for a hierarchy of classes::
#include <type_traits>
#include <fmt/format.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return "A"; }
};
struct B : A {
virtual std::string name() const { return "B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> {
template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}
Output Iterator Support
-----------------------
.. doxygenfunction:: fmt::format_to(OutputIt, string_view, const Args&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, string_view, const Args&...)
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, Args&&...)
.. doxygenstruct:: fmt::format_to_n_result
:members:
@ -154,17 +203,27 @@ The following user-defined literals are defined in ``fmt/format.h``.
Utilities
---------
.. doxygenstruct:: fmt::is_char
.. doxygentypedef:: fmt::char_t
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
.. doxygenfunction:: fmt::to_string(const T&)
.. doxygenfunction:: fmt::to_wstring(const T&)
.. doxygenfunction:: fmt::to_string_view(const Char *)
.. doxygenfunction:: fmt::join(const Range&, string_view)
.. doxygenfunction:: fmt::join(It, It, string_view)
.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
:members:
System errors
System Errors
-------------
fmt does not use ``errno`` to communicate errors to the user, but it may call
@ -181,7 +240,7 @@ the value of ``errno`` being preserved by library functions.
.. _formatstrings:
Custom allocators
Custom Allocators
-----------------
The {fmt} library supports custom dynamic memory allocators.
@ -216,29 +275,29 @@ arguments, the container that stores pointers to them will be allocated using
the default allocator. Also floating-point formatting falls back on ``sprintf``
which may do allocations.
Custom formatting of built-in types
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)
: 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();
void operator()(int value) {
if (spec().type() == 'x')
(*this)(static_cast<unsigned>(value)); // convert to unsigned and format
else
arg_formatter::operator()(value);
auto operator()(int value) {
if (specs() && specs()->type == 'x')
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value);
}
};
@ -260,16 +319,41 @@ custom argument formatter class::
.. doxygenclass:: fmt::arg_formatter
:members:
.. _time-api:
.. _ranges-api:
Date and time formatting
Ranges and Tuple Formatting
===========================
The library also supports convenient formatting of ranges and tuples::
#include <fmt/ranges.h>
std::tuple<char, int, float> t{'a', 1, 2.0f};
// Prints "('a', 1, 2.0)"
fmt::print("{}", t);
NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main
``format.h`` header, but expect this to change in the future.
Using ``fmt::join``, you can separate tuple elements with a custom separator::
#include <fmt/ranges.h>
std::tuple<int, char> t = {1, 'a'};
// Prints "1, a"
fmt::print("{}", fmt::join(t, ", "));
.. _chrono-api:
Date and Time Formatting
========================
The library supports `strftime
<http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time
formatting::
#include <fmt/time.h>
#include <fmt/chrono.h>
std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date)
@ -280,7 +364,7 @@ The format string syntax is described in the documentation of
.. _ostream-api:
``std::ostream`` support
``std::ostream`` Support
========================
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
@ -301,11 +385,11 @@ user-defined types that have overloaded ``operator<<``::
std::string s = fmt::format("The date is {}", date(2012, 12, 9));
// s == "The date is 2012-12-9"
.. doxygenfunction:: print(std::ostream&, string_view, const Args&...)
.. doxygenfunction:: print(std::basic_ostream<Char>&, const S&, Args&&...)
.. _printf-api:
``printf`` formatting
``printf`` Formatting
=====================
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
@ -315,10 +399,10 @@ the POSIX extension for positional arguments. Unlike their standard
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
argument type doesn't match its format specification.
.. doxygenfunction:: printf(string_view, const Args&...)
.. doxygenfunction:: printf(const S&, const Args&...)
.. doxygenfunction:: fprintf(std::FILE *, string_view, const Args&...)
.. doxygenfunction:: fprintf(std::FILE *, const S&, const Args&...)
.. doxygenfunction:: fprintf(std::ostream&, string_view, const Args&...)
.. doxygenfunction:: fprintf(std::basic_ostream<Char>&, const S&, const Args&...)
.. doxygenfunction:: sprintf(string_view, const Args&...)
.. doxygenfunction:: sprintf(const S&, const Args&...)

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from 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']
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']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."
@ -89,9 +89,12 @@ def build_docs(version='dev', **kwargs):
FMT_USE_VARIADIC_TEMPLATES=1 \
FMT_USE_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=1 \
FMT_API= \
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}"
"FMT_END_NAMESPACE=}}" \
"FMT_STRING_ALIAS=1" \
"FMT_ENABLE_IF(B)="
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
if p.returncode != 0:

View File

@ -1,7 +1,7 @@
Overview
========
**fmt** (formerly cppformat) is an open-source formatting library.
**fmt** is an open-source formatting library.
It can be used as a fast and safe alternative to printf and IOStreams.
.. raw:: html
@ -40,7 +40,7 @@ The ``fmt::format`` function returns a string "The answer is 42.". You can use
format_to(out, "For a moment, {} happened.", "nothing");
out.data(); // returns a pointer to the formatted data
The ``fmt::print`` function performs formatting and writes the result to a file:
The ``fmt::print`` function performs formatting and writes the result to a stream:
.. code:: c++
@ -94,7 +94,7 @@ Safety
The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions or at compile
tim. For example, the code
time. For example, the code
.. code:: c++
@ -109,7 +109,8 @@ string", because the argument ``"forty-two"`` is a string while the format code
format(fmt("The answer is {:d}"), "forty-two");
reports a compile-time error for the same reason on compilers that support
relaxed ``constexpr``.
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for how to enable
compile-time checks.
The following code
@ -129,7 +130,7 @@ its numeric value being written to the stream (i.e. 1070 instead of letter 'ю'
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
needed.
Compact binary code
Compact Binary Code
-------------------
The library is designed to produce compact per-call compiled code. For example
@ -174,8 +175,9 @@ The library is highly portable and relies only on a small set of C++11 features:
* decltype
* trailing return types
* deleted functions
* alias templates
These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
compilers use fmt `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98.

View File

@ -76,7 +76,7 @@ The general form of a *standard format specifier* is:
.. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
fill: <a character other than '{' or '}'>
fill: <a character other than '{', '}' or '\0'>
align: "<" | ">" | "=" | "^"
sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}"
@ -84,11 +84,11 @@ The general form of a *standard format specifier* is:
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
The *fill* character can be any character other than '{' or '}'. The presence
of a fill character is signaled by the character following it, which must be
one of the alignment options. If the second character of *format_spec* is not
a valid alignment option, then it is assumed that both the fill character and
the alignment option are absent.
The *fill* character can be any character other than '{', '}' or '\\0'. The
presence of a fill character is signaled by the character following it, which
must be one of the alignment options. If the second character of *format_spec*
is not a valid alignment option, then it is assumed that both the fill character
and the alignment option are absent.
The meaning of the various alignment options is as follows:
@ -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
@ -244,7 +241,7 @@ The available presentation types for floating-point values are:
| | notation using the letter 'e' to indicate the exponent. |
+---------+----------------------------------------------------------+
| ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an |
| | upper-case 'E' as the separator character. |
| | upper-case ``'E'`` as the separator character. |
+---------+----------------------------------------------------------+
| ``'f'`` | Fixed point. Displays the number as a fixed-point |
| | number. |
@ -264,10 +261,19 @@ 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. |
+---------+----------------------------------------------------------+
| none | The same as ``'g'``. |
| ``'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. |
+---------+----------------------------------------------------------+
| none | Similar to ``'g'``, except that fixed-point notation, |
| | when used, has at least one digit past the decimal |
| | point. The default precision is as high as needed to |
| | represent the particular value. |
+---------+----------------------------------------------------------+
Floating-point formatting is locale-dependent.
.. ifconfig:: False
@ -303,7 +309,7 @@ The available presentation types for pointers are:
.. _formatexamples:
Format examples
Format Examples
===============
This section contains examples of the format syntax and comparison with
@ -357,6 +363,13 @@ Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::
format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
// Result: "3.140000; -3.140000"
As a percentage::
format("{0:f} or {0:%}", .635);
// Result: "0.635000 or 63.500000%"
format("{:*^{}.{}%}", 1., 15, 2); // With fill, dynamic width and dynamic precision.
// Result: "****100.00%****"
Replacing ``%x`` and ``%o`` and converting the value to different bases::
format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
@ -412,4 +425,3 @@ Padded hex byte with prefix and always prints both hex characters::
9 9 11 1001
10 A 12 1010
11 B 13 1011

View File

@ -2,21 +2,15 @@
Usage
*****
To use the fmt library, add :file:`format.h` and :file:`format.cc` from
a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_
or the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
To use the {fmt} library, add :file:`fmt/core.h`, :file:`fmt/format.h`,
:file:`fmt/format-inl.h`, :file:`src/format.cc` and optionally other headers
from a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_ or
the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
Alternatively, you can :ref:`build the library with CMake <building>`.
If you are using Visual C++ with precompiled headers, you might need to add
the line ::
#include "stdafx.h"
before other includes in :file:`format.cc`.
.. _building:
Building the library
Building the Library
====================
The included `CMake build script`__ can be used to build the fmt
@ -31,7 +25,7 @@ workflow starts with::
mkdir build # Create a directory to hold the build output.
cd build
cmake <path/to/fmt> # Generate native build scripts.
cmake .. # Generate native build scripts.
where :file:`{<path/to/fmt>}` is a path to the ``fmt`` repository.
@ -45,7 +39,7 @@ You can control generation of the make ``test`` target with the ``FMT_TEST``
CMake option. This can be useful if you include fmt as a subdirectory in
your project but don't want to add fmt's tests to your ``test`` target.
If you use Windows and have Visual Studio installed, a :file:`FORMAT.sln`
If you use Windows and have Visual Studio installed, a :file:`FMT.sln`
file and several :file:`.vcproj` files will be created. You can then build them
using Visual Studio or msbuild.
@ -58,8 +52,14 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
Header-only usage with CMake
============================
Installing the Library
======================
After building the library you can install it on a Unix-like system by running
:command:`sudo make install`.
Usage with CMake
================
You can add the ``fmt`` library directory into your project and include it in
your ``CMakeLists.txt`` file::
@ -74,11 +74,16 @@ or
to exclude it from ``make``, ``make all``, or ``cmake --build .``.
Settting up your target to use a header-only version of ``fmt`` is equaly easy::
You can detect and use an installed version of {fmt} as follows::
target_link_libraries(<your-target> PRIVATE fmt-header-only)
find_package(fmt)
target_link_libraries(<your-target> fmt::fmt)
Building the documentation
Setting up your target to use a header-only version of ``fmt`` is equally easy::
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
Building the Documentation
==========================
To build the documentation you need the following software installed on your

828
include/fmt/chrono.h Normal file
View File

@ -0,0 +1,828 @@
// Formatting library for C++ - chrono support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
// 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
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher gt(time);
// Too big time values may be unsupported.
if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t* format, const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
}
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
}
basic_memory_buffer<Char> tm_format;
};
namespace internal {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
template <> FMT_CONSTEXPR const char* get_units<std::atto>() { return "as"; }
template <> FMT_CONSTEXPR const char* get_units<std::femto>() { return "fs"; }
template <> FMT_CONSTEXPR const char* get_units<std::pico>() { return "ps"; }
template <> FMT_CONSTEXPR const char* get_units<std::nano>() { return "ns"; }
template <> FMT_CONSTEXPR const char* get_units<std::micro>() { return "µs"; }
template <> FMT_CONSTEXPR const char* get_units<std::milli>() { return "ms"; }
template <> FMT_CONSTEXPR const char* get_units<std::centi>() { return "cs"; }
template <> FMT_CONSTEXPR const char* get_units<std::deci>() { return "ds"; }
template <> FMT_CONSTEXPR const char* get_units<std::ratio<1>>() { return "s"; }
template <> FMT_CONSTEXPR const char* get_units<std::deca>() { return "das"; }
template <> FMT_CONSTEXPR const char* get_units<std::hecto>() { return "hs"; }
template <> FMT_CONSTEXPR const char* get_units<std::kilo>() { return "ks"; }
template <> FMT_CONSTEXPR const char* get_units<std::mega>() { return "Ms"; }
template <> FMT_CONSTEXPR const char* get_units<std::giga>() { return "Gs"; }
template <> FMT_CONSTEXPR const char* get_units<std::tera>() { return "Ts"; }
template <> FMT_CONSTEXPR const char* get_units<std::peta>() { return "Ps"; }
template <> FMT_CONSTEXPR const char* get_units<std::exa>() { return "Es"; }
template <> FMT_CONSTEXPR const char* get_units<std::ratio<60>>() {
return "m";
}
template <> FMT_CONSTEXPR const char* get_units<std::ratio<3600>>() {
return "h";
}
enum class numeric_system {
standard,
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
alternative
};
// Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
const Char* end,
Handler&& handler) {
auto ptr = begin;
while (ptr != end) {
auto c = *ptr;
if (c == '}') break;
if (c != '%') {
++ptr;
continue;
}
if (begin != ptr) handler.on_text(begin, ptr);
++ptr; // consume '%'
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case '%':
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
// Day of the week:
case 'a':
handler.on_abbr_weekday();
break;
case 'A':
handler.on_full_weekday();
break;
case 'w':
handler.on_dec0_weekday(numeric_system::standard);
break;
case 'u':
handler.on_dec1_weekday(numeric_system::standard);
break;
// Month:
case 'b':
handler.on_abbr_month();
break;
case 'B':
handler.on_full_month();
break;
// Hour, minute, second:
case 'H':
handler.on_24_hour(numeric_system::standard);
break;
case 'I':
handler.on_12_hour(numeric_system::standard);
break;
case 'M':
handler.on_minute(numeric_system::standard);
break;
case 'S':
handler.on_second(numeric_system::standard);
break;
// Other:
case 'c':
handler.on_datetime(numeric_system::standard);
break;
case 'x':
handler.on_loc_date(numeric_system::standard);
break;
case 'X':
handler.on_loc_time(numeric_system::standard);
break;
case 'D':
handler.on_us_date();
break;
case 'F':
handler.on_iso_date();
break;
case 'r':
handler.on_12_hour_time();
break;
case 'R':
handler.on_24_hour_time();
break;
case 'T':
handler.on_iso_time();
break;
case 'p':
handler.on_am_pm();
break;
case 'Q':
handler.on_duration_value();
break;
case 'q':
handler.on_duration_unit();
break;
case 'z':
handler.on_utc_offset();
break;
case 'Z':
handler.on_tz_name();
break;
// Alternative representation:
case 'E': {
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case 'c':
handler.on_datetime(numeric_system::alternative);
break;
case 'x':
handler.on_loc_date(numeric_system::alternative);
break;
case 'X':
handler.on_loc_time(numeric_system::alternative);
break;
default:
FMT_THROW(format_error("invalid format"));
}
break;
}
case 'O':
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case 'w':
handler.on_dec0_weekday(numeric_system::alternative);
break;
case 'u':
handler.on_dec1_weekday(numeric_system::alternative);
break;
case 'H':
handler.on_24_hour(numeric_system::alternative);
break;
case 'I':
handler.on_12_hour(numeric_system::alternative);
break;
case 'M':
handler.on_minute(numeric_system::alternative);
break;
case 'S':
handler.on_second(numeric_system::alternative);
break;
default:
FMT_THROW(format_error("invalid format"));
}
break;
default:
FMT_THROW(format_error("invalid format"));
}
begin = ptr;
}
if (begin != ptr) handler.on_text(begin, ptr);
return ptr;
}
struct chrono_format_checker {
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
template <typename Char> void on_text(const Char*, const Char*) {}
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
FMT_NORETURN void on_full_weekday() { report_no_date(); }
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_abbr_month() { report_no_date(); }
FMT_NORETURN void on_full_month() { report_no_date(); }
void on_24_hour(numeric_system) {}
void on_12_hour(numeric_system) {}
void on_minute(numeric_system) {}
void on_second(numeric_system) {}
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
FMT_NORETURN void on_us_date() { report_no_date(); }
FMT_NORETURN void on_iso_date() { report_no_date(); }
void on_12_hour_time() {}
void on_24_hour_time() {}
void on_iso_time() {}
void on_am_pm() {}
void on_duration_value() {}
void on_duration_unit() {}
FMT_NORETURN void on_utc_offset() { report_no_date(); }
FMT_NORETURN void on_tz_name() { report_no_date(); }
};
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isnan(T) {
return false;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline bool isnan(T value) {
return std::isnan(value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isfinite(T) {
return true;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline bool isfinite(T value) {
return std::isfinite(value);
}
// 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");
(void)upper;
return static_cast<int>(value);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(
std::isnan(value) || (value >= 0 && value <= static_cast<T>(upper)),
"invalid value");
(void)upper;
return static_cast<int>(value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
return std::fmod(x, static_cast<T>(y));
}
// If T is an integral type, maps T to its unsigned counterpart, otherwise
// leaves it unchanged (unlike std::make_unsigned).
template <typename T, bool INTEGRAL = std::is_integral<T>::value>
struct make_unsigned_or_unchanged {
using type = T;
};
template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type;
};
#if FMT_SAFE_DURATION_CAST
// throwing version of safe_duration_cast
template <typename To, typename FromRep, typename FromPeriod>
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
int ec;
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
if (ec) FMT_THROW(format_error("cannot format duration"));
return to;
}
#endif
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
using CommonSecondsType =
typename std::common_type<decltype(d), std::chrono::seconds>::type;
const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
const auto d_as_whole_seconds =
fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
// this conversion should be nonproblematic
const auto diff = d_as_common - d_as_whole_seconds;
const auto ms =
fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
return ms;
#else
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
#endif
}
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
using common_type = typename std::common_type<Rep, std::intmax_t>::type;
auto ms = mod(d.count() * static_cast<common_type>(Period::num) /
static_cast<common_type>(Period::den) * 1000,
1000);
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
}
template <typename Rep, typename OutputIt>
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
val);
}
template <typename Period, typename OutputIt>
static OutputIt format_chrono_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
return format_to(out, "[{}/{}]s", Period::num, Period::den);
}
template <typename FormatContext, typename OutputIt, typename Rep,
typename Period>
struct chrono_formatter {
FormatContext& context;
OutputIt out;
int precision;
// rep is unsigned to avoid overflow.
using rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
rep val;
using seconds = std::chrono::duration<rep>;
seconds s;
using milliseconds = std::chrono::duration<rep, std::milli>;
bool negative;
using char_type = typename FormatContext::char_type;
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
}
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
// might need checked conversion (rep!=Rep)
auto tmpval = std::chrono::duration<rep, Period>(val);
s = fmt_safe_duration_cast<seconds>(tmpval);
#else
s = std::chrono::duration_cast<seconds>(
std::chrono::duration<rep, Period>(val));
#endif
}
// returns true if nan or inf, writes to out.
bool handle_nan_inf() {
if (isfinite(val)) {
return false;
}
if (isnan(val)) {
write_nan();
return true;
}
// must be +-inf
if (val > 0) {
write_pinf();
} else {
write_ninf();
}
return true;
}
Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
Rep hour12() const {
Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
return hour <= 0 ? 12 : hour;
}
Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
std::tm time() const {
auto time = std::tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
time.tm_min = to_nonnegative_int(minute(), 60);
time.tm_sec = to_nonnegative_int(second(), 60);
return time;
}
void write_sign() {
if (negative) {
*out++ = '-';
negative = false;
}
}
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
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);
}
void write_nan() { std::copy_n("nan", 3, out); }
void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); }
void format_localized(const tm& time, const char* format) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
void on_text(const char_type* begin, const char_type* end) {
std::copy(begin, end, out);
}
// These are not implemented because durations don't have date information.
void on_abbr_weekday() {}
void on_full_weekday() {}
void on_dec0_weekday(numeric_system) {}
void on_dec1_weekday(numeric_system) {}
void on_abbr_month() {}
void on_full_month() {}
void on_datetime(numeric_system) {}
void on_loc_date(numeric_system) {}
void on_loc_time(numeric_system) {}
void on_us_date() {}
void on_iso_date() {}
void on_utc_offset() {}
void on_tz_name() {}
void on_24_hour(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH");
}
void on_12_hour(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI");
}
void on_minute(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM");
}
void on_second(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) {
write(second(), 2);
#if FMT_SAFE_DURATION_CAST
// convert rep->Rep
using duration_rep = std::chrono::duration<rep, Period>;
using duration_Rep = std::chrono::duration<Rep, Period>;
auto tmpval = fmt_safe_duration_cast<duration_Rep>(duration_rep{val});
#else
auto tmpval = std::chrono::duration<Rep, Period>(val);
#endif
auto ms = get_milliseconds(tmpval);
if (ms != std::chrono::milliseconds(0)) {
*out++ = '.';
write(ms.count(), 3);
}
return;
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS");
}
void on_12_hour_time() {
if (handle_nan_inf()) return;
format_localized(time(), "%r");
}
void on_24_hour_time() {
if (handle_nan_inf()) {
*out++ = ':';
handle_nan_inf();
return;
}
write(hour(), 2);
*out++ = ':';
write(minute(), 2);
}
void on_iso_time() {
on_24_hour_time();
*out++ = ':';
if (handle_nan_inf()) return;
write(second(), 2);
}
void on_am_pm() {
if (handle_nan_inf()) return;
format_localized(time(), "%p");
}
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
}
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
};
} // namespace internal
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
using arg_ref_type = internal::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>;
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
context.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
return arg_ref_type(context.next_arg_id());
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
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 end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(arg_id);
}
template <typename Id> void on_dynamic_precision(Id arg_id) {
f.precision_ref = make_arg_ref(arg_id);
}
};
using iterator = typename basic_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_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};
begin = internal::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
if (*begin == '.') {
if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for this argument type");
}
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
return {begin, end};
}
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
return range.end;
}
template <typename FormatContext>
auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) {
auto begin = format_str.begin(), end = format_str.end();
// As a possible future optimization, we could avoid extra copying if width
// is not specified.
basic_memory_buffer<Char> buf;
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);
internal::handle_dynamic_spec<internal::precision_checker>(
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);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
}
};
FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_

586
include/fmt/color.h Normal file
View File

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

581
include/fmt/compile.h Normal file
View File

@ -0,0 +1,581 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
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;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(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_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_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_parse_context<char_type> parse_ctx(to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename 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, 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)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!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>;
cf.format(std::back_inserter(buffer), 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) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(
range(out), cf, {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
}
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

77
include/fmt/locale.h Normal file
View File

@ -0,0 +1,77 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

View File

@ -1,6 +1,6 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
@ -8,22 +8,21 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include "format.h"
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <class Char>
class formatbuf : public std::basic_streambuf<Char> {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
basic_buffer<Char> &buffer_;
buffer<Char>& buffer_;
public:
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
@ -39,104 +38,87 @@ class formatbuf : public std::basic_streambuf<Char> {
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
template <typename Char>
struct test_stream : std::basic_ostream<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 std::ostream).
template <typename T, typename Char>
class is_streamable {
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype(
internal::declval<test_stream<Char>&>()
<< internal::declval<U>(), std::true_type()) test(int);
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(...);
template <typename> static std::false_type test(...);
typedef decltype(test<T>(0)) result;
using result = decltype(test<T>(0));
public:
// std::string operator<< is not considered user-defined because we handle strings
// specially.
static const bool value = result::value && !std::is_same<T, std::string>::value;
};
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template <typename T, typename Char>
class convert_to_int<T, Char, true> {
public:
static const bool value =
convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
static const bool value = result::value;
};
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
const Char *data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
UnsignedStreamSize size = buf.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
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(max_value<std::streamsize>());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) {
internal::formatbuf<Char> format_buf(buffer);
void format_value(buffer<Char>& buf, const T& value) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buffer.resize(buffer.size());
buf.resize(buf.size());
}
// Disable builtin formatting of enums and use operator<< instead.
template <typename T>
struct format_enum<T,
typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type {};
} // namespace internal
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct formatter<T, Char,
typename std::enable_if<internal::is_streamable<T, Char>::value>::type>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value);
format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size());
formatter<basic_string_view<Char>, Char>::format(str, ctx);
return ctx.out();
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
};
} // namespace internal
template <typename Char>
inline void vprint(std::basic_ostream<Char> &os,
basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
vformat_to(buffer, format_str, args);
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
@ -146,16 +128,11 @@ inline void vprint(std::basic_ostream<Char> &os,
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
template <typename... Args>
inline void print(std::ostream &os, string_view format_str,
const Args & ... args) {
vprint<char>(os, format_str, make_format_args<format_context>(args...));
}
template <typename... Args>
inline void print(std::wostream &os, wstring_view format_str,
const Args & ... args) {
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE

View File

@ -10,7 +10,7 @@
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
# undef __STRICT_ANSI__
#endif
#include <errno.h>
@ -22,42 +22,42 @@
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@ -69,7 +69,7 @@ FMT_BEGIN_NAMESPACE
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following typedefs for common character types:
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
@ -89,28 +89,27 @@ FMT_BEGIN_NAMESPACE
format(std::string("{}"), 42);
\endrst
*/
template <typename Char>
class basic_cstring_view {
template <typename Char> class basic_cstring_view {
private:
const Char *data_;
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s) : data_(s) {}
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char *c_str() const { return data_; }
const Char* c_str() const { return data_; }
};
typedef basic_cstring_view<char> cstring_view;
typedef basic_cstring_view<wchar_t> wcstring_view;
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
@ -126,78 +125,34 @@ class error_code {
// A buffered file.
class buffered_file {
private:
FILE *file_;
FILE* file_;
friend class file;
explicit buffered_file(FILE *f) : file_(f) {}
explicit buffered_file(FILE* f) : file_(f) {}
public:
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// A "move constructor" for moving from an lvalue.
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary.
buffered_file &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
buffered_file &operator=(buffered_file &other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
// Returns a proxy object for moving from a temporary:
// buffered_file file = buffered_file(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL;
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file &&other) {
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
other.file_ = nullptr;
return *this;
}
#endif
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
@ -206,18 +161,18 @@ public:
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; }
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int (fileno)() const;
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args & ... args) {
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
@ -238,9 +193,9 @@ class file {
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
@ -249,68 +204,22 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
int fd;
};
file(const file&) = delete;
void operator=(const file&) = delete;
public:
// A "move constructor" for moving from a temporary.
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
// A "move constructor" for moving from an lvalue.
file(file &other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
file &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
file &operator=(file &other) {
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// file f = file(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(file);
public:
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
file& operator=(file &&other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_DTOR_NOEXCEPT;
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
@ -323,10 +232,10 @@ class file {
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count);
FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
@ -338,66 +247,59 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end);
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char *mode);
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) {
_free_locale(locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
# endif
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
typedef locale_t Type;
using type = locale_t;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
if (!locale_)
FMT_THROW(system_error(errno, "cannot create locale"));
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
Type get() const { return locale_; }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const {
char *end = FMT_NULL;
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
@ -406,12 +308,4 @@ class Locale {
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; }
inline fmt::file &move(fmt::file &f) { return f; }
}
#endif
#endif // FMT_POSIX_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Formatting library for C++ - the core API
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
@ -12,20 +12,19 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include "format.h"
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char>
struct formatting_base {
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
@ -33,7 +32,8 @@ struct formatting_base {
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
@ -55,84 +55,78 @@ struct formatting_tuple : formatting_base<Char> {
namespace internal {
template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) {
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
void copy(const char *str, OutputIterator out) {
const char *p_curr = str;
while (*p_curr) {
*out++ = *p_curr++;
}
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
void copy(char ch, OutputIterator out) {
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T>
class is_like_std_string {
template <typename T> class is_like_std_string {
template <typename U>
static auto check(U *p) ->
decltype(p->find('a'), p->length(), p->data(), int());
template <typename>
static void check(...);
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename... Ts>
struct conditional_helper {};
template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T, typename _ = void>
struct is_range_ : std::false_type {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T>
struct is_range_<T, typename std::conditional<
false,
conditional_helper<decltype(internal::declval<T>().begin()),
decltype(internal::declval<T>().end())>,
void>::type> : std::true_type {};
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif
/// tuple_size and tuple_element check.
template <typename T>
class is_tuple_like_ {
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U *p) ->
decltype(std::tuple_size<U>::value,
internal::declval<typename std::tuple_element<0, U>::type>(), int());
template <typename>
static void check(...);
static auto check(U* p)
-> decltype(std::tuple_size<U>::value,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N>
using index_sequence = std::index_sequence<N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N>
struct integer_sequence {
typedef T value_type;
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() {
return sizeof...(N);
}
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
@ -148,7 +142,7 @@ using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -156,26 +150,25 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
get_indexes(T const &) { return {}; }
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F>
void for_each(Tuple &&tup, F &&f) {
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template<typename Arg>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
typename std::enable_if<
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " {}" : "{}";
}
template<typename Arg>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
typename std::enable_if<
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " \"{}\"" : "\"{}\"";
}
@ -183,61 +176,58 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
template <typename T>
struct is_tuple_like {
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char,
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
private:
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext>
struct format_each {
template <typename T>
void operator()(const T& v) {
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
out = internal::copy(formatting.delimiter, out);
}
format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
public:
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
@ -252,54 +242,124 @@ public:
}
};
template <typename T>
struct is_range {
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;
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(
const RangeT &values, FormatContext &ctx) {
auto out = ctx.out();
internal::copy(formatting.prefix, out);
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
}
format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>");
out = format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
#endif // FMT_RANGES_H_

View File

@ -0,0 +1,293 @@
/*
* 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 (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
FMT_END_NAMESPACE

View File

@ -1,152 +0,0 @@
// Formatting library for C++ - time formatting
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
FMT_BEGIN_NAMESPACE
namespace internal{
inline null<> localtime_r(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
}
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::null<>) {
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
dispatcher lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(format_error("time_t value out of range"));
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
dispatcher gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(format_error("time_t value out of range"));
}
namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format,
const std::tm *time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t *str, std::size_t count,
const wchar_t *format, const std::tm *time) {
return std::wcsftime(str, count, format, time);
}
}
template <typename Char>
struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto it = internal::null_terminating_iterator<Char>(ctx);
if (*it == ':')
++it;
auto end = it;
while (*end && *end != '}')
++end;
tm_format.reserve(end - it + 1);
using internal::pointer_from;
tm_format.append(pointer_from(it), pointer_from(end));
tm_format.push_back('\0');
return pointer_from(end);
}
template <typename FormatContext>
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
std::size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return ctx.out();
}
basic_memory_buffer<Char> tm_format;
};
FMT_END_NAMESPACE
#endif // FMT_TIME_H_

View File

@ -8,45 +8,50 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Force linking of inline functions into the library.
std::string (*vformat_ref)(string_view, format_args) = vformat;
std::wstring (*vformat_wref)(wstring_view, wformat_args) = vformat;
}
template struct FMT_API internal::basic_data<void>;
template struct 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;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API char internal::thousands_sep(locale_provider *lp);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template void basic_fixed_buffer<char>::grow(std::size_t);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API int internal::char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format, int precision,
double value);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API int internal::char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format, int precision,
long double value);
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);
// Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep(locale_provider *lp);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template void basic_fixed_buffer<wchar_t>::grow(std::size_t);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context> &args);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context>&);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
int precision, double value);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
int precision, long double value);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

View File

@ -7,49 +7,49 @@
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/posix.h"
#include <limits.h>
#include <sys/types.h>
#include <climits>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
# include <unistd.h>
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <io.h>
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
# undef fileno
#endif
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,11 +58,11 @@ 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
}
} // namespace
FMT_BEGIN_NAMESPACE
@ -72,19 +72,17 @@ buffered_file::~buffered_file() FMT_NOEXCEPT {
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_,
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_)
return;
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = FMT_NULL;
if (result != 0)
FMT_THROW(system_error(errno, "cannot close file"));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
@ -92,8 +90,7 @@ void buffered_file::close() {
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(system_error(errno, "cannot get file descriptor"));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
@ -117,14 +114,12 @@ file::~file() FMT_NOEXCEPT {
}
void file::close() {
if (fd_ == -1)
return;
if (fd_ == -1) return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
FMT_THROW(system_error(errno, "cannot close file"));
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
@ -143,29 +138,27 @@ 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"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
"return type of file::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t file::read(void *buffer, std::size_t count) {
std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(system_error(errno, "cannot read from file"));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t file::write(const void *buffer, std::size_t count) {
std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(system_error(errno, "cannot write to file"));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
@ -182,19 +175,18 @@ void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = error_code(errno);
if (result == -1) ec = error_code(errno);
}
void file::pipe(file &read_end, file &write_end) {
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
@ -209,20 +201,19 @@ void file::pipe(file &read_end, file &write_end) {
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
FMT_THROW(system_error(errno, "cannot create pipe"));
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char *mode) {
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(system_error(errno,
"cannot associate stream with file descriptor"));
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
@ -235,10 +226,8 @@ long getpagesize() {
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
FMT_THROW(system_error(errno, "cannot get memory page size"));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
#endif
}
FMT_END_NAMESPACE

View File

@ -0,0 +1,4 @@
#ifndef BOOST_ASSERT
#include <assert.h>
# define BOOST_ASSERT(condition) assert(condition)
#endif

View File

@ -0,0 +1,13 @@
#ifndef TEXT_BOOST_CONTAINER_SMALL_VECTOR_HPP
#define TEXT_BOOST_CONTAINER_SMALL_VECTOR_HPP
#include <vector>
namespace boost {
namespace container {
template <typename T, size_t>
using small_vector = std::vector<T>;
}
}
#endif // TEXT_BOOST_CONTAINER_SMALL_VECTOR_HPP

102
src/text/boost/text/config.hpp Executable file
View File

@ -0,0 +1,102 @@
#ifndef BOOST_TEXT_CONFIG_HPP
#define BOOST_TEXT_CONFIG_HPP
/** There are ICU-based implementations of many operations, but those are only
defined when BOOST_TEXT_HAS_ICU is nonzero. If you define this, you must
make sure the the ICU headers are in your path, and that your build
properly links in ICU. */
#ifndef BOOST_TEXT_HAS_ICU
# define BOOST_TEXT_HAS_ICU 0
#endif
/** There are ICU-based implementations of many operations, but those are only
used when BOOST_TEXT_HAS_ICU and BOOST_TEXT_USE_ICU are both nonzero. */
#ifndef BOOST_TEXT_USE_ICU
# define BOOST_TEXT_USE_ICU 0
#endif
/** When you insert into a rope, the incoming sequence may be inserted as a
new segment, or if it falls within an existing string-segment, it may be
inserted into the string object used to represent that segment. This only
happens if the incoming sequence will fit within the existing segment's
capacity, or if the segment is smaller than a certain limit.
BOOST_TEXT_STRING_INSERT_MAX is that limit. */
#ifndef BOOST_TEXT_STRING_INSERT_MAX
# define BOOST_TEXT_STRING_INSERT_MAX 4096
#endif
#ifndef BOOST_TEXT_DOXYGEN
// Nothing before GCC 6 has proper C++14 constexpr support.
#if defined(__GNUC__) && __GNUC__ < 6 && !defined(__clang__)
# define BOOST_TEXT_CXX14_CONSTEXPR
# define BOOST_TEXT_NO_CXX14_CONSTEXPR
#elif defined(_MSC_VER) && _MSC_VER <= 1915
# define BOOST_TEXT_CXX14_CONSTEXPR
# define BOOST_TEXT_NO_CXX14_CONSTEXPR
#else
# define BOOST_TEXT_CXX14_CONSTEXPR
# if defined(BOOST_NO_CXX14_CONSTEXPR)
# define BOOST_TEXT_NO_CXX14_CONSTEXPR
# endif
#endif
// Implements separate compilation features as described in
// http://www.boost.org/more/separate_compilation.html
// normalize macros
#if !defined(BOOST_TEXT_DYN_LINK) && !defined(BOOST_TEXT_STATIC_LINK) && \
!defined(BOOST_ALL_DYN_LINK) && !defined(BOOST_ALL_STATIC_LINK)
# define BOOST_TEXT_STATIC_LINK
#endif
#if defined(BOOST_ALL_DYN_LINK) && !defined(BOOST_TEXT_DYN_LINK)
# define BOOST_TEXT_DYN_LINK
#elif defined(BOOST_ALL_STATIC_LINK) && !defined(BOOST_TEXT_STATIC_LINK)
# define BOOST_TEXT_STATIC_LINK
#endif
#if defined(BOOST_TEXT_DYN_LINK) && defined(BOOST_TEXT_STATIC_LINK)
# error Must not define both BOOST_TEXT_DYN_LINK and BOOST_TEXT_STATIC_LINK
#endif
// enable dynamic or static linking as requested
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TEXT_DYN_LINK)
# if defined(BOOST_TEXT_SOURCE)
# define BOOST_TEXT_DECL BOOST_SYMBOL_EXPORT
# else
# define BOOST_TEXT_DECL BOOST_SYMBOL_IMPORT
# endif
#else
# define BOOST_TEXT_DECL
#endif
#if 0 // TODO: Disabled for now.
// enable automatic library variant selection
#if !defined(BOOST_TEXT_SOURCE) && !defined(BOOST_ALL_NO_LIB) && \
!defined(BOOST_TEXT_NO_LIB)
//
// Set the name of our library, this will get undef'ed by auto_link.hpp
// once it's done with it:
//
#define BOOST_LIB_NAME boost_text
//
// If we're importing code from a dll, then tell auto_link.hpp about it:
//
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TEXT_DYN_LINK)
# define BOOST_DYN_LINK
#endif
//
// And include the header that does the work:
//
#include <boost/config/auto_link.hpp>
#endif // auto-linking disabled
#endif
#endif // doxygen
#endif

View File

@ -0,0 +1,51 @@
#ifndef BOOST_TEXT_DETAIL_BREAK_PROP_ITER_HPP
#define BOOST_TEXT_DETAIL_BREAK_PROP_ITER_HPP
#include <boost/text/detail/lzw.hpp>
#include <unordered_map>
namespace boost { namespace text { namespace detail {
template<typename Enum>
struct lzw_to_break_prop_iter
{
using value_type = std::pair<uint32_t, Enum>;
using difference_type = int;
using pointer = unsigned char *;
using reference = unsigned char &;
using iterator_category = std::output_iterator_tag;
using buffer_t = container::small_vector<unsigned char, 256>;
lzw_to_break_prop_iter(
std::unordered_map<uint32_t, Enum> & map, buffer_t & buf) :
map_(&map),
buf_(&buf)
{}
lzw_to_break_prop_iter & operator=(unsigned char c)
{
buf_->push_back(c);
auto const element_bytes = 4;
auto it = buf_->begin();
for (auto end = buf_->end() - buf_->size() % element_bytes;
it != end;
it += element_bytes) {
(*map_)[bytes_to_cp(&*it)] = Enum(*(it + 3));
}
buf_->erase(buf_->begin(), it);
return *this;
}
lzw_to_break_prop_iter & operator*() { return *this; }
lzw_to_break_prop_iter & operator++() { return *this; }
lzw_to_break_prop_iter & operator++(int) { return *this; }
private:
std::unordered_map<uint32_t, Enum> * map_;
buffer_t * buf_;
};
}}}
#endif

View File

@ -0,0 +1,104 @@
#ifndef BOOST_TEXT_DETAIL_LZW_HPP
#define BOOST_TEXT_DETAIL_LZW_HPP
#include <boost/assert.hpp>
#include <boost/container/small_vector.hpp>
#include <vector>
namespace boost { namespace text { namespace detail {
inline uint32_t bytes_to_uint32_t(unsigned char const * chars)
{
return chars[0] << 24 | chars[1] << 16 | chars[2] << 8 | chars[3] << 0;
}
inline uint32_t bytes_to_cp(unsigned char const * chars)
{
return chars[0] << 16 | chars[1] << 8 | chars[2] << 0;
}
inline uint32_t bytes_to_uint16_t(unsigned char const * chars)
{
return chars[0] << 8 | chars[1] << 0;
}
enum : uint16_t { no_predecessor = 0xffff, no_value = 0xffff };
struct lzw_reverse_table_element
{
lzw_reverse_table_element(
uint16_t pred = no_predecessor, uint16_t value = no_value) :
pred_(pred),
value_(value)
{}
uint16_t pred_;
uint16_t value_;
};
using lzw_reverse_table = std::vector<lzw_reverse_table_element>;
template<typename OutIter>
OutIter
copy_table_entry(lzw_reverse_table const & table, uint16_t i, OutIter out)
{
*out++ = table[i].value_;
while (table[i].pred_ != no_predecessor) {
i = table[i].pred_;
*out++ = table[i].value_;
}
return out;
}
// Hardcoded to 16 bits. Takes unsigned 16-bit LZW-compressed values as
// input and writes the decompressed unsigned char values to out.
template<typename Iter, typename OutIter>
OutIter lzw_decompress(Iter first, Iter last, OutIter out)
{
lzw_reverse_table reverse_table(1 << 16);
for (uint16_t i = 0; i < 256u; ++i) {
reverse_table[i].value_ = i;
}
container::small_vector<unsigned char, 256> table_entry;
uint32_t next_table_value = 256;
uint32_t const end_table_value = 1 << 16;
uint16_t prev_code = *first++;
BOOST_ASSERT(prev_code < 256);
unsigned char c = (unsigned char)prev_code;
table_entry.push_back(c);
*out++ = table_entry;
while (first != last) {
uint16_t const code = *first++;
table_entry.clear();
if (reverse_table[code].value_ == no_value) {
table_entry.push_back(c);
copy_table_entry(
reverse_table, prev_code, std::back_inserter(table_entry));
} else {
copy_table_entry(
reverse_table, code, std::back_inserter(table_entry));
}
*out++ = table_entry;
c = table_entry.back();
if (next_table_value < end_table_value) {
reverse_table[next_table_value++] =
lzw_reverse_table_element{prev_code, c};
}
prev_code = code;
}
return out;
}
}}}
#endif

View File

@ -0,0 +1,224 @@
#ifndef BOOST_TEXT_GRAPHEME_BREAK_HPP
#define BOOST_TEXT_GRAPHEME_BREAK_HPP
#include <array>
#include <unordered_map>
#include <stdint.h>
#define BOOST_TEXT_DECL
namespace boost { namespace text {
/** Analogue of `std::find()` that finds the last value `v` in `[first,
last)` for which `p(v)` is true. */
template<typename BidiIter, typename Pred>
BidiIter find_if_backward(BidiIter first, BidiIter last, Pred p)
{
auto it = last;
while (it != first) {
if (p(*--it))
return it;
}
return last;
}
/** The grapheme properties defined by Unicode. */
enum class grapheme_property {
Other,
CR,
LF,
Control,
Extend,
Regional_Indicator,
Prepend,
SpacingMark,
L,
V,
T,
LV,
LVT,
ExtPict,
ZWJ
};
namespace detail {
struct grapheme_prop_interval
{
uint32_t lo_;
uint32_t hi_;
grapheme_property prop_;
};
inline bool operator<(
grapheme_prop_interval lhs, grapheme_prop_interval rhs) noexcept
{
return lhs.hi_ <= rhs.lo_;
}
BOOST_TEXT_DECL std::array<grapheme_prop_interval, 6> const &
make_grapheme_prop_intervals();
BOOST_TEXT_DECL std::unordered_map<uint32_t, grapheme_property>
make_grapheme_prop_map();
}
/** Returns the grapheme property associated with code point `cp`. */
inline grapheme_property grapheme_prop(uint32_t cp) noexcept
{
static auto const map = detail::make_grapheme_prop_map();
static auto const intervals = detail::make_grapheme_prop_intervals();
auto const it = map.find(cp);
if (it == map.end()) {
auto const it2 = std::lower_bound(
intervals.begin(),
intervals.end(),
detail::grapheme_prop_interval{cp, cp + 1});
if (it2 == intervals.end() || cp < it2->lo_ || it2->hi_ <= cp)
return grapheme_property::Other;
return it2->prop_;
}
return it->second;
}
namespace detail {
inline bool skippable(grapheme_property prop) noexcept
{
return prop == grapheme_property::Extend;
}
enum class grapheme_break_emoji_state_t {
none,
first_emoji, // Indicates that prop points to an odd-count
// emoji.
second_emoji // Indicates that prop points to an even-count
// emoji.
};
template<typename CPIter>
struct grapheme_break_state
{
CPIter it;
grapheme_property prev_prop;
grapheme_property prop;
grapheme_break_emoji_state_t emoji_state;
};
template<typename CPIter>
grapheme_break_state<CPIter> next(grapheme_break_state<CPIter> state)
{
++state.it;
state.prev_prop = state.prop;
return state;
}
template<typename CPIter>
grapheme_break_state<CPIter> prev(grapheme_break_state<CPIter> state)
{
--state.it;
state.prop = state.prev_prop;
return state;
}
template<typename CPIter>
bool gb11_prefix(CPIter first, CPIter prev_it)
{
auto final_prop = grapheme_property::Other;
find_if_backward(first, prev_it, [&final_prop](uint32_t cp) {
final_prop = grapheme_prop(cp);
return final_prop != grapheme_property::Extend;
});
return final_prop == grapheme_property::ExtPict;
}
inline bool table_grapheme_break(
grapheme_property lhs, grapheme_property rhs) noexcept
{
// Note that RI.RI was changed to '1' since that case is handled
// in the grapheme break FSM.
// clang-format off
// See chart at https://unicode.org/Public/11.0.0/ucd/auxiliary/GraphemeBreakTest.html .
constexpr std::array<std::array<bool, 15>, 15> grapheme_breaks = {{
// Other CR LF Ctrl Ext RI Pre SpcMk L V T LV LVT ExtPict ZWJ
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}}, // Other
{{1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, // CR
{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, // LF
{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, // Control
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}}, // Extend
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}}, // RI
{{0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, // Prepend
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}}, // SpacingMark
{{1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0}}, // L
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0}}, // V
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0}}, // T
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0}}, // LV
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0}}, // LVT
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}}, // ExtPict
{{1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}}, // ZWJ
}};
// clang-format on
auto const lhs_int = static_cast<int>(lhs);
auto const rhs_int = static_cast<int>(rhs);
return grapheme_breaks[lhs_int][rhs_int];
}
}
template<typename CPIter, typename Sentinel>
CPIter next_grapheme_break(CPIter first, Sentinel last) noexcept
{
if (first == last)
return first;
detail::grapheme_break_state<CPIter> state;
state.it = first;
if (++state.it == last)
return state.it;
state.prev_prop = grapheme_prop(*std::prev(state.it));
state.prop = grapheme_prop(*state.it);
state.emoji_state =
state.prev_prop == grapheme_property::Regional_Indicator
? detail::grapheme_break_emoji_state_t::first_emoji
: detail::grapheme_break_emoji_state_t::none;
for (; state.it != last; state = next(state)) {
state.prop = grapheme_prop(*state.it);
// GB11
if (state.prev_prop == grapheme_property::ZWJ &&
state.prop == grapheme_property::ExtPict &&
detail::gb11_prefix(first, std::prev(state.it))) {
continue;
}
if (state.emoji_state ==
detail::grapheme_break_emoji_state_t::first_emoji) {
if (state.prop == grapheme_property::Regional_Indicator) {
state.emoji_state =
detail::grapheme_break_emoji_state_t::none;
continue;
} else {
state.emoji_state =
detail::grapheme_break_emoji_state_t::none;
}
} else if (state.prop == grapheme_property::Regional_Indicator) {
state.emoji_state =
detail::grapheme_break_emoji_state_t::first_emoji;
}
if (detail::table_grapheme_break(state.prev_prop, state.prop))
return state.it;
}
return state.it;
}
}}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
#ifndef TEXT_BOOST_THROW_EXCEPTION_HPP
#define TEXT_BOOST_THROW_EXCEPTION_HPP
namespace boost {
template <typename E>
void throw_exception(const E& e) { throw e; }
}
#endif // TEXT_BOOST_THROW_EXCEPTION_HPP

3589
src/text/grapheme_break.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
<manifest package="fmt" />
<manifest package="net.fmtlib" />

2061
support/C++.sublime-syntax Normal file

File diff suppressed because it is too large Load Diff

19
support/Vagrantfile vendored Normal file
View File

@ -0,0 +1,19 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
end
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y g++ make wget git
wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz
tar xzf cmake-3.14.4-Linux-x86_64.tar.gz
ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin
SHELL
end

View File

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

View File

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

View File

@ -1,15 +1,27 @@
// General gradle arguments for root project
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
//
// https://developer.android.com/studio/releases/gradle-plugin
//
// Notice that 3.3.0 here is the version of [Android Gradle Plugin]
// Accroding to URL above you will need Gradle 5.0 or higher
//
// If you are using Android Studio, and it is using Gradle's lower
// version, Use the plugin version 3.1.3 ~ 3.2.0 for Gradle 4.4 ~ 4.10
classpath 'com.android.tools.build:gradle:3.3.0'
}
}
repositories {
google()
jcenter()
}
// Output: Shared library (.so) for Android
apply plugin: 'com.android.library'
@ -24,20 +36,17 @@ android {
splits {
abi {
enable true
// Be general, as much as possible ...
// universalApk true
// Specify platforms for Application
reset()
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
include "arm64-v8a", "armeabi-v7a", "x86_64"
}
}
defaultConfig {
minSdkVersion 21 // Android 5.0+
targetSdkVersion 25 // Follow Compile SDK
versionCode 16 // Follow release count
versionName "4.1.0" // Follow Official version
versionCode 21 // Follow release count
versionName "5.3.0" // Follow Official version
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
@ -46,7 +55,7 @@ android {
arguments "-DBUILD_SHARED_LIBS=true" // Build shared object
arguments "-DFMT_TEST=false" // Skip test
arguments "-DFMT_DOC=false" // Skip document
cppFlags "-std=c++14"
cppFlags "-std=c++17"
}
}
println("Gradle CMake Plugin: ")

View File

@ -50,6 +50,7 @@ set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
# Can be removed once gcc 4.4 support is dropped.
check_cxx_source_compiles("
template <class T, class ...Types>
struct S { typedef typename S<Types...>::type type; };
@ -58,33 +59,6 @@ if (NOT SUPPORTS_VARIADIC_TEMPLATES)
set (SUPPORTS_VARIADIC_TEMPLATES OFF)
endif ()
# Check if initializer lists are supported.
check_cxx_source_compiles("
#include <initializer_list>
int main() {}" SUPPORTS_INITIALIZER_LIST)
if (NOT SUPPORTS_INITIALIZER_LIST)
set (SUPPORTS_INITIALIZER_LIST OFF)
endif ()
# Check if enum bases are available
check_cxx_source_compiles("
enum C : char {A};
int main() {}"
SUPPORTS_ENUM_BASE)
if (NOT SUPPORTS_ENUM_BASE)
set (SUPPORTS_ENUM_BASE OFF)
endif ()
# Check if type traits are available
check_cxx_source_compiles("
#include <type_traits>
class C { void operator=(const C&); };
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
SUPPORTS_TYPE_TRAITS)
if (NOT SUPPORTS_TYPE_TRAITS)
set (SUPPORTS_TYPE_TRAITS OFF)
endif ()
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);
@ -94,4 +68,14 @@ if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif ()
# Check if <variant> is available
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
check_cxx_source_compiles("
#include <variant>
int main() {}"
FMT_HAS_VARIANT)
if (NOT FMT_HAS_VARIANT)
set (FMT_HAS_VARIANT OFF)
endif ()
set(CMAKE_REQUIRED_FLAGS )

11
support/cmake/fmt.pc.in Normal file
View File

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

View File

@ -1,11 +0,0 @@
@echo on
rem This scripts configures build environment and runs CMake.
rem Use it instead of running CMake directly when building with
rem the Microsoft SDK toolchain rather than Visual Studio.
rem It is used in the same way as cmake, for example:
rem
rem run-cmake -G "Visual Studio 10 Win64" .
for /F "delims=" %%i IN ('cmake "-DPRINT_PATH=1" -P %~dp0/FindSetEnv.cmake') DO set setenv=%%i
if NOT "%setenv%" == "" call "%setenv%"
cmake %*

View File

@ -39,7 +39,7 @@ for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)):
print('Significands:', end='')
for i, fp in enumerate(powers):
if i % 4 == 0:
if i % 3 == 0:
print(end='\n ')
print(' {:0<#16x}'.format(fp.f, ), end=',')

View File

@ -5,6 +5,9 @@
Usage:
manage.py release [<branch>]
manage.py site
For the release command $FMT_TOKEN should contain a GitHub personal access token
obtained from https://github.com/settings/tokens.
"""
from __future__ import print_function
@ -142,6 +145,7 @@ def update_site(env):
b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned')
b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(', size_t', ', std::size_t')
# Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b:
@ -247,7 +251,8 @@ def release(args):
package = 'fmt-{}.zip'.format(version)
r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package),
params=params, files={package: open('build/fmt/' + package, 'rb')})
headers={'Content-Type': 'application/zip'},
params=params, data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))

34
support/rst2md.py Normal file → Executable file
View File

@ -1,6 +1,7 @@
#!/usr/bin/env python
# reStructuredText (RST) to GitHub-flavored Markdown converter
import re
import re, sys
from docutils import core, nodes, writers
@ -35,6 +36,9 @@ class Translator(nodes.NodeVisitor):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren
def visit_title_reference(self, node):
raise Exception(node)
def depart_title(self, node):
pass
@ -109,6 +113,30 @@ class Translator(nodes.NodeVisitor):
def depart_image(self, node):
pass
def write_row(self, row, widths):
for i, entry in enumerate(row):
text = entry[0][0] if len(entry) > 0 else ''
if i != 0:
self.write('|')
self.write('{:{}}'.format(text, widths[i]))
self.write('\n')
def visit_table(self, node):
table = node.children[0]
colspecs = table[:-2]
thead = table[-2]
tbody = table[-1]
widths = [int(cs['colwidth']) for cs in colspecs]
sep = '|'.join(['-' * w for w in widths]) + '\n'
self.write('\n\n')
self.write_row(thead[0], widths)
self.write(sep)
for row in tbody:
self.write_row(row, widths)
raise nodes.SkipChildren
def depart_table(self, node):
pass
class MDWriter(writers.Writer):
"""GitHub-flavored markdown writer"""
@ -125,3 +153,7 @@ class MDWriter(writers.Writer):
def convert(rst_path):
"""Converts RST file to Markdown."""
return core.publish_file(source_path=rst_path, writer=MDWriter())
if __name__ == '__main__':
convert(sys.argv[1])

View File

@ -1,2 +1,2 @@
If you are not redirected automatically, follow the
`link to the fmt documentation <http://fmtlib.net/latest/>`_.
`link to the fmt documentation <https://fmt.dev/latest/>`_.

View File

@ -2,15 +2,15 @@
{% block extrahead %}
<meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/">
<meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/">
<script type="text/javascript">
window.location.href = "http://fmtlib.net/latest/"
window.location.href = "https://fmt.dev/latest/"
</script>
<title>Page Redirection</title>
{% endblock %}
{% block document %}
If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>.
If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>.
{% endblock %}
{% block footer %}

View File

@ -83,19 +83,25 @@ install_dir = os.path.join(fmt_dir, "_install")
build_dir = os.path.join(fmt_dir, "_build")
test_build_dir = os.path.join(fmt_dir, "_build_test")
# Configure library.
# Configure the library.
makedirs_if_not_exist(build_dir)
cmake_flags = [
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
'-DCMAKE_CXX_STANDARD=' + standard
]
# Make sure the fuzzers still compile.
main_cmake_flags = list(cmake_flags)
if 'ENABLE_FUZZING' in os.environ:
main_cmake_flags += ['-DFMT_FUZZ=ON', '-DFMT_FUZZ_LINKMAIN=On']
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] +
cmake_flags, cwd=build_dir)
main_cmake_flags, cwd=build_dir)
# Build library.
check_call(['make', '-j4'], cwd=build_dir)
# Build the library.
check_call(['cmake', '--build','.'], cwd=build_dir)
# Test library.
# Test the library.
env = os.environ.copy()
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
if call(['make', 'test'], env=env, cwd=build_dir):
@ -103,7 +109,7 @@ if call(['make', 'test'], env=env, cwd=build_dir):
print(f.read())
sys.exit(-1)
# Install library.
# Install the library.
check_call(['make', 'install'], cwd=build_dir)
# Test installation.

View File

@ -17,13 +17,20 @@ else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
if (NOT SUPPORTS_VARIADIC_TEMPLATES OR NOT SUPPORTS_INITIALIZER_LIST)
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
# Workaround a bug in implementation of variadic templates in MSVC11.
if (MSVC)
# Workaround a bug in implementation of variadic templates in MSVC11.
target_compile_definitions(gmock PUBLIC _VARIADIC_MAX=10)
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
target_compile_definitions(gmock PRIVATE _CRT_SECURE_NO_WARNINGS)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Disable MSVC warnings of POSIX functions.
target_compile_options(gmock PUBLIC -Wno-deprecated-declarations)
endif ()
endif ()
# GTest doesn't detect <tuple> with clang.
@ -33,7 +40,7 @@ endif ()
# Silence MSVC tr1 deprecation warning in gmock.
target_compile_definitions(gmock
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=0)
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1)
#------------------------------------------------------------------------------
# Build the actual library tests
@ -74,28 +81,39 @@ function(add_fmt_test name)
target_link_libraries(${name} test-main)
# Define if certain C++ features can be used.
target_compile_definitions(${name} PRIVATE
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
FMT_USE_ENUM_BASE=$<BOOL:${SUPPORTS_ENUM_BASE}>)
if (FMT_PEDANTIC)
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()
add_fmt_test(assert-test)
add_fmt_test(chrono-test)
add_fmt_test(color-test)
add_fmt_test(core-test)
add_fmt_test(grisu-test)
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
add_fmt_test(gtest-extra-test)
add_fmt_test(format-test)
add_fmt_test(format-impl-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 ()
add_fmt_test(locale-test)
add_fmt_test(ostream-test)
add_fmt_test(compile-test)
add_fmt_test(printf-test)
add_fmt_test(time-test)
add_fmt_test(util-test mock-allocator.h)
add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
if (HAVE_OPEN)
if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
@ -106,6 +124,9 @@ if (HAVE_OPEN)
if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (HAVE_STRTOD_L)
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
endif ()
add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(posix-test)
endif ()
@ -122,22 +143,37 @@ else ()
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
endif ()
# Test that the library can be compiled with exceptions disabled.
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../src/format.cc)
target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions)
if (FMT_PEDANTIC)
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(noexception-test SYSTEM PUBLIC gtest gmock)
endif ()
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
if (FMT_PEDANTIC)
# MSVC fails to compile GMock when C++17 is enabled.
if (FMT_HAS_VARIANT AND NOT MSVC)
add_fmt_test(std-format-test)
set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
endif ()
# Test that the library can be compiled with exceptions disabled.
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
endif ()
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../src/format.cc)
target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions)
if (FMT_PEDANTIC)
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
endif ()
# Test that the library compiles without locale.
add_library(nolocale-test ../src/format.cc)
target_include_directories(
nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(
nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1)
# Test that the library compiles without windows.h.
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_library(no-windows-h-test ../src/format.cc)
@ -150,10 +186,10 @@ if (FMT_PEDANTIC)
target_include_directories(no-windows-h-test SYSTEM PUBLIC gtest gmock)
endif ()
add_test(compile-test ${CMAKE_CTEST_COMMAND}
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-test"
"${CMAKE_CURRENT_BINARY_DIR}/compile-test"
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
"${CMAKE_CURRENT_BINARY_DIR}/compile-error-test"
--build-generator ${CMAKE_GENERATOR}
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options
@ -162,8 +198,11 @@ if (FMT_PEDANTIC)
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
endif ()
# test if the targets are findable from the build directory
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# Test if the targets are found from the build directory.
add_test(find-package-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}
--build-and-test
@ -178,7 +217,7 @@ if (FMT_PEDANTIC)
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
# test if the targets are findable when add_subdirectory is used
# Test if the targets are found when add_subdirectory is used.
add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}
--build-and-test
@ -192,3 +231,21 @@ if (FMT_PEDANTIC)
"-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

@ -9,14 +9,14 @@
#include "gtest.h"
#if GTEST_HAS_DEATH_TEST
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEBUG_DEATH(statement, regex)
#else
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
#endif
TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(
FMT_ASSERT(false, "don't panic!"), "don't panic!");
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"),
"don't panic!");
}

344
test/chrono-test.cc Normal file
View File

@ -0,0 +1,344 @@
// Formatting library for C++ - time formatting tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifdef WIN32
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/chrono.h"
#include "gtest-extra.h"
#include <iomanip>
std::tm make_tm() {
auto time = std::tm();
time.tm_mday = 1;
return time;
}
std::tm make_hour(int h) {
auto time = make_tm();
time.tm_hour = h;
return time;
}
std::tm make_minute(int m) {
auto time = make_tm();
time.tm_min = m;
return time;
}
std::tm make_second(int s) {
auto time = make_tm();
time.tm_sec = s;
return time;
}
std::string format_tm(const std::tm& time, const char* spec,
const std::locale& loc) {
auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os;
os.imbue(loc);
facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec));
return os.str();
}
TEST(TimeTest, Format) {
std::tm tm = std::tm();
tm.tm_year = 116;
tm.tm_mon = 3;
tm.tm_mday = 25;
EXPECT_EQ("The date is 2016-04-25.",
fmt::format("The date is {:%Y-%m-%d}.", tm));
}
TEST(TimeTest, GrowBuffer) {
std::string s = "{:";
for (int i = 0; i < 30; ++i) s += "%c";
s += "}\n";
std::time_t t = std::time(nullptr);
fmt::format(s, *std::localtime(&t));
}
TEST(TimeTest, FormatToEmptyContainer) {
std::string s;
auto time = std::tm();
time.tm_sec = 42;
fmt::format_to(std::back_inserter(s), "{:%S}", time);
EXPECT_EQ(s, "42");
}
TEST(TimeTest, EmptyResult) { EXPECT_EQ("", fmt::format("{}", std::tm())); }
static bool EqualTime(const std::tm& lhs, const std::tm& rhs) {
return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min &&
lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday &&
lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year &&
lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday &&
lhs.tm_isdst == rhs.tm_isdst;
}
TEST(TimeTest, LocalTime) {
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
}
TEST(TimeTest, GMTime) {
std::time_t t = std::time(nullptr);
std::tm tm = *std::gmtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
}
#define EXPECT_TIME(spec, time, duration) \
{ \
std::locale loc("ja_JP.utf8"); \
EXPECT_EQ(format_tm(time, spec, loc), \
fmt::format(loc, "{:" spec "}", duration)); \
}
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
TEST(ChronoTest, FormatDefault) {
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42as",
fmt::format("{}", std::chrono::duration<int, std::atto>(42)));
EXPECT_EQ("42fs",
fmt::format("{}", std::chrono::duration<int, std::femto>(42)));
EXPECT_EQ("42ps",
fmt::format("{}", std::chrono::duration<int, std::pico>(42)));
EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42)));
EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42)));
EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42)));
EXPECT_EQ("42cs",
fmt::format("{}", std::chrono::duration<int, std::centi>(42)));
EXPECT_EQ("42ds",
fmt::format("{}", std::chrono::duration<int, std::deci>(42)));
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42das",
fmt::format("{}", std::chrono::duration<int, std::deca>(42)));
EXPECT_EQ("42hs",
fmt::format("{}", std::chrono::duration<int, std::hecto>(42)));
EXPECT_EQ("42ks",
fmt::format("{}", std::chrono::duration<int, std::kilo>(42)));
EXPECT_EQ("42Ms",
fmt::format("{}", std::chrono::duration<int, std::mega>(42)));
EXPECT_EQ("42Gs",
fmt::format("{}", std::chrono::duration<int, std::giga>(42)));
EXPECT_EQ("42Ts",
fmt::format("{}", std::chrono::duration<int, std::tera>(42)));
EXPECT_EQ("42Ps",
fmt::format("{}", std::chrono::duration<int, std::peta>(42)));
EXPECT_EQ("42Es",
fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
EXPECT_EQ(
"42[15]s",
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ(
"42[15/4]s",
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
}
TEST(ChronoTest, Align) {
auto s = std::chrono::seconds(42);
EXPECT_EQ("42s ", fmt::format("{:5}", s));
EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
EXPECT_EQ(" 42s", fmt::format("{:>5}", s));
EXPECT_EQ("**42s**", fmt::format("{:*^7}", s));
EXPECT_EQ("03:25:45 ",
fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ(" 03:25:45",
fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("~~03:25:45~~",
fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25:45 ",
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
}
TEST(ChronoTest, FormatSpecs) {
EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0)));
EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0)));
EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0)));
EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60)));
EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42)));
EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234)));
EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0)));
EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60)));
EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42)));
EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61)));
EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0)));
EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24)));
EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14)));
EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24)));
EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4)));
EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14)));
EXPECT_EQ("03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345)));
EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345)));
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345)));
}
TEST(ChronoTest, InvalidSpecs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG(fmt::format("{:%a}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%A}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%c}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%x}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Ex}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%X}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%EX}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%D}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%F}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Ec}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%w}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%u}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%b}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%B}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error,
"invalid format");
}
TEST(ChronoTest, Locale) {
const char* loc_name = "ja_JP.utf8";
bool has_locale = false;
std::locale loc;
try {
loc = std::locale(loc_name);
has_locale = true;
} catch (const std::runtime_error&) {
}
if (!has_locale) {
fmt::print("{} locale is missing.\n", loc_name);
return;
}
EXPECT_TIME("%OH", make_hour(14), std::chrono::hours(14));
EXPECT_TIME("%OI", make_hour(14), std::chrono::hours(14));
EXPECT_TIME("%OM", make_minute(42), std::chrono::minutes(42));
EXPECT_TIME("%OS", make_second(42), std::chrono::seconds(42));
auto time = make_tm();
time.tm_hour = 3;
time.tm_min = 25;
time.tm_sec = 45;
auto sec = std::chrono::seconds(12345);
EXPECT_TIME("%r", time, sec);
EXPECT_TIME("%p", time, sec);
}
typedef std::chrono::duration<double, std::milli> dms;
TEST(ChronoTest, FormatDefaultFP) {
typedef std::chrono::duration<float> fs;
EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234)));
typedef std::chrono::duration<float, std::milli> fms;
EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234)));
typedef std::chrono::duration<double> ds;
EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234)));
EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234)));
}
TEST(ChronoTest, FormatPrecision) {
EXPECT_THROW_MSG(fmt::format("{:.2}", std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
}
TEST(ChronoTest, FormatFullSpecs) {
EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8));
EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3));
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
}
TEST(ChronoTest, FormatSimpleQq) {
typedef std::chrono::duration<float> fs;
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234)));
typedef std::chrono::duration<float, std::milli> fms;
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234)));
typedef std::chrono::duration<double> ds;
EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234)));
EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234)));
}
TEST(ChronoTest, FormatPrecisionQq) {
EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
}
TEST(ChronoTest, FormatFullSpecsQq) {
EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2));
EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9));
EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3));
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
}
TEST(ChronoTest, InvalidWidthId) {
EXPECT_THROW(fmt::format("{:{o}", std::chrono::seconds(0)),
fmt::format_error);
}
TEST(ChronoTest, InvalidColons) {
EXPECT_THROW(fmt::format("{0}=:{0::", std::chrono::seconds(0)),
fmt::format_error);
}
TEST(ChronoTest, NegativeDurations) {
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
EXPECT_EQ("-00:01",
fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-00.127",
fmt::format("{:%S}",
std::chrono::duration<signed char, std::milli>{-127}));
auto min = std::numeric_limits<int>::min();
EXPECT_EQ(fmt::format("{}", min),
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
}
TEST(ChronoTest, SpecialDurations) {
EXPECT_EQ(
"40.",
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
"nan nan nan nan nan:nan nan",
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
fmt::format("{:%S}",
std::chrono::duration<float, std::atto>(1.79400457e+31f));
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)),
"1Es");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
"1as");
EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration<char, std::mega>{2}),
"03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20");
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR

83
test/color-test.cc Normal file
View File

@ -0,0 +1,83 @@
// Formatting library for C++ - color tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/color.h"
#include "gtest-extra.h"
TEST(ColorsTest, ColorsPrint) {
EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_WRITE(
stdout,
fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, "bold"),
"\x1b[1mbold\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::italic, "italic"),
"\x1b[3mitalic\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\x1b[0m");
EXPECT_WRITE(stdout,
fmt::print(fmt::emphasis::strikethrough, "strikethrough"),
"\x1b[9mstrikethrough\x1b[0m");
EXPECT_WRITE(
stdout,
fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
EXPECT_WRITE(stderr, fmt::print(stderr, fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m");
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
"\x1b[38;2;000;000;255mblue log\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi");
EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"),
"\x1b[31mtred\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), "tcyan"),
"\x1b[46mtcyan\x1b[0m");
EXPECT_WRITE(stdout,
fmt::print(fg(fmt::terminal_color::bright_green), "tbgreen"),
"\x1b[92mtbgreen\x1b[0m");
EXPECT_WRITE(stdout,
fmt::print(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
"\x1b[105mtbmagenta\x1b[0m");
}
TEST(ColorsTest, Format) {
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_EQ(
fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"),
"\x1b[3mitalic\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"),
"\x1b[9mstrikethrough\x1b[0m");
EXPECT_EQ(
fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"),
"\x1b[38;2;000;000;255mblue log\x1b[0m");
EXPECT_EQ(fmt::format(fmt::text_style(), "hi"), "hi");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "tred"),
"\x1b[31mtred\x1b[0m");
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::cyan), "tcyan"),
"\x1b[46mtcyan\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::bright_green), "tbgreen"),
"\x1b[92mtbgreen\x1b[0m");
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
"\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m");
}

143
test/compile-test.cc Normal file
View File

@ -0,0 +1,143 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <stdint.h>
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstring>
#include <deque>
#include <list>
#include <memory>
#include <string>
// Check if fmt/compile.h compiles with windows.h included before it.
#ifdef _WIN32
# include <windows.h>
#endif
#include "fmt/compile.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
#include "util.h"
#undef ERROR
#undef min
#undef max
using testing::Return;
using testing::StrictMock;
// compiletime_prepared_parts_type_provider is useful only with relaxed
// constexpr.
#if FMT_USE_CONSTEXPR
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
void check_prepared_parts_type(Format format) {
typedef fmt::internal::compiled_format_base<decltype(format)> provider;
typedef fmt::internal::format_part<char>
expected_parts_type[EXPECTED_PARTS_COUNT];
static_assert(std::is_same<typename provider::parts_container,
expected_parts_type>::value,
"CompileTimePreparedPartsTypeProvider test failed");
}
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
check_prepared_parts_type<1u>(FMT_STRING("text"));
check_prepared_parts_type<1u>(FMT_STRING("{}"));
check_prepared_parts_type<2u>(FMT_STRING("text{}"));
check_prepared_parts_type<2u>(FMT_STRING("{}text"));
check_prepared_parts_type<3u>(FMT_STRING("text{}text"));
check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}"));
check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}'
check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{'
check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' '
check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text
check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text'
check_prepared_parts_type<4u>(
FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text'
}
#endif
TEST(CompileTest, PassStringLiteralFormat) {
const auto prepared = fmt::compile<int>("test {}");
EXPECT_EQ("test 42", fmt::format(prepared, 42));
const auto wprepared = fmt::compile<int>(L"test {}");
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
}
#if FMT_USE_CONSTEXPR
TEST(CompileTest, PassCompileString) {
const auto prepared = fmt::compile<int>(FMT_STRING("test {}"));
EXPECT_EQ("test 42", fmt::format(prepared, 42));
const auto wprepared = fmt::compile<int>(FMT_STRING(L"test {}"));
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
}
#endif
TEST(CompileTest, FormatToArrayOfChars) {
char buffer[32] = {0};
const auto prepared = fmt::compile<int>("4{}");
fmt::format_to(fmt::internal::make_checked(buffer, 32), prepared, 2);
EXPECT_EQ(std::string("42"), buffer);
wchar_t wbuffer[32] = {0};
const auto wprepared = fmt::compile<int>(L"4{}");
fmt::format_to(fmt::internal::make_checked(wbuffer, 32), wprepared, 2);
EXPECT_EQ(std::wstring(L"42"), wbuffer);
}
TEST(CompileTest, FormatToIterator) {
std::string s(2, ' ');
const auto prepared = fmt::compile<int>("4{}");
fmt::format_to(s.begin(), prepared, 2);
EXPECT_EQ("42", s);
std::wstring ws(2, L' ');
const auto wprepared = fmt::compile<int>(L"4{}");
fmt::format_to(ws.begin(), wprepared, 2);
EXPECT_EQ(L"42", ws);
}
TEST(CompileTest, FormatToN) {
char buf[5];
auto f = fmt::compile<int>("{:10}");
auto result = fmt::format_to_n(buf, 5, f, 42);
EXPECT_EQ(result.size, 10);
EXPECT_EQ(result.out, buf + 5);
EXPECT_EQ(fmt::string_view(buf, 5), " ");
}
TEST(CompileTest, FormattedSize) {
auto f = fmt::compile<int>("{:10}");
EXPECT_EQ(fmt::formatted_size(f, 42), 10);
}
TEST(CompileTest, MultipleTypes) {
auto f = fmt::compile<int, int>("{} {}");
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
}
struct formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<formattable> : formatter<const char*> {
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
return formatter<const char*>::format("foo", ctx);
}
};
FMT_END_NAMESPACE
TEST(CompileTest, FormatUserDefinedType) {
auto f = fmt::compile<formattable>("{}");
EXPECT_EQ(fmt::format(f, formattable()), "foo");
}
TEST(CompileTest, EmptyFormatString) {
auto f = fmt::compile<>("");
EXPECT_EQ(fmt::format(f), "");
}

637
test/core-test.cc Normal file
View File

@ -0,0 +1,637 @@
// Formatting library for C++ - core tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <algorithm>
#include <climits>
#include <cstring>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <type_traits>
#include "test-assert.h"
#include "gmock.h"
// Check if fmt/core.h compiles with windows.h included before it.
#ifdef _WIN32
# include <windows.h>
#endif
#include "fmt/core.h"
#undef min
#undef max
using fmt::basic_format_arg;
using fmt::string_view;
using fmt::internal::buffer;
using fmt::internal::value;
using testing::_;
using testing::StrictMock;
namespace {
struct test_struct {};
template <typename Context, typename T>
basic_format_arg<Context> make_arg(const T& value) {
return fmt::internal::make_arg<Context>(value);
}
} // namespace
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatter<test_struct, Char> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
typedef std::back_insert_iterator<buffer<Char>> iterator;
auto format(test_struct, basic_format_context<iterator, char>& ctx)
-> decltype(ctx.out()) {
const Char* test = "test";
return std::copy_n(test, std::strlen(test), ctx.out());
}
};
FMT_END_NAMESPACE
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(BufferTest, Noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
# if !FMT_MSC_VER
// std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
# endif
}
TEST(BufferTest, Nonmoveable) {
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
# if !FMT_MSC_VER
// std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
# endif
}
#endif
// A test buffer with a dummy grow method.
template <typename T> struct test_buffer : buffer<T> {
void grow(std::size_t capacity) { this->set(nullptr, capacity); }
};
template <typename T> struct mock_buffer : buffer<T> {
MOCK_METHOD1(do_grow, void(std::size_t capacity));
void grow(std::size_t capacity) {
this->set(this->data(), capacity);
do_grow(capacity);
}
mock_buffer() {}
mock_buffer(T* data) { this->set(data, 0); }
mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); }
};
TEST(BufferTest, Ctor) {
{
mock_buffer<int> buffer;
EXPECT_EQ(nullptr, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
{
int dummy;
mock_buffer<int> buffer(&dummy);
EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
{
int dummy;
std::size_t capacity = std::numeric_limits<std::size_t>::max();
mock_buffer<int> buffer(&dummy, capacity);
EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(capacity, buffer.capacity());
}
}
struct dying_buffer : test_buffer<int> {
MOCK_METHOD0(die, void());
~dying_buffer() { die(); }
};
TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer;
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
EXPECT_CALL(*mock_buffer, die());
buffer<int>* buffer = mock_buffer;
delete buffer;
}
TEST(BufferTest, Access) {
char data[10];
mock_buffer<char> buffer(data, sizeof(data));
buffer[0] = 11;
EXPECT_EQ(11, buffer[0]);
buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3));
const fmt::internal::buffer<char>& const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]);
}
TEST(BufferTest, Resize) {
char data[123];
mock_buffer<char> buffer(data, sizeof(data));
buffer[10] = 42;
EXPECT_EQ(42, buffer[10]);
buffer.resize(20);
EXPECT_EQ(20u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
EXPECT_EQ(42, buffer[10]);
buffer.resize(5);
EXPECT_EQ(5u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
EXPECT_EQ(42, buffer[10]);
// Check if resize calls grow.
EXPECT_CALL(buffer, do_grow(124));
buffer.resize(124);
EXPECT_CALL(buffer, do_grow(200));
buffer.resize(200);
}
TEST(BufferTest, Clear) {
test_buffer<char> buffer;
buffer.resize(20);
buffer.resize(0);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(20u, buffer.capacity());
}
TEST(BufferTest, Append) {
char data[15];
mock_buffer<char> buffer(data, 10);
const char* test = "test";
buffer.append(test, test + 5);
EXPECT_STREQ(test, &buffer[0]);
EXPECT_EQ(5u, buffer.size());
buffer.resize(10);
EXPECT_CALL(buffer, do_grow(12));
buffer.append(test, test + 2);
EXPECT_EQ('t', buffer[10]);
EXPECT_EQ('e', buffer[11]);
EXPECT_EQ(12u, buffer.size());
}
TEST(BufferTest, AppendAllocatesEnoughStorage) {
char data[19];
mock_buffer<char> buffer(data, 10);
const char* test = "abcdefgh";
buffer.resize(10);
EXPECT_CALL(buffer, do_grow(19));
buffer.append(test, test + 9);
}
TEST(ArgTest, FormatArgs) {
fmt::format_args args;
EXPECT_FALSE(args.get(1));
}
struct custom_context {
typedef char char_type;
template <typename T> struct formatter_type {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
const char* format(const T&, custom_context& ctx) {
ctx.called = true;
return nullptr;
}
};
bool called;
fmt::format_parse_context ctx;
fmt::format_parse_context& parse_context() { return ctx; }
void advance_to(const char*) {}
};
TEST(ArgTest, MakeValueWithCustomContext) {
test_struct t;
fmt::internal::value<custom_context> arg(
fmt::internal::arg_mapper<custom_context>().map(t));
custom_context ctx = {false, fmt::format_parse_context("")};
arg.custom.format(&t, ctx.parse_context(), ctx);
EXPECT_TRUE(ctx.called);
}
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
return lhs.value == rhs.value;
}
} // namespace internal
FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable
// conversions.
struct test_result {};
template <typename T> struct mock_visitor {
template <typename U> struct result { typedef test_result type; };
mock_visitor() {
ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
}
MOCK_METHOD1_T(visit, test_result(T value));
MOCK_METHOD0_T(unexpected, void());
test_result operator()(T value) { return visit(value); }
template <typename U> test_result operator()(U) {
unexpected();
return test_result();
}
};
template <typename T> struct visit_type { typedef T Type; };
#define VISIT_TYPE(Type_, visit_type_) \
template <> struct visit_type<Type_> { typedef visit_type_ Type; }
VISIT_TYPE(signed char, int);
VISIT_TYPE(unsigned char, unsigned);
VISIT_TYPE(short, int);
VISIT_TYPE(unsigned short, unsigned);
#if LONG_MAX == INT_MAX
VISIT_TYPE(long, int);
VISIT_TYPE(unsigned long, unsigned);
#else
VISIT_TYPE(long, long long);
VISIT_TYPE(unsigned long, unsigned long long);
#endif
VISIT_TYPE(float, double);
#define CHECK_ARG_(Char, expected, value) \
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
EXPECT_CALL(visitor, visit(expected)); \
typedef std::back_insert_iterator<buffer<Char>> iterator; \
fmt::visit_format_arg( \
visitor, make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
}
#define CHECK_ARG(value, typename_) \
{ \
typedef decltype(value) value_type; \
typename_ visit_type<value_type>::Type expected = value; \
CHECK_ARG_(char, expected, value) \
CHECK_ARG_(wchar_t, expected, value) \
}
template <typename T> class NumericArgTest : public testing::Test {};
typedef ::testing::Types<bool, signed char, unsigned char, signed,
unsigned short, int, unsigned, long, unsigned long,
long long, unsigned long long, float, double,
long double>
Types;
TYPED_TEST_CASE(NumericArgTest, Types);
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
return static_cast<T>(42);
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
test_value() {
return static_cast<T>(4.2);
}
TYPED_TEST(NumericArgTest, MakeAndVisit) {
CHECK_ARG(test_value<TypeParam>(), typename);
CHECK_ARG(std::numeric_limits<TypeParam>::min(), typename);
CHECK_ARG(std::numeric_limits<TypeParam>::max(), typename);
}
TEST(ArgTest, CharArg) {
CHECK_ARG_(char, 'a', 'a');
CHECK_ARG_(wchar_t, L'a', 'a');
CHECK_ARG_(wchar_t, L'a', L'a');
}
TEST(ArgTest, StringArg) {
char str_data[] = "test";
char* str = str_data;
const char* cstr = str;
CHECK_ARG_(char, cstr, str);
string_view sref(str);
CHECK_ARG_(char, sref, std::string(str));
}
TEST(ArgTest, WStringArg) {
wchar_t str_data[] = L"test";
wchar_t* str = str_data;
const wchar_t* cstr = str;
fmt::wstring_view sref(str);
CHECK_ARG_(wchar_t, cstr, str);
CHECK_ARG_(wchar_t, cstr, cstr);
CHECK_ARG_(wchar_t, sref, std::wstring(str));
CHECK_ARG_(wchar_t, sref, fmt::wstring_view(str));
}
TEST(ArgTest, PointerArg) {
void* p = nullptr;
const void* cp = nullptr;
CHECK_ARG_(char, cp, p);
CHECK_ARG_(wchar_t, cp, p);
CHECK_ARG(cp, );
}
struct check_custom {
test_result operator()(
fmt::basic_format_arg<fmt::format_context>::handle h) const {
struct test_buffer : fmt::internal::buffer<char> {
char data[10];
test_buffer() : fmt::internal::buffer<char>(data, 0, 10) {}
void grow(std::size_t) {}
} buffer;
fmt::internal::buffer<char>& base = buffer;
fmt::format_parse_context parse_ctx("");
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
h.format(parse_ctx, ctx);
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
return test_result();
}
};
TEST(ArgTest, CustomArg) {
test_struct test;
typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
visitor;
testing::StrictMock<visitor> v;
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom()));
fmt::visit_format_arg(v, make_arg<fmt::format_context>(test));
}
TEST(ArgTest, VisitInvalidArg) {
testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
EXPECT_CALL(visitor, visit(_));
fmt::basic_format_arg<fmt::format_context> arg;
fmt::visit_format_arg(visitor, arg);
}
TEST(StringViewTest, Length) {
// Test that StringRef::size() returns string length, not buffer size.
char str[100] = "some string";
EXPECT_EQ(std::strlen(str), string_view(str).size());
EXPECT_LT(std::strlen(str), sizeof(str));
}
// Check string_view's comparison operator.
template <template <typename> class Op> void check_op() {
const char* inputs[] = {"foo", "fop", "fo"};
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (std::size_t i = 0; i < num_inputs; ++i) {
for (std::size_t j = 0; j < num_inputs; ++j) {
string_view lhs(inputs[i]), rhs(inputs[j]);
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
}
}
}
TEST(StringViewTest, Compare) {
EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
check_op<std::equal_to>();
check_op<std::not_equal_to>();
check_op<std::less>();
check_op<std::less_equal>();
check_op<std::greater>();
check_op<std::greater_equal>();
}
struct enabled_formatter {};
struct disabled_formatter {};
struct disabled_formatter_convertible {
operator int() const { return 42; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<enabled_formatter> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(CoreTest, HasFormatter) {
using fmt::internal::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));
}
struct convertible_to_int {
operator int() const { return 42; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_int> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) {
return std::copy_n("foo", 3, ctx.out());
}
};
FMT_END_NAMESPACE
TEST(CoreTest, FormatterOverridesImplicitConversion) {
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
}
namespace my_ns {
template <typename Char> class my_string {
public:
my_string(const Char* s) : s_(s) {}
const Char* data() const FMT_NOEXCEPT { return s_.data(); }
std::size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); }
private:
std::basic_string<Char> s_;
};
template <typename Char>
inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
FMT_NOEXCEPT {
return {s.data(), s.length()};
}
struct non_string {};
} // namespace my_ns
namespace FakeQt {
class QString {
public:
QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
private:
std::shared_ptr<std::wstring> s_;
};
inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
FMT_NOEXCEPT {
return {s.utf16(), static_cast<std::size_t>(s.size())};
}
} // namespace FakeQt
template <typename T> class IsStringTest : public testing::Test {};
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
TYPED_TEST_CASE(IsStringTest, StringCharTypes);
namespace {
template <typename Char>
struct derived_from_string_view : fmt::basic_string_view<Char> {};
} // namespace
TYPED_TEST(IsStringTest, IsString) {
EXPECT_TRUE(fmt::internal::is_string<TypeParam*>::value);
EXPECT_TRUE(fmt::internal::is_string<const TypeParam*>::value);
EXPECT_TRUE(fmt::internal::is_string<TypeParam[2]>::value);
EXPECT_TRUE(fmt::internal::is_string<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::internal::is_string<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(
fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value);
EXPECT_TRUE(
fmt::internal::is_string<derived_from_string_view<TypeParam>>::value);
using string_view = fmt::internal::std_string_view<TypeParam>;
EXPECT_TRUE(std::is_empty<string_view>::value !=
fmt::internal::is_string<string_view>::value);
EXPECT_TRUE(fmt::internal::is_string<my_ns::my_string<TypeParam>>::value);
EXPECT_FALSE(fmt::internal::is_string<my_ns::non_string>::value);
EXPECT_TRUE(fmt::internal::is_string<FakeQt::QString>::value);
}
TEST(CoreTest, Format) {
// This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test
#endif
EXPECT_EQ(fmt::format("{}", 42), "42");
}
TEST(CoreTest, FormatTo) {
// This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test
#endif
std::string s;
fmt::format_to(std::back_inserter(s), "{}", 42);
EXPECT_EQ(s, "42");
}
TEST(CoreTest, ToStringViewForeignStrings) {
using namespace my_ns;
using namespace FakeQt;
EXPECT_EQ(to_string_view(my_string<char>("42")), "42");
EXPECT_EQ(to_string_view(my_string<wchar_t>(L"42")), L"42");
EXPECT_EQ(to_string_view(QString(L"42")), L"42");
fmt::internal::type type =
fmt::internal::mapped_type_constant<my_string<char>,
fmt::format_context>::value;
EXPECT_EQ(type, fmt::internal::string_type);
type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::string_type);
type =
fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::string_type);
// Does not compile: only wide format contexts are compatible with QString!
// type = fmt::internal::mapped_type_constant<QString,
// fmt::format_context>::value;
}
TEST(CoreTest, FormatForeignStrings) {
using namespace my_ns;
using namespace FakeQt;
EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42");
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), my_string<wchar_t>(L"42")), L"42");
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42");
}
struct implicitly_convertible_to_string {
operator std::string() const { return "foo"; }
};
struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
}
// std::is_constructible is broken in MSVC until version 2015.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#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

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

861
test/format Normal file
View File

@ -0,0 +1,861 @@
// Formatting library for C++ - the standard API implementation
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <variant>
#include "fmt/format.h"
// This implementation verifies the correctness of the standard API proposed in
// P0645 Text Formatting and is optimized for copy-pasting from the paper, not
// for efficiency or readability. An efficient implementation should not use
// std::variant and should store packed argument type tags separately from
// values in basic_format_args for small number of arguments.
namespace std {
template<class T>
constexpr bool Integral = is_integral_v<T>;
template <class O>
using iter_difference_t = ptrdiff_t;
}
// https://fmt.dev/Text%20Formatting.html#format.syn
namespace std {
// [format.error], class format_error
class format_error;
// [format.formatter], formatter
template<class charT> class basic_format_parse_context;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
template<class Out, class charT> class basic_format_context;
using format_context = basic_format_context<
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<char>>, char>;
using wformat_context = basic_format_context<
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<wchar_t>>, wchar_t>;
template<class T, class charT = char> struct formatter {
formatter() = delete;
};
// [format.arguments], arguments
template<class Context> class basic_format_arg;
template<class Visitor, class Context>
/* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
template<class Context, class... Args> struct format_arg_store; // exposition only
template<class Context> class basic_format_args;
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
template<class Out, class charT>
using format_args_t = basic_format_args<basic_format_context<Out, charT>>;
template<class Context = format_context, class... Args>
format_arg_store<Context, Args...>
make_format_args(const Args&... args);
template<class... Args>
format_arg_store<wformat_context, Args...>
make_wformat_args(const Args&... args);
// [format.functions], formatting functions
template<class... Args>
string format(string_view fmt, const Args&... args);
template<class... Args>
wstring format(wstring_view fmt, const Args&... args);
string vformat(string_view fmt, format_args args);
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args);
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args);
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args);
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
template<class Out>
struct format_to_n_result {
Out out;
iter_difference_t<Out> size;
};
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
}
// https://fmt.dev/Text%20Formatting.html#format.error
namespace std {
class format_error : public runtime_error {
public:
explicit format_error(const string& what_arg) : runtime_error(what_arg) {}
explicit format_error(const char* what_arg) : runtime_error(what_arg) {}
};
}
namespace std {
namespace detail {
struct error_handler {
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char* message) {
throw std::format_error(message);
}
};
}
}
// https://fmt.dev/Text%20Formatting.html#format.parse_context
namespace std {
template<class charT>
class basic_format_parse_context {
public:
using char_type = charT;
using const_iterator = typename basic_string_view<charT>::const_iterator;
using iterator = const_iterator;
private:
iterator begin_; // exposition only
iterator end_; // exposition only
enum indexing { unknown, manual, automatic }; // exposition only
indexing indexing_; // exposition only
size_t next_arg_id_; // exposition only
size_t num_args_; // exposition only
public:
explicit constexpr basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args = 0) noexcept;
basic_format_parse_context(const basic_format_parse_context&) = delete;
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr void advance_to(const_iterator it);
constexpr size_t next_arg_id();
constexpr void check_arg_id(size_t id);
// Implementation detail:
constexpr void check_arg_id(fmt::string_view) {}
detail::error_handler error_handler() const { return {}; }
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class charT>
/* explicit */ constexpr basic_format_parse_context<charT>::
basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args) noexcept
: begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {}
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::begin() const noexcept { return begin_; }
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::end() const noexcept { return end_; }
template<class charT>
constexpr void basic_format_parse_context<charT>::advance_to(typename basic_format_parse_context<charT>::iterator it) { begin_ = it; }
template<class charT>
constexpr size_t basic_format_parse_context<charT>::next_arg_id() {
if (indexing_ == manual)
throw format_error("manual to automatic indexing");
if (indexing_ == unknown)
indexing_ = automatic;
return next_arg_id_++;
}
template<class charT>
constexpr void basic_format_parse_context<charT>::check_arg_id(size_t id) {
// clang doesn't support __builtin_is_constant_evaluated yet
//if (!(!__builtin_is_constant_evaluated() || id < num_args_))
// throw format_error(invalid index is out of range");
if (indexing_ == automatic)
throw format_error("automatic to manual indexing");
if (indexing_ == unknown)
indexing_ = manual;
}
}
// https://fmt.dev/Text%20Formatting.html#format.context
namespace std {
template<class Out, class charT>
class basic_format_context {
basic_format_args<basic_format_context> args_; // exposition only
Out out_; // exposition only
public:
using iterator = Out;
using char_type = charT;
template<class T> using formatter_type = formatter<T, charT>;
basic_format_arg<basic_format_context> arg(size_t id) const;
iterator out();
void advance_to(iterator it);
// Implementation details:
using format_arg = basic_format_arg<basic_format_context>;
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::internal::locale_ref)
: args_(args), out_(out) {}
detail::error_handler error_handler() const { return {}; }
basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const {
return {}; // unused: named arguments are not supported yet
}
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class O, class charT>
basic_format_arg<basic_format_context<O, charT>> basic_format_context<O, charT>::arg(size_t id) const { return args_.get(id); }
template<class O, class charT>
typename basic_format_context<O, charT>::iterator basic_format_context<O, charT>::out() { return out_; }
template<class O, class charT>
void basic_format_context<O, charT>::advance_to(typename basic_format_context<O, charT>::iterator it) { out_ = it; }
}
namespace std {
namespace detail {
template <typename T>
constexpr bool is_standard_integer_v =
std::is_same_v<T, signed char> ||
std::is_same_v<T, short int> ||
std::is_same_v<T, int> ||
std::is_same_v<T, long int> ||
std::is_same_v<T, long long int>;
template <typename T>
constexpr bool is_standard_unsigned_integer_v =
std::is_same_v<T, unsigned char> ||
std::is_same_v<T, unsigned short int> ||
std::is_same_v<T, unsigned int> ||
std::is_same_v<T, unsigned long int> ||
std::is_same_v<T, unsigned long long int>;
template <typename T, typename Char> struct formatter;
}
}
// https://fmt.dev/Text%20Formatting.html#format.arg
namespace std {
template<class Context>
class basic_format_arg {
public:
class handle;
private:
using char_type = typename Context::char_type; // exposition only
variant<monostate, bool, char_type,
int, unsigned int, long long int, unsigned long long int,
double, long double,
const char_type*, basic_string_view<char_type>,
const void*, handle> value; // exposition only
template<typename T,
typename = enable_if_t<
std::is_same_v<T, bool> ||
std::is_same_v<T, char_type> ||
(std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) ||
detail::is_standard_integer_v<T> ||
detail::is_standard_unsigned_integer_v<T> ||
sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0
>> explicit basic_format_arg(const T& v) noexcept; // exposition only
explicit basic_format_arg(float n) noexcept; // exposition only
explicit basic_format_arg(double n) noexcept; // exposition only
explicit basic_format_arg(long double n) noexcept; // exposition only
explicit basic_format_arg(const char_type* s); // exposition only
template<class traits>
explicit basic_format_arg(
basic_string_view<char_type, traits> s) noexcept; // exposition only
template<class traits, class Allocator>
explicit basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
explicit basic_format_arg(nullptr_t) noexcept; // exposition only
template<class T, typename = enable_if_t<is_void_v<T>>>
explicit basic_format_arg(const T* p) noexcept; // exposition only
// Fails due to a bug in clang
//template<class Visitor, class Ctx>
// friend auto visit_format_arg(Visitor&& vis,
// basic_format_arg<Ctx> arg); // exposition only
friend auto get_value(basic_format_arg arg) {
return arg.value;
}
template <typename T, typename Char> friend struct detail::formatter;
template<class Ctx, class... Args>
friend format_arg_store<Ctx, Args...>
make_format_args(const Args&... args); // exposition only
public:
basic_format_arg() noexcept;
explicit operator bool() const noexcept;
};
}
namespace std {
template<class Context>
basic_format_arg<Context>::basic_format_arg() noexcept {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T& v) noexcept {
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char_type>)
value = v;
else if constexpr (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>)
value = static_cast<wchar_t>(v);
else if constexpr (detail::is_standard_integer_v<T> && sizeof(T) <= sizeof(int))
value = static_cast<int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T> && sizeof(T) <= sizeof(unsigned))
value = static_cast<unsigned>(v);
else if constexpr (detail::is_standard_integer_v<T>)
value = static_cast<long long int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T>)
value = static_cast<unsigned long long int>(v);
else if constexpr (sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0)
value = handle(v);
}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(float n) noexcept
: value(static_cast<double>(n)) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(long double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(const typename basic_format_arg<Context>::char_type* s)
: value(s) {
assert(s != nullptr);
}
template<class Context>
template<class traits>
/* explicit */ basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type, traits> s) noexcept
: value(s) {}
template<class Context>
template<class traits, class Allocator>
/* explicit */ basic_format_arg<Context>::basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept
: value(basic_string_view<char_type>(s.data(), s.size())) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(nullptr_t) noexcept
: value(static_cast<const void*>(nullptr)) {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T* p) noexcept
: value(p) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::operator bool() const noexcept {
return !holds_alternative<monostate>(value);
}
}
namespace std {
template<class Context>
class basic_format_arg<Context>::handle {
const void* ptr_; // exposition only
void (*format_)(basic_format_parse_context<char_type>&,
Context&, const void*); // exposition only
template<class T> explicit handle(const T& val) noexcept; // exposition only
friend class basic_format_arg<Context>; // exposition only
public:
void format(basic_format_parse_context<char_type>&, Context& ctx) const;
};
}
namespace std {
template<class Context>
template<class T> /* explicit */ basic_format_arg<Context>::handle::handle(const T& val) noexcept
: ptr_(&val), format_([](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) {
typename Context::template formatter_type<T> f;
parse_ctx.advance_to(f.parse(parse_ctx));
format_ctx.advance_to(f.format(*static_cast<const T*>(ptr), format_ctx));
}) {}
template<class Context>
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const {
format_(parse_ctx, format_ctx, ptr_);
}
// https://fmt.dev/Text%20Formatting.html#format.visit
template<class Visitor, class Context>
auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg) {
return visit(vis, get_value(arg));
}
}
// https://fmt.dev/Text%20Formatting.html#format.store
namespace std {
template<class Context, class... Args>
struct format_arg_store { // exposition only
array<basic_format_arg<Context>, sizeof...(Args)> args;
};
}
// https://fmt.dev/Text%20Formatting.html#format.basic_args
namespace std {
template<class Context>
class basic_format_args {
size_t size_; // exposition only
const basic_format_arg<Context>* data_; // exposition only
public:
basic_format_args() noexcept;
template<class... Args>
basic_format_args(const format_arg_store<Context, Args...>& store) noexcept;
basic_format_arg<Context> get(size_t i) const noexcept;
};
}
namespace std {
template<class Context>
basic_format_args<Context>::basic_format_args() noexcept : size_(0) {}
template<class Context>
template<class... Args>
basic_format_args<Context>::basic_format_args(const format_arg_store<Context, Args...>& store) noexcept
: size_(sizeof...(Args)), data_(store.args.data()) {}
template<class Context>
basic_format_arg<Context> basic_format_args<Context>::get(size_t i) const noexcept {
return i < size_ ? data_[i] : basic_format_arg<Context>();
}
}
namespace std {
// https://fmt.dev/Text%20Formatting.html#format.make_args
template<class Context /*= format_context*/, class... Args>
format_arg_store<Context, Args...> make_format_args(const Args&... args) {
return {basic_format_arg<Context>(args)...};
}
// https://fmt.dev/Text%20Formatting.html#format.make_wargs
template<class... Args>
format_arg_store<wformat_context, Args...> make_wformat_args(const Args&... args) {
return make_format_args<wformat_context>(args...);
}
}
namespace std {
namespace detail {
template <typename Range>
class arg_formatter
: public fmt::internal::arg_formatter_base<Range, error_handler> {
private:
using char_type = typename Range::value_type;
using base = fmt::internal::arg_formatter_base<Range, error_handler>;
using format_context = std::basic_format_context<typename base::iterator, char_type>;
using parse_context = basic_format_parse_context<char_type>;
parse_context* parse_ctx_;
format_context& ctx_;
public:
typedef Range range;
typedef typename base::iterator iterator;
typedef typename base::format_specs format_specs;
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
: base(Range(ctx.out()), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator();
/** Formats an argument of a user-defined type. */
iterator operator()(typename std::basic_format_arg<format_context>::handle handle) {
handle.format(*parse_ctx_, ctx_);
return this->out();
}
iterator operator()(monostate) {
throw format_error("");
}
};
template <typename Context>
inline fmt::internal::type get_type(basic_format_arg<Context> arg) {
return visit_format_arg([&] (auto val) {
using char_type = typename Context::char_type;
using T = decltype(val);
if (std::is_same_v<T, monostate>)
return fmt::internal::none_type;
if (std::is_same_v<T, bool>)
return fmt::internal::bool_type;
if (std::is_same_v<T, char_type>)
return fmt::internal::char_type;
if (std::is_same_v<T, int>)
return fmt::internal::int_type;
if (std::is_same_v<T, unsigned int>)
return fmt::internal::uint_type;
if (std::is_same_v<T, long long int>)
return fmt::internal::long_long_type;
if (std::is_same_v<T, unsigned long long int>)
return fmt::internal::ulong_long_type;
if (std::is_same_v<T, double>)
return fmt::internal::double_type;
if (std::is_same_v<T, long double>)
return fmt::internal::long_double_type;
if (std::is_same_v<T, const char_type*>)
return fmt::internal::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::internal::string_type;
if (std::is_same_v<T, const void*>)
return fmt::internal::pointer_type;
assert(get_value(arg).index() == 12);
return fmt::internal::custom_type;
}, arg);
}
template <typename Context>
class custom_formatter {
private:
using parse_context = basic_format_parse_context<typename Context::char_type>;
parse_context& parse_ctx_;
Context& format_ctx_;
public:
custom_formatter(parse_context& parse_ctx, Context& ctx) : parse_ctx_(parse_ctx), format_ctx_(ctx) {}
bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(parse_ctx_, format_ctx_);
return true;
}
template <typename T> bool operator()(T) const { return false; }
};
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler {
typedef typename ArgFormatter::range range;
format_handler(range r, basic_string_view<Char> str,
basic_format_args<Context> format_args,
fmt::internal::locale_ref loc)
: parse_ctx(str), context(r.begin(), format_args, loc) {}
void on_text(const Char* begin, const Char* end) {
auto size = fmt::internal::to_unsigned(end - begin);
auto out = context.out();
auto&& it = fmt::internal::reserve(out, size);
it = std::copy_n(begin, size, it);
context.advance_to(out);
}
void on_arg_id() {
arg = context.arg(parse_ctx.next_arg_id());
}
void on_arg_id(unsigned id) {
parse_ctx.check_arg_id(id);
arg = context.arg(id);
}
void on_arg_id(fmt::basic_string_view<Char>) {}
void on_replacement_field(const Char* p) {
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
}
const Char* on_format_specs(const Char* begin, const Char* end) {
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
fmt::basic_format_specs<Char> specs;
using fmt::internal::specs_handler;
using parse_context = basic_format_parse_context<Char>;
fmt::internal::specs_checker<specs_handler<parse_context, Context>> handler(
specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg));
begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string");
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx, &specs), arg));
return begin;
}
basic_format_parse_context<Char> parse_ctx;
Context context;
basic_format_arg<Context> arg;
};
template <typename T, typename Char>
struct formatter {
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
namespace internal = fmt::internal;
typedef internal::dynamic_specs_handler<ParseContext> handler_type;
auto type = internal::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto type_spec = specs_.type;
auto eh = ctx.error_handler();
switch (type) {
case internal::none_type:
case internal::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
case internal::int_type:
case internal::uint_type:
case internal::long_long_type:
case internal::ulong_long_type:
case internal::bool_type:
handle_int_type_spec(type_spec,
internal::int_type_checker<decltype(eh)>(eh));
break;
case internal::char_type:
handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(type_spec, eh));
break;
case internal::double_type:
case internal::long_double_type:
handle_float_type_spec(type_spec,
internal::float_type_checker<decltype(eh)>(eh));
break;
case internal::cstring_type:
internal::handle_cstring_type_spec(
type_spec, internal::cstring_type_checker<decltype(eh)>(eh));
break;
case internal::string_type:
internal::check_string_type_spec(type_spec, eh);
break;
case internal::pointer_type:
internal::check_pointer_type_spec(type_spec, eh);
break;
case internal::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
}
return it;
}
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
using range_type = fmt::internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val));
}
private:
fmt::internal::dynamic_format_specs<Char> specs_;
};
} // namespace detail
// https://fmt.dev/Text%20Formatting.html#format.functions
template<class... Args>
string format(string_view fmt, const Args&... args) {
return vformat(fmt, make_format_args(args...));
}
template<class... Args>
wstring format(wstring_view fmt, const Args&... args) {
return vformat(fmt, make_wformat_args(args...));
}
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::internal::buffer<char>& buf = mbuf;
using range = fmt::buffer_range<char>;
detail::format_handler<detail::arg_formatter<range>, char, format_context>
h(range(std::back_inserter(buf)), fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf);
}
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, {make_format_args<context>(args...)});
}
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, {make_format_args<context>(args...)});
}
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args) {
using range = fmt::internal::output_range<Out, char>;
detail::format_handler<detail::arg_formatter<range>, char, basic_format_context<Out, char>>
h(range(out), fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);
return h.context.out();
}
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
#define charT char
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t>;
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
#define charT wchar_t
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t> : detail::formatter<charT, charT> {};
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<std::basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<std::basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
template<> struct formatter<const wchar_t, char> {
formatter() = delete;
};
}
#endif // FMT_FORMAT_

View File

@ -5,7 +5,6 @@
//
// For the license information refer to format.h.
#define FMT_EXTENDED_COLORS
#define FMT_NOEXCEPT
#undef FMT_SHARED
#include "test-assert.h"
@ -21,42 +20,206 @@
#include "gtest-extra.h"
#include "util.h"
#undef min
#undef max
template <typename T>
struct ValueExtractor: fmt::internal::function<T> {
T operator()(T value) {
return value;
}
using fmt::internal::bigint;
using fmt::internal::fp;
using fmt::internal::max_value;
template <typename U>
FMT_NORETURN T operator()(U) {
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, 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);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
auto max = max_value<uint32_t>();
bigint bigmax(max);
bigmax *= max;
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
}
template <bool is_iec559> void test_construct_from_double() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <> void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
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, Multiply) {
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
EXPECT_EQ(v.f, 123u * 56u);
EXPECT_EQ(v.e, 4 + 7 + 64);
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
EXPECT_EQ(v.e, 4 + 8 + 64);
}
TEST(FPTest, GetCachedPower) {
typedef std::numeric_limits<double> limits;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
EXPECT_LE(exp, fp.e);
int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(static_cast<double>(fp.f), fp.e));
}
}
TEST(FPTest, GetRoundDirection) {
using fmt::internal::get_round_direction;
EXPECT_EQ(fmt::internal::down, get_round_direction(100, 50, 0));
EXPECT_EQ(fmt::internal::up, get_round_direction(100, 51, 0));
EXPECT_EQ(fmt::internal::down, get_round_direction(100, 40, 10));
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 = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
// Check that remainder + error doesn't overflow.
EXPECT_EQ(fmt::internal::up, get_round_direction(max, max - 1, 2));
// Check that 2 * (remainder + error) doesn't overflow.
EXPECT_EQ(fmt::internal::unknown,
get_round_direction(max, max / 2 + 1, max / 2));
// Check that remainder - error doesn't overflow.
EXPECT_EQ(fmt::internal::unknown, get_round_direction(100, 40, 41));
// Check that 2 * (remainder - error) doesn't overflow.
EXPECT_EQ(fmt::internal::up, get_round_direction(max, max - 1, 1));
}
TEST(FPTest, FixedHandler) {
struct handler : fmt::internal::fixed_handler {
char buffer[10];
handler(int prec = 0) : fmt::internal::fixed_handler() {
buf = buffer;
precision = prec;
}
};
int exp = 0;
handler().on_digit('0', 100, 99, 0, exp, false);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
assertion_failure);
namespace digits = fmt::internal::digits;
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done);
// Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
// Check that 2 * error doesn't overflow.
uint64_t max = max_value<uint64_t>();
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false),
digits::error);
}
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
int exp = 0;
grisu_format(4.2f, buf, -1, false, exp);
}
template <typename T> struct value_extractor {
T operator()(T value) { return value; }
template <typename U> FMT_NORETURN T operator()(U) {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
}
#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);
visit(fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
arg);
EXPECT_EQ(value, visit(ValueExtractor<long long>(), arg));
fmt::visit_format_arg(
fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
arg);
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
}
TEST(FormatTest, FormatNegativeNaN) {
double nan = std::numeric_limits<double>::quiet_NaN();
if (fmt::internal::fputil::isnegative(-nan))
if (std::signbit(-nan))
EXPECT_EQ("-nan", fmt::format("{}", -nan));
else
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
}
TEST(FormatTest, StrError) {
char *message = nullptr;
char* message = nullptr;
char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = nullptr, 0), "invalid buffer");
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = nullptr, 0),
"invalid buffer");
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer");
buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
@ -67,17 +230,19 @@ TEST(FormatTest, StrError) {
int error_code = EDOM;
#endif
int result = fmt::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(0, result);
int result =
fmt::internal::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(result, 0);
std::size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(get_system_error(error_code), message);
// safe_strerror never uses buffer on MinGW.
#ifndef __MINGW32__
result = fmt::safe_strerror(error_code, message = buffer, message_size);
#if !defined(__MINGW32__) && !defined(__sun)
result =
fmt::internal::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result);
result = fmt::safe_strerror(error_code, message = buffer, 1);
result = fmt::internal::safe_strerror(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message);
@ -89,14 +254,14 @@ TEST(FormatTest, FormatErrorCode) {
{
fmt::memory_buffer buffer;
format_to(buffer, "garbage");
fmt::format_error_code(buffer, 42, "test");
fmt::internal::format_error_code(buffer, 42, "test");
EXPECT_EQ("test: " + msg, to_string(buffer));
}
{
fmt::memory_buffer buffer;
std::string prefix(
fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
fmt::format_error_code(buffer, 42, prefix);
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
'x');
fmt::internal::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer));
}
int codes[] = {42, -1};
@ -104,23 +269,44 @@ TEST(FormatTest, FormatErrorCode) {
// Test maximum buffer size.
msg = fmt::format("error {}", codes[i]);
fmt::memory_buffer buffer;
std::string prefix(
fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::format_error_code(buffer, codes[i], prefix);
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::internal::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
std::size_t size = fmt::inline_buffer_size;
EXPECT_EQ(size, buffer.size());
buffer.resize(0);
// Test with a message that doesn't fit into the buffer.
prefix += 'x';
fmt::format_error_code(buffer, codes[i], prefix);
fmt::internal::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(msg, to_string(buffer));
}
}
TEST(ColorsTest, Colors) {
EXPECT_WRITE(stdout, fmt::print(fmt::rgb(255,20,30), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::color::blue,"blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
TEST(FormatTest, CountCodePoints) {
EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик")));
}
// 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 = 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));
}
}
TEST(UtilTest, CountDigits) {
test_count_digits<uint32_t>();
test_count_digits<uint64_t>();
}
TEST(UtilTest, WriteUIntPtr) {
fmt::memory_buffer buf;
fmt::internal::writer writer(buf);
writer.write_pointer(fmt::internal::bit_cast<fmt::internal::fallback_uintptr>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ("0xface", to_string(buf));
}

File diff suppressed because it is too large Load Diff

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

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

View File

@ -0,0 +1,38 @@
# Copyright (c) 2019, Paul Dreik
# License: see LICENSE.rst in the fmt root directory
# settings this links in a main. useful for reproducing,
# kcov, gdb, afl, valgrind.
# (note that libFuzzer can also reproduce, just pass it the files)
option(FMT_FUZZ_LINKMAIN "enables the reproduce mode, instead of libFuzzer" On)
# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for
# the fuzz targets, otherwise the cmake configuration step fails.
set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
# Find all fuzzers.
set(SOURCES
chrono_duration.cpp
named_arg.cpp
one_arg.cpp
sprintf.cpp
two_args.cpp
)
macro(implement_fuzzer sourcefile)
get_filename_component(basename ${sourcefile} NAME_WE)
set(name fuzzer_${basename})
add_executable(${name} ${sourcefile} fuzzer_common.h)
if (FMT_FUZZ_LINKMAIN)
target_sources(${name} PRIVATE main.cpp)
endif ()
target_link_libraries(${name} PRIVATE fmt)
if (FMT_FUZZ_LDFLAGS)
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
endif ()
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
endmacro ()
foreach (X IN ITEMS ${SOURCES})
implement_fuzzer(${X})
endforeach ()

43
test/fuzzing/README.md Normal file
View File

@ -0,0 +1,43 @@
# FMT Fuzzer
Fuzzing has revealed [several bugs](https://github.com/fmtlib/fmt/issues?&q=is%3Aissue+fuzz)
in fmt. It is a part of the continous fuzzing at
[oss-fuzz](https://github.com/google/oss-fuzz).
The source code is modified to make the fuzzing possible without locking up on
resource exhaustion:
```cpp
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if(spec.precision>100000) {
throw std::runtime_error("fuzz mode - avoiding large precision");
}
#endif
```
This macro is the defacto standard for making fuzzing practically possible, see
[the libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
## Running the fuzzers locally
There is a [helper script](build.sh) to build the fuzzers, which has only been
tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on
Windows (using clang>=8) or on Mac, but the script will probably not work out of
the box.
Something along
```sh
mkdir build
cd build
export CXX=clang++
export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build .
```
should work to build the fuzzers for all platforms which clang supports.
Execute a fuzzer with for instance
```sh
cd build
export UBSAN_OPTIONS=halt_on_error=1
mkdir out_chrono
bin/fuzzer_chrono_duration out_chrono
```

110
test/fuzzing/build.sh Executable file
View File

@ -0,0 +1,110 @@
#!/bin/sh
#
# Creates fuzzer builds of various kinds
# - reproduce mode (no fuzzing, just enables replaying data through the fuzzers)
# - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works)
# - libFuzzer build (you will need clang)
# - afl build (you will need afl)
#
#
# Copyright (c) 2019 Paul Dreik
#
# License: see LICENSE.rst in the fmt root directory
set -e
me=$(basename $0)
root=$(readlink -f "$(dirname "$0")/../..")
echo $me: root=$root
here=$(pwd)
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
#builds the fuzzers as one would do if using afl or just making
#binaries for reproducing.
builddir=$here/build-fuzzers-reproduce
mkdir -p $builddir
cd $builddir
CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL" cmake \
$CMAKEFLAGSALL
cmake --build $builddir
#for performance analysis of the fuzzers
builddir=$here/build-fuzzers-perfanalysis
mkdir -p $builddir
cd $builddir
CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL -g" cmake \
$CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=On \
-DCMAKE_BUILD_TYPE=Release
cmake --build $builddir
#builds the fuzzers as oss-fuzz does
builddir=$here/build-fuzzers-ossfuzz
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build $builddir
#builds fuzzers for local fuzzing with libfuzzer with asan+usan
builddir=$here/build-fuzzers-libfuzzer
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build $builddir
#builds fuzzers for local fuzzing with libfuzzer with asan only
builddir=$here/build-fuzzers-libfuzzer-addr
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,undefined" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
cmake --build $builddir
#builds a fast fuzzer for making coverage fast
builddir=$here/build-fuzzers-fast
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" \
-DCMAKE_BUILD_TYPE=Release
cmake --build $builddir
#builds fuzzers for local fuzzing with afl
builddir=$here/build-fuzzers-afl
mkdir -p $builddir
cd $builddir
CXX="afl-g++" \
CXXFLAGS="$CXXFLAGSALL -fsanitize=address,undefined" \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=On
cmake --build $builddir
echo $me: all good

View File

@ -0,0 +1,152 @@
// Copyright (c) 2019, Paul Dreik
// License: see LICENSE.rst in the fmt root directory
#include <fmt/chrono.h>
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <vector>
#include "fuzzer_common.h"
template <typename Item, typename Ratio>
void invoke_inner(fmt::string_view formatstring, const Item item) {
const std::chrono::duration<Item, Ratio> value(item);
try {
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(formatstring, value);
#else
fmt::memory_buffer buf;
fmt::format_to(buf, formatstring, value);
#endif
} catch (std::exception& /*e*/) {
}
}
// Item is the underlying type for duration (int, long etc)
template <typename Item>
void invoke_outer(const uint8_t* Data, std::size_t Size, const int scaling) {
// always use a fixed location of the data
using fmt_fuzzer::Nfixed;
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "fixed size is too small");
if (Size <= Nfixed + 1) {
return;
}
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
// fast forward
Data += Nfixed;
Size -= Nfixed;
// Data is already allocated separately in libFuzzer so reading past
// the end will most likely be detected anyway
const auto formatstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
// doit_impl<Item,std::yocto>(buf.data(),item);
// doit_impl<Item,std::zepto>(buf.data(),item);
switch (scaling) {
case 1:
invoke_inner<Item, std::atto>(formatstring, item);
break;
case 2:
invoke_inner<Item, std::femto>(formatstring, item);
break;
case 3:
invoke_inner<Item, std::pico>(formatstring, item);
break;
case 4:
invoke_inner<Item, std::nano>(formatstring, item);
break;
case 5:
invoke_inner<Item, std::micro>(formatstring, item);
break;
case 6:
invoke_inner<Item, std::milli>(formatstring, item);
break;
case 7:
invoke_inner<Item, std::centi>(formatstring, item);
break;
case 8:
invoke_inner<Item, std::deci>(formatstring, item);
break;
case 9:
invoke_inner<Item, std::deca>(formatstring, item);
break;
case 10:
invoke_inner<Item, std::kilo>(formatstring, item);
break;
case 11:
invoke_inner<Item, std::mega>(formatstring, item);
break;
case 12:
invoke_inner<Item, std::giga>(formatstring, item);
break;
case 13:
invoke_inner<Item, std::tera>(formatstring, item);
break;
case 14:
invoke_inner<Item, std::peta>(formatstring, item);
break;
case 15:
invoke_inner<Item, std::exa>(formatstring, item);
}
// doit_impl<Item,std::zeta>(buf.data(),item);
// doit_impl<Item,std::yotta>(buf.data(),item);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
if (Size <= 4) {
return 0;
}
const auto representation = Data[0];
const auto scaling = Data[1];
Data += 2;
Size -= 2;
switch (representation) {
case 1:
invoke_outer<char>(Data, Size, scaling);
break;
case 2:
invoke_outer<unsigned char>(Data, Size, scaling);
break;
case 3:
invoke_outer<signed char>(Data, Size, scaling);
break;
case 4:
invoke_outer<short>(Data, Size, scaling);
break;
case 5:
invoke_outer<unsigned short>(Data, Size, scaling);
break;
case 6:
invoke_outer<int>(Data, Size, scaling);
break;
case 7:
invoke_outer<unsigned int>(Data, Size, scaling);
break;
case 8:
invoke_outer<long>(Data, Size, scaling);
break;
case 9:
invoke_outer<unsigned long>(Data, Size, scaling);
break;
case 10:
invoke_outer<float>(Data, Size, scaling);
break;
case 11:
invoke_outer<double>(Data, Size, scaling);
break;
case 12:
invoke_outer<long double>(Data, Size, scaling);
break;
default:
break;
}
return 0;
}

View File

@ -0,0 +1,67 @@
#ifndef FUZZER_COMMON_H
#define FUZZER_COMMON_H
// Copyright (c) 2019, Paul Dreik
// License: see LICENSE.rst in the fmt root directory
#include <cstdint> // std::uint8_t
#include <cstring> // memcpy
#include <type_traits> // trivially copyable
// one can format to either a string, or a buf. buf is faster,
// but one may be interested in formatting to a string instead to
// verify it works as intended. to avoid a combinatoric explosion,
// select this at compile time instead of dynamically from the fuzz data
#define FMT_FUZZ_FORMAT_TO_STRING 0
// if fmt is given a buffer that is separately allocated,
// chances that address sanitizer detects out of bound reads is
// much higher. However, it slows down the fuzzing.
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
// To let the the fuzzer mutation be efficient at cross pollinating
// between different types, use a fixed size format.
// The same bit pattern, interpreted as another type,
// is likely interesting.
// For this, we must know the size of the largest possible type in use.
// There are some problems on travis, claiming Nfixed is not a constant
// expression which seems to be an issue with older versions of libstdc++
#if _GLIBCXX_RELEASE >= 7
# include <algorithm>
namespace fmt_fuzzer {
constexpr auto Nfixed = std::max(sizeof(long double), sizeof(std::intmax_t));
}
#else
namespace fmt_fuzzer {
constexpr auto Nfixed = 16;
}
#endif
namespace fmt_fuzzer {
// view data as a c char pointer.
template <typename T> inline const char* as_chars(const T* data) {
return static_cast<const char*>(static_cast<const void*>(data));
}
// view data as a byte pointer
template <typename T> inline const std::uint8_t* as_bytes(const T* data) {
return static_cast<const std::uint8_t*>(static_cast<const void*>(data));
}
// blits bytes from Data to form an (assumed trivially constructible) object
// of type Item
template <class Item> inline Item assignFromBuf(const std::uint8_t* Data) {
Item item{};
std::memcpy(&item, Data, sizeof(Item));
return item;
}
// reads a boolean value by looking at the first byte from Data
template <> inline bool assignFromBuf<bool>(const std::uint8_t* Data) {
return !!Data[0];
}
} // namespace fmt_fuzzer
#endif // FUZZER_COMMON_H

21
test/fuzzing/main.cpp Normal file
View File

@ -0,0 +1,21 @@
#include <cassert>
#include <fstream>
#include <sstream>
#include <vector>
#include "fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size);
int main(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
std::ifstream in(argv[i]);
assert(in);
in.seekg(0, std::ios_base::end);
const auto pos = in.tellg();
assert(pos >= 0);
in.seekg(0, std::ios_base::beg);
std::vector<char> buf(static_cast<std::size_t>(pos));
in.read(buf.data(), static_cast<long>(buf.size()));
assert(in.gcount() == pos);
LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size());
}
}

128
test/fuzzing/named_arg.cpp Normal file
View File

@ -0,0 +1,128 @@
// Copyright (c) 2019, Paul Dreik
// License: see LICENSE.rst in the fmt root directory
#include <fmt/chrono.h>
#include <fmt/core.h>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include <vector>
#include "fuzzer_common.h"
template <typename Item1>
void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
constexpr auto N1 = sizeof(Item1);
static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
if (Size <= fmt_fuzzer::Nfixed) {
return;
}
const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
Data += fmt_fuzzer::Nfixed;
Size -= fmt_fuzzer::Nfixed;
// how many chars should be used for the argument name?
if (argsize <= 0 || argsize >= Size) {
return;
}
// allocating buffers separately is slower, but increases chances
// of detecting memory errors
#if FMT_FUZZ_SEPARATE_ALLOCATION
std::vector<char> argnamebuffer(argsize);
std::memcpy(argnamebuffer.data(), Data, argsize);
auto argname = fmt::string_view(argnamebuffer.data(), argsize);
#else
auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize);
#endif
Data += argsize;
Size -= argsize;
#if FMT_FUZZ_SEPARATE_ALLOCATION
// allocates as tight as possible, making it easier to catch buffer overruns.
std::vector<char> fmtstringbuffer(Size);
std::memcpy(fmtstringbuffer.data(), Data, Size);
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
#else
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
#endif
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(fmtstring, fmt::arg(argname, item1));
#else
fmt::memory_buffer outbuf;
fmt::format_to(outbuf, fmtstring, fmt::arg(argname, item1));
#endif
}
// for dynamic dispatching to an explicit instantiation
template <typename Callback> void invoke(int index, Callback callback) {
switch (index) {
case 0:
callback(bool{});
break;
case 1:
callback(char{});
break;
case 2:
using sc = signed char;
callback(sc{});
break;
case 3:
using uc = unsigned char;
callback(uc{});
break;
case 4:
callback(short{});
break;
case 5:
using us = unsigned short;
callback(us{});
break;
case 6:
callback(int{});
break;
case 7:
callback(unsigned{});
break;
case 8:
callback(long{});
break;
case 9:
using ul = unsigned long;
callback(ul{});
break;
case 10:
callback(float{});
break;
case 11:
callback(double{});
break;
case 12:
using LD = long double;
callback(LD{});
break;
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
if (Size <= 3) {
return 0;
}
// switch types depending on the first byte of the input
const auto first = Data[0] & 0x0F;
const unsigned int second = (Data[0] & 0xF0) >> 4;
Data++;
Size--;
auto outerfcn = [=](auto param1) {
invoke_fmt<decltype(param1)>(Data, Size, second);
};
try {
invoke(first, outerfcn);
} catch (std::exception& /*e*/) {
}
return 0;
}

131
test/fuzzing/one_arg.cpp Normal file
View File

@ -0,0 +1,131 @@
// Copyright (c) 2019, Paul Dreik
// License: see LICENSE.rst in the fmt root directory
#include <fmt/core.h>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include <vector>
#include <fmt/chrono.h>
#include "fuzzer_common.h"
using fmt_fuzzer::Nfixed;
template <typename Item>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed is too small");
if (Size <= Nfixed) {
return;
}
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
Data += Nfixed;
Size -= Nfixed;
#if FMT_FUZZ_SEPARATE_ALLOCATION
// allocates as tight as possible, making it easier to catch buffer overruns.
std::vector<char> fmtstringbuffer(Size);
std::memcpy(fmtstringbuffer.data(), Data, Size);
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
#else
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
#endif
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(fmtstring, item);
#else
fmt::memory_buffer message;
fmt::format_to(message, fmtstring, item);
#endif
}
void invoke_fmt_time(const uint8_t* Data, std::size_t Size) {
using Item = std::time_t;
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed too small");
if (Size <= Nfixed) {
return;
}
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
Data += Nfixed;
Size -= Nfixed;
#if FMT_FUZZ_SEPARATE_ALLOCATION
// allocates as tight as possible, making it easier to catch buffer overruns.
std::vector<char> fmtstringbuffer(Size);
std::memcpy(fmtstringbuffer.data(), Data, Size);
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
#else
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
#endif
auto* b = std::localtime(&item);
if (b) {
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(fmtstring, *b);
#else
fmt::memory_buffer message;
fmt::format_to(message, fmtstring, *b);
#endif
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
if (Size <= 3) {
return 0;
}
const auto first = Data[0];
Data++;
Size--;
try {
switch (first) {
case 0:
invoke_fmt<bool>(Data, Size);
break;
case 1:
invoke_fmt<char>(Data, Size);
break;
case 2:
invoke_fmt<unsigned char>(Data, Size);
break;
case 3:
invoke_fmt<signed char>(Data, Size);
break;
case 4:
invoke_fmt<short>(Data, Size);
break;
case 5:
invoke_fmt<unsigned short>(Data, Size);
break;
case 6:
invoke_fmt<int>(Data, Size);
break;
case 7:
invoke_fmt<unsigned int>(Data, Size);
break;
case 8:
invoke_fmt<long>(Data, Size);
break;
case 9:
invoke_fmt<unsigned long>(Data, Size);
break;
case 10:
invoke_fmt<float>(Data, Size);
break;
case 11:
invoke_fmt<double>(Data, Size);
break;
case 12:
invoke_fmt<long double>(Data, Size);
break;
case 13:
invoke_fmt_time(Data, Size);
break;
default:
break;
}
} catch (std::exception& /*e*/) {
}
return 0;
}

116
test/fuzzing/sprintf.cpp Normal file
View File

@ -0,0 +1,116 @@
// Copyright (c) 2019, Paul Dreik
// License: see LICENSE.rst in the fmt root directory
#include <fmt/format.h>
#include <fmt/printf.h>
#include <cstdint>
#include <stdexcept>
#include "fuzzer_common.h"
using fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded");
static_assert(N2 <= Nfixed, "size2 exceeded");
if (Size <= Nfixed + Nfixed) {
return;
}
Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
Data += Nfixed;
Size -= Nfixed;
Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
Data += Nfixed;
Size -= Nfixed;
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(fmtstring, item1, item2);
#else
fmt::memory_buffer message;
fmt::format_to(message, fmtstring, item1, item2);
#endif
}
// for dynamic dispatching to an explicit instantiation
template <typename Callback> void invoke(int index, Callback callback) {
switch (index) {
case 0:
callback(bool{});
break;
case 1:
callback(char{});
break;
case 2:
using sc = signed char;
callback(sc{});
break;
case 3:
using uc = unsigned char;
callback(uc{});
break;
case 4:
callback(short{});
break;
case 5:
using us = unsigned short;
callback(us{});
break;
case 6:
callback(int{});
break;
case 7:
callback(unsigned{});
break;
case 8:
callback(long{});
break;
case 9:
using ul = unsigned long;
callback(ul{});
break;
case 10:
callback(float{});
break;
case 11:
callback(double{});
break;
case 12:
using LD = long double;
callback(LD{});
break;
case 13:
using ptr = void*;
callback(ptr{});
break;
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
if (Size <= 3) {
return 0;
}
// switch types depending on the first byte of the input
const auto first = Data[0] & 0x0F;
const auto second = (Data[0] & 0xF0) >> 4;
Data++;
Size--;
auto outer = [=](auto param1) {
auto inner = [=](auto param2) {
invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
};
invoke(second, inner);
};
try {
invoke(first, outer);
} catch (std::exception& /*e*/) {
}
return 0;
}

112
test/fuzzing/two_args.cpp Normal file
View File

@ -0,0 +1,112 @@
// Copyright (c) 2019, Paul Dreik
// License: see LICENSE.rst in the fmt root directory
#include <fmt/format.h>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include "fuzzer_common.h"
constexpr auto Nfixed = fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded");
static_assert(N2 <= Nfixed, "size2 exceeded");
if (Size <= Nfixed + Nfixed) {
return;
}
const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
Data += Nfixed;
Size -= Nfixed;
const Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
Data += Nfixed;
Size -= Nfixed;
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(fmtstring, item1, item2);
#else
fmt::memory_buffer message;
fmt::format_to(message, fmtstring, item1, item2);
#endif
}
// for dynamic dispatching to an explicit instantiation
template <typename Callback> void invoke(int index, Callback callback) {
switch (index) {
case 0:
callback(bool{});
break;
case 1:
callback(char{});
break;
case 2:
using sc = signed char;
callback(sc{});
break;
case 3:
using uc = unsigned char;
callback(uc{});
break;
case 4:
callback(short{});
break;
case 5:
using us = unsigned short;
callback(us{});
break;
case 6:
callback(int{});
break;
case 7:
callback(unsigned{});
break;
case 8:
callback(long{});
break;
case 9:
using ul = unsigned long;
callback(ul{});
break;
case 10:
callback(float{});
break;
case 11:
callback(double{});
break;
case 12:
using LD = long double;
callback(LD{});
break;
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
if (Size <= 3) {
return 0;
}
// switch types depending on the first byte of the input
const auto first = Data[0] & 0x0F;
const auto second = (Data[0] & 0xF0) >> 4;
Data++;
Size--;
auto outer = [=](auto param1) {
auto inner = [=](auto param2) {
invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
};
invoke(second, inner);
};
try {
invoke(first, outer);
} catch (std::exception& /*e*/) {
}
return 0;
}

File diff suppressed because it is too large Load Diff

57
test/grisu-test.cc Normal file
View File

@ -0,0 +1,57 @@
// Formatting library for C++ - Grisu tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/format.h"
#include "gtest.h"
static bool reported_skipped;
#undef TEST
#define TEST(test_fixture, test_name) \
void test_fixture##test_name(); \
GTEST_TEST(test_fixture, test_name) { \
if (FMT_USE_GRISU) { \
test_fixture##test_name(); \
} else if (!reported_skipped) { \
reported_skipped = true; \
fmt::print("Skipping Grisu tests.\n"); \
} \
} \
void test_fixture##test_name()
TEST(GrisuTest, NaN) {
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", fmt::format("{}", nan));
EXPECT_EQ("-nan", fmt::format("{}", -nan));
}
TEST(GrisuTest, Inf) {
auto inf = std::numeric_limits<double>::infinity();
EXPECT_EQ("inf", fmt::format("{}", inf));
EXPECT_EQ("-inf", fmt::format("{}", -inf));
}
TEST(GrisuTest, Zero) { EXPECT_EQ("0.0", fmt::format("{}", 0.0)); }
TEST(GrisuTest, Round) {
EXPECT_EQ("1.9156918820264798e-56",
fmt::format("{}", 1.9156918820264798e-56));
EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15));
}
TEST(GrisuTest, Prettify) {
EXPECT_EQ("0.0001", fmt::format("{}", 1e-4));
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
EXPECT_EQ("1e+11", 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));
}
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }

View File

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

View File

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

View File

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

52
test/locale-test.cc Normal file
View File

@ -0,0 +1,52 @@
// Formatting library for C++ - locale tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/locale.h"
#include "gmock.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '?'; }
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("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);
}
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"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));
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@ -8,58 +8,51 @@
#ifndef FMT_MOCK_ALLOCATOR_H_
#define FMT_MOCK_ALLOCATOR_H_
#include "fmt/format.h"
#include "gmock.h"
template <typename T>
class MockAllocator {
template <typename T> class mock_allocator {
public:
MockAllocator() {}
MockAllocator(const MockAllocator &) {}
mock_allocator() {}
mock_allocator(const mock_allocator&) {}
typedef T value_type;
MOCK_METHOD1_T(allocate, T* (std::size_t n));
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
MOCK_METHOD1_T(allocate, T*(std::size_t n));
MOCK_METHOD2_T(deallocate, void(T* p, std::size_t n));
};
template <typename Allocator>
class AllocatorRef {
template <typename Allocator> class allocator_ref {
private:
Allocator *alloc_;
Allocator* alloc_;
public:
typedef typename Allocator::value_type value_type;
explicit AllocatorRef(Allocator *alloc = nullptr) : alloc_(alloc) {}
AllocatorRef(const AllocatorRef &other) : alloc_(other.alloc_) {}
AllocatorRef& operator=(const AllocatorRef &other) {
alloc_ = other.alloc_;
return *this;
}
#if FMT_USE_RVALUE_REFERENCES
private:
void move(AllocatorRef &other) {
void move(allocator_ref& other) {
alloc_ = other.alloc_;
other.alloc_ = nullptr;
}
public:
AllocatorRef(AllocatorRef &&other) {
move(other);
}
typedef typename Allocator::value_type value_type;
AllocatorRef& operator=(AllocatorRef &&other) {
explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {}
allocator_ref(const allocator_ref& other) : alloc_(other.alloc_) {}
allocator_ref(allocator_ref&& other) { move(other); }
allocator_ref& operator=(allocator_ref&& other) {
assert(this != &other);
move(other);
return *this;
}
#endif
Allocator *get() const { return alloc_; }
allocator_ref& operator=(const allocator_ref& other) {
alloc_ = other.alloc_;
return *this;
}
public:
Allocator* get() const { return alloc_; }
value_type* allocate(std::size_t n) {
return fmt::internal::allocate(*alloc_, n);
return std::allocator_traits<Allocator>::allocate(*alloc_, n);
}
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
};

View File

@ -5,6 +5,22 @@
//
// For the license information refer to format.h.
#define FMT_STRING_ALIAS 1
#include "fmt/format.h"
struct test {};
// Test that there is no issues with specializations when fmt/ostream.h is
// included after fmt/format.h.
namespace fmt {
template <> struct formatter<test> : formatter<int> {
template <typename FormatContext>
typename FormatContext::iterator format(const test&, FormatContext& ctx) {
return formatter<int>::format(42, ctx);
}
};
} // namespace fmt
#include "fmt/ostream.h"
#include <sstream>
@ -15,51 +31,56 @@
using fmt::format;
using fmt::format_error;
static std::ostream &operator<<(std::ostream &os, const Date &d) {
static std::ostream& operator<<(std::ostream& os, const Date& d) {
os << d.year() << '-' << d.month() << '-' << d.day();
return os;
}
static std::wostream &operator<<(std::wostream &os, const Date &d) {
static std::wostream& operator<<(std::wostream& os, const Date& d) {
os << d.year() << L'-' << d.month() << L'-' << d.day();
return os;
}
enum TestEnum {};
static std::ostream &operator<<(std::ostream &os, TestEnum) {
return os << "TestEnum";
// Make sure that overloaded comma operators do no harm to is_streamable.
struct type_with_comma_op {};
template <typename T> void operator,(type_with_comma_op, const T&);
template <typename T> type_with_comma_op operator<<(T&, const Date&);
enum streamable_enum {};
static std::ostream& operator<<(std::ostream& os, streamable_enum) {
return os << "streamable_enum";
}
static std::wostream &operator<<(std::wostream &os, TestEnum) {
return os << L"TestEnum";
static std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"streamable_enum";
}
enum TestEnum2 {A};
enum unstreamable_enum {};
TEST(OStreamTest, Enum) {
EXPECT_FALSE((fmt::internal::convert_to_int<TestEnum, char>::value));
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
EXPECT_FALSE((fmt::internal::convert_to_int<TestEnum, wchar_t>::value));
EXPECT_EQ(L"TestEnum", fmt::format(L"{}", TestEnum()));
EXPECT_EQ(L"0", fmt::format(L"{}", A));
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
}
typedef fmt::back_insert_range<fmt::internal::buffer> range;
using range = fmt::buffer_range<char>;
struct test_arg_formatter: fmt::arg_formatter<range> {
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
: fmt::arg_formatter<range>(ctx, s) {}
struct test_arg_formatter : fmt::arg_formatter<range> {
fmt::format_parse_context parse_ctx;
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
: fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {}
};
TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer;
fmt::internal::buffer &base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
fmt::internal::buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
fmt::format_specs spec;
test_arg_formatter af(ctx, spec);
visit(af, fmt::internal::make_arg<fmt::format_context>(TestEnum()));
EXPECT_EQ("TestEnum", std::string(buffer.data(), buffer.size()));
fmt::visit_format_arg(
af, fmt::internal::make_arg<fmt::format_context>(streamable_enum()));
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
}
TEST(OStreamTest, Format) {
@ -74,20 +95,22 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
format_error, "format specifier requires numeric argument");
#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, "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
@ -95,7 +118,7 @@ TEST(OStreamTest, FormatSpecs) {
}
struct EmptyTest {};
static std::ostream &operator<<(std::ostream &os, EmptyTest) {
static std::ostream& operator<<(std::ostream& os, EmptyTest) {
return os << "";
}
@ -115,42 +138,40 @@ TEST(OStreamTest, Print) {
TEST(OStreamTest, WriteToOStream) {
std::ostringstream os;
fmt::memory_buffer buffer;
const char *foo = "foo";
const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo));
fmt::internal::write(os, buffer);
EXPECT_EQ("foo", os.str());
}
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();
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
return;
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 {
struct test_buffer : fmt::internal::buffer<char> {
explicit test_buffer(std::size_t size) { resize(size); }
void grow(std::size_t) {}
} buffer(max_size);
struct mock_streambuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
std::streamsize xsputn(const char *s, std::streamsize n) {
const void *v = s;
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
std::streamsize xsputn(const char* s, std::streamsize n) {
const void* v = s;
return xsputn(v, n);
}
} streambuf;
struct test_ostream : std::ostream {
explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {}
explicit test_ostream(mock_streambuf& buffer) : std::ostream(&buffer) {}
} os(streambuf);
testing::InSequence sequence;
const char *data = nullptr;
std::size_t size = max_size;
const char* data = nullptr;
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize size = max_size;
do {
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize n = std::min<ustreamsize>(
size, fmt::internal::to_unsigned(max_streamsize));
auto n = std::min(size, fmt::internal::to_unsigned(max_streamsize));
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
@ -167,5 +188,70 @@ 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")));
}
#endif
namespace fmt_test {
struct ABC {};
template <typename Output> Output& operator<<(Output& out, ABC) {
out << "ABC";
return out;
}
} // namespace fmt_test
template <typename T> struct TestTemplate {};
template <typename T>
std::ostream& operator<<(std::ostream& os, TestTemplate<T>) {
return os << 1;
}
namespace fmt {
template <typename T> struct formatter<TestTemplate<T>> : formatter<int> {
template <typename FormatContext>
typename FormatContext::iterator format(TestTemplate<T>, FormatContext& ctx) {
return formatter<int>::format(2, ctx);
}
};
} // namespace fmt
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407
TEST(OStreamTest, Template) {
EXPECT_EQ("2", fmt::format("{}", TestTemplate<int>()));
}
TEST(FormatTest, FormatToN) {
char buffer[4];
buffer[3] = 'x';
auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::ABC());
EXPECT_EQ(3u, result.size);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("ABCx", fmt::string_view(buffer, 4));
result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::ABC());
EXPECT_EQ(5u, result.size);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
}
#endif
#if FMT_USE_USER_DEFINED_LITERALS
TEST(FormatTest, UDL) {
using namespace fmt::literals;
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")));
}

View File

@ -6,8 +6,8 @@
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#ifdef _MSC_VER
# define _CRT_SECURE_NO_WARNINGS
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "posix-mock.h"
@ -16,11 +16,12 @@
#include <errno.h>
#include <fcntl.h>
#include <climits>
#include <memory>
#ifdef _WIN32
# include <io.h>
# undef max
# undef ERROR
# include <io.h>
# undef max
# undef ERROR
#endif
#include "gmock.h"
@ -31,10 +32,9 @@ using fmt::buffered_file;
using fmt::error_code;
using fmt::file;
using testing::internal::scoped_ptr;
using testing::_;
using testing::StrEq;
using testing::Return;
using testing::StrEq;
namespace {
int open_count;
@ -53,24 +53,24 @@ std::size_t write_nbyte;
bool sysconf_error;
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
}
} // namespace
#define EMULATE_EINTR(func, error_result) \
if (func##_count != 0) { \
if (func##_count++ != 3) { \
errno = EINTR; \
return error_result; \
} \
if (func##_count != 0) { \
if (func##_count++ != 3) { \
errno = EINTR; \
return error_result; \
} \
}
#ifndef _MSC_VER
int test::open(const char *path, int oflag, int mode) {
int test::open(const char* path, int oflag, int mode) {
EMULATE_EINTR(open, -1);
return ::open(path, oflag, mode);
}
#else
errno_t test::sopen_s(
int* pfh, const char *filename, int oflag, int shflag, int pmode) {
errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
int pmode) {
EMULATE_EINTR(open, EINTR);
return _sopen_s(pfh, filename, oflag, shflag, pmode);
}
@ -80,8 +80,7 @@ errno_t test::sopen_s(
long test::sysconf(int name) {
long result = ::sysconf(name);
if (!sysconf_error)
return result;
if (!sysconf_error) return result;
// Simulate an error.
errno = EINVAL;
return -1;
@ -89,10 +88,9 @@ long test::sysconf(int name) {
static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
int test::fstat(int fd, struct stat *buf) {
int test::fstat(int fd, struct stat* buf) {
int result = ::fstat(fd, buf);
if (fstat_sim == MAX_SIZE)
buf->st_size = max_file_size();
if (fstat_sim == MAX_SIZE) buf->st_size = max_file_size();
return result;
}
@ -132,18 +130,18 @@ int test::dup2(int fildes, int fildes2) {
return ::FMT_POSIX(dup2(fildes, fildes2));
}
FILE *test::fdopen(int fildes, const char *mode) {
FILE* test::fdopen(int fildes, const char* mode) {
EMULATE_EINTR(fdopen, nullptr);
return ::FMT_POSIX(fdopen(fildes, mode));
}
test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) {
test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) {
read_nbyte = nbyte;
EMULATE_EINTR(read, -1);
return ::FMT_POSIX(read(fildes, buf, nbyte));
}
test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) {
test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) {
write_nbyte = nbyte;
EMULATE_EINTR(write, -1);
return ::FMT_POSIX(write(fildes, buf, nbyte));
@ -155,23 +153,23 @@ int test::pipe(int fildes[2]) {
return ::pipe(fildes);
}
#else
int test::pipe(int *pfds, unsigned psize, int textmode) {
int test::pipe(int* pfds, unsigned psize, int textmode) {
EMULATE_EINTR(pipe, -1);
return _pipe(pfds, psize, textmode);
}
#endif
FILE *test::fopen(const char *filename, const char *mode) {
FILE* test::fopen(const char* filename, const char* mode) {
EMULATE_EINTR(fopen, nullptr);
return ::fopen(filename, mode);
}
int test::fclose(FILE *stream) {
int test::fclose(FILE* stream) {
EMULATE_EINTR(fclose, EOF);
return ::fclose(stream);
}
int (test::fileno)(FILE *stream) {
int(test::fileno)(FILE* stream) {
EMULATE_EINTR(fileno, -1);
#ifdef fileno
return FMT_POSIX(fileno(stream));
@ -181,18 +179,18 @@ int (test::fileno)(FILE *stream) {
}
#ifndef _WIN32
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
statement; \
EXPECT_EQ(4, func##_count); \
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
statement; \
EXPECT_EQ(4, func##_count); \
func##_count = 0;
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
#else
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
# define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \
EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
func##_count = 0;
# define EXPECT_EQ_POSIX(expected, actual)
# define EXPECT_EQ_POSIX(expected, actual)
#endif
static void write_file(fmt::cstring_view filename, fmt::string_view content) {
@ -208,17 +206,17 @@ TEST(UtilTest, GetPageSize) {
#else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true;
EXPECT_SYSTEM_ERROR(
fmt::getpagesize(), EINVAL, "cannot get memory page size");
EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
"cannot get memory page size");
sysconf_error = false;
#endif
}
TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here");
scoped_ptr<file> f{nullptr};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)),
open, "cannot open file test");
std::unique_ptr<file> f{nullptr};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
"cannot open file test");
#ifndef _WIN32
char c = 0;
f->read(&c, 1);
@ -228,14 +226,16 @@ TEST(FileTest, OpenRetry) {
TEST(FileTest, CloseNoRetryInDtor) {
file read_end, write_end;
file::pipe(read_end, write_end);
scoped_ptr<file> f(new file(std::move(read_end)));
std::unique_ptr<file> f(new file(std::move(read_end)));
int saved_close_count = 0;
EXPECT_WRITE(stderr, {
close_count = 1;
f.reset(nullptr);
saved_close_count = close_count;
close_count = 0;
}, format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_WRITE(stderr,
{
close_count = 1;
f.reset(nullptr);
saved_close_count = close_count;
close_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_close_count);
}
@ -256,8 +256,8 @@ TEST(FileTest, Size) {
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
#ifdef _WIN32
fmt::memory_buffer message;
fmt::internal::format_windows_error(
message, ERROR_ACCESS_DENIED, "cannot get file size");
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
"cannot get file size");
fstat_sim = ERROR;
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
fstat_sim = NONE;
@ -284,8 +284,8 @@ TEST(FileTest, ReadRetry) {
write_end.close();
char buffer[SIZE];
std::size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE),
read, "cannot read from file");
EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
"cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
}
@ -294,8 +294,8 @@ TEST(FileTest, WriteRetry) {
file::pipe(read_end, write_end);
enum { SIZE = 4 };
std::size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE),
write, "cannot write to file");
EXPECT_RETRY(count = write_end.write("test", SIZE), write,
"cannot write to file");
write_end.close();
#ifndef _WIN32
EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
@ -312,8 +312,7 @@ TEST(FileTest, ConvertReadCount) {
file::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t))
++size;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
read_count = 1;
read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
@ -326,8 +325,7 @@ TEST(FileTest, ConvertWriteCount) {
file::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t))
++size;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
write_count = 1;
write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
@ -339,7 +337,8 @@ TEST(FileTest, ConvertWriteCount) {
TEST(FileTest, DupNoRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout));
dup_count = 1;
EXPECT_SYSTEM_ERROR(file::dup(stdout_fd), EINTR,
EXPECT_SYSTEM_ERROR(
file::dup(stdout_fd), EINTR,
fmt::format("cannot duplicate file descriptor {}", stdout_fd));
dup_count = 0;
}
@ -348,8 +347,8 @@ TEST(FileTest, Dup2Retry) {
int stdout_fd = FMT_POSIX(fileno(stdout));
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor()));
fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor()));
}
TEST(FileTest, Dup2NoExceptRetry) {
@ -369,8 +368,8 @@ TEST(FileTest, Dup2NoExceptRetry) {
TEST(FileTest, PipeNoRetry) {
file read_end, write_end;
pipe_count = 1;
EXPECT_SYSTEM_ERROR(
file::pipe(read_end, write_end), EINTR, "cannot create pipe");
EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
"cannot create pipe");
pipe_count = 0;
}
@ -378,16 +377,16 @@ TEST(FileTest, FdopenNoRetry) {
file read_end, write_end;
file::pipe(read_end, write_end);
fdopen_count = 1;
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
EINTR, "cannot associate stream with file descriptor");
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR,
"cannot associate stream with file descriptor");
fdopen_count = 0;
}
TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here");
scoped_ptr<buffered_file> f{nullptr};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")),
fopen, "cannot open file test");
std::unique_ptr<buffered_file> f{nullptr};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
"cannot open file test");
#ifndef _WIN32
char c = 0;
if (fread(&c, 1, 1, f->get()) < 1)
@ -398,14 +397,16 @@ TEST(BufferedFileTest, OpenRetry) {
TEST(BufferedFileTest, CloseNoRetryInDtor) {
file read_end, write_end;
file::pipe(read_end, write_end);
scoped_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
int saved_fclose_count = 0;
EXPECT_WRITE(stderr, {
fclose_count = 1;
f.reset(nullptr);
saved_fclose_count = fclose_count;
fclose_count = 0;
}, format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_WRITE(stderr,
{
fclose_count = 1;
f.reset(nullptr);
saved_fclose_count = fclose_count;
fclose_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_fclose_count);
}
@ -430,14 +431,14 @@ TEST(BufferedFileTest, FilenoNoRetry) {
}
struct TestMock {
static TestMock *instance;
} *TestMock::instance;
static TestMock* instance;
} * TestMock::instance;
TEST(ScopedMock, Scope) {
{
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
TestMock &copy = mock;
TestMock& copy = mock;
static_cast<void>(copy);
}
EXPECT_EQ(nullptr, TestMock::instance);
@ -445,23 +446,27 @@ TEST(ScopedMock, Scope) {
#ifdef FMT_LOCALE
typedef fmt::Locale::Type LocaleType;
typedef fmt::Locale::type LocaleType;
struct LocaleMock {
static LocaleMock *instance;
MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale,
LocaleType base));
MOCK_METHOD1(freelocale, void (LocaleType locale));
static LocaleMock* instance;
MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
LocaleType base));
MOCK_METHOD1(freelocale, void(LocaleType locale));
MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr,
LocaleType locale));
} *LocaleMock::instance;
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, LocaleType locale));
} * LocaleMock::instance;
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4273)
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4273)
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Winconsistent-dllimport"
# endif
_locale_t _create_locale(int category, const char *locale) {
_locale_t _create_locale(int category, const char* locale) {
return LocaleMock::instance->newlocale(category, locale, 0);
}
@ -469,38 +474,44 @@ void _free_locale(_locale_t locale) {
LocaleMock::instance->freelocale(locale);
}
double _strtod_l(const char *nptr, char **endptr, _locale_t locale) {
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
}
# pragma warning(pop)
#endif
# ifdef __clang__
# pragma clang diagnostic pop
# endif
# pragma warning(pop)
# endif
#if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
#define FMT_LOCALE_THROW __THROW
#else
#define FMT_LOCALE_THROW
#endif
# if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
# define FMT_LOCALE_THROW __THROW
# else
# define FMT_LOCALE_THROW
# endif
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) FMT_LOCALE_THROW {
LocaleType newlocale(int category_mask, const char* locale,
LocaleType base) FMT_LOCALE_THROW {
return LocaleMock::instance->newlocale(category_mask, locale, base);
}
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1200002)
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult;
#else
# else
typedef void FreeLocaleResult;
#endif
# endif
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
LocaleMock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char *nptr, char **endptr, LocaleType locale) FMT_LOCALE_THROW {
double strtod_l(const char* nptr, char** endptr,
LocaleType locale) FMT_LOCALE_THROW {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
}
#undef FMT_LOCALE_THROW
# undef FMT_LOCALE_THROW
TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock;
@ -510,9 +521,9 @@ TEST(LocaleTest, LocaleMock) {
}
TEST(LocaleTest, Locale) {
#ifndef LC_NUMERIC_MASK
# ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
#endif
# endif
ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
@ -528,7 +539,7 @@ TEST(LocaleTest, Strtod) {
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
const char *str = "4.2";
const char* str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));

View File

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

View File

@ -7,21 +7,20 @@
#include <cstdlib> // std::exit
#include <cstring>
#include <memory>
#include "fmt/posix.h"
#include "gtest-extra.h"
#include "util.h"
#ifdef fileno
# undef fileno
# undef fileno
#endif
using fmt::buffered_file;
using fmt::error_code;
using fmt::file;
using testing::internal::scoped_ptr;
// Checks if the file is open by reading one character from it.
static bool isopen(int fd) {
char buffer;
@ -45,9 +44,9 @@ static file open_file() {
}
// Attempts to write a string to a file.
static void write(file &f, fmt::string_view s) {
static void write(file& f, fmt::string_view s) {
std::size_t num_chars_left = s.size();
const char *ptr = s.data();
const char* ptr = s.data();
do {
std::size_t count = f.write(ptr, num_chars_left);
ptr += count;
@ -64,7 +63,7 @@ TEST(BufferedFileTest, DefaultCtor) {
TEST(BufferedFileTest, MoveCtor) {
buffered_file bf = open_buffered_file();
FILE *fp = bf.get();
FILE* fp = bf.get();
EXPECT_TRUE(fp != nullptr);
buffered_file bf2(std::move(bf));
EXPECT_EQ(fp, bf2.get());
@ -73,7 +72,7 @@ TEST(BufferedFileTest, MoveCtor) {
TEST(BufferedFileTest, MoveAssignment) {
buffered_file bf = open_buffered_file();
FILE *fp = bf.get();
FILE* fp = bf.get();
EXPECT_TRUE(fp != nullptr);
buffered_file bf2;
bf2 = std::move(bf);
@ -90,13 +89,13 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
}
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = nullptr;
FILE* fp = nullptr;
buffered_file f(open_buffered_file(&fp));
EXPECT_EQ(fp, f.get());
}
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
FILE *fp = nullptr;
FILE* fp = nullptr;
buffered_file f;
f = open_buffered_file(&fp);
EXPECT_EQ(fp, f.get());
@ -119,15 +118,17 @@ TEST(BufferedFileTest, CloseFileInDtor) {
}
TEST(BufferedFileTest, CloseErrorInDtor) {
scoped_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the
// output in EXPECT_STDERR and the second close will break output
// redirection.
FMT_POSIX(close(f->fileno()));
SUPPRESS_ASSERT(f.reset(nullptr));
}, format_system_error(EBADF, "cannot close file") + "\n");
std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr,
{
// The close function must be called inside EXPECT_WRITE,
// otherwise the system may recycle closed file descriptor when
// redirecting the output in EXPECT_STDERR and the second close
// will break output redirection.
FMT_POSIX(close(f->fileno()));
SUPPRESS_ASSERT(f.reset(nullptr));
},
format_system_error(EBADF, "cannot close file") + "\n");
}
TEST(BufferedFileTest, Close) {
@ -150,13 +151,15 @@ TEST(BufferedFileTest, Fileno) {
#ifndef __COVERITY__
// fileno on a null FILE pointer either crashes or returns an error.
// Disable Coverity because this is intentional.
EXPECT_DEATH_IF_SUPPORTED({
try {
f.fileno();
} catch (const fmt::system_error&) {
std::exit(1);
}
}, "");
EXPECT_DEATH_IF_SUPPORTED(
{
try {
f.fileno();
} catch (const fmt::system_error&) {
std::exit(1);
}
},
"");
#endif
f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1);
@ -170,7 +173,7 @@ TEST(FileTest, DefaultCtor) {
}
TEST(FileTest, OpenBufferedFileInCtor) {
FILE *fp = safe_fopen("test-file", "w");
FILE* fp = safe_fopen("test-file", "w");
std::fputs(FILE_CONTENT, fp);
std::fclose(fp);
file f("test-file", file::RDONLY);
@ -178,8 +181,8 @@ TEST(FileTest, OpenBufferedFileInCtor) {
}
TEST(FileTest, OpenBufferedFileError) {
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY),
ENOENT, "cannot open file nonexistent");
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
"cannot open file nonexistent");
}
TEST(FileTest, MoveCtor) {
@ -209,7 +212,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
EXPECT_TRUE(isclosed(old_fd));
}
static file OpenBufferedFile(int &fd) {
static file OpenBufferedFile(int& fd) {
file f = open_file();
fd = f.descriptor();
return f;
@ -246,15 +249,17 @@ TEST(FileTest, CloseFileInDtor) {
}
TEST(FileTest, CloseErrorInDtor) {
scoped_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the
// output in EXPECT_STDERR and the second close will break output
// redirection.
FMT_POSIX(close(f->descriptor()));
SUPPRESS_ASSERT(f.reset(nullptr));
}, format_system_error(EBADF, "cannot close file") + "\n");
std::unique_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr,
{
// The close function must be called inside EXPECT_WRITE,
// otherwise the system may recycle closed file descriptor when
// redirecting the output in EXPECT_STDERR and the second close
// will break output redirection.
FMT_POSIX(close(f->descriptor()));
SUPPRESS_ASSERT(f.reset(nullptr));
},
format_system_error(EBADF, "cannot close file") + "\n");
}
TEST(FileTest, Close) {
@ -310,8 +315,8 @@ TEST(FileTest, Dup) {
#ifndef __COVERITY__
TEST(FileTest, DupError) {
int value = -1;
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value),
EBADF, "cannot duplicate file descriptor -1");
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
"cannot duplicate file descriptor -1");
}
#endif
@ -325,8 +330,9 @@ TEST(FileTest, Dup2) {
TEST(FileTest, Dup2Error) {
file f = open_file();
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
EXPECT_SYSTEM_ERROR_NOASSERT(
f.dup2(-1), EBADF,
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
}
TEST(FileTest, Dup2NoExcept) {
@ -334,7 +340,7 @@ TEST(FileTest, Dup2NoExcept) {
file copy = open_file();
error_code ec;
f.dup2(copy.descriptor(), ec);
EXPECT_EQ(0, ec.get());
EXPECT_EQ(ec.get(), 0);
EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_READ(copy, FILE_CONTENT);
}
@ -362,12 +368,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;

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;
@ -32,22 +33,33 @@ static std::wstring make_positional(fmt::wstring_view format) {
return s;
}
#define EXPECT_PRINTF(expected_output, format, arg) \
EXPECT_EQ(expected_output, fmt::sprintf(format, arg)) \
<< "format: " << format; \
// A wrapper around fmt::sprintf to workaround bogus warnings about invalid
// format strings in MSVC.
template <typename... Args>
std::string test_sprintf(fmt::string_view format, const Args&... args) {
return fmt::sprintf(format, args...);
}
template <typename... Args>
std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) {
return fmt::sprintf(format, args...);
}
#define EXPECT_PRINTF(expected_output, format, arg) \
EXPECT_EQ(expected_output, test_sprintf(format, arg)) \
<< "format: " << format; \
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
TEST(PrintfTest, NoArgs) {
EXPECT_EQ("test", fmt::sprintf("test"));
EXPECT_EQ("test", test_sprintf("test"));
EXPECT_EQ(L"test", fmt::sprintf(L"test"));
}
TEST(PrintfTest, Escape) {
EXPECT_EQ("%", fmt::sprintf("%%"));
EXPECT_EQ("before %", fmt::sprintf("before %%"));
EXPECT_EQ("% after", fmt::sprintf("%% after"));
EXPECT_EQ("before % after", fmt::sprintf("before %% after"));
EXPECT_EQ("%s", fmt::sprintf("%%s"));
EXPECT_EQ("%", test_sprintf("%%"));
EXPECT_EQ("before %", test_sprintf("before %%"));
EXPECT_EQ("% after", test_sprintf("%% after"));
EXPECT_EQ("before % after", test_sprintf("before %% after"));
EXPECT_EQ("%s", test_sprintf("%%s"));
EXPECT_EQ(L"%", fmt::sprintf(L"%%"));
EXPECT_EQ(L"before %", fmt::sprintf(L"before %%"));
EXPECT_EQ(L"% after", fmt::sprintf(L"%% after"));
@ -56,61 +68,60 @@ TEST(PrintfTest, Escape) {
}
TEST(PrintfTest, PositionalArgs) {
EXPECT_EQ("42", fmt::sprintf("%1$d", 42));
EXPECT_EQ("before 42", fmt::sprintf("before %1$d", 42));
EXPECT_EQ("42 after", fmt::sprintf("%1$d after",42));
EXPECT_EQ("before 42 after", fmt::sprintf("before %1$d after", 42));
EXPECT_EQ("answer = 42", fmt::sprintf("%1$s = %2$d", "answer", 42));
EXPECT_EQ("42 is the answer",
fmt::sprintf("%2$d is the %1$s", "answer", 42));
EXPECT_EQ("abracadabra", fmt::sprintf("%1$s%2$s%1$s", "abra", "cad"));
EXPECT_EQ("42", test_sprintf("%1$d", 42));
EXPECT_EQ("before 42", test_sprintf("before %1$d", 42));
EXPECT_EQ("42 after", test_sprintf("%1$d after", 42));
EXPECT_EQ("before 42 after", test_sprintf("before %1$d after", 42));
EXPECT_EQ("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42));
EXPECT_EQ("42 is the answer", test_sprintf("%2$d is the %1$s", "answer", 42));
EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
}
TEST(PrintfTest, AutomaticArgIndexing) {
EXPECT_EQ("abc", fmt::sprintf("%c%c%c", 'a', 'b', 'c'));
EXPECT_EQ("abc", test_sprintf("%c%c%c", 'a', 'b', 'c'));
}
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$", BIG_NUM)),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM)),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), format_error,
"number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), format_error,
"number is too big");
}
TEST(PrintfTest, SwitchArgIndexing) {
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2),
format_error, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2),
format_error, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2),
format_error, "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2),
format_error, "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), format_error,
"number is too big");
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error,
"cannot switch from automatic to manual argument indexing");
// Indexing errors override width errors.
EXPECT_THROW_MSG(fmt::sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big");
}
TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(fmt::sprintf("%0$d", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%2$d", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", INT_MAX), 42),
format_error, "argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%2$", 42),
format_error, "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM), 42),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
"number is too big");
}
TEST(PrintfTest, DefaultAlignRight) {
@ -164,8 +175,8 @@ TEST(PrintfTest, HashFlag) {
EXPECT_PRINTF("0x42", "%#x", 0x42);
EXPECT_PRINTF("0X42", "%#X", 0x42);
EXPECT_PRINTF(
fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x", -0x42);
EXPECT_PRINTF(fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x",
-0x42);
EXPECT_PRINTF("0", "%#x", 0);
EXPECT_PRINTF("0x0042", "%#06x", 0x42);
@ -196,24 +207,24 @@ TEST(PrintfTest, Width) {
EXPECT_PRINTF(" abc", "%5s", "abc");
// Width cannot be specified twice.
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), format_error,
"invalid type specifier");
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
"invalid type specifier");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}d", BIG_NUM), 42),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf(format("%1${}d", BIG_NUM), 42),
format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), format_error,
"number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), format_error,
"number is too big");
}
TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ(" 42", fmt::sprintf("%*d", 5, 42));
EXPECT_EQ("42 ", fmt::sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), format_error,
"width is not integer");
EXPECT_THROW_MSG(fmt::sprintf("%*d"), format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big");
EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42));
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
"width is not integer");
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big");
}
TEST(PrintfTest, IntPrecision) {
@ -242,8 +253,7 @@ TEST(PrintfTest, FloatPrecision) {
safe_sprintf(buffer, "%.3e", 1234.5678);
EXPECT_PRINTF(buffer, "%.3e", 1234.5678);
EXPECT_PRINTF("1234.568", "%.3f", 1234.5678);
safe_sprintf(buffer, "%.3g", 1234.5678);
EXPECT_PRINTF(buffer, "%.3g", 1234.5678);
EXPECT_PRINTF("1.23e+03", "%.3g", 1234.5678);
safe_sprintf(buffer, "%.3a", 1234.5678);
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
}
@ -253,27 +263,25 @@ TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
}
TEST(PrintfTest, DynamicPrecision) {
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer");
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big");
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big");
if (sizeof(long long) != sizeof(int)) {
long long prec = static_cast<long long>(INT_MIN) - 1;
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), format_error,
"number is too big");
}
EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error,
"number is too big");
}
}
template <typename T>
struct make_signed { typedef T type; };
template <typename T> struct make_signed { typedef T type; };
#define SPECIALIZE_MAKE_SIGNED(T, S) \
template <> \
struct make_signed<T> { typedef S type; }
template <> struct make_signed<T> { typedef S type; }
SPECIALIZE_MAKE_SIGNED(char, signed char);
SPECIALIZE_MAKE_SIGNED(unsigned char, signed char);
@ -284,17 +292,16 @@ SPECIALIZE_MAKE_SIGNED(unsigned long long, long long);
// Test length format specifier ``length_spec``.
template <typename T, typename U>
void TestLength(const char *length_spec, U value) {
void TestLength(const char* length_spec, U value) {
long long signed_value = 0;
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);
}
@ -324,27 +331,26 @@ void TestLength(const char *length_spec, U value) {
EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value);
}
template <typename T>
void TestLength(const char *length_spec) {
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
template <typename T> void TestLength(const char* length_spec) {
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();
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) {
@ -360,9 +366,9 @@ 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();
EXPECT_PRINTF(fmt::format("{}", max), "%g", max);
EXPECT_PRINTF(fmt::format("{}", max), "%Lg", max);
long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
}
TEST(PrintfTest, Bool) {
@ -383,7 +389,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);
}
@ -400,6 +406,8 @@ TEST(PrintfTest, Float) {
EXPECT_PRINTF(buffer, "%E", 392.65);
EXPECT_PRINTF("392.65", "%g", 392.65);
EXPECT_PRINTF("392.65", "%G", 392.65);
EXPECT_PRINTF("392", "%g", 392.0);
EXPECT_PRINTF("392", "%G", 392.0);
safe_sprintf(buffer, "%a", -392.65);
EXPECT_EQ(buffer, format("{:a}", -392.65));
safe_sprintf(buffer, "%A", -392.65);
@ -417,34 +425,34 @@ 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("x", "%lc", L'x');
EXPECT_PRINTF(L"x", L"%c", L'x');
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
}
TEST(PrintfTest, String) {
EXPECT_PRINTF("abc", "%s", "abc");
const char *null_str = nullptr;
const char* null_str = nullptr;
EXPECT_PRINTF("(null)", "%s", null_str);
EXPECT_PRINTF(" (null)", "%10s", null_str);
EXPECT_PRINTF(L"abc", L"%s", L"abc");
const wchar_t *null_wstr = nullptr;
const wchar_t* null_wstr = nullptr;
EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
}
TEST(PrintfTest, Pointer) {
int n;
void *p = &n;
void* p = &n;
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
p = nullptr;
EXPECT_PRINTF("(nil)", "%p", p);
EXPECT_PRINTF(" (nil)", "%10p", p);
const char *s = "test";
const char* s = "test";
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
const char *null_str = nullptr;
const char* null_str = nullptr;
EXPECT_PRINTF("(nil)", "%p", null_str);
p = &n;
@ -452,9 +460,9 @@ TEST(PrintfTest, Pointer) {
p = nullptr;
EXPECT_PRINTF(L"(nil)", L"%p", p);
EXPECT_PRINTF(L" (nil)", L"%10p", p);
const wchar_t *w = L"test";
const wchar_t* w = L"test";
EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w);
const wchar_t *null_wstr = nullptr;
const wchar_t* null_wstr = nullptr;
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
}
@ -464,14 +472,12 @@ TEST(PrintfTest, Location) {
enum E { A = 42 };
TEST(PrintfTest, Enum) {
EXPECT_PRINTF("42", "%d", A);
}
TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
#if FMT_USE_FILE_DESCRIPTORS
TEST(PrintfTest, Examples) {
const char *weekday = "Thursday";
const char *month = "August";
const char* weekday = "Thursday";
const char* month = "August";
int day = 21;
EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day),
"Thursday, 21 August");
@ -485,12 +491,10 @@ TEST(PrintfTest, PrintfError) {
}
#endif
TEST(PrintfTest, WideString) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
TEST(PrintfTest, PrintfCustom) {
EXPECT_EQ("abc", fmt::sprintf("%s", TestString("abc")));
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
}
TEST(PrintfTest, OStream) {
@ -499,3 +503,109 @@ TEST(PrintfTest, OStream) {
EXPECT_EQ("Don't panic!", os.str());
EXPECT_EQ(12, ret);
}
TEST(PrintfTest, VPrintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as);
EXPECT_EQ(fmt::vsprintf("%d", args), "42");
EXPECT_WRITE(stdout, fmt::vprintf("%d", args), "42");
EXPECT_WRITE(stdout, fmt::vfprintf(stdout, "%d", args), "42");
EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42");
}
template <typename... Args>
void check_format_string_regression(fmt::string_view s, const Args&... args) {
fmt::sprintf(s, args...);
}
TEST(PrintfTest, CheckFormatStringRegression) {
check_format_string_regression("%c%s", 'x', "");
}
TEST(PrintfTest, FixedLargeExponent) {
EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21));
}
TEST(PrintfTest, VSPrintfMakeArgsExample) {
fmt::format_arg_store<fmt::printf_context, int, const char*> as{42,
"something"};
fmt::basic_format_args<fmt::printf_context> args(as);
EXPECT_EQ("[42] something happened", fmt::vsprintf("[%d] %s happened", args));
auto as2 = fmt::make_printf_args(42, "something");
fmt::basic_format_args<fmt::printf_context> args2(as2);
EXPECT_EQ("[42] something happened",
fmt::vsprintf("[%d] %s happened", args2));
// The older gcc versions can't cast the return value.
#if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ("[42] something happened",
fmt::vsprintf("[%d] %s happened",
{fmt::make_printf_args(42, "something")}));
#endif
}
TEST(PrintfTest, VSPrintfMakeWArgsExample) {
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{
42, L"something"};
fmt::basic_format_args<fmt::wprintf_context> args(as);
EXPECT_EQ(L"[42] something happened",
fmt::vsprintf(L"[%d] %s happened", args));
auto as2 = fmt::make_wprintf_args(42, L"something");
fmt::basic_format_args<fmt::wprintf_context> args2(as2);
EXPECT_EQ(L"[42] something happened",
fmt::vsprintf(L"[%d] %s happened", args2));
// the older gcc versions can't cast the return value
#if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ(L"[42] something happened",
fmt::vsprintf(L"[%d] %s happened",
{fmt::make_wprintf_args(42, L"something")}));
#endif
}
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t;
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
// A custom printf argument formatter that doesn't print `-` for floating-point
// values rounded to 0.
class custom_printf_arg_formatter : public formatter_t {
public:
using formatter_t::iterator;
custom_printf_arg_formatter(formatter_t::iterator iter,
formatter_t::format_specs& specs, context_t& ctx)
: formatter_t(iter, specs, ctx) {}
using formatter_t::operator();
#if FMT_MSC_VER > 0 && FMT_MSC_VER <= 1804
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value){
#else
iterator operator()(double value) {
#endif
// Comparing a float to 0.0 is safe.
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
return formatter_t::operator()(value);
}
}
;
typedef fmt::basic_format_args<context_t> format_args_t;
std::string custom_vformat(fmt::string_view format_str, format_args_t args) {
fmt::memory_buffer buffer;
fmt::vprintf<custom_printf_arg_formatter>(buffer, format_str, args);
return std::string(buffer.data(), buffer.size());
}
template <typename... Args>
std::string custom_format(const char* format_str, const Args&... args) {
auto va = fmt::make_printf_args(args...);
return custom_vformat(format_str, {va});
}
TEST(PrintfTest, CustomFormat) {
EXPECT_EQ("0.00", custom_format("%.2f", -.00001));
EXPECT_EQ("0.00", custom_format("%.2f", .00001));
EXPECT_EQ("1.00", custom_format("%.2f", 1.00001));
EXPECT_EQ("-1.00", custom_format("%.2f", -1.00001));
}

View File

@ -10,17 +10,16 @@
// {fmt} support for ranges, containers and types tuple interface.
#include "fmt/ranges.h"
#include "gtest.h"
/// Check if 'if constexpr' is supported.
// Check if 'if constexpr' is supported.
#if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
#include "gtest.h"
#include <vector>
#include <array>
#include <map>
#include <string>
# include <array>
# include <map>
# include <string>
# include <vector>
TEST(RangesTest, FormatVector) {
std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
@ -40,21 +39,39 @@ TEST(RangesTest, FormatMap) {
}
TEST(RangesTest, FormatPair) {
std::pair<int64_t, float> pa1{42, 3.14159265358979f};
EXPECT_EQ("(42, 3.14159)", fmt::format("{}", pa1));
std::pair<int64_t, float> pa1{42, 1.5f};
EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
}
TEST(RangesTest, FormatTuple) {
std::tuple<int64_t, float, std::string, char> tu1{42, 3.14159265358979f,
"this is tuple", 'i'};
EXPECT_EQ("(42, 3.14159, \"this is tuple\", 'i')", fmt::format("{}", tu1));
std::tuple<int64_t, float, std::string, char> tu1{42, 1.5f, "this is tuple",
'i'};
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
template <std::size_t N>
decltype(auto) get() const noexcept {
template <std::size_t N> decltype(auto) get() const noexcept {
if constexpr (N == 0)
return i;
else if constexpr (N == 1)
@ -62,8 +79,7 @@ struct my_struct {
}
};
template <std::size_t N>
decltype(auto) get(const my_struct& s) noexcept {
template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept {
return s.get<N>();
}
@ -72,8 +88,7 @@ namespace std {
template <>
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
template <std::size_t N>
struct tuple_element<N, my_struct> {
template <std::size_t N> struct tuple_element<N, my_struct> {
using type = decltype(std::declval<my_struct>().get<N>());
};
@ -84,5 +99,36 @@ TEST(RangesTest, FormatStruct) {
EXPECT_EQ("(13, \"my struct\")", fmt::format("{}", mst));
}
TEST(RangesTest, FormatTo) {
char buf[10];
auto end = fmt::format_to(buf, "{}", std::vector{1, 2, 3});
*end = '\0';
EXPECT_STREQ(buf, "{1, 2, 3}");
}
struct path_like {
const path_like* begin() const;
const path_like* end() const;
operator std::string() const;
};
TEST(RangesTest, PathLike) {
EXPECT_FALSE((fmt::is_range<path_like, char>::value));
}
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
// 201402L && _MSC_VER >= 1910)
#ifdef FMT_USE_STRING_VIEW
struct string_like {
const char* begin();
const char* end();
explicit operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; }
};
TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like()));
}
#endif // FMT_USE_STRING_VIEW

114
test/scan-test.cc Normal file
View File

@ -0,0 +1,114 @@
// Formatting library for C++ - scanning API test
//
// Copyright (c) 2019 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <time.h>
#include <climits>
#include "gmock.h"
#include "gtest-extra.h"
#include "scan.h"
TEST(ScanTest, ReadText) {
fmt::string_view s = "foo";
auto end = fmt::scan(s, "foo");
EXPECT_EQ(end, s.end());
EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input");
}
TEST(ScanTest, ReadInt) {
int n = 0;
fmt::scan("42", "{}", n);
EXPECT_EQ(n, 42);
fmt::scan("-42", "{}", n);
EXPECT_EQ(n, -42);
}
TEST(ScanTest, ReadLongLong) {
long long n = 0;
fmt::scan("42", "{}", n);
EXPECT_EQ(n, 42);
fmt::scan("-42", "{}", n);
EXPECT_EQ(n, -42);
}
TEST(ScanTest, ReadUInt) {
unsigned n = 0;
fmt::scan("42", "{}", n);
EXPECT_EQ(n, 42);
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
"invalid input");
}
TEST(ScanTest, ReadULongLong) {
unsigned long long n = 0;
fmt::scan("42", "{}", n);
EXPECT_EQ(n, 42);
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
"invalid input");
}
TEST(ScanTest, ReadString) {
std::string s;
fmt::scan("foo", "{}", s);
EXPECT_EQ(s, "foo");
}
TEST(ScanTest, ReadStringView) {
fmt::string_view s;
fmt::scan("foo", "{}", s);
EXPECT_EQ(s, "foo");
}
#ifndef _WIN32
namespace fmt {
template <> struct scanner<tm> {
std::string format;
scan_parse_context::iterator parse(scan_parse_context& ctx) {
auto it = ctx.begin();
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
format.reserve(internal::to_unsigned(end - it + 1));
format.append(it, end);
format.push_back('\0');
return end;
}
template <class ScanContext>
typename ScanContext::iterator scan(tm& t, ScanContext& ctx) {
auto result = strptime(ctx.begin(), format.c_str(), &t);
if (!result) throw format_error("failed to parse time");
return result;
}
};
} // namespace fmt
TEST(ScanTest, ReadCustom) {
const char* input = "Date: 1985-10-25";
auto t = tm();
fmt::scan(input, "Date: {0:%Y-%m-%d}", t);
EXPECT_EQ(t.tm_year, 85);
EXPECT_EQ(t.tm_mon, 9);
EXPECT_EQ(t.tm_mday, 25);
}
#endif
TEST(ScanTest, InvalidFormat) {
EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error,
"argument index out of range");
EXPECT_THROW_MSG(fmt::scan("", "{"), fmt::format_error,
"invalid format string");
}
TEST(ScanTest, Example) {
std::string key;
int value;
fmt::scan("answer = 42", "{} = {}", key, value);
EXPECT_EQ(key, "answer");
EXPECT_EQ(value, 42);
}

235
test/scan.h Normal file
View File

@ -0,0 +1,235 @@
// Formatting library for C++ - scanning API proof of concept
//
// Copyright (c) 2019 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <array>
#include "fmt/format.h"
FMT_BEGIN_NAMESPACE
template <typename T, typename Char = char> struct scanner {
// A deleted default constructor indicates a disabled scanner.
scanner() = delete;
};
class scan_parse_context {
private:
string_view format_;
public:
using iterator = string_view::iterator;
explicit FMT_CONSTEXPR scan_parse_context(string_view format)
: format_(format) {}
FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
FMT_CONSTEXPR iterator end() const { return format_.end(); }
void advance_to(iterator it) {
format_.remove_prefix(internal::to_unsigned(it - begin()));
}
};
struct scan_context {
private:
string_view input_;
public:
using iterator = const char*;
explicit scan_context(string_view input) : input_(input) {}
iterator begin() const { return input_.data(); }
iterator end() const { return begin() + input_.size(); }
void advance_to(iterator it) {
input_.remove_prefix(internal::to_unsigned(it - begin()));
}
};
namespace internal {
enum class scan_type {
none_type,
int_type,
uint_type,
long_long_type,
ulong_long_type,
string_type,
string_view_type,
custom_type
};
struct custom_scan_arg {
void* value;
void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
};
class scan_arg {
public:
scan_type type;
union {
int* int_value;
unsigned* uint_value;
long long* long_long_value;
unsigned long long* ulong_long_value;
std::string* string;
fmt::string_view* string_view;
custom_scan_arg custom;
// TODO: more types
};
scan_arg() : type(scan_type::none_type) {}
scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {}
scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {}
scan_arg(long long& value)
: type(scan_type::long_long_type), long_long_value(&value) {}
scan_arg(unsigned long long& value)
: type(scan_type::ulong_long_type), ulong_long_value(&value) {}
scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {}
scan_arg(fmt::string_view& value)
: type(scan_type::string_view_type), string_view(&value) {}
template <typename T> scan_arg(T& value) : type(scan_type::custom_type) {
custom.value = &value;
custom.scan = scan_custom_arg<T>;
}
private:
template <typename T>
static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
scan_context& ctx) {
scanner<T> s;
parse_ctx.advance_to(s.parse(parse_ctx));
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
}
};
} // namespace internal
struct scan_args {
int size;
const internal::scan_arg* data;
template <size_t N>
scan_args(const std::array<internal::scan_arg, N>& store)
: size(N), data(store.data()) {
static_assert(N < INT_MAX, "too many arguments");
}
};
namespace internal {
struct scan_handler : error_handler {
private:
scan_parse_context parse_ctx_;
scan_context scan_ctx_;
scan_args args_;
int next_arg_id_;
scan_arg arg_;
template <typename T = unsigned> T read_uint() {
T value = 0;
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
while (it != end) {
char c = *it++;
if (c < '0' || c > '9') on_error("invalid input");
// TODO: check overflow
value = value * 10 + (c - '0');
}
scan_ctx_.advance_to(it);
return value;
}
template <typename T = int> T read_int() {
T value = 0;
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
bool negative = it != end && *it == '-';
if (negative) ++it;
scan_ctx_.advance_to(it);
value = read_uint<typename std::make_unsigned<T>::type>();
if (negative) value = -value;
return value;
}
public:
scan_handler(string_view format, string_view input, scan_args args)
: parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
const char* pos() const { return scan_ctx_.begin(); }
void on_text(const char* begin, const char* end) {
auto size = end - begin;
auto it = scan_ctx_.begin();
if (it + size > scan_ctx_.end() ||
!std::equal(begin, end, make_checked(it, size))) {
on_error("invalid input");
}
scan_ctx_.advance_to(it + size);
}
void on_arg_id() { on_arg_id(next_arg_id_++); }
void on_arg_id(int id) {
if (id >= args_.size) on_error("argument index out of range");
arg_ = args_.data[id];
}
void on_arg_id(string_view) { on_error("invalid format"); }
void on_replacement_field(const char*) {
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
switch (arg_.type) {
case scan_type::int_type:
*arg_.int_value = read_int();
break;
case scan_type::uint_type:
*arg_.uint_value = read_uint();
break;
case scan_type::long_long_type:
*arg_.long_long_value = read_int<long long>();
break;
case scan_type::ulong_long_type:
*arg_.ulong_long_value = read_uint<unsigned long long>();
break;
case scan_type::string_type:
while (it != end && *it != ' ') arg_.string->push_back(*it++);
scan_ctx_.advance_to(it);
break;
case scan_type::string_view_type: {
auto s = it;
while (it != end && *it != ' ') ++it;
*arg_.string_view = fmt::string_view(s, it - s);
scan_ctx_.advance_to(it);
break;
}
default:
assert(false);
}
}
const char* on_format_specs(const char* begin, const char*) {
if (arg_.type != scan_type::custom_type) return begin;
parse_ctx_.advance_to(begin);
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
return parse_ctx_.begin();
}
};
} // namespace internal
template <typename... Args>
std::array<internal::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
return {{args...}};
}
string_view::iterator vscan(string_view input, string_view format_str,
scan_args args) {
internal::scan_handler h(format_str, input, args);
internal::parse_format_string<false>(format_str, h);
return input.begin() + (h.pos() - &*input.begin());
}
template <typename... Args>
string_view::iterator scan(string_view input, string_view format_str,
Args&... args) {
return vscan(input, format_str, make_scan_args(args...));
}
FMT_END_NAMESPACE

156
test/std-format-test.cc Normal file
View File

@ -0,0 +1,156 @@
#include <format>
#include "gtest.h"
TEST(StdFormatTest, Escaping) {
using namespace std;
string s = format("{0}-{{", 8); // s == "8-{"
EXPECT_EQ(s, "8-{");
}
TEST(StdFormatTest, Indexing) {
using namespace std;
string s0 = format("{} to {}", "a", "b"); // OK: automatic indexing
string s1 = format("{1} to {0}", "a", "b"); // OK: manual indexing
EXPECT_EQ(s0, "a to b");
EXPECT_EQ(s1, "b to a");
// Error: mixing automatic and manual indexing
EXPECT_THROW(string s2 = format("{0} to {}", "a", "b"), std::format_error);
// Error: mixing automatic and manual indexing
EXPECT_THROW(string s3 = format("{} to {1}", "a", "b"), std::format_error);
}
TEST(StdFormatTest, Alignment) {
using namespace std;
char c = 120;
string s0 = format("{:6}", 42); // s0 == " 42"
string s1 = format("{:6}", 'x'); // s1 == "x "
string s2 = format("{:*<6}", 'x'); // s2 == "x*****"
string s3 = format("{:*>6}", 'x'); // s3 == "*****x"
string s4 = format("{:*^6}", 'x'); // s4 == "**x***"
// Error: '=' with charT and no integer presentation type
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
string s6 = format("{:6d}", c); // s6 == " 120"
string s7 = format("{:6}", true); // s9 == "true "
EXPECT_EQ(s0, " 42");
EXPECT_EQ(s1, "x ");
EXPECT_EQ(s2, "x*****");
EXPECT_EQ(s3, "*****x");
EXPECT_EQ(s4, "**x***");
EXPECT_EQ(s6, " 120");
EXPECT_EQ(s7, "true ");
}
TEST(StdFormatTest, Float) {
using namespace std;
double inf = numeric_limits<double>::infinity();
double nan = numeric_limits<double>::quiet_NaN();
string s0 = format("{0:} {0:+} {0:-} {0: }", 1); // s0 == "1 +1 1 1"
string s1 = format("{0:} {0:+} {0:-} {0: }", -1); // s1 == "-1 -1 -1 -1"
string s2 =
format("{0:} {0:+} {0:-} {0: }", inf); // s2 == "inf +inf inf inf"
string s3 =
format("{0:} {0:+} {0:-} {0: }", nan); // s3 == "nan +nan nan nan"
EXPECT_EQ(s0, "1 +1 1 1");
EXPECT_EQ(s1, "-1 -1 -1 -1");
EXPECT_EQ(s2, "inf +inf inf inf");
EXPECT_EQ(s3, "nan +nan nan nan");
}
TEST(StdFormatTest, Int) {
using namespace std;
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:n}", 1234); // s3 == "1,234" (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");
}
#include <format>
enum color { red, green, blue };
const char* color_names[] = {"red", "green", "blue"};
template <> struct std::formatter<color> : std::formatter<const char*> {
auto format(color c, format_context& ctx) {
return formatter<const char*>::format(color_names[c], ctx);
}
};
struct err {};
TEST(StdFormatTest, Formatter) {
std::string s0 = std::format("{}", 42); // OK: library-provided formatter
// std::string s1 = std::format("{}", L"foo"); // Ill-formed: disabled
// formatter
std::string s2 = std::format("{}", red); // OK: user-provided formatter
// std::string s3 = std::format("{}", err{}); // Ill-formed: disabled
// formatter
EXPECT_EQ(s0, "42");
EXPECT_EQ(s2, "red");
}
struct S {
int value;
};
template <> struct std::formatter<S> {
size_t width_arg_id = 0;
// Parses a width argument id in the format { <digit> }.
constexpr auto parse(format_parse_context& ctx) {
auto iter = ctx.begin();
// auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; };
auto get_char = [&]() { return iter != ctx.end() ? *iter : '\0'; };
if (get_char() != '{') return iter;
++iter;
char c = get_char();
if (!isdigit(c) || (++iter, get_char()) != '}')
throw format_error("invalid format");
width_arg_id = c - '0';
ctx.check_arg_id(width_arg_id);
return ++iter;
}
// Formats S with width given by the argument width_arg_id.
auto format(S s, format_context& ctx) {
int width = visit_format_arg(
[](auto value) -> int {
using type = decltype(value);
if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
throw format_error("width is not integral");
// else if (value < 0 || value > numeric_limits<int>::max())
else if (fmt::internal::is_negative(value) ||
value > numeric_limits<int>::max())
throw format_error("invalid width");
else
return static_cast<int>(value);
},
ctx.arg(width_arg_id));
return format_to(ctx.out(), "{0:{1}}", s.value, width);
}
};
TEST(StdFormatTest, Parsing) {
std::string s = std::format("{0:{1}}", S{42}, 10); // s == " 42"
EXPECT_EQ(s, " 42");
}
#if FMT_USE_INT128
template <> struct std::formatter<__int128_t> : std::formatter<long long> {
auto format(__int128_t n, format_context& ctx) {
// Format as a long long since we only want to check if it is possible to
// specialize formatter for __int128_t.
return formatter<long long>::format(static_cast<long long>(n), ctx);
}
};
TEST(StdFormatTest, Int128) {
__int128_t n = 42;
auto s = std::format("{}", n);
EXPECT_EQ(s, "42");
}
#endif // FMT_USE_INT128

View File

@ -5,23 +5,22 @@
//
// For the license information refer to format.h.
#ifndef FMT_TEST_ASSERT_H
#define FMT_TEST_ASSERT_H
#ifndef FMT_TEST_ASSERT_H_
#define FMT_TEST_ASSERT_H_
#include <stdexcept>
#include "gtest.h"
class AssertionFailure : public std::logic_error {
class assertion_failure : public std::logic_error {
public:
explicit AssertionFailure(const char *message) : std::logic_error(message) {}
explicit assertion_failure(const char* message) : std::logic_error(message) {}
};
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw AssertionFailure(message);
#include "gtest-extra.h"
if (!(condition)) throw assertion_failure(message);
// Expects an assertion failure.
#define EXPECT_ASSERT(stmt, message) \
EXPECT_THROW_MSG(stmt, AssertionFailure, message)
FMT_TEST_THROW_(stmt, assertion_failure, message, GTEST_NONFATAL_FAILURE_)
#endif // FMT_TEST_ASSERT_H
#endif // FMT_TEST_ASSERT_H_

View File

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

View File

@ -1,59 +0,0 @@
// Formatting library for C++ - time formatting tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "gmock.h"
#include "fmt/time.h"
TEST(TimeTest, Format) {
std::tm tm = std::tm();
tm.tm_year = 116;
tm.tm_mon = 3;
tm.tm_mday = 25;
EXPECT_EQ("The date is 2016-04-25.",
fmt::format("The date is {:%Y-%m-%d}.", tm));
}
TEST(TimeTest, GrowBuffer) {
std::string s = "{:";
for (int i = 0; i < 30; ++i)
s += "%c";
s += "}\n";
std::time_t t = std::time(nullptr);
fmt::format(s, *std::localtime(&t));
}
TEST(TimeTest, EmptyResult) {
EXPECT_EQ("", fmt::format("{}", std::tm()));
}
static bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
return lhs.tm_sec == rhs.tm_sec &&
lhs.tm_min == rhs.tm_min &&
lhs.tm_hour == rhs.tm_hour &&
lhs.tm_mday == rhs.tm_mday &&
lhs.tm_mon == rhs.tm_mon &&
lhs.tm_year == rhs.tm_year &&
lhs.tm_wday == rhs.tm_wday &&
lhs.tm_yday == rhs.tm_yday &&
lhs.tm_isdst == rhs.tm_isdst;
}
TEST(TimeTest, LocalTime) {
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
}
TEST(TimeTest, GMTime) {
std::time_t t = std::time(nullptr);
std::tm tm = *std::gmtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
}

Some files were not shown because too many files have changed in this diff Show More