Compare commits

...

607 Commits
text ... 7.0.2

Author SHA1 Message Date
b9d749095e Update version 2020-07-29 07:30:55 -07:00
86b63bb71a Bump version 2020-07-29 07:14:25 -07:00
cbf6be9604 Update changelog 2020-07-29 07:07:56 -07:00
229ee9b469 Workaround broken numeric_limits (#1725) 2020-07-29 07:06:45 -07:00
2b7a146fa1 Fix a regression in handling digit separators (#1782) 2020-07-29 07:04:11 -07:00
89d0c7124b Fix compatibility with CMake 3.4 (#1779) 2020-07-29 07:03:59 -07:00
f19b1a521e Update version 2020-07-07 07:47:44 -07:00
5c67fefb26 Fix a changelog entry 2020-07-07 06:58:44 -07:00
1d2a556e1b Fix undefined reference error 2020-07-07 06:37:17 -07:00
04c9b62fb4 Merge release branch 2020-07-07 06:34:39 -07:00
6be6762e57 Fix date 2020-07-07 06:32:19 -07:00
f1dd2eb3c0 Bump version 2020-07-07 06:24:32 -07:00
fbf3b943cc Workaround a bug in gcc 2020-07-07 06:06:50 -07:00
a29a01d304 Fix docs 2020-07-07 06:05:00 -07:00
9f0b3afb79 Bump version in namespace 2020-07-06 09:47:27 -07:00
86b2f99f8c Fix the docs 2020-07-06 07:53:07 -07:00
c472ff12d8 Update version 2020-07-06 06:45:20 -07:00
5173a76ba4 Update version 2020-07-05 08:43:47 -07:00
1614af3520 Minor corrections in the changelog 2020-07-05 08:43:01 -07:00
569a9b3a7f Bump version 2020-07-05 07:59:11 -07:00
4e7e3c65a3 Update docs 2020-07-05 07:32:43 -07:00
0f7a6bfa12 Add a section on std::format compatibility 2020-07-05 07:07:29 -07:00
4faec5a5ee Update README.rst 2020-07-05 06:41:51 -07:00
7dbc8ac716 Update changelog 2020-07-05 06:29:05 -07:00
c87dd746fa Update changelog 2020-07-05 06:21:54 -07:00
372175caf8 Revert changelog changes 2020-07-05 06:21:54 -07:00
9047548766 Add ClickHouse to the list of projects (#1751) 2020-07-05 05:57:04 -07:00
d30bca64e3 Revert changelog conversion since GFM is not supported there 2020-07-04 17:43:30 -07:00
d6047cdc4a Update changelog 2020-07-04 15:46:48 -07:00
810241b36e Convert changlog to markdown 2020-07-04 15:20:33 -07:00
661c474739 Rename changelog 2020-07-04 15:14:42 -07:00
7c33059fad Update ChangeLog.rst 2020-07-04 10:16:55 -07:00
9e20883ab3 Update README.rst 2020-07-04 09:37:27 -07:00
41899d5225 Update changelog 2020-07-04 09:31:12 -07:00
f42f459089 Update changelog 2020-07-04 08:40:42 -07:00
2381df654f Update readme 2020-07-04 08:27:57 -07:00
7ae816563e Update README.rst 2020-07-04 08:20:45 -07:00
c56cf3d074 Update changelog and readme 2020-07-04 08:20:10 -07:00
01309a34ab Deprecate arg_formatter 2020-07-04 07:29:11 -07:00
a62d060554 Update changelog 2020-07-04 07:24:49 -07:00
23e3a2eee8 Update changelog 2020-07-03 19:39:08 -07:00
d8e0554b97 Disable numeric formatting by default 2020-07-03 10:35:49 -07:00
1e8eea4f44 Update changelog 2020-07-03 10:31:14 -07:00
44bd5384a9 Fix formatting 2020-07-03 09:18:18 -07:00
20e19387a2 Update changelog 2020-07-03 09:14:11 -07:00
56fed78149 FMT_NUMERIC_ALIGN -> FMT_DEPRECATED_NUMERIC_ALIGN 2020-07-03 08:53:01 -07:00
56e63078ff Make the n specifier an opt-in 2020-07-03 08:02:48 -07:00
31ce6bc702 Fix a conversion warning with Clang10 on Windows (#1750)
Fixes #1747
2020-07-03 05:53:42 -07:00
c9c5b90da7 Fix a typo. Thanks Tracy Chapman from TripleChecker 2020-07-02 15:09:34 -07:00
1f3f84631c Fix a typo 2020-07-02 07:29:38 -07:00
5de62af604 Fix possible infinite recursion in FMT_ASSERT (#1744)
Use std::fprintf for assertion message output preventing infinite
recursion when output to stderr is limited or broken.
2020-07-01 08:28:27 -07:00
cbddab2fe2 Use consistent include style 2020-06-29 09:59:25 -07:00
f69b6eaabd Add a simple buffered stream with no sync 2020-06-28 09:55:29 -07:00
ba363b3a24 Use digit pairs as in unrolledlut 2020-06-27 12:05:38 -07:00
a6f8e7d860 Update changelog 2020-06-27 09:35:02 -07:00
e753244ab6 Update changelog 2020-06-27 06:02:34 -07:00
98a7a8b405 Update changelog and disable internal 2020-06-26 19:07:33 -07:00
3135d95fd9 Don't use non-portable attribute 2020-06-26 10:21:14 -07:00
8630a8f5ff Tweak the docs 2020-06-25 11:29:49 -07:00
cc3a88e6b3 Extract docs from compile.h 2020-06-25 08:36:07 -07:00
79c4b6bd70 Apply clang-format 2020-06-25 08:12:10 -07:00
d130ee070f Document format string compilation 2020-06-25 08:12:10 -07:00
d0f90b5be7 Spelling fixes 2020-06-24 06:29:54 -07:00
6e080660dc Update README.rst 2020-06-24 06:26:58 -07:00
31c3a24266 Spelling fixes 2020-06-23 15:56:16 -07:00
613b3b459d Spelling fixes 2020-06-23 15:55:31 -07:00
978521bb81 Fix a compile error introduced in #1738 2020-06-23 15:17:43 -07:00
4e94c649f9 Deprecate compile 2020-06-23 14:03:37 -07:00
1a83443e6c Add user-defined type support to compilation 2020-06-23 12:45:57 -07:00
8bef1c3b3a Tweaks for EDG based compilers (Intel, nVidia, MCST/Elbrus, etc).
Using the __EDG_VERSION__ macro for checks related to EDG-based
compilers instead of a macros specific for each of ones.
2020-06-23 05:50:25 -07:00
b287c37c65 Do not use -Wl,--as-needed with emscripten.
On some platform, fmt is using the following link flag:
```cmake
  target_link_libraries(fmt -Wl,--as-needed)
```

This is not supported by wasm-ld as of today.
This patch stop using it when compiling using emscripten.

Bug: https://github.com/fmtlib/fmt/issues/1736
2020-06-21 11:46:04 -07:00
2cac8a9d2e Reintroduce UDT support to fmt::to_string and test ADL 2020-06-21 09:21:07 -07:00
9a4cc88426 Add FMT_COMPILE support to format_to 2020-06-20 15:15:41 -07:00
5ddf9ee1bd Streamline default FP formatting 2020-06-20 07:12:50 -07:00
0b3a83f7f4 Update README.rst 2020-06-20 05:37:58 -07:00
5aa5c98738 Added #define WIN32_LEAN_AND_MEAN before including windows.h (#1729)
Co-authored-by: gabime <gmelman1@mgail.com>
2020-06-17 07:44:03 -07:00
397ad1bec3 Optimize common case 2020-06-15 18:30:31 -07:00
7431165f38 Make to_string bypass format 2020-06-15 17:55:16 -07:00
ee4d4c7fd0 Inline compiled format 2020-06-14 11:38:39 -07:00
ab2f8484e0 Finish text::format 2020-06-14 11:04:41 -07:00
e900d735bb Re-enable assert in format_decimal 2020-06-14 09:30:03 -07:00
f4de7b684a Fix ambiguity 2020-06-14 08:26:12 -07:00
1f8f5450b5 Reuse format_decimal 2020-06-14 07:42:52 -07:00
d702a68df2 Fix formatting of bool with FMT_COMPILE and add more tests 2020-06-14 07:16:50 -07:00
e956a14e9e Use write instead of format_int in to_string 2020-06-13 20:07:08 -07:00
98dcc251eb Undo branching reduction 2020-06-13 18:23:52 -07:00
5b8641dddf Undo branching reduction 2020-06-13 16:50:55 -07:00
8c88abde64 Fix sign handling in 'L' 2020-06-13 06:57:19 -07:00
23b976a615 Reduce branching 2020-06-12 19:46:00 -07:00
9edee0e727 Optimize small string parsing 2020-06-12 19:22:33 -07:00
a909d42b76 Fix a warning 2020-06-12 13:49:31 -07:00
16637341b9 Enable compilation for all types 2020-06-12 13:27:21 -07:00
2d71d7e030 Add a simple format string compilation API 2020-06-12 08:37:15 -07:00
d259fcfb05 Tweak comments 2020-06-12 06:22:05 -07:00
704ed557a1 Move project in order to solve a CMake warning
CMake Warning (dev) at /usr/share/cmake-3.17/Modules/GNUInstallDirs.cmake:225 (message):
  Unable to determine default CMAKE_INSTALL_LIBDIR directory because no
  target architecture is known.  Please enable at least one language before
  including GNUInstallDirs.
2020-06-12 06:04:31 -07:00
8603bd20d0 Update README.rst 2020-06-11 17:25:20 -07:00
547f12ae63 Fix a warning (#1722) 2020-06-11 15:02:42 -07:00
f904e8a1b4 c++11 use formatting user-defined types (#1721) 2020-06-11 10:10:39 -07:00
100e8af08a Update README.rst 2020-06-10 07:46:01 -07:00
c11d0f056e Update README.rst 2020-06-10 07:02:12 -07:00
2453ee576a Improve default formatting 2020-06-09 08:59:01 -07:00
47ae521557 MINGW cross compiler fixes 2020-06-09 06:19:22 -07:00
936a1833c2 Add default_arg_formatter 2020-06-08 08:14:34 -07:00
f2c9cb6244 Fix a UB 2020-06-08 06:32:11 -07:00
d3107f8551 Cleanup arg_formatter_base 2020-06-07 20:55:38 -07:00
5e7c70e206 Simplify arg_formatter_base 2020-06-07 19:38:43 -07:00
38cc68b3e5 Inline visitor 2020-06-07 08:36:33 -07:00
6732ea5000 Make symbols readable 2020-06-07 07:49:11 -07:00
57ddc77ce7 Make advance_to a noop for back_insert_iterator 2020-06-06 19:36:39 -07:00
50bad7d62d Optimize format string parsing 2020-06-06 10:15:19 -07:00
8f7a824e47 Inline visit 2020-06-06 09:02:32 -07:00
f11e968708 Optimize format string parsing 2020-06-06 08:15:33 -07:00
09737dd83b Optimize format handler 2020-06-06 07:17:02 -07:00
d9e3d6e6ed Move format_handler to detail 2020-06-06 06:38:48 -07:00
795b47a7b5 Fix a warning (#1712) 2020-06-05 08:24:09 -07:00
95c6ac0cc8 fix typo which caused the loss of the counting information when using a printf context with a truncating_iterator 2020-06-05 07:37:54 -07:00
21409cfdda Fix warnings 2020-06-04 07:50:07 -07:00
88c8d534e7 Move digits10 to where they belong and add comments 2020-06-04 06:39:18 -07:00
0f3eaeac0a Fix a warning 2020-06-04 06:33:11 -07:00
344218510d Ignore /doc/node_modules directory 2020-06-04 05:19:39 -07:00
16aec06179 Cleanup arg_formatter_base 2020-05-31 10:58:52 -07:00
1e1193590d Fix format_decimal overloads 2020-05-31 10:12:34 -07:00
0893c9c2ef Inline parse_format_string 2020-05-30 20:30:18 -07:00
3245145a41 Remove undocumented buffer_range and output_range 2020-05-30 13:07:52 -07:00
57fc44907f Increase VM disk size 2020-05-30 11:25:40 -07:00
7d22bebb6f Remove uses of buffer_range 2020-05-30 06:50:45 -07:00
8f2b5fe74d Don't install sphinx cache files
When building documentation sphinx creates cached files in the .doctrees
directory and aren't required for viewing documentation only for
building. As added benefit this makes fmt reprodcubile as the cached
files are different when the build environment is varied.
2020-05-30 06:14:02 -07:00
f095c67b6a Remove uses of buffer_range 2020-05-29 18:14:03 -07:00
5aabf1f715 Simplify copy_str 2020-05-29 17:59:08 -07:00
19c5b5d159 Simplify arg_formatter 2020-05-29 16:53:12 -07:00
519571edec Simplify arg_formatter_base 2020-05-29 14:44:18 -07:00
ac8dfd841f Improve handling of separators 2020-05-29 08:15:44 -07:00
2c6165a22b Reduce the number of comparisons 2020-05-29 05:42:35 -07:00
28639969ef Use memcpy for copying digits 2020-05-28 22:33:08 -07:00
f5fa1dee54 Support custom FMT_INC_DIR in pkgconfig and cmake configs (#1702)
* Support custom FMT_INC_DIR in pkgconfig and cmake configs

When CMAKE_INSTALL_INCLUDEDIR or FMT_INC_DIR override the header
installation directory, they should be used instead of
${CMAKE_INSTALL_PREFIX}/include in fmt-targets.cmake and fmt.pc.

* Put headers into FMT_INC_DIR/fmt rather than directly into FMT_INC_DIR

This makes FMT_INC_DIR an alias for CMAKE_INSTALL_INCLUDEDIR and
simplifies generation of pkgconfig and cmake configs.

* Do not split target_include_directories
2020-05-28 08:17:34 -07:00
51bf9cfacb Fix Mingw support
If the ``_POSIX_`` flag is set, _fdopen will not be defined by
Mingw headers, which is addressed by this commit.

For what is worth, as opposed to ``fdopen``, ``_pipe`` *will*
actually have the ``_`` prefix when ``_POSIX_`` is set.
2020-05-26 06:39:22 -07:00
1a716caf5d Optimize common case 2020-05-25 19:16:35 -07:00
98d4bbf813 Update README.rst 2020-05-24 06:28:35 -07:00
8c8f74a870 fix zero flag for char types and make zero flag ignored if a precision is specified 2020-05-23 12:46:26 -07:00
bc1b89da26 Temporarily revert parsing changes 2020-05-22 15:39:33 -07:00
a7fb321ac6 Remove a redundant branch 2020-05-22 15:39:33 -07:00
8cadb96506 fix max/min macro (#1697) 2020-05-22 06:38:43 -07:00
297c3b2ed5 Fix an example (thanks Alexey Kuzmenko) 2020-05-20 15:52:19 -07:00
943532fece Make ostream formatter work with compile-time format strings (#1692) 2020-05-20 14:59:57 -07:00
bd8804019b Update README.rst 2020-05-19 19:25:46 -07:00
f230300ac5 Knuth is using fmt library (#1691) 2020-05-19 18:08:32 -07:00
a265e25b79 Optimize small string parsing 2020-05-19 13:44:22 -07:00
2aa2526f64 Optimize small string concatenation 2020-05-19 11:45:09 -07:00
8d78045e7c Move void_t to where it's used 2020-05-18 10:46:25 -07:00
7aafa6bc64 Update analytics 2020-05-18 10:26:22 -07:00
c66aae1652 Adding sentinel support to fmt::join(). (#1689) 2020-05-18 06:31:01 -07:00
6d66de3805 Add c specifier support to integral types (#1652) 2020-05-17 08:44:59 -07:00
6b219a58db fix interaction of space flag and '+' flag, as well as '-' flag and '0' flag (#1687) 2020-05-17 04:27:18 -07:00
eee2023c2a Update signatures 2020-05-15 15:25:02 -07:00
c5ed73aab2 Add fmt::detail::buffer to the docs (#704) 2020-05-15 14:35:55 -07:00
ea1cd9638c Fix apidoc 2020-05-15 10:03:02 -07:00
d3964d7b1e Merge branch 'master' of github.com:fmtlib/fmt 2020-05-15 09:43:38 -07:00
d18c6723a2 Update docs 2020-05-15 09:43:35 -07:00
96c18b26c2 make plus flag for printf not be ignored for char argument (#1683)
* make plus flag for printf not be ignored for char argument

* clarify overwriting of alignment specifiers for printf with char argument
2020-05-15 06:45:51 -07:00
ba25baeb97 Apply doc patch to 6.2.1 2020-05-14 19:11:40 -07:00
981b517ccf nested replacement fields may omit arg_id (#1681)
syntax.html already has examples like `fmt::format("{:.{}f}", 3.14, 1)` using this, 
and https://en.cppreference.com/w/cpp/utility/format/formatter#width_and_precision shows that this is the case for the C++20 std::format

The Format Specification Mini-Language grammar seems to be the only one not showing this; update it to match.
2020-05-13 17:20:05 -07:00
922ea924bf Make dynamic_format_arg_store reusable and add reserve() (#1677)
Implemented #1674: make dynamic_format_arg_store reusable and add
reserve() for better memory menagement.
2020-05-12 11:00:42 -07:00
e0d98923c7 Update version 2020-05-10 18:05:46 -07:00
8069265373 internal -> detail (#1538) 2020-05-10 07:34:30 -07:00
963ee08310 Simplify named arguments 2020-05-09 16:43:31 -07:00
02a6fe59fb Named arguments go brrr 2020-05-09 13:22:38 -07:00
de290f5c4c Ditch internal::arg_map 2020-05-09 12:43:11 -07:00
d0623de510 Bump version 2020-05-09 10:57:04 -07:00
73e335ed38 Make implicit capture explicit for C++20 (#1669) 2020-05-09 10:55:37 -07:00
b4d46e3988 Update changelog 2020-05-09 10:05:16 -07:00
a182f7341e Update changelog 2020-05-09 08:30:31 -07:00
68201831a5 Support named args in dynamic_format_arg_store (#1655). (#1663)
Dynamic arguments storage. Implementation of enhancement from issue
#1170.
2020-05-09 06:25:03 -07:00
7f723fbcb8 Consistently namespace qualify size_t 2020-05-07 15:59:46 -07:00
c06851456d Purge basic_writer 2020-05-07 15:04:48 -07:00
2f05054dd3 Purge basic_writer 2020-05-07 11:35:41 -07:00
f0ce21164c Revert enum change 2020-05-07 07:25:33 -07:00
44639b11fe Fix some warnings (#1667)
* Fix sign-conversion warning

* Add missing "extern template" declarations for non-header-only build

* Use typed enums to fix Wsigned-enum-bitfield warnings

* Consolidate FMT_HEADER_ONLY code
2020-05-07 06:14:07 -07:00
1c86a99e8f Purge basic_writer 2020-05-06 18:46:01 -07:00
8f511fc12f Make copyfmt not throw (#1666) 2020-05-06 17:38:46 -07:00
59fe455f36 Remove compatibility stubs 2020-05-06 07:31:35 -07:00
b0f47a13e6 Separate nonfinite formatting 2020-05-06 07:19:37 -07:00
d6cea50d01 Remove deprecated APIs 2020-05-05 20:03:48 -07:00
40bc7163fb Move FMT_MAYBE_UNUSED to where it's actually used 2020-05-05 19:41:07 -07:00
080e44d0bf Fix inconsistent type detection (#1662) 2020-05-05 19:09:21 -07:00
7e57cace5d Exclude std::abort from compilation when compiling CUDA with Clang (#1661) 2020-05-04 06:21:21 -07:00
7b66e2f219 Inherit arg_formatter_base from basic_writer 2020-05-03 21:05:45 -07:00
bab3f58003 Refactor pointer formatting 2020-05-03 20:33:25 -07:00
9cc7edfddc Move int_writer to the namespace scope 2020-05-03 17:34:48 -07:00
8d9d528bf5 Improve handling of alignment 2020-05-03 10:58:18 -07:00
8efd1a8ef6 Improve handling of alignment 2020-05-03 10:08:57 -07:00
a71bc9c825 Use '0' fill with numeric align for consistency with std::format 2020-05-03 09:52:31 -07:00
60d85d598c Suppress ubsan warning 2020-05-03 08:43:10 -07:00
c3099beb6f Cleanup 2020-05-02 22:00:34 -07:00
cbb4cb8991 Remove undocumented deprecated APIs 2020-05-02 21:34:44 -07:00
b85e9ac38b Simplify vformat_to 2020-05-02 21:34:44 -07:00
e3710ab972 FMT_CONSTEXPR -> constexpr 2020-05-02 21:34:44 -07:00
d59751f0f0 Update date formatting example to use threadsafe localtime 2020-05-02 09:04:31 -07:00
d6abb2fa03 Reduce library size 2020-05-01 17:23:45 -07:00
e9fdea90b5 Update README.rst 2020-05-01 10:40:07 -07:00
44b6584f22 Update README.rst 2020-05-01 10:39:28 -07:00
78f041ab5b build: Fix installation paths
It is not generally true that `CMAKE_INSTALL_<dir>` variables are relative paths:

https://github.com/jtojnar/cmake-snips#concatenating-paths-when-building-pkg-config-files
2020-05-01 10:27:24 -07:00
7ca89bf87a Reduce template bloat in write_int 2020-05-01 07:56:57 -07:00
3c114d091b Fix a shadowing warning (#1658) 2020-05-01 07:00:25 -07:00
e2ef12a8c0 Allow to avoid inclusion of os.cc in fmt target
- Allows to avoid part of #1654
- Not possible to use with MASTER_PROJECT as testing uses it a lot
2020-05-01 06:24:22 -07:00
bca82719ab Pass iterator by value 2020-04-30 21:06:17 -07:00
99da389627 Make write_padded non-members 2020-04-30 16:05:30 -07:00
f19d667943 Bump fuzzer allocation limit 2020-04-30 08:45:50 -07:00
3e69847616 Reduce branching in write_padded 2020-04-30 06:33:45 -07:00
9ac1eebd47 Reduce library size 2020-04-29 17:37:05 -07:00
e2ff910675 Replace FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION with fmt-specific macro (#1650)
Signed-off-by: Asra Ali <asraa@google.com>
2020-04-29 09:12:43 -07:00
f2ed03b919 Fix a warning (#1649) 2020-04-28 06:39:24 -07:00
9dde9f0131 Reduce library size 2020-04-27 20:43:30 -07:00
b1af642d1d Reduce library size 2020-04-27 18:50:49 -07:00
4a617f25c6 Clarify encoding conversion in chrono 2020-04-27 09:57:08 -07:00
6f435f55c8 Improve compile time by using extern template (#1452) 2020-04-26 14:02:15 -07:00
cb475cb884 Clarify why we don't check argument id 2020-04-26 13:38:42 -07:00
1e1ac6e964 Check dynamic width/precision id at compile time (#1614) 2020-04-25 08:36:24 -07:00
e51c449fe6 Revert "Check dynamic widht/precision id at compile time (#1614)"
This reverts commit 7d748a6f82.
2020-04-25 07:43:56 -07:00
0463665ef1 Don't access a C string past precision in printf (#1595) 2020-04-22 17:10:23 -07:00
7d748a6f82 Check dynamic widht/precision id at compile time (#1614) 2020-04-22 14:45:37 -07:00
2b75bd7ce6 Get rid of do_check_format_string 2020-04-22 12:15:29 -07:00
4a1d5931cb Simplify udl_formatter with FMT_STRING 2020-04-22 11:26:05 -07:00
811b0f9054 Enable compile-time error tests 2020-04-22 11:00:20 -07:00
450e8eed97 Fix markup 2020-04-22 09:30:09 -07:00
b8fbcec1be Clarify formatter reuse 2020-04-22 09:15:52 -07:00
56bc86ffac Suppress bogus MSVC analysis warnings 2020-04-22 07:51:25 -07:00
3f79357ef0 Fix a recent regression in handling max packed arguments 2020-04-22 07:15:19 -07:00
8a11148f99 Add Facebook Folly to the list of projects 2020-04-22 06:16:07 -07:00
e371e8b686 Tweak readme 2020-04-22 06:13:36 -07:00
813732fede Improve readme formatting 2020-04-22 06:07:12 -07:00
3670d5b3f6 README: add vectorized.io/redpanda in the list of users 2020-04-22 06:04:09 -07:00
9e2ad7cf68 Add windows terminal to the projects using {fmt} 2020-04-21 15:47:04 -07:00
63479c8519 Use a delegating ctor and add inlines 2020-04-20 19:17:16 -07:00
5944fcad37 Remove remaining wchar_t instantiation 2020-04-19 16:48:15 -07:00
e253b371b2 Don't generate RTTI for allocator 2020-04-19 15:57:48 -07:00
0c86f467bc Fix build on ancient gcc 2020-04-19 09:52:40 -07:00
1929df4bc2 Simplify format_args 2020-04-19 09:21:39 -07:00
a138221813 Always inline arg_data functions 2020-04-19 08:20:00 -07:00
04e0dfd4bd Always inline value ctors 2020-04-19 08:05:02 -07:00
04cde756bc Simplify checks 2020-04-19 07:35:19 -07:00
c9a57b9a81 Fix incorrect assumptions about nul termination 2020-04-18 06:51:52 -07:00
f46f5ecaf0 Reenable constexpr _compile on GCC 9 2020-04-17 06:10:57 -07:00
6e8d7e2776 Don't use constexpr on Intel compiler (#1628) 2020-04-16 07:08:04 -07:00
567ed03f88 Merge arg overloads and cleanup 2020-04-15 08:44:58 -07:00
c3fa333140 Remove warning in core.h with when compiling with gcc and -Wshadow
In file included from build/_deps/fmt-src/include/fmt/format.h:44:0,
                 from src/main.cpp:5:
build/_deps/fmt-src/include/fmt/core.h: In member function ‘const T& fmt::v6::internal::dynamic_arg_list::push(const Arg&)’:
build/_deps/fmt-src/include/fmt/core.h:1256:10: error: declaration of ‘node’ shadows a member of ‘fmt::v6::internal::dynamic_arg_list’ [-Werror=shadow]
     auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
          ^~~~
build/_deps/fmt-src/include/fmt/core.h:1236:37: note: shadowed declaration is here
   template <typename = void> struct node {
2020-04-15 07:31:24 -07:00
84898b4626 Remove warning in format.h when compiling with gcc and -Wshadow
build/_deps/fmt-src/include/fmt/format.h: In member function ‘decltype (ctx.out()) fmt::v6::formatter<fmt::v6::bytes>::format(fmt::v6::bytes, FormatContext&)’:
build/_deps/fmt-src/include/fmt/format.h:3251:58: error: declaration of ‘writer’ shadows a global declaration [-Werror=shadow]
     internal::basic_writer<range_type> writer(range_type(ctx.out()));
                                                          ^~~
build/_deps/fmt-src/include/fmt/format.h:2741:53: note: shadowed declaration is here
 using writer FMT_DEPRECATED_ALIAS = internal::writer;
2020-04-15 07:31:24 -07:00
538d83fd08 Cleanup named arguments 2020-04-15 06:28:41 -07:00
8a4630686e Improve handling of named arguments 2020-04-14 11:56:55 -07:00
a9d62d3f35 Add check for CompiledFormat to avoid ambiguous call 2020-04-14 05:59:02 -07:00
fdcf7870a2 Add stack-based named argument storage 2020-04-13 08:03:51 -07:00
5899267c47 Fix a clang-tidy warning 2020-04-12 08:27:39 -07:00
07b4c246ea Fix a typo 2020-04-12 08:18:47 -07:00
e99809f29d Fix ostream support in sprintf (#1631) 2020-04-12 07:53:17 -07:00
3cd5179f32 Fixed clang tidy warning -multiple declarations in a single statement reduces readability 2020-04-12 06:29:09 -07:00
7404e33a73 Fix clang warning about explicit ctor 2020-04-12 06:19:16 -07:00
3aab2171ed Clean up basic_format_args 2020-04-11 08:22:53 -07:00
7645ca0724 Clean up printf 2020-04-11 08:01:00 -07:00
e30d8391e4 Suppress an MSVC warning (#1622) 2020-04-11 07:49:00 -07:00
8cd8ef03eb Simplify warning suppression 2020-04-11 06:17:31 -07:00
bbb6b357c7 Add floating-point L specifier (#1624) 2020-04-10 07:16:20 -07:00
36ea32640f Suppress a bogus MSVC warning 2020-04-10 06:44:07 -07:00
141a00d642 Define FMT_EXTERN_TEMPLATE_API on export 2020-04-09 11:54:55 -07:00
3860edc5d9 Bump version 2020-04-08 14:48:14 -07:00
7d01859ef1 Fix handling of unsigned char strings in printf 2020-04-08 13:05:42 -07:00
63b23e786a Merge branch 'master' of github.com:fmtlib/fmt 2020-04-08 13:03:44 -07:00
4999796c15 Fix the docs 2020-04-08 07:52:36 -07:00
34b3f7b7aa Avoid windows issue with min() max() macros
Including the ``windows.h`` file without defining ``NOMINMAX`` will define the `min()` and `max()` macros which will result in issues compiling any C++ code that uses any variant of `max`, for example `std::numeric_limits<std::streamsize>::max()` and many others.  Although max() isn't used in Fmt anywhere, it is often used in codes that include a format include file so simply upgrading to the current version of lib::fmt will break the windows build which worked prior to the update...
2020-04-07 08:34:34 -07:00
27e3c0fe9b Update signature in the docs 2020-04-06 07:17:41 -07:00
9bdd1596ce Update version 2020-04-05 06:46:41 -07:00
d151562bdd Fix punctuation in changelog 2020-04-04 12:49:56 -07:00
346500e70b Fix gcc version check 2020-04-04 11:23:40 -07:00
a434a8f778 Update changelog 2020-04-04 06:34:23 -07:00
9eb47d951a Fix markup 2020-04-03 08:49:29 -07:00
51c58a56ba Bump version 2020-04-03 08:32:45 -07:00
3fc33f6273 Update changelog 2020-04-03 08:29:02 -07:00
2e32db5b99 Update changelog 2020-04-03 07:42:02 -07:00
c1ce6e01f7 Update changelog 2020-04-02 08:18:42 -07:00
1c3c80dc1f Update changelog 2020-04-02 07:27:58 -07:00
d1d653d895 Implement the L specifier 2020-04-02 06:58:38 -07:00
73c8437485 Follow naming conventions 2020-04-01 09:30:28 -07:00
e588b02b17 Fix posix-mock-test 2020-04-01 08:42:14 -07:00
1a62711d01 Reduce binary size 2020-03-31 08:07:11 -07:00
5b02881582 Merge branch 'master' of github.com:fmtlib/fmt 2020-03-28 09:46:44 -07:00
a133187a8c Update changelog 2020-03-28 09:44:27 -07:00
80ce222ca6 Fix wide print overload (#1609) 2020-03-28 09:44:18 -07:00
770a94edef Use FMT_THROW macro where applicable 2020-03-27 12:17:22 -07:00
2864e8432a Update readme and add compatibility option 2020-03-26 07:18:27 -07:00
21a295c272 Undo comment change 2020-03-25 08:14:31 -07:00
96c68afe69 Fix -Wsign-conversion warnings 2020-03-25 07:46:00 -07:00
664dd88e31 Enable FMT_STRING() use with types other than string literals 2020-03-24 12:56:21 -07:00
69779b4ed6 Fix handling of small precision in general format 2020-03-24 09:01:57 -07:00
01a172c969 Add .vs to .gitignore 2020-03-23 15:01:02 -07:00
08ca40ea91 Detect /utf-8 in MSVC 2020-03-22 08:19:38 -07:00
dd97f4920c Improve exception safety in dynamic_format_arg_store 2020-03-21 08:58:50 -07:00
2951169481 Move FMT_USE_FLOAT and friends to fmt/format.h 2020-03-20 07:55:43 -07:00
d3e668418f Allow disabling floating point support (#1590)
* Allow disabling floating point support

Add FMT_USE_FLOAT, FMT_USE_DOUBLE and FMT_USE_LONG_DOUBLE to allow a
user of the library to configure the float types they want to allow.
This is specially useful in embedded environements where code size is
important.

* Avoid conditional macros to disable float support

* Add is_supported_floating_point constexpr function

* Fix empty-body warning
2020-03-20 06:46:31 -07:00
52d0e1bbe3 Don't use properties when setting FMT_LIB_NAME 2020-03-19 08:35:09 -07:00
5d32ccfc31 Add back missing OUTPUT_NAME in target properties. (#1598) 2020-03-19 07:01:51 -07:00
3cf619de55 Simplify dynamic_format_arg_store 2020-03-17 07:13:46 -07:00
2559983e7a Color formatting fixed for wide strings (fixes issue #1594) (#1596)
* Use std::char_traits::length for ansi_color_escape::begin

-Fixes issue #1594 https://github.com/fmtlib/fmt/issues/1594
2020-03-17 06:24:42 -07:00
026f99178e Simplify dynamic store 2020-03-16 19:10:41 -07:00
9f70fc3e7a Minor tweaks for dynamic_format_arg_store 2020-03-16 07:58:15 -07:00
6012dc9ab4 Dynamic arguments storage. Implementation of enhancement from issue #1170. (#1584) 2020-03-16 07:00:29 -07:00
85050aa2e6 Ability to join elements of std::initializer_list was added 2020-03-15 16:10:00 -07:00
ff486a72a7 Allow leading zeros in precision (#1579) 2020-03-14 11:37:38 -07:00
678341275b Deprecate fmt::char8_t 2020-03-14 10:32:34 -07:00
6f01b6ebb6 Fix a typo in CMake config: STRINGS -> STRING 2020-03-14 09:50:25 -07:00
61c5a51604 Fix handling of empty tuples (#1588) 2020-03-14 07:41:08 -07:00
02bfd8a9a5 Add FMT_HAS_CPP14_ATTRIBUTE / FMT_HAS_CPP17_ATTRIBUTE to test for language-specific attributes.
FMT_DEPRECATED is now defined as FMT_HAS_CPP14_ATTRIBUTE(deprecated), as this attribute was introduced in C++14.

FMT_FALLTHROUGH is now defined as FMT_HAS_CPP17_ATTRIBUTE(fallthrough), as this attribute was introduced in C++17.

FMT_MAYBE_UNUSED is defined as FMT_HAS_CPP17_ATTRIBUTE(maybe_unused), as this attribute was introduced in C++17.

FMT_MAYBE_UNUSED has been applied to fix a couple of -Wunused-member-function warnings from clang.
2020-03-13 09:03:52 -07:00
3c24052cf1 Workaround 'cannot call member function without object' error on gcc 4.9 2020-03-11 17:39:32 -07:00
f72a905eb3 Fix handling of volatile enums 2020-03-11 08:40:57 -07:00
941d5e147a Workaround broken fallthrough attribute in the PGI compiler (#1583) 2020-03-11 07:56:23 -07:00
ee2b828b9a Tweak a comment 2020-03-09 11:27:14 -07:00
5bb8856655 Workaround for broken [[deprecated]] in PGI compiler (#1581)
* Workaround broken [[deprecated]] in PGI compiler
  - similar to Intel and NVCC, add workaround for PGI compiler
2020-03-09 11:25:38 -07:00
1c0c59d4a0 Fix empty debug postfix 2020-03-09 08:34:02 -07:00
b1adaa9881 Remove gcc 4.4 workaround 2020-03-07 14:50:52 -08:00
48e8d0ebef set_doc -> set_verbose 2020-03-07 14:31:39 -08:00
ce00979152 Cleanup CMake config 2020-03-07 08:18:01 -08:00
db4a6cfbf9 is_static_compiled_format -> is_compiled_format 2020-03-06 07:25:34 -08:00
29a1ea795a Fix clang -Wdisabled-macro-expansion warning from FMT_STRING_IMPL.
FMT_STRING_IMPL has an internal helper named FMT_STRING, however FMT_STRING is also the name of the macro that invokes FMT_STRING_IMPL.

Renaming this helper avoids the appearance of a recursive macro.
2020-03-06 07:00:47 -08:00
8a06ca84c7 Fix ambiguous overloads of format & format_to 2020-03-05 12:22:43 -08:00
153f753bde Deprecate undocumented _u suffix 2020-03-04 19:20:19 -08:00
eafd079868 Improve width computation 2020-03-04 17:17:34 -08:00
0c6919ec72 Make FMT_DEBUG_POSTFIX a cache variable (#1566) 2020-03-04 08:37:11 -08:00
197a5c3721 Apply clang-format 2020-03-04 08:37:00 -08:00
68742e1d87 Fix clang -Wsign-conversion warning in grisu_count_digits. (#1573)
grisu_count_digits is only used by grisu_gen_digits, which assigns the unsigned result to a (signed) int.

Although grisu_count_digits always returns a positive integer this keeps its return type in sync with the type its result is assigned to.
2020-03-04 06:48:41 -08:00
1e8493196e Make compile-time checks in format_to handle references 2020-03-01 07:57:34 -08:00
58e6c84f5a Fix simple -Wsign-conversion cases. (#1571)
* Fix -Wsign-conversion in bigint::subtract_aligned.

n is assigned a size_t, and only used for comparisons with j.

j is assigned 0, compared to n (size_t), and passed to basic_memory_buffer::operator[] (size_t).

* Fix -Wsign-conversion in bigint::assign.

num_bigits is initialised to 0, is only ever incremented, and is passed to basic_memory_buffer::operator[] (size_t) and basic_memory_buffer::resize (size_t).
2020-03-01 07:22:15 -08:00
75a4525e5f Move FMT_CLANG_VERSION definition to core.h (#1568)
Previously format.h defined FMT_CLANG_VERSION after including core.h, however core.h tests FMT_CLANG_VERSION when it defines FMT_API.
2020-02-29 17:19:34 -08:00
6ccb2e241b Add FMT_NORETURN to assert_fail prototype. (#1569)
When building with -Werror,-Wmissing-noreturn clang identifies that assert_fail could be declared with the 'noreturn' attribute.
2020-02-29 17:17:55 -08:00
bed134a4aa Tentative fix for default template param in friend error 2020-02-27 15:29:46 -08:00
b2d3a86ec0 Make FMT_ASSERT work in constexpr on clang 4.0.1 2020-02-26 06:26:46 -08:00
13d82e32bd Don't use internal GTest API 2020-02-24 11:32:34 -08:00
2161a73f2b Fix FMT_FORMAT_AS const specifier position (#1554)
The current `FMT_FORMAT_AS` macro will make `formatter<Char *>::format`
have the first argument type `const Char *&` which is incorrect an
should be `Char *const &`.  This pull request fixes that by changing the
first argument type in the macro definition body from `const Type &` to
`Type const &`.
2020-02-23 07:27:22 -08:00
e00997b004 improved use of find (#1560)
* improved use of find

*begin is supposed to be different from '{' when this find is used, so we can avoid checking it.
2020-02-21 14:43:06 -08:00
0415cf2350 add const begin and end overload to buffer (#1553)
* add const begin and end overload to buffer

since there is a const overload for data I think there should also be one for begin and end
2020-02-19 14:59:50 +01:00
3bafd0749b Fix to_string docs 2020-02-15 09:51:35 +01:00
f733882b55 Remove misleading FMT_USE_WINDOWS_H 2020-02-14 14:09:27 +01:00
dc22360c34 Workaround broken UDL templates in GCC < 6.4 2020-02-10 17:08:14 +01:00
1f1b50707c Make formatter override implicit conversion to a C string 2020-02-07 19:24:36 -08:00
24924128e3 Fix a link error in gcc8 (#1548) 2020-02-07 18:34:05 -08:00
c54cd71800 only modify CMAKE_RUNTIME_OUTPUT_DIRECTORY if it is not already set 2020-02-07 07:10:03 -08:00
43e9b29e50 Only use compiler features if available 2020-02-06 15:43:33 -08:00
b55ea58705 string_view::char_type -> value_type (#1539) 2020-02-01 12:27:44 -08:00
4098970db2 Update README.rst 2020-01-31 11:33:51 -08:00
314e15001f Fix symbol visibility on Linux when compiling with -fvisibility=hidden (#1535)
Make FMT_API symbols use the default visibility on non-Windows
platforms. Otherwise, one cannot use the generated fmt library when
compiling globally with -fvisibility=hidden.

Fixes compile errors like:

```
../3rdParty/fmt/include/fmt/core.h:757: error: undefined reference to 'fmt::v6::internal::assert_fail(char const*, int, char const*)'
```

Note that the symbol exists, but is local:

```
$ nm -C libfmtd.so.6.1.3  | grep assert_fail
                 U __assert_fail
0000000000233ffa t fmt::v6::internal::assert_fail(char const*, int, char const*)
```

With this patch, the compile error is gone and the symbol is properly
exported:

```
$ nm -a bin/libfmtd.so -C | grep assert_fail
                 U __assert_fail
00000000002366ba T fmt::v6::internal::assert_fail(char const*, int, char const*)
```

Change-Id: I96054e622d9a2ae81907e1b01a1033e629767a91
2020-01-30 06:26:18 -08:00
f499b393d1 Apply coding conventions 2020-01-26 19:48:48 -08:00
6c30f41443 Configure fmt.pc library name correctly.
Simplify getting library name.

Add FMT_DEBUG_SUFFIX variable.
2020-01-26 19:48:14 -08:00
1acb73f970 Fix formatting std::chrono::duration types to wide strings (#1533)
* Fix formatting chrono durations to wide strings

* Make format buffers const correct

* Add FormatWide chrono test case

* Fix incorrect wide encoding of 'µs'
I think might be a source file encoding issue, so I used \u00B5 instead.

* Update FormatWide test to use proper encoding of µs

* Revert changes to format_localized's parameters

* Use different overload of `std::time_put<T>::put` to avoid needing a format string

* Use utf8_to_utf16 instead of having redundant overloads of get_units

* Revert some minor changes

* Remove FMT_CONSTEXPR from expression

This should hopefully fix compilation on VS <2019

* Make suggested changes from code review

* Run clang-format on chrono.h

* Make sure unit isn't null before constructing a string_view from it
2020-01-23 18:48:36 -08:00
09a13244c8 Disallow passing non-string-literals to FMT_STRING 2020-01-22 21:05:46 -08:00
419db8baa1 Fix length computation of constexpr C strings 2020-01-22 18:25:07 -08:00
9fc4161f5e fix interal compiler error when building with mingw 2020-01-22 18:14:46 -08:00
25d6916b3a Fix so can work without locale defined
If `FMT_STATIC_THOUSANDS_SEPARATOR` defined, then locale is not included or defined, so this call will be unresolved.  I think this is the correct fix based on the code in `format-inl.h` and `format.h`
2020-01-22 07:26:03 -08:00
0b2eb6501c Add locale example 2020-01-20 08:42:16 -08:00
fd1cabe464 Workaround a bogus MSVC warning 2020-01-20 06:56:22 -08:00
a844d7ab81 Add namespaces 2020-01-19 19:20:48 -08:00
47d3968092 Add more examples 2020-01-19 19:15:54 -08:00
7800173eb1 Update fill docs 2020-01-19 18:57:32 -08:00
b4218aa0f8 Test invalid fill 2020-01-19 16:52:36 -08:00
8a3a8177d6 Bump version 2020-01-19 15:34:30 -08:00
e5f2f8ce7a Add variable-width fill support (#1109) 2020-01-19 14:49:51 -08:00
75765bfad5 Avoid unnecessary unsigned overflows (#1515) 2020-01-18 08:19:27 -08:00
9bd9738da0 Remove static and simplify names 2020-01-18 07:43:59 -08:00
bd5f903f28 Add a locale example 2020-01-18 07:11:45 -08:00
06e437fd98 Move docs to the proper place 2020-01-18 06:59:21 -08:00
1bd4f54fa6 update format 2020-01-17 11:07:55 -08:00
11cc2903e4 re-fix url link 2020-01-17 11:07:55 -08:00
b124e3e8e7 fix url link 2020-01-17 11:07:55 -08:00
ffd5f3469f Correct display format 2020-01-17 11:07:55 -08:00
0f0e5ddf5f Add vcpkg installation instructions 2020-01-17 11:07:55 -08:00
1f110702a1 Remove redundant braces 2020-01-15 15:07:30 -08:00
4ccbe4b5f2 Avoid namespace clash for fmt
## Problem

In the case of an existing `fmt` namespace (in my project this looks like `Project::fmt`) it is possible to get a namespace clash in debug builds (MSVC 2017)

## Proposed Solution

When referencing `fmt` internally, be explicit that it is relative to the global namespace using `::fmt`
2020-01-15 11:23:24 -08:00
40638a75b3 Use C++11 compatible std::is_same operations
The `operator()` member function of `std::is_same` was added in C++14.  For C++11, the `::value` needs to be used instead.
2020-01-15 11:20:47 -08:00
c8dd9cc99d Use type_identity to block unnecessary template argument deduction (thanks Tim Song) 2020-01-15 10:27:50 -08:00
4bbe57cebf Work-around for nvcc
The nvcc compiler (at least up to 9.2) defines `__SIZEOF_INT128__`, but doesn't support 128-bit integers on device code:
```
error: "fmt::v6::format_arg_store<fmt::v6::basic_format_context<std::back_insert_iterator<fmt::v6::internal::buffer<char>>, char>, const char *, int, const char *>" contains a 128-bit integer, which is not supported in device code
```
2020-01-15 07:31:58 -08:00
55b6130055 Use C++11-compatible operations
The `std::is_base_of<T,U>()` and `std::is_reference<T>()` member functions were added in C++14.  To maintain C++11 compatibility, use the `::value` instead.

Current code fails on intel-17 and other compilers if using strict C++11
2020-01-15 07:23:39 -08:00
ae3ea156ea Fix for older versions of intel compiler
The intel-17 and intel-18 compilers seem to require that `u` be `const`:
```
/src/fmt/format.h(226): warning #437: reference to local variable of enclosing function is not allowed
        char data[sizeof(u)];
```
If `u` is declared as `const auto u =1u` instead of just `auto u=1u`, the file compiles with no warnings.
2020-01-14 17:56:06 -08:00
77165fdf85 Use FMT_NOEXCEPT instead of noexcept directly
Otherwise breaks on compilers without noexcept support
2020-01-14 09:31:31 -08:00
65ac626c58 Improve join docs 2020-01-12 07:26:16 -08:00
cd0b3f9695 check if _SECURE_SCL is defined not equal to 0 2020-01-09 15:01:48 -08:00
cef1e4354b Optimize grisu_gen_digits 2020-01-07 16:11:18 -10:00
0201c8db21 Restructure float_format 2020-01-07 14:41:30 -10:00
9e3f3e8cff Fix handling of output iterators in format_to_n (#1506) 2020-01-05 09:59:01 -10:00
aa07c57654 Move vprint_mojibake to the internal namespace 2020-01-04 08:31:18 -10:00
a73d89e9c7 Catch invalid uses of fmt::arg 2020-01-04 07:11:35 -10:00
cb8e7caf7c Convert 'char8_t' character sequences to 'char' sequences
Otherwise, Google Test will insist on inserting 'char8_t' NTBS into 'char' streams, but basic_ostream<char>::operator<< overloads taking 'char8_t' arguments are defined as deleted by P1423.
Handling individual 'char8_t's is done inline.

This fixes the compilation errors seen in C++20 mode beginning with VS2019 Update 2.

Signed-off-by: Daniela Engert <dani@ngrt.de>
2020-01-04 07:01:48 -10:00
b3fd0005dd Suppress a bogus -Wdouble-promotion warning 2020-01-03 06:57:14 -10:00
7b478f9dec Simplify example 2020-01-02 07:31:45 -10:00
c85efef312 More showpoint fixes and tests (#1498) 2019-12-30 13:08:35 -10:00
455a7c0787 Clarify lifetime of basic_format_args 2019-12-30 08:51:47 -10:00
674c326d7c Update syntax.rst 2019-12-29 07:25:34 -10:00
061a9897fe Update syntax.rst 2019-12-26 16:49:40 -10:00
d2d1c9c560 warning C4267: 'argument': conversion from 'size_t' to 'DWORD', possible loss of data 2019-12-26 08:07:24 -08:00
b6e19e5953 Update apidoc 2019-12-24 12:08:37 -08:00
f219dcd59b Add fmt::bytes 2019-12-24 10:45:15 -08:00
dea7fde8b7 Deprecate u8string_view 2019-12-24 09:44:57 -08:00
5390e29d42 Enable mojibake 2019-12-24 08:42:29 -08:00
9f6434dcde Improve UTF-8 handling on Windows 2019-12-23 16:19:11 -08:00
dac9a7f99d Improve UTF-8 handling on Windows 2019-12-22 12:05:28 -08:00
3ca9533f38 Flatten forward 2019-12-22 08:23:54 -08:00
7eec036d9a Improve UTF-8 support 2019-12-21 19:53:52 -08:00
e6b37b4aff Handle block boundaries in utf8_to_utf16 2019-12-21 16:33:34 -08:00
8cf4c52068 Apply clang-format 2019-12-21 13:10:45 -08:00
74532c23a3 Make type a scoped enum 2019-12-21 12:22:17 -08:00
b308159be5 Make round_direction a scoped enum 2019-12-21 09:24:42 -08:00
162995fedd Add os.h to docs 2019-12-18 14:17:49 -08:00
8b41362a0a Add trailing decimal point if # is specified (#1476) 2019-12-18 12:41:56 -08:00
1b1c70108a trailing_zeros -> showpoint 2019-12-18 12:12:09 -08:00
d7e72a09e0 Simplify FMT_STRING_IMPL 2019-12-18 11:50:47 -08:00
2201890d7a Apply clang-format and update inclusion guards 2019-12-18 11:17:36 -08:00
6100ed4bb3 Eliminate NVCC NVidia compiler emits unreachable code warnings
Similar to the MSC Compiler, the NVidia NVCC compiler also
emits unreachable code warnings when there is a return
statement following an exception.  These changes eliminate
those warnings.
2019-12-18 10:39:40 -08:00
1afe201ae8 Handle block boundaries in utf8_to_utf16 2019-12-18 10:35:51 -08:00
cd2b99032f Chore(readme): use https (#1481) 2019-12-17 06:48:27 -08:00
9acf89fef6 Mitigate MSVC issue with min/max macros (#1480) 2019-12-16 08:24:00 -08:00
9ea42fb26e Rename posix-test to os-test 2019-12-15 16:43:40 -08:00
da2569827e posix.cc -> os.cc 2019-12-15 12:36:15 -08:00
35959a31d7 Move OS-specific APIs to a separate header 2019-12-15 11:46:45 -08:00
ec2463c905 Implement utf8_to_utf16 using utf8_decode 2019-12-15 09:35:26 -08:00
0012917f69 Add a UTF-8 decoder 2019-12-15 07:28:19 -08:00
9e450911fa Give an error on precision overflow 2019-12-14 07:45:27 -08:00
068d20bc31 Avoid shadowing warnings in FMT_STRING 2019-12-14 07:00:08 -08:00
a99fbe67b9 Apply a typo fix retroactively 2019-12-14 06:50:53 -08:00
adbed11ed4 Fix a typo 2019-12-13 15:49:40 -08:00
8ab1c5c6e8 Squelch MSVC warning exporting subclasses of runtime_error (fix for PR #1433) (#1470)
* Squelch MSVC warning exporting subclasses of runtime_error

When compiling {fmt} as a DLL, MSVC complains that we are exporting
classes that inherit from "std::runtime_error", which we are not
exporting.

In this case, it's not really a problem because that symbol is already
exported via the C++ stdlib. So we just add a pragma to silence the
warning.

* Fix compilation with MinGW

Commit 3bc28fcc6b ("Squelch MSVC warning exporting subclasses of
runtime_error", 2019-11-29) silenced a MSVC warning under. The MinGW
compiler also defines _WIN32, but does not support the "warning" pragma.

Introduce a helper macro to squelch the MSVC warning only when using the
Microsoft compiler.

Signed-off-by: Beat Bolli <dev@drbeat.li>

* Fix compilation with VS2015 (#1450)

VS2015 does not support the __pragma(...) syntax in the midst of a
class declaration, so move it to just before the declaration.
2019-12-13 12:16:36 -08:00
a770009fcc Improve error reporting 2019-12-13 11:34:03 -08:00
598e6042d1 warning C4468: 'fallthrough': attribute must be followed by a case label or a default label 2019-12-12 06:25:33 -08:00
e09814dc93 Merge branch 'master' of github.com:fmtlib/fmt 2019-12-11 15:44:44 -08:00
b272fb3605 Extend FMT_FALLTHROUGH compatibily to gcc and clang pre-C++17 (#1469) 2019-12-11 14:28:20 -08:00
f94b7364b9 Update version 2019-12-11 06:16:42 -08:00
7abec071b5 Update changelog 2019-12-11 06:15:06 -08:00
b7eb8c8921 Prepare for the next release 2019-12-10 21:50:14 -08:00
ae7c50185d Reintroduce sprintf_format for ABI compatibility 2019-12-10 20:44:08 -08:00
9f2e7edaeb Fix handling of types convertible to std::string_view 2019-12-09 13:25:08 -08:00
fd52de0c6b Add FMT_CUDA_TEST CMake option to enable cuda-test 2019-12-09 07:30:34 -08:00
f675cb887e Remove redundant cast 2019-12-08 18:01:59 -08:00
73a16b827f Fix handling of int128_t in format-impl-test (#1461) 2019-12-08 17:07:20 -08:00
72879db40e Clean-up sign-conversion warnings in public headers 2019-12-08 16:07:55 -08:00
d3aa0c3a28 Clean-up sign-conversion warnings in test code 2019-12-08 16:07:43 -08:00
31de9a1b80 Revert "Clean-up sign-conversion warnings in test code"
This reverts commit 227bfe62dd.
2019-12-08 15:47:24 -08:00
227bfe62dd Clean-up sign-conversion warnings in test code 2019-12-08 15:21:38 -08:00
95dfdc6cc4 Update README.rst 2019-12-07 10:12:56 -08:00
5916ff63c4 Update README.rst 2019-12-07 10:12:15 -08:00
1ab80aa92c Fix handling of types with custom formatters that are convertible to std::string_view 2019-12-06 11:40:21 -08:00
4f4d876616 Remove '%' from the docs 2019-12-06 07:06:19 -08:00
f443bd3baf Ditch decimal_formatter (#1363) 2019-12-05 19:07:45 -08:00
1219b65f21 Relax fallthrough attribute detection 2019-12-05 10:40:15 -08:00
071794ec65 Update version 2019-12-04 12:21:48 -08:00
d22e4ad85b Remove trailing comma 2019-12-04 12:20:52 -08:00
983806b0c1 Update changelog 2019-12-04 12:03:44 -08:00
02af5beb8a Bump version and update changelog 2019-12-04 10:22:07 -08:00
123e7f7fc3 Revert #1433 because of build failures (#1450) 2019-12-03 09:24:15 -08:00
168460f02c Remove TYPES 2019-12-03 06:45:00 -08:00
a64f60c849 Remove unneeded FMT_API. 2019-12-03 05:55:04 -08:00
1a599117d8 Export assert_fail with FMT_API. This fixes dll build. 2019-12-03 05:55:04 -08:00
b160123e39 Update ChangeLog.rst 2019-12-02 16:18:06 -08:00
5981588565 Fix compilation with MinGW
Commit 3bc28fcc6b ("Squelch MSVC warning exporting subclasses of
runtime_error", 2019-11-29) silenced a MSVC warning under. The MinGW
compiler also defines _WIN32, but does not support the "warning" pragma.

Introduce a helper macro to squelch the MSVC warning only when using the
Microsoft compiler.

Signed-off-by: Beat Bolli <dev@drbeat.li>
2019-12-02 12:56:46 -08:00
8bbe76af3a Add a missing decimal point in exponent notation with trailing zeros 2019-12-02 11:36:33 -08:00
4ca6821e8f Update version 2019-12-01 16:58:40 -08:00
7111a1eb9f Bump version 2019-12-01 16:50:07 -08:00
ae00bbdc91 Update changelog 2019-12-01 16:39:58 -08:00
e71e07d9fb Update changlog 2019-12-01 16:26:07 -08:00
0184df7020 Update docs 2019-12-01 14:49:56 -08:00
1cbae6e9ba Put vprint declarations in one place 2019-12-01 14:43:51 -08:00
159f89e2b8 Fixing installation directory of '*.dll' files on Windows 2019-12-01 14:36:21 -08:00
4b120b68ae Clean up includes 2019-12-01 10:19:07 -08:00
186b225d9d Update changlog 2019-12-01 10:16:01 -08:00
4cbf4888ea Update changelog 2019-12-01 10:04:38 -08:00
e31f2b3d03 Update changelog 2019-12-01 09:27:28 -08:00
62da1db62a Avoid wchar_t instantiations 2019-12-01 07:34:09 -08:00
3bc28fcc6b Squelch MSVC warning exporting subclasses of runtime_error
When compiling {fmt} as a DLL, MSVC complains that we are exporting
classes that inherit from "std::runtime_error", which we are not
exporting.

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

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

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

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

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

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

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

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

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

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

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

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

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

* More fixes for VS2019 pedantic warnings

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

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

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

* Add defaulted copy and move operations to format_error and system_error

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

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

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

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

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

* Fix undefined in printf-test

Fixes "signed integer overflow" in PrintfTest.Length

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

* Fix undefined in printf %0$

Printf counts arguments from 1.

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

`do_get` is called with index -1 when `basic_printf_context.arg` is called with
id 4294967295 when basic_printf_context::get_arg subtracts 1 from arg_index 0 in
the format string "%0$d".
2019-10-08 06:28:39 -07:00
b60114533f Implement more bigint operations 2019-10-06 12:49:23 -07:00
c41cea8b18 Initial implementation of square 2019-10-05 16:37:52 -07:00
0c7650373c Fix handling of types convertible to std::string_view 2019-10-05 06:58:37 -07:00
0571013709 Repoint one more Python 2 link to Python 3
One more python reference, this one acknowledging the `str.format` function.
2019-10-01 11:44:36 -07:00
d2c9276fcd let README point to python 3 instead of 2 2019-09-29 08:30:47 -07:00
70 changed files with 7986 additions and 5254 deletions

2
.gitignore vendored
View File

@ -1,4 +1,5 @@
.vscode/
.vs/
*.iml
.idea/
@ -14,6 +15,7 @@ bin/
/CMakeScripts
/doc/doxyxml
/doc/html
/doc/node_modules
virtualenv
/Testing
/install_manifest.txt

View File

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

View File

@ -24,15 +24,40 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE)
endfunction()
include(CMakeParseArguments)
# Sets a cache variable with a docstring joined from multiple arguments:
# set(<variable> <value>... CACHE <type> <docstring>...)
# This allows splitting a long docstring for readability.
function(set_verbose)
# cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
# list instead.
list(GET ARGN 0 var)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 val)
list(REMOVE_AT ARGN 0)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 type)
list(REMOVE_AT ARGN 0)
join(doc ${ARGN})
set(${var} ${val} CACHE ${type} ${doc})
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 (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})
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif ()
project(FMT CXX)
include(GNUInstallDirs)
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
"Installation directory for include files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
@ -42,8 +67,8 @@ 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 CXX)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
# Get version from core.h
file(READ include/fmt/core.h core_h)
@ -60,15 +85,24 @@ message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths)
set(FMT_REQUIRED_FEATURES cxx_auto_type cxx_variadic_templates)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
# compatibility with older CMake versions.
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
endif ()
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
@ -78,7 +112,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion
-Wconversion -Wswitch-enum
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
@ -98,7 +132,7 @@ endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion)
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@ -122,7 +156,9 @@ if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
endif ()
# Set FrameworkPathOverride to get rid of MSB3644 warnings.
set(netfxpath "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.0")
join(netfxpath
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
".NETFramework\\v4.0")
file(WRITE run-msbuild.bat "
${MSBUILD_SETUP}
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
@ -135,10 +171,8 @@ endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(open fcntl.h HAVE_OPEN)
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
@ -152,12 +186,11 @@ endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
locale.h ostream.h printf.h ranges.h
safe-duration-cast.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
locale.h os.h ostream.h posix.h printf.h ranges.h)
if (FMT_OS)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
@ -167,6 +200,10 @@ if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (MINGW)
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif ()
if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif ()
@ -178,14 +215,23 @@ target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX d)
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
# property because it's not set by default.
set(FMT_LIB_NAME fmt)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
endif ()
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND NOT EMSCRIPTEN)
# 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)
@ -200,43 +246,40 @@ 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>)
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
# Install targets.
if (FMT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
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)
if (TARGET fmt-header-only)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
${version_config}
VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion)
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}"
@ -245,6 +288,8 @@ if (FMT_INSTALL)
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only)
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
@ -259,11 +304,13 @@ if (FMT_INSTALL)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif ()
@ -279,13 +326,14 @@ endif ()
# Control fuzzing independent of the unit tests.
if (FMT_FUZZ)
add_subdirectory(test/fuzzing)
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore.
file (STRINGS ${gitignore} lines)
LIST(REMOVE_ITEM lines /doc/html)
list(REMOVE_ITEM lines /doc/html)
foreach (line ${lines})
string(REPLACE "." "[.]" line "${line}")
string(REPLACE "*" ".*" line "${line}")

View File

@ -1,3 +1,827 @@
7.0.2 - 2020-07-29
------------------
* Worked around broken ``numeric_limits`` for 128-bit integers
(`#1725 <https://github.com/fmtlib/fmt/issues/1725>`_).
* Fixed compatibility with CMake 3.4
(`#1779 <https://github.com/fmtlib/fmt/issues/1779>`_).
* Fixed handling of digit separators in locale-specific formatting
(`#1782 <https://github.com/fmtlib/fmt/issues/1782>`_).
7.0.1 - 2020-07-07
------------------
* Updated the inline version namespace name.
* Worked around a gcc bug in mangling of alias templates
(`#1753 <https://github.com/fmtlib/fmt/issues/1753>`_).
* Fixed a linkage error on Windows
(`#1757 <https://github.com/fmtlib/fmt/issues/1757>`_).
Thanks `@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_.
* Fixed minor issues with the documentation.
7.0.0 - 2020-07-05
------------------
* Reduced the library size. For example, on macOS a stripped test binary
statically linked with {fmt} `shrank from ~368k to less than 100k
<http://www.zverovich.net/2020/05/21/reducing-library-size.html>`_.
* Added a simpler and more efficient `format string compilation API
<https://fmt.dev/dev/api.html#compile-api>`_:
.. code:: c++
#include <fmt/compile.h>
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
The old ``fmt::compile`` API is now deprecated.
* Optimized integer formatting: ``format_to`` with format string compilation
and a stack-allocated buffer is now `faster than to_chars on both
libc++ and libstdc++
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
* Optimized handling of small format strings. For example,
.. code:: c++
fmt::format("Result: {}: ({},{},{},{})", str1, str2, str3, str4, str5)
is now ~40% faster (`#1685 <https://github.com/fmtlib/fmt/issues/1685>`_).
* Applied extern templates to improve compile times when using the core API
and ``fmt/format.h`` (`#1452 <https://github.com/fmtlib/fmt/issues/1452>`_).
For example, on macOS with clang the compile time of a test translation unit
dropped from 2.3s to 0.3s with ``-O2`` and from 0.6s to 0.3s with the default
settings (``-O0``).
Before (``-O2``)::
% time c++ -c test.cc -I include -std=c++17 -O2
c++ -c test.cc -I include -std=c++17 -O2 2.22s user 0.08s system 99% cpu 2.311 total
After (``-O2``)::
% time c++ -c test.cc -I include -std=c++17 -O2
c++ -c test.cc -I include -std=c++17 -O2 0.26s user 0.04s system 98% cpu 0.303 total
Before (default)::
% time c++ -c test.cc -I include -std=c++17
c++ -c test.cc -I include -std=c++17 0.53s user 0.06s system 98% cpu 0.601 total
After (default)::
% time c++ -c test.cc -I include -std=c++17
c++ -c test.cc -I include -std=c++17 0.24s user 0.06s system 98% cpu 0.301 total
It is still recommended to use ``fmt/core.h`` instead of ``fmt/format.h`` but
the compile time difference is now smaller. Thanks
`@alex3d <https://github.com/alex3d>`_ for the suggestion.
* Named arguments are now stored on stack (no dynamic memory allocations) and
the compiled code is more compact and efficient. For example
.. code:: c++
#include <fmt/core.h>
int main() {
fmt::print("The answer is {answer}\n", fmt::arg("answer", 42));
}
compiles to just (`godbolt <https://godbolt.org/z/NcfEp_>`__)
.. code:: asm
.LC0:
.string "answer"
.LC1:
.string "The answer is {answer}\n"
main:
sub rsp, 56
mov edi, OFFSET FLAT:.LC1
mov esi, 23
movabs rdx, 4611686018427387905
lea rax, [rsp+32]
lea rcx, [rsp+16]
mov QWORD PTR [rsp+8], 1
mov QWORD PTR [rsp], rax
mov DWORD PTR [rsp+16], 42
mov QWORD PTR [rsp+32], OFFSET FLAT:.LC0
mov DWORD PTR [rsp+40], 0
call fmt::v6::vprint(fmt::v6::basic_string_view<char>,
fmt::v6::format_args)
xor eax, eax
add rsp, 56
ret
.L.str.1:
.asciz "answer"
* Implemented compile-time checks for dynamic width and precision
(`#1614 <https://github.com/fmtlib/fmt/issues/1614>`_):
.. code:: c++
#include <fmt/format.h>
int main() {
fmt::print(FMT_STRING("{0:{1}}"), 42);
}
now gives a compilation error because argument 1 doesn't exist::
In file included from test.cc:1:
include/fmt/format.h:2726:27: error: constexpr variable 'invalid_format' must be
initialized by a constant expression
FMT_CONSTEXPR_DECL bool invalid_format =
^
...
include/fmt/core.h:569:26: note: in call to
'&checker(s, {}).context_->on_error(&"argument not found"[0])'
if (id >= num_args_) on_error("argument not found");
^
* Added sentinel support to ``fmt::join``
(`#1689 <https://github.com/fmtlib/fmt/pull/1689>`_)
.. code:: c++
struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
};
auto s = fmt::format("{}", fmt::join(zstring{"hello"}, "_"));
// s == "h_e_l_l_o"
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Added support for named args, ``clear`` and ``reserve`` to
``dynamic_format_arg_store``
(`#1655 <https://github.com/fmtlib/fmt/issues/1655>`_,
`#1663 <https://github.com/fmtlib/fmt/pull/1663>`_,
`#1674 <https://github.com/fmtlib/fmt/pull/1674>`_,
`#1677 <https://github.com/fmtlib/fmt/pull/1677>`_).
Thanks `@vsolontsov-ll (Vladimir Solontsov)
<https://github.com/vsolontsov-ll>`_.
* Added support for the ``'c'`` format specifier to integral types for
compatibility with ``std::format``
(`#1652 <https://github.com/fmtlib/fmt/issues/1652>`_).
* Replaced the ``'n'`` format specifier with ``'L'`` for compatibility with
``std::format`` (`#1624 <https://github.com/fmtlib/fmt/issues/1624>`_).
The ``'n'`` specifier can be enabled via the ``FMT_DEPRECATED_N_SPECIFIER``
macro.
* The ``'='`` format specifier is now disabled by default for compatibility with
``std::format``. It can be enabled via the ``FMT_DEPRECATED_NUMERIC_ALIGN``
macro.
* Removed the following deprecated APIs:
* ``FMT_STRING_ALIAS`` and ``fmt`` macros - replaced by ``FMT_STRING``
* ``fmt::basic_string_view::char_type`` - replaced by
``fmt::basic_string_view::value_type``
* ``convert_to_int``
* ``format_arg_store::types``
* ``*parse_context`` - replaced by ``*format_parse_context``
* ``FMT_DEPRECATED_INCLUDE_OS``
* ``FMT_DEPRECATED_PERCENT`` - incompatible with ``std::format``
* ``*writer`` - replaced by compiled format API
* Renamed the ``internal`` namespace to ``detail``
(`#1538 <https://github.com/fmtlib/fmt/issues/1538>`_). The former is still
provided as an alias if the ``FMT_USE_INTERNAL`` macro is defined.
* Improved compatibility between ``fmt::printf`` with the standard specs
(`#1595 <https://github.com/fmtlib/fmt/issues/1595>`_,
`#1682 <https://github.com/fmtlib/fmt/pull/1682>`_,
`#1683 <https://github.com/fmtlib/fmt/pull/1683>`_,
`#1687 <https://github.com/fmtlib/fmt/pull/1687>`_,
`#1699 <https://github.com/fmtlib/fmt/pull/1699>`_).
Thanks `@rimathia <https://github.com/rimathia>`_.
* Fixed handling of ``operator<<`` overloads that use ``copyfmt``
(`#1666 <https://github.com/fmtlib/fmt/issues/1666>`_).
* Added the ``FMT_OS`` CMake option to control inclusion of OS-specific APIs
in the fmt target. This can be useful for embedded platforms
(`#1654 <https://github.com/fmtlib/fmt/issues/1654>`_,
`#1656 <https://github.com/fmtlib/fmt/pull/1656>`_).
Thanks `@kwesolowski (Krzysztof Wesolowski)
<https://github.com/kwesolowski>`_.
* Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ``
macro to prevent interferring with fuzzing of projects using {fmt}
(`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_).
Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_.
* Fixed compatibility with emscripten
(`#1636 <https://github.com/fmtlib/fmt/issues/1636>`_,
`#1637 <https://github.com/fmtlib/fmt/pull/1637>`_).
Thanks `@ArthurSonzogni (Arthur Sonzogni)
<https://github.com/ArthurSonzogni>`_.
* Improved documentation
(`#704 <https://github.com/fmtlib/fmt/issues/704>`_,
`#1643 <https://github.com/fmtlib/fmt/pull/1643>`_,
`#1660 <https://github.com/fmtlib/fmt/pull/1660>`_,
`#1681 <https://github.com/fmtlib/fmt/pull/1681>`_,
`#1691 <https://github.com/fmtlib/fmt/pull/1691>`_,
`#1706 <https://github.com/fmtlib/fmt/pull/1706>`_,
`#1714 <https://github.com/fmtlib/fmt/pull/1714>`_,
`#1721 <https://github.com/fmtlib/fmt/pull/1721>`_,
`#1739 <https://github.com/fmtlib/fmt/pull/1739>`_,
`#1740 <https://github.com/fmtlib/fmt/pull/1740>`_,
`#1741 <https://github.com/fmtlib/fmt/pull/1741>`_,
`#1751 <https://github.com/fmtlib/fmt/pull/1751>`_).
Thanks `@senior7515 (Alexander Gallego) <https://github.com/senior7515>`_,
`@lsr0 (Lindsay Roberts) <https://github.com/lsr0>`_,
`@puetzk (Kevin Puetz) <https://github.com/puetzk>`_,
`@fpelliccioni (Fernando Pelliccioni) <https://github.com/fpelliccioni>`_,
Alexey Kuzmenko, `@jelly (jelle van der Waa) <https://github.com/jelly>`_,
`@claremacrae (Clare Macrae) <https://github.com/claremacrae>`_,
`@jiapengwen (文佳鹏) <https://github.com/jiapengwen>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@alexey-milovidov <https://github.com/alexey-milovidov>`_.
* Implemented various build configuration fixes and improvements
(`#1603 <https://github.com/fmtlib/fmt/pull/1603>`_,
`#1657 <https://github.com/fmtlib/fmt/pull/1657>`_,
`#1702 <https://github.com/fmtlib/fmt/pull/1702>`_,
`#1728 <https://github.com/fmtlib/fmt/pull/1728>`_).
Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_,
`@jtojnar (Jan Tojnar) <https://github.com/jtojnar>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@flagarde <https://github.com/flagarde>`_.
* Fixed various warnings and compilation issues
(`#1616 <https://github.com/fmtlib/fmt/pull/1616>`_,
`#1620 <https://github.com/fmtlib/fmt/issues/1620>`_,
`#1622 <https://github.com/fmtlib/fmt/issues/1622>`_,
`#1625 <https://github.com/fmtlib/fmt/issues/1625>`_,
`#1627 <https://github.com/fmtlib/fmt/pull/1627>`_,
`#1628 <https://github.com/fmtlib/fmt/issues/1628>`_,
`#1629 <https://github.com/fmtlib/fmt/pull/1629>`_,
`#1631 <https://github.com/fmtlib/fmt/issues/1631>`_,
`#1633 <https://github.com/fmtlib/fmt/pull/1633>`_,
`#1649 <https://github.com/fmtlib/fmt/pull/1649>`_,
`#1658 <https://github.com/fmtlib/fmt/issues/1658>`_,
`#1661 <https://github.com/fmtlib/fmt/pull/1661>`_,
`#1667 <https://github.com/fmtlib/fmt/pull/1667>`_,
`#1668 <https://github.com/fmtlib/fmt/issues/1668>`_,
`#1669 <https://github.com/fmtlib/fmt/pull/1669>`_,
`#1692 <https://github.com/fmtlib/fmt/issues/1692>`_,
`#1696 <https://github.com/fmtlib/fmt/pull/1696>`_,
`#1697 <https://github.com/fmtlib/fmt/pull/1697>`_,
`#1707 <https://github.com/fmtlib/fmt/issues/1707>`_,
`#1712 <https://github.com/fmtlib/fmt/pull/1712>`_,
`#1716 <https://github.com/fmtlib/fmt/pull/1716>`_,
`#1722 <https://github.com/fmtlib/fmt/pull/1722>`_,
`#1724 <https://github.com/fmtlib/fmt/issues/1724>`_,
`#1729 <https://github.com/fmtlib/fmt/pull/1729>`_,
`#1738 <https://github.com/fmtlib/fmt/pull/1738>`_,
`#1742 <https://github.com/fmtlib/fmt/issues/1742>`_,
`#1743 <https://github.com/fmtlib/fmt/issues/1743>`_,
`#1744 <https://github.com/fmtlib/fmt/pull/1744>`_,
`#1747 <https://github.com/fmtlib/fmt/issues/1747>`_,
`#1750 <https://github.com/fmtlib/fmt/pull/1750>`_).
Thanks `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@johnor (Johan) <https://github.com/johnor>`_,
`@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_,
`@invexed (James Beach) <https://github.com/invexed>`_,
`@peterbell10 <https://github.com/peterbell10>`_,
`@daixtrose (Markus Werle) <https://github.com/daixtrose>`_,
`@petrutlucian94 (Lucian Petrut) <https://github.com/petrutlucian94>`_,
`@Neargye (Daniil Goncharov) <https://github.com/Neargye>`_,
`@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@erthink (Leonid Yuriev) <https://github.com/erthink>`_,
`@tohammer (Tobias Hammer) <https://github.com/tohammer>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
6.2.1 - 2020-05-09
------------------
* Fixed ostream support in ``sprintf``
(`#1631 <https://github.com/fmtlib/fmt/issues/1631>`_).
* Fixed type detection when using implicit conversion to ``string_view`` and
ostream ``operator<<`` inconsistently
(`#1662 <https://github.com/fmtlib/fmt/issues/1662>`_).
6.2.0 - 2020-04-05
------------------
* Improved error reporting when trying to format an object of a non-formattable
type:
.. code:: c++
fmt::format("{}", S());
now gives::
include/fmt/core.h:1015:5: error: static_assert failed due to requirement
'formattable' "Cannot format argument. To make type T formattable provide a
formatter<T> specialization:
https://fmt.dev/latest/api.html#formatting-user-defined-types"
static_assert(
^
...
note: in instantiation of function template specialization
'fmt::v6::format<char [3], S, char>' requested here
fmt::format("{}", S());
^
if ``S`` is not formattable.
* Reduced the library size by ~10%.
* Always print decimal point if ``#`` is specified
(`#1476 <https://github.com/fmtlib/fmt/issues/1476>`_,
`#1498 <https://github.com/fmtlib/fmt/issues/1498>`_):
.. code:: c++
fmt::print("{:#.0f}", 42.0);
now prints ``42.``
* Implemented the ``'L'`` specifier for locale-specific numeric formatting to
improve compatibility with ``std::format``. The ``'n'`` specifier is now
deprecated and will be removed in the next major release.
* Moved OS-specific APIs such as ``windows_error`` from ``fmt/format.h`` to
``fmt/os.h``. You can define ``FMT_DEPRECATED_INCLUDE_OS`` to automatically
include ``fmt/os.h`` from ``fmt/format.h`` for compatibility but this will be
disabled in the next major release.
* Added precision overflow detection in floating-point formatting.
* Implemented detection of invalid use of ``fmt::arg``.
* Used ``type_identity`` to block unnecessary template argument deduction.
Thanks Tim Song.
* Improved UTF-8 handling
(`#1109 <https://github.com/fmtlib/fmt/issues/1109>`_):
.. code:: c++
fmt::print("┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Привет, мир!", 20);
now prints::
┌────────────────────┐
│ Привет, мир! │
└────────────────────┘
on systems that support Unicode.
* Added experimental dynamic argument storage
(`#1170 <https://github.com/fmtlib/fmt/issues/1170>`_,
`#1584 <https://github.com/fmtlib/fmt/pull/1584>`_):
.. code:: c++
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back("answer");
store.push_back(42);
fmt::vprint("The {} is {}.\n", store);
prints::
The answer is 42.
Thanks `@vsolontsov-ll (Vladimir Solontsov)
<https://github.com/vsolontsov-ll>`_.
* Made ``fmt::join`` accept ``initializer_list``
(`#1591 <https://github.com/fmtlib/fmt/pull/1591>`_).
Thanks `@Rapotkinnik (Nikolay Rapotkin) <https://github.com/Rapotkinnik>`_.
* Fixed handling of empty tuples
(`#1588 <https://github.com/fmtlib/fmt/issues/1588>`_).
* Fixed handling of output iterators in ``format_to_n``
(`#1506 <https://github.com/fmtlib/fmt/issues/1506>`_).
* Fixed formatting of ``std::chrono::duration`` types to wide output
(`#1533 <https://github.com/fmtlib/fmt/pull/1533>`_).
Thanks `@zeffy (pilao) <https://github.com/zeffy>`_.
* Added const ``begin`` and ``end`` overload to buffers
(`#1553 <https://github.com/fmtlib/fmt/pull/1553>`_).
Thanks `@dominicpoeschko <https://github.com/dominicpoeschko>`_.
* Added the ability to disable floating-point formatting via ``FMT_USE_FLOAT``,
``FMT_USE_DOUBLE`` and ``FMT_USE_LONG_DOUBLE`` macros for extremely
memory-constrained embedded system
(`#1590 <https://github.com/fmtlib/fmt/pull/1590>`_).
Thanks `@albaguirre (Alberto Aguirre) <https://github.com/albaguirre>`_.
* Made ``FMT_STRING`` work with ``constexpr`` ``string_view``
(`#1589 <https://github.com/fmtlib/fmt/pull/1589>`_).
Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_.
* Implemented a minor optimization in the format string parser
(`#1560 <https://github.com/fmtlib/fmt/pull/1560>`_).
Thanks `@IkarusDeveloper <https://github.com/IkarusDeveloper>`_.
* Improved attribute detection
(`#1469 <https://github.com/fmtlib/fmt/pull/1469>`_,
`#1475 <https://github.com/fmtlib/fmt/pull/1475>`_,
`#1576 <https://github.com/fmtlib/fmt/pull/1576>`_).
Thanks `@federico-busato (Federico) <https://github.com/federico-busato>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@refnum <https://github.com/refnum>`_.
* Improved documentation
(`#1481 <https://github.com/fmtlib/fmt/pull/1481>`_,
`#1523 <https://github.com/fmtlib/fmt/pull/1523>`_).
Thanks `@JackBoosY (Jack·Boos·Yu) <https://github.com/JackBoosY>`_,
`@imba-tjd (谭九鼎) <https://github.com/imba-tjd>`_.
* Fixed symbol visibility on Linux when compiling with ``-fvisibility=hidden``
(`#1535 <https://github.com/fmtlib/fmt/pull/1535>`_).
Thanks `@milianw (Milian Wolff) <https://github.com/milianw>`_.
* Implemented various build configuration fixes and improvements
(`#1264 <https://github.com/fmtlib/fmt/issues/1264>`_,
`#1460 <https://github.com/fmtlib/fmt/issues/1460>`_,
`#1534 <https://github.com/fmtlib/fmt/pull/1534>`_,
`#1536 <https://github.com/fmtlib/fmt/issues/1536>`_,
`#1545 <https://github.com/fmtlib/fmt/issues/1545>`_,
`#1546 <https://github.com/fmtlib/fmt/pull/1546>`_,
`#1566 <https://github.com/fmtlib/fmt/issues/1566>`_,
`#1582 <https://github.com/fmtlib/fmt/pull/1582>`_,
`#1597 <https://github.com/fmtlib/fmt/issues/1597>`_,
`#1598 <https://github.com/fmtlib/fmt/pull/1598>`_).
Thanks `@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_,
`@jwillikers (Jordan Williams) <https://github.com/jwillikers>`_,
`@stac47 (Laurent Stacul) <https://github.com/stac47>`_.
* Fixed various warnings and compilation issues
(`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_,
`#1461 <https://github.com/fmtlib/fmt/issues/1461>`_,
`#1470 <https://github.com/fmtlib/fmt/pull/1470>`_,
`#1480 <https://github.com/fmtlib/fmt/pull/1480>`_,
`#1485 <https://github.com/fmtlib/fmt/pull/1485>`_,
`#1492 <https://github.com/fmtlib/fmt/pull/1492>`_,
`#1493 <https://github.com/fmtlib/fmt/issues/1493>`_,
`#1504 <https://github.com/fmtlib/fmt/issues/1504>`_,
`#1505 <https://github.com/fmtlib/fmt/pull/1505>`_,
`#1512 <https://github.com/fmtlib/fmt/pull/1512>`_,
`#1515 <https://github.com/fmtlib/fmt/issues/1515>`_,
`#1516 <https://github.com/fmtlib/fmt/pull/1516>`_,
`#1518 <https://github.com/fmtlib/fmt/pull/1518>`_,
`#1519 <https://github.com/fmtlib/fmt/pull/1519>`_,
`#1520 <https://github.com/fmtlib/fmt/pull/1520>`_,
`#1521 <https://github.com/fmtlib/fmt/pull/1521>`_,
`#1522 <https://github.com/fmtlib/fmt/pull/1522>`_,
`#1524 <https://github.com/fmtlib/fmt/issues/1524>`_,
`#1530 <https://github.com/fmtlib/fmt/pull/1530>`_,
`#1531 <https://github.com/fmtlib/fmt/issues/1531>`_,
`#1532 <https://github.com/fmtlib/fmt/pull/1532>`_,
`#1539 <https://github.com/fmtlib/fmt/issues/1539>`_,
`#1547 <https://github.com/fmtlib/fmt/issues/1547>`_,
`#1548 <https://github.com/fmtlib/fmt/issues/1548>`_,
`#1554 <https://github.com/fmtlib/fmt/pull/1554>`_,
`#1567 <https://github.com/fmtlib/fmt/issues/1567>`_,
`#1568 <https://github.com/fmtlib/fmt/pull/1568>`_,
`#1569 <https://github.com/fmtlib/fmt/pull/1569>`_,
`#1571 <https://github.com/fmtlib/fmt/pull/1571>`_,
`#1573 <https://github.com/fmtlib/fmt/pull/1573>`_,
`#1575 <https://github.com/fmtlib/fmt/pull/1575>`_,
`#1581 <https://github.com/fmtlib/fmt/pull/1581>`_,
`#1583 <https://github.com/fmtlib/fmt/issues/1583>`_,
`#1586 <https://github.com/fmtlib/fmt/issues/1586>`_,
`#1587 <https://github.com/fmtlib/fmt/issues/1587>`_,
`#1594 <https://github.com/fmtlib/fmt/issues/1594>`_,
`#1596 <https://github.com/fmtlib/fmt/pull/1596>`_,
`#1604 <https://github.com/fmtlib/fmt/issues/1604>`_,
`#1606 <https://github.com/fmtlib/fmt/pull/1606>`_,
`#1607 <https://github.com/fmtlib/fmt/issues/1607>`_,
`#1609 <https://github.com/fmtlib/fmt/issues/1609>`_).
Thanks `@marti4d (Chris Martin) <https://github.com/marti4d>`_,
`@iPherian <https://github.com/iPherian>`_,
`@parkertomatoes <https://github.com/parkertomatoes>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@torsten48 <https://github.com/torsten48>`_,
`@tohammer (Tobias Hammer) <https://github.com/tohammer>`_,
`@lefticus (Jason Turner) <https://github.com/lefticus>`_,
`@ryusakki (Haise) <https://github.com/ryusakki>`_,
`@adnsv (Alex Denisov) <https://github.com/adnsv>`_,
`@fghzxm <https://github.com/fghzxm>`_,
`@refnum <https://github.com/refnum>`_,
`@pramodk (Pramod Kumbhar) <https://github.com/pramodk>`_,
`@Spirrwell <https://github.com/Spirrwell>`_,
`@scramsby (Scott Ramsby) <https://github.com/scramsby>`_.
6.1.2 - 2019-12-11
------------------
* Fixed ABI compatibility with ``libfmt.so.6.0.0``
(`#1471 <https://github.com/fmtlib/fmt/issues/1471>`_).
* Fixed handling types convertible to ``std::string_view``
(`#1451 <https://github.com/fmtlib/fmt/pull/1451>`_).
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* Made CUDA test an opt-in enabled via the ``FMT_CUDA_TEST`` CMake option.
* Fixed sign conversion warnings
(`#1440 <https://github.com/fmtlib/fmt/pull/1440>`_).
Thanks `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
6.1.1 - 2019-12-04
------------------
* Fixed shared library build on Windows
(`#1443 <https://github.com/fmtlib/fmt/pull/1443>`_,
`#1445 <https://github.com/fmtlib/fmt/issues/1445>`_,
`#1446 <https://github.com/fmtlib/fmt/pull/1446>`_,
`#1450 <https://github.com/fmtlib/fmt/issues/1450>`_).
Thanks `@egorpugin (Egor Pugin) <https://github.com/egorpugin>`_,
`@bbolli (Beat Bolli) <https://github.com/bbolli>`_.
* Added a missing decimal point in exponent notation with trailing zeros.
* Removed deprecated ``format_arg_store::TYPES``.
6.1.0 - 2019-12-01
------------------
* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal
representation with correct rounding by default:
.. code:: c++
#include <cmath>
#include <fmt/core.h>
int main() {
fmt::print("{}", M_PI);
}
prints ``3.141592653589793``.
* Made the fast binary to decimal floating-point formatter the default,
simplified it and improved performance. {fmt} is now 15 times faster than
libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10%
faster than double-conversion on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_:
================== ========= =======
Function Time (ns) Speedup
================== ========= =======
ostringstream 1,346.30 1.00x
ostrstream 1,195.74 1.13x
sprintf 995.08 1.35x
doubleconv 99.10 13.59x
fmt 88.34 15.24x
================== ========= =======
.. image:: https://user-images.githubusercontent.com/576385/
69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
* {fmt} no longer converts ``float`` arguments to ``double``. In particular this
improves the default (shortest) representation of floats and makes
``fmt::format`` consistent with ``std::format`` specs
(`#1336 <https://github.com/fmtlib/fmt/issues/1336>`_,
`#1353 <https://github.com/fmtlib/fmt/issues/1353>`_,
`#1360 <https://github.com/fmtlib/fmt/pull/1360>`_,
`#1361 <https://github.com/fmtlib/fmt/pull/1361>`_):
.. code:: c++
fmt::print("{}", 0.1f);
prints ``0.1`` instead of ``0.10000000149011612``.
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_.
* Made floating-point formatting output consistent with ``printf``/iostreams
(`#1376 <https://github.com/fmtlib/fmt/issues/1376>`_,
`#1417 <https://github.com/fmtlib/fmt/issues/1417>`_).
* Added support for 128-bit integers
(`#1287 <https://github.com/fmtlib/fmt/pull/1287>`_):
.. code:: c++
fmt::print("{}", std::numeric_limits<__int128_t>::max());
prints ``170141183460469231731687303715884105727``.
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the
output from different threads doesn't interleave
(`#1351 <https://github.com/fmtlib/fmt/pull/1351>`_).
Thanks `@tankiJong (Tanki Zhang) <https://github.com/tankiJong>`_.
* Made compile time in the header-only mode ~20% faster by reducing the number
of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from
``fmt/core.h`` to ``fmt/format.h``.
* Added an overload of ``fmt::join`` that works with tuples
(`#1322 <https://github.com/fmtlib/fmt/issues/1322>`_,
`#1330 <https://github.com/fmtlib/fmt/pull/1330>`_):
.. code:: c++
#include <tuple>
#include <fmt/ranges.h>
int main() {
std::tuple<char, int, float> t{'a', 1, 2.0f};
fmt::print("{}", t);
}
prints ``('a', 1, 2.0)``.
Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_.
* Changed formatting of octal zero with prefix from "00" to "0":
.. code:: c++
fmt::print("{:#o}", 0);
prints ``0``.
* The locale is now passed to ostream insertion (``<<``) operators
(`#1406 <https://github.com/fmtlib/fmt/pull/1406>`_):
.. code:: c++
#include <fmt/locale.h>
#include <fmt/ostream.h>
struct S {
double value;
};
std::ostream& operator<<(std::ostream& os, S s) {
return os << s.value;
}
int main() {
auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42});
// s == "0,42"
}
Thanks `@dlaugt (Daniel Laügt) <https://github.com/dlaugt>`_.
* Locale-specific number formatting now uses grouping
(`#1393 <https://github.com/fmtlib/fmt/issues/1393>`_
`#1394 <https://github.com/fmtlib/fmt/pull/1394>`_).
Thanks `@skrdaniel <https://github.com/skrdaniel>`_.
* Fixed handling of types with deleted implicit rvalue conversion to
``const char**`` (`#1421 <https://github.com/fmtlib/fmt/issues/1421>`_):
.. code:: c++
struct mystring {
operator const char*() const&;
operator const char*() &;
operator const char*() const&& = delete;
operator const char*() && = delete;
};
mystring str;
fmt::print("{}", str); // now compiles
* Enums are now mapped to correct underlying types instead of ``int``
(`#1286 <https://github.com/fmtlib/fmt/pull/1286>`_).
Thanks `@agmt (Egor Seredin) <https://github.com/agmt>`_.
* Enum classes are no longer implicitly converted to ``int``
(`#1424 <https://github.com/fmtlib/fmt/issues/1424>`_).
* Added ``basic_format_parse_context`` for consistency with C++20
``std::format`` and deprecated ``basic_parse_context``.
* Fixed handling of UTF-8 in precision
(`#1389 <https://github.com/fmtlib/fmt/issues/1389>`_,
`#1390 <https://github.com/fmtlib/fmt/pull/1390>`_).
Thanks `@tajtiattila (Attila Tajti) <https://github.com/tajtiattila>`_.
* {fmt} can now be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__ using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__
(`#1410 <https://github.com/fmtlib/fmt/pull/1410>`_)::
conda install -c conda-forge fmt
Thanks `@tdegeus (Tom de Geus) <https://github.com/tdegeus>`_.
* Added a CUDA test (`#1285 <https://github.com/fmtlib/fmt/pull/1285>`_,
`#1317 <https://github.com/fmtlib/fmt/pull/1317>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and
`@risa2000 <https://github.com/risa2000>`_.
* Improved documentation (`#1276 <https://github.com/fmtlib/fmt/pull/1276>`_,
`#1291 <https://github.com/fmtlib/fmt/issues/1291>`_,
`#1296 <https://github.com/fmtlib/fmt/issues/1296>`_,
`#1315 <https://github.com/fmtlib/fmt/pull/1315>`_,
`#1332 <https://github.com/fmtlib/fmt/pull/1332>`_,
`#1337 <https://github.com/fmtlib/fmt/pull/1337>`_,
`#1395 <https://github.com/fmtlib/fmt/issues/1395>`_
`#1418 <https://github.com/fmtlib/fmt/pull/1418>`_).
Thanks
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Various code improvements
(`#1358 <https://github.com/fmtlib/fmt/pull/1358>`_,
`#1407 <https://github.com/fmtlib/fmt/pull/1407>`_).
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@dpacbach (David P. Sicilia) <https://github.com/dpacbach>`_,
* Fixed compile-time format string checks for user-defined types
(`#1292 <https://github.com/fmtlib/fmt/issues/1292>`_).
* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer
(`#1377 <https://github.com/fmtlib/fmt/issues/1377>`_).
* Fixed various warnings and compilation issues
(`#1273 <https://github.com/fmtlib/fmt/issues/1273>`_,
`#1278 <https://github.com/fmtlib/fmt/pull/1278>`_,
`#1280 <https://github.com/fmtlib/fmt/pull/1280>`_,
`#1281 <https://github.com/fmtlib/fmt/issues/1281>`_,
`#1288 <https://github.com/fmtlib/fmt/issues/1288>`_,
`#1290 <https://github.com/fmtlib/fmt/pull/1290>`_,
`#1301 <https://github.com/fmtlib/fmt/pull/1301>`_,
`#1305 <https://github.com/fmtlib/fmt/issues/1305>`_,
`#1306 <https://github.com/fmtlib/fmt/issues/1306>`_,
`#1309 <https://github.com/fmtlib/fmt/issues/1309>`_,
`#1312 <https://github.com/fmtlib/fmt/pull/1312>`_,
`#1313 <https://github.com/fmtlib/fmt/issues/1313>`_,
`#1316 <https://github.com/fmtlib/fmt/issues/1316>`_,
`#1319 <https://github.com/fmtlib/fmt/issues/1319>`_,
`#1320 <https://github.com/fmtlib/fmt/pull/1320>`_,
`#1326 <https://github.com/fmtlib/fmt/pull/1326>`_,
`#1328 <https://github.com/fmtlib/fmt/pull/1328>`_,
`#1344 <https://github.com/fmtlib/fmt/issues/1344>`_,
`#1345 <https://github.com/fmtlib/fmt/pull/1345>`_,
`#1347 <https://github.com/fmtlib/fmt/pull/1347>`_,
`#1349 <https://github.com/fmtlib/fmt/pull/1349>`_,
`#1354 <https://github.com/fmtlib/fmt/issues/1354>`_,
`#1362 <https://github.com/fmtlib/fmt/issues/1362>`_,
`#1366 <https://github.com/fmtlib/fmt/issues/1366>`_,
`#1364 <https://github.com/fmtlib/fmt/pull/1364>`_,
`#1370 <https://github.com/fmtlib/fmt/pull/1370>`_,
`#1371 <https://github.com/fmtlib/fmt/pull/1371>`_,
`#1385 <https://github.com/fmtlib/fmt/issues/1385>`_,
`#1388 <https://github.com/fmtlib/fmt/issues/1388>`_,
`#1397 <https://github.com/fmtlib/fmt/pull/1397>`_,
`#1414 <https://github.com/fmtlib/fmt/pull/1414>`_,
`#1416 <https://github.com/fmtlib/fmt/pull/1416>`_,
`#1422 <https://github.com/fmtlib/fmt/issues/1422>`_
`#1427 <https://github.com/fmtlib/fmt/pull/1427>`_,
`#1431 <https://github.com/fmtlib/fmt/issues/1431>`_,
`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_).
Thanks `@hhb <https://github.com/hhb>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@neheb (Rosen Penev) <https://github.com/neheb>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_
`@leonklingele <https://github.com/leonklingele>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@kent-tri <https://github.com/kent-tri>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_,
`@marti4d (Chris Martin) <https://github.com/marti4d>`_.
6.0.0 - 2019-08-26
------------------
@ -92,16 +916,16 @@
#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
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>`_,
* Added experimental ``%`` 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>`_):

View File

@ -7,55 +7,61 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/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
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: https://stackoverflow.com/questions/tagged/fmt
**{fmt}** is an open-source formatting library for C++.
It can be used as a safe and fast alternative to (s)printf and iostreams.
`Documentation <https://fmt.dev/latest/>`__
Q&A: ask questions on `StackOverflow with the tag fmt <http://stackoverflow.com/questions/tagged/fmt>`_.
Q&A: ask questions on `StackOverflow with the tag fmt
<https://stackoverflow.com/questions/tagged/fmt>`_.
Features
--------
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization.
* Simple `format API <https://fmt.dev/dev/api.html>`_ with positional arguments
for localization
* Implementation of `C++20 std::format
<https://en.cppreference.com/w/cpp/utility/format>`__
* `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.
of Python's
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
* Safe `printf implementation
<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.
the POSIX extension for positional arguments
* Extensibility: support for user-defined types
* 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>`_.
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_,
iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ and
`Converting a hundred million integers to strings per second
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.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`_.
``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>`_ and is continuously fuzzed.
<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.
errors
* Ease of use: small self-contained code base, no external dependencies,
permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers.
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.
(``-Wall -Wextra -pedantic``)
* Locale-independence by default
* Support for wide strings
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
See the `documentation <https://fmt.dev/latest/>`_ for more details.
@ -66,38 +72,49 @@ Print ``Hello, world!`` to ``stdout``:
.. code:: c++
fmt::print("Hello, {}!", "world"); // Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // printf format string syntax
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
}
Format a string and use positional arguments:
Format a string:
.. code:: c++
std::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."
Format a string using positional arguments:
.. code:: c++
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."
Print a chrono duration:
.. code:: c++
#include <fmt/chrono.h>
int main() {
using namespace std::chrono_literals;
fmt::print("Elapsed time: {}", 42ms);
}
prints "Elapsed time: 42ms".
Check a format string at compile time:
.. code:: c++
// test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{:d}"), "hello");
.. code::
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message);
^
include/fmt/format.h:2226:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])'
context_.on_error("argument index out of range");
^
gives a compile-time error because ``d`` is an invalid format specifier for a
string.
Use {fmt} as a safe portable replacement for ``itoa``
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
@ -114,7 +131,7 @@ Format objects of user-defined types via a simple `extension API
.. code:: c++
#include "fmt/format.h"
#include <fmt/format.h>
struct date {
int year, month, day;
@ -122,11 +139,10 @@ Format objects of user-defined types via a simple `extension API
template <>
struct fmt::formatter<date> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) {
auto format(const date& d, FormatContext& ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
}
};
@ -142,12 +158,12 @@ which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++
// Prints formatted error message.
void vreport_error(const char *format, fmt::format_args args) {
void vreport_error(const char* format, fmt::format_args args) {
fmt::print("Error: ");
fmt::vprint(format, args);
}
template <typename... Args>
void report_error(const char *format, const Args & ... args) {
void report_error(const char* format, const Args & ... args) {
vreport_error(format, fmt::make_format_args(args...));
}
@ -166,28 +182,27 @@ Speed tests
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.01
libc++ std::ostream 3.04
{fmt} 1632f72 fmt::print 0.86
tinyformat 2.0.1 tfm::printf 3.23
Boost Format 1.67 boost::format 7.98
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~17% faster than ``printf``.
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.3 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
10.14.6 with ``clang++ -O3 -DNDEBUG -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>`_:
{fmt} is up to 10x faster than ``std::ostringstream`` and ``sprintf`` on
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and faster than `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
@ -211,7 +226,6 @@ printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
@ -232,14 +246,13 @@ printf 2.2 33 30
printf+string 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
tinyformat 32.6 88 82
Boost Format 54.1 365 303
Folly Format 79.9 445 430
============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
compare formatting function overhead only. Boost Format and tinyformat are
header-only libraries so they don't provide any linkage options.
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
@ -269,12 +282,15 @@ or the 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. <https://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
* `Aseprite <https://github.com/aseprite/aseprite>`_:
Animated sprite editor & pixel art tool
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
operations suite
@ -284,21 +300,12 @@ Projects using this library
* `ccache <https://ccache.dev/>`_: A compiler cache
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database management system
* `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
* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
@ -306,17 +313,36 @@ Projects using this library
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
* `KBEngine <https://kbengine.org/>`_: An open-source MMOG server engine
* `Keypirinha <https://keypirinha.com/>`_: A semantic launcher for Windows
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Knuth <https://kth.cash/>`_: High-performance Bitcoin full-node
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
Research programming language for concurrent ownership
* `MongoDB <https://mongodb.com/>`_: Distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization
framework
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
astrovisualization framework
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
An MMO server, compatible with most Ultima Online clients
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
learning library
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
associative database
@ -325,16 +351,17 @@ Projects using this library
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
proxy
* `redpanda <https://vectorized.io/redpanda>`_: A 10x faster Kafka® replacement
for mission critical systems written in C++
* `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
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
* `Salesforce Analytics Cloud
<https://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
* `Scylla <https://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++
@ -349,6 +376,9 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
Terminal
`More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know
@ -373,7 +403,7 @@ 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 somewhat mitigated with `__attribute__ ((format (printf, ...))
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
<https://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
@ -412,8 +442,8 @@ Format also has excessive build times and severe code bloat issues (see
FastFormat
~~~~~~~~~~
This is an interesting library which is fast, safe and has positional
arguments. However it has significant limitations, citing its author:
This is an interesting library which is fast, safe and has positional arguments.
However, it has significant limitations, citing its author:
Three features that have no hope of being accommodated within the
current design are:
@ -422,22 +452,8 @@ arguments. However it has significant limitations, citing its author:
* Octal/hexadecimal encoding
* Runtime width/alignment specification
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses ``printf``-like format strings and
is type safe. It doesn't support user-defined types or positional arguments and
makes unconventional use of ``operator()`` for passing format arguments.
Tinyformat
~~~~~~~~~~
This library supports ``printf``-like format strings and is very small .
It doesn't support positional arguments and wrapping it in C++98 is somewhat
difficult. Tinyformat relies on iostreams which limits its performance.
It is also quite big and has a heavy dependency, STLSoft, which might be too
restrictive for using it in some projects.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
@ -445,32 +461,9 @@ 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::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>`_.
FAQ
---
Q: how can I capture formatting arguments and format them later?
A: use ``std::tuple``:
.. code:: c++
template <typename... Args>
auto capture(const Args&... args) {
return std::make_tuple(args...);
}
auto print_message = [](const auto&... args) {
fmt::print(args...);
};
// Capture and store arguments:
auto args = capture("{} {}", 42, "foo");
// Do formatting:
std::apply(print_message, args);
than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
see `Converting a hundred million integers to strings per second
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
License
-------
@ -478,18 +471,19 @@ License
{fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax
<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
distributed under the Python Software Foundation license available in
`doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of fmt.
Documentation License
---------------------
Acknowledgments
---------------
The `Format String Syntax <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>`_.
For this reason the documentation is distributed under the Python Software
Foundation license available in `doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of {fmt}.
Maintainers
-----------
The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
@ -498,23 +492,3 @@ See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and
we'll make it right.
The benchmark section of this readme file and the performance tests are taken
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
written by Chris Foster. Boost Format library is acknowledged transitively
since it had some influence on tinyformat.
Some ideas used in the implementation are borrowed from `Loki
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
formatting. Thanks `Ruslan Baratov <https://github.com/ruslo>`_ for comprehensive
`comparison of integer formatting algorithms <https://github.com/ruslo/int-dec-format-tests>`_
and useful comments regarding performance, `Boris Kaul <https://github.com/localvoid>`_ for
`C++ counting digits benchmark <https://github.com/localvoid/cxx-benchmark-count-digits>`_.
Thanks to `CarterLi <https://github.com/CarterLi>`_ for contributing various
improvements to the code.

View File

@ -9,4 +9,5 @@ add_custom_target(doc
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)
DESTINATION share/doc/fmt OPTIONAL
PATTERN ".doctrees" EXCLUDE)

View File

@ -6,14 +6,13 @@
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
{# Google Analytics #}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;
a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-20116650-4', 'fmtlib.net');
ga('send', 'pageview');
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
{% endblock %}
@ -84,7 +83,7 @@
<div class="jumbotron">
<div class="tb-container">
<h1>{fmt}</h1>
<p class="lead">Small, safe and fast formatting library</p>
<p class="lead">A modern formatting library</p>
<div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success"

View File

@ -9,15 +9,17 @@ The {fmt} library API consists of the following parts:
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling
facilities and a lightweight subset of formatting functions
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
format string checks, output iterator and user-defined type support
format string checks, wide string, output iterator and user-defined type
support
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/compile.h <compile-api>`: format string compilation
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
All functions and types provided by the library reside in namespace ``fmt`` and
macros have prefix ``FMT_`` or ``fmt``.
macros have prefix ``FMT_``.
.. _core-api:
@ -43,7 +45,7 @@ participate in an overload resolution if the latter is not a string.
.. _format:
.. doxygenfunction:: format(const S&, Args&&...)
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>)
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
.. _print:
@ -52,7 +54,6 @@ participate in an overload resolution if the latter is not a string.
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
Named Arguments
---------------
@ -69,6 +70,9 @@ Argument Lists
.. doxygenclass:: fmt::format_arg_store
:members:
.. doxygenclass:: fmt::dynamic_format_arg_store
:members:
.. doxygenclass:: fmt::basic_format_args
:members:
@ -86,19 +90,35 @@ Compatibility
.. doxygentypedef:: fmt::string_view
.. doxygentypedef:: fmt::wstring_view
Locale
------
All formatting is locale-independent by default. Use the ``'n'`` format
specifier to insert the appropriate number separator characters from the
locale::
#include <fmt/core.h>
#include <locale>
std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:L}", 1000000); // s == "1,000,000"
.. _format-api:
Format API
==========
``fmt/format.h`` defines the full format API providing compile-time format
string checks, output iterator and user-defined type support.
string checks, wide string, output iterator and user-defined type support.
Compile-time Format String Checks
---------------------------------
Compile-time checks are supported for built-in and string types as well as
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
specializations.
.. doxygendefine:: FMT_STRING
.. doxygendefine:: fmt
Formatting User-defined Types
-----------------------------
@ -110,40 +130,65 @@ template and implement ``parse`` and ``format`` methods::
struct point { double x, y; };
namespace fmt {
template <>
struct formatter<point> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) {
// auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) // c++11
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". The formatter should
// parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'.
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range:
if (it != end && *it != '}')
throw format_error("invalid format");
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point &p, FormatContext &ctx) {
return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
auto format(const point& p, FormatContext& ctx) {
// auto format(const point &p, FormatContext &ctx) -> decltype(ctx.out()) // c++11
// ctx.out() is an output iterator to write to.
return format_to(
ctx.out(),
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
p.x, p.y);
}
};
}
Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2};
std::string s = fmt::format("{}", p);
std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)"
In the example above the ``formatter<point>::parse`` function ignores the
contents of the format string referred to by ``ctx.begin()`` so the object will
always be formatted in the same way. See ``formatter<tm>::parse`` in
:file:`fmt/chrono.h` for an advanced example of how to parse the format string and
customize the formatted output.
You can also reuse existing formatters, for example::
You can also reuse existing formatters via inheritance or composition, for
example::
enum class color {red, green, blue};
template <>
struct fmt::formatter<color>: formatter<string_view> {
template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext &ctx) {
auto format(color c, FormatContext& ctx) {
string_view name = "unknown";
switch (c) {
case color::red: name = "red"; break;
@ -154,6 +199,15 @@ You can also reuse existing formatters, for example::
}
};
Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize
all string format specifications, for example
.. code-block:: c++
fmt::format("{:>10}", color::blue)
will return ``" blue"``.
You can also write a formatter for a hierarchy of classes::
#include <type_traits>
@ -183,11 +237,14 @@ You can also write a formatter for a hierarchy of classes::
fmt::print("{}", a); // prints "B"
}
.. doxygenclass:: fmt::basic_format_parse_context
:members:
Output Iterator Support
-----------------------
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, Args&&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, const S&, const Args&...)
.. doxygenstruct:: fmt::format_to_n_result
:members:
@ -196,9 +253,9 @@ Literal-based API
The following user-defined literals are defined in ``fmt/format.h``.
.. doxygenfunction:: operator""_format(const char *, std::size_t)
.. doxygenfunction:: operator""_format(const char *, size_t)
.. doxygenfunction:: operator""_a(const char *, std::size_t)
.. doxygenfunction:: operator""_a(const char *, size_t)
Utilities
---------
@ -217,7 +274,10 @@ Utilities
.. doxygenfunction:: fmt::join(const Range&, string_view)
.. doxygenfunction:: fmt::join(It, It, string_view)
.. doxygenfunction:: fmt::join(It, Sentinel, string_view)
.. doxygenclass:: fmt::detail::buffer
:members:
.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
@ -266,7 +326,7 @@ allocator::
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args & ... args) {
const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_args(args...));
}
@ -275,50 +335,6 @@ arguments, the container that stores pointers to them will be allocated using
the default allocator. Also floating-point formatting falls back on ``sprintf``
which may do allocations.
Custom Formatting of Built-in Types
-----------------------------------
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
using arg_formatter = fmt::arg_formatter<fmt::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_parse_context* parse_ctx = nullptr,
fmt::format_specs* spec = nullptr)
: arg_formatter(ctx, parse_ctx, spec) {}
using arg_formatter::operator();
auto operator()(int value) {
if (specs() && specs()->type == 'x')
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value);
}
};
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer;
// Pass custom argument formatter as a template arg to vformat_to.
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args);
return fmt::to_string(buffer);
}
template <typename ...Args>
inline std::string custom_format(
fmt::string_view format_str, const Args &... args) {
return custom_vformat(format_str, fmt::make_format_args(args...));
}
std::string s = custom_format("{:x}", -42); // s == "ffffffd6"
.. doxygenclass:: fmt::arg_formatter
:members:
.. _ranges-api:
Ranges and Tuple Formatting
@ -357,11 +373,26 @@ formatting::
std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date)
fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t));
fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
The format string syntax is described in the documentation of
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
.. _compile-api:
Format string compilation
=========================
``fmt/compile.h`` provides format string compilation support. Format strings
are parsed at compile time and converted into efficient formatting code. This
supports arguments of built-in and string types as well as user-defined types
with ``constexpr`` ``parse`` functions in their ``formatter`` specializations.
Format string compilation can generate more binary code compared to the default
API and is only recommended in places where formatting is a performance
bottleneck.
.. doxygendefine:: FMT_COMPILE
.. _ostream-api:
``std::ostream`` Support
@ -377,7 +408,7 @@ user-defined types that have overloaded ``operator<<``::
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const date &d) {
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
@ -406,3 +437,19 @@ argument type doesn't match its format specification.
.. doxygenfunction:: fprintf(std::basic_ostream<Char>&, const S&, const Args&...)
.. doxygenfunction:: sprintf(const S&, const Args&...)
Compatibility with C++20 ``std::format``
========================================
{fmt} implements nearly all of the `C++20 formatting library
<https://en.cppreference.com/w/cpp/utility/format>`_ with the following
differences:
* Names are defined in the ``fmt`` namespace instead of ``std`` to avoid
collisions with standard library implementations.
* The ``'L'`` format specifier cannot be combined with presentation specifiers
yet.
* Width calculation doesn't use grapheme clusterization. The latter has been
implemented in a separate branch but hasn't been integrated yet.
* Chrono formatting doesn't support C++20 date types since they are not provided
by standard library implementations.

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0']
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."
@ -74,8 +74,8 @@ def build_docs(version='dev', **kwargs):
GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = {0}/core.h {0}/format.h {0}/ostream.h \
{0}/printf.h {0}/time.h
INPUT = {0}/core.h {0}/compile.h {0}/format.h {0}/os.h \
{0}/ostream.h {0}/printf.h {0}/time.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
AUTOLINK_SUPPORT = NO

View File

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

View File

@ -76,19 +76,19 @@ 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 '\0'>
align: "<" | ">" | "=" | "^"
fill: <a character other than '{' or '}'>
align: "<" | ">" | "^"
sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}"
precision: `integer` | "{" `arg_id` "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
width: `integer` | "{" [`arg_id`] "}"
precision: `integer` | "{" [`arg_id`] "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
int_type: "b" | "B" | "d" | "o" | "x" | "X"
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 *fill* character can be any Unicode code point 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 meaning of the various alignment options is as follows:
@ -143,7 +143,7 @@ conversions, trailing zeros are not removed from the result.
.. ifconfig:: False
The ``','`` option signals the use of a comma for a thousands separator.
For a locale aware separator, use the ``'n'`` integer presentation type
For a locale aware separator, use the ``'L'`` integer presentation type
instead.
*width* is a decimal integer defining the minimum field width. If not
@ -214,9 +214,9 @@ The available integer presentation types are:
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
| ``'L'`` | Locale-specific format. This is the same as ``'d'``, |
| | except that it uses the current locale setting to insert |
| | the appropriate number separator characters. |
+---------+----------------------------------------------------------+
| none | The same as ``'d'``. |
+---------+----------------------------------------------------------+
@ -261,13 +261,9 @@ The available presentation types for floating-point values are:
| | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
+---------+----------------------------------------------------------+
| ``'%'`` | Fixed point as a percentage. This is similar to ``'f'``, |
| | but the argument is multiplied by 100 and a percent sign |
| | ``%`` is appended. |
| ``'L'`` | Locale-specific format. This is the same as ``'g'``, |
| | except that it uses the current locale setting to insert |
| | the appropriate number separator characters. |
+---------+----------------------------------------------------------+
| none | Similar to ``'g'``, except that fixed-point notation, |
| | when used, has at least one digit past the decimal |
@ -324,79 +320,94 @@ following examples.
Accessing arguments by position::
format("{0}, {1}, {2}", 'a', 'b', 'c');
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c"
format("{}, {}, {}", 'a', 'b', 'c');
fmt::format("{}, {}, {}", 'a', 'b', 'c');
// Result: "a, b, c"
format("{2}, {1}, {0}", 'a', 'b', 'c');
fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
// Result: "c, b, a"
format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
// Result: "abracadabra"
Aligning the text and specifying a width::
format("{:<30}", "left aligned");
fmt::format("{:<30}", "left aligned");
// Result: "left aligned "
format("{:>30}", "right aligned");
fmt::format("{:>30}", "right aligned");
// Result: " right aligned"
format("{:^30}", "centered");
fmt::format("{:^30}", "centered");
// Result: " centered "
format("{:*^30}", "centered"); // use '*' as a fill char
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********"
Dynamic width::
format("{:<{}}", "left aligned", 30);
fmt::format("{:<{}}", "left aligned", 30);
// Result: "left aligned "
Dynamic precision::
format("{:.{}f}", 3.14, 1);
fmt::format("{:.{}f}", 3.14, 1);
// Result: "3.1"
Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::
format("{:+f}; {:+f}", 3.14, -3.14); // show it always
fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
// Result: "+3.140000; -3.140000"
format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
// Result: " 3.140000; -3.140000"
format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
fmt::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);
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
// with 0x or 0 or 0b as prefix:
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
Padded hex byte with prefix and always prints both hex characters::
format("{:#04x}", 0);
fmt::format("{:#04x}", 0);
// Result: "0x00"
Box drawing using Unicode fill::
fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
prints::
┌────────────────────┐
│ Hello, world! │
└────────────────────┘
Using type-specific formatting::
#include <fmt/chrono.h>
auto t = tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 6;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58
Using the comma as a thousands separator::
#include <fmt/locale.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"
.. ifconfig:: False
Using the comma as a thousands separator::
format("{:,}", 1234567890);
'1,234,567,890'
Using type-specific formatting::
>>> import datetime
>>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)
Format("{:%Y-%m-%d %H:%M:%S}") << d)
'2010-07-04 12:15:58'
Nesting arguments and more complex examples::
>>> for align, text in zip('<^>', ['left', 'center', 'right']):

View File

@ -103,7 +103,33 @@ the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documentation in ``doc/html``.
Conda
=====
fmt can be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__, using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows::
conda install -c conda-forge fmt
Vcpkg
=====
You can download and install fmt using the `vcpkg
<https://github.com/Microsoft/vcpkg>`__ dependency manager::
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install fmt
The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please `create an issue or pull
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
Android NDK
===========

View File

@ -8,35 +8,311 @@
#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
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < (T::min)() || from > (T::max)()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::detail::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 = detail::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 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
namespace detail {
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
} // namespace detail
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
@ -47,22 +323,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
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;
bool fallback(detail::null<>) {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@ -84,21 +360,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@ -111,17 +387,17 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
const std::tm* time) {
namespace detail {
inline size_t strftime(char* str, 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) {
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
} // namespace detail
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
@ -130,7 +406,7 @@ template <typename Char> struct formatter<std::tm, Char> {
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.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
@ -139,11 +415,10 @@ template <typename Char> struct formatter<std::tm, Char> {
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();
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);
size_t size = buf.capacity() - start;
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
@ -155,7 +430,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
@ -164,7 +439,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format;
};
namespace internal {
namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
@ -220,12 +495,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
const Char newline[] = {'\n'};
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
const Char tab[] = {'\t'};
handler.on_text(tab, tab + 1);
break;
}
@ -421,7 +696,7 @@ inline int to_nonnegative_int(T value, int upper) {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
return x % static_cast<T>(y);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
@ -484,18 +759,36 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
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}" : "{}",
template <typename Char, typename Rep, typename OutputIt>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0};
if (precision >= 0) return format_to(out, pr_f, val, precision);
const Char fp_f[] = {'{', ':', 'g', '}', 0};
const Char format[] = {'{', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
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 OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den);
}
template <typename FormatContext, typename OutputIt, typename Rep,
@ -518,7 +811,10 @@ struct chrono_formatter {
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
: context(ctx),
out(o),
val(static_cast<rep>(d.count())),
negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
@ -582,24 +878,24 @@ struct chrono_formatter {
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);
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = detail::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);
out = format_decimal<char_type>(out, n, num_digits).end;
}
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) {
void format_localized(const tm& time, char format, char modifier = 0) {
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));
facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
@ -629,7 +925,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH");
format_localized(time, 'H', 'O');
}
void on_12_hour(numeric_system ns) {
@ -638,7 +934,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI");
format_localized(time, 'I', 'O');
}
void on_minute(numeric_system ns) {
@ -647,7 +943,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM");
format_localized(time, 'M', 'O');
}
void on_second(numeric_system ns) {
@ -672,13 +968,12 @@ struct chrono_formatter {
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS");
format_localized(time, 'S', 'O');
}
void on_12_hour_time() {
if (handle_nan_inf()) return;
format_localized(time(), "%r");
format_localized(time(), 'r');
}
void on_24_hour_time() {
@ -702,25 +997,27 @@ struct chrono_formatter {
void on_am_pm() {
if (handle_nan_inf()) return;
format_localized(time(), "%p");
format_localized(time(), 'p');
}
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
out = format_duration_value<char_type>(out, val, precision);
}
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal
} // namespace detail
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>;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
@ -728,7 +1025,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@ -741,15 +1038,15 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::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_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned _precision) { f.precision = _precision; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
@ -761,38 +1058,38 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
using iterator = typename basic_parse_context<Char>::iterator;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
begin = detail::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);
begin = detail::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());
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end;
}
@ -803,23 +1100,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// 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);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
ctx);
detail::handle_dynamic_spec<detail::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);
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
detail::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();
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};

View File

@ -198,7 +198,7 @@ struct rgb {
uint8_t b;
};
namespace internal {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace internal
} // namespace detail
// Experimental text formatting support.
class text_style {
@ -298,22 +298,22 @@ class text_style {
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");
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
@ -326,23 +326,23 @@ class text_style {
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
detail::color_type foreground_color;
detail::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 {
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
FMT_CONSTEXPR ansi_color_escape(detail::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;
bool is_background = esc == detail::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;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
@ -412,7 +412,7 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
return buffer + std::char_traits<Char>::length(buffer);
}
private:
@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
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);
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::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);
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color);
}
template <typename Char>
@ -455,11 +455,11 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
fputs(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
@ -470,58 +470,37 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buffer, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
}
return fmt::to_string(buffer);
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace internal
} // namespace detail
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
/**
@ -532,11 +511,11 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::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> >;
detail::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));
}
@ -549,16 +528,18 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::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> >
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
@ -573,12 +554,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S> >
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return internal::vformat(
ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
return vformat(ts, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -9,10 +9,37 @@
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
@ -26,11 +53,11 @@ template <typename Char> struct format_part {
kind part_kind;
union value {
unsigned arg_index;
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
@ -40,7 +67,7 @@ template <typename Char> struct format_part {
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) {
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
@ -61,13 +88,15 @@ template <typename Char> struct part_counter {
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 int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
@ -101,7 +130,7 @@ class format_string_compiler : public error_handler {
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_parse_context<Char> parse_context_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
@ -115,29 +144,32 @@ class format_string_compiler : public error_handler {
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_parse_context<Char>> handler(repl.specs,
parse_context_);
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
@ -159,21 +191,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> 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)));
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(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 {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt 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);
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
@ -194,12 +229,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
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);
detail::format_arg<OutputIt>(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);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
@ -223,7 +258,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
@ -237,7 +274,7 @@ 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>>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
@ -302,7 +339,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
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()));
detail::to_string_view(S()));
return compiled_parts.data;
}
};
@ -315,8 +352,8 @@ class compiled_format : private compiled_format_base<S> {
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
@ -348,43 +385,27 @@ template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_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);
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
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;
@ -393,10 +414,30 @@ template <typename Char, typename T, int N> struct field {
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);
return write<Char>(out, arg);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
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...);
basic_format_context<OutputIt, Char> ctx(out, {});
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
@ -409,6 +450,9 @@ template <typename L, typename R> struct concat {
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
@ -429,7 +473,8 @@ 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()) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(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>())
@ -441,6 +486,21 @@ constexpr auto parse_tail(T head, S format_str) {
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1};
}
// 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>
@ -454,12 +514,13 @@ constexpr auto compile_format_string(S format_str) {
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();
}
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else if constexpr (str[POS + 1] == ':') {
using type = get_type<ID, Args>;
constexpr auto result = parse_specs<type>(str, POS + 2);
return parse_tail<Args, result.end, ID + 1>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else {
return unknown_format();
}
@ -473,107 +534,130 @@ constexpr auto compile_format_string(S format_str) {
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_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);
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
detail::compile_format_string<detail::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));
detail::unknown_format>()) {
return detail::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
#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));
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
#endif // __cpp_if_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...>(
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
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) {
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE 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...)});
detail::buffer<Char>& base = buffer;
cf.format(std::back_inserter(base), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::buffer<Char>& base = buffer;
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::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...)});
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
}
template <
typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
detail::basic_compiled_format, CompiledFormat>::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...);
format_to(detail::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();
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE

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

View File

@ -9,63 +9,64 @@
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
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));
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
args, detail::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) {
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
detail::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
} // namespace detail
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);
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return detail::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(
return detail::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
detail::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));
detail::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<type_identity_t<OutputIt>, Char> args) {
using af = detail::arg_formatter<OutputIt, Char>;
return vformat_to<af>(out, to_string_view(format_str), args,
detail::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
detail::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);
detail::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),

450
include/fmt/os.h Normal file
View File

@ -0,0 +1,450 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# 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)
#else
# 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
#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)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this 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;
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) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
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.
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// 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;
// 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);
// 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);
};
// Returns the memory page size.
long getpagesize();
class direct_buffered_file;
template <typename S, typename... Args>
void print(direct_buffered_file& f, const S& format_str,
const Args&... args);
// A buffered file with a direct buffer access and no synchronization.
class direct_buffered_file {
private:
file file_;
enum { buffer_size = 4096 };
char buffer_[buffer_size];
int pos_;
void flush() {
if (pos_ == 0) return;
file_.write(buffer_, pos_);
pos_ = 0;
}
int free_capacity() const { return buffer_size - pos_; }
public:
direct_buffered_file(cstring_view path, int oflag)
: file_(path, oflag), pos_(0) {}
~direct_buffered_file() {
flush();
}
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
friend void print(direct_buffered_file& f, const S& format_str,
const Args&... args) {
// We could avoid double buffering.
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf), format_str, args...);
auto remaining_pos = 0;
auto remaining_size = buf.size();
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
auto size = f.free_capacity();
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
f.pos_ += size;
f.flush();
remaining_pos += size;
remaining_size -= size;
}
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
f.pos_ += static_cast<int>(remaining_size);
}
};
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(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 = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@ -9,10 +9,15 @@
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
@ -75,7 +80,7 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
void write_buffer(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();
@ -89,34 +94,57 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
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()) {
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace internal
} // namespace detail
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
@ -129,10 +157,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::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...)});
detail::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -1,311 +1,2 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# 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)
#else
# 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
#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)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this 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;
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) {
vprint(format_str, make_format_args(args...));
}
};
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
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.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
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);
// 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);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// 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;
// 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);
// 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);
};
// Returns the memory page size.
long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# 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) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
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_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@ -1,4 +1,4 @@
// Formatting library for C++
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@ -8,17 +8,13 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
@ -32,7 +28,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
@ -94,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
@ -106,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
@ -137,7 +133,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
@ -145,6 +141,13 @@ template <typename Context> class char_converter {
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
@ -159,7 +162,7 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
@ -176,23 +179,25 @@ template <typename Char> class printf_width_handler {
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
} // namespace detail
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
@ -200,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = typename Range::iterator;
using iterator = OutputIt;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_;
@ -233,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@ -250,7 +255,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.align = align::right;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
@ -307,6 +316,8 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
@ -314,17 +325,21 @@ template <typename T> struct printf_formatter {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
@ -332,24 +347,23 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@ -359,16 +373,18 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
format_arg arg(unsigned id) const { return args_.get(id); }
detail::locale_ref locale() { return {}; }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
@ -388,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0';
break;
case ' ':
specs.sign = sign::space;
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
@ -401,24 +419,25 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == internal::max_value<unsigned>())
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = internal::max_value<unsigned>();
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
unsigned value = parse_nonnegative_int(it, end, eh);
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
@ -436,12 +455,12 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg());
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -468,38 +487,53 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.align = align::right;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision =
visit_format_arg(internal::printf_precision_handler(), get_arg());
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@ -523,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
@ -548,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
break;
}
@ -557,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
@ -599,9 +632,10 @@ inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
vprintf(buffer, to_string_view(format), args);
return to_string(buffer);
}
@ -615,18 +649,19 @@ inline std::basic_string<Char> vsprintf(
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
return vsprintf(to_string_view(format), make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
vprintf(buffer, to_string_view(format), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
@ -642,16 +677,17 @@ inline int vfprintf(std::FILE* f, const S& format,
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
@ -665,19 +701,20 @@ inline int vprintf(const S& format,
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
vprintf(buffer, to_string_view(format), args);
detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size());
}
@ -685,9 +722,9 @@ inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
basic_string_view<Char> format_str,
basic_format_args<Context> args) {
typename ArgFormatter::iterator vprintf(
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
@ -707,7 +744,7 @@ inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
FMT_END_NAMESPACE

View File

@ -12,7 +12,9 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
@ -31,7 +33,7 @@ template <typename Char> 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 =
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
@ -52,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
@ -104,10 +106,7 @@ struct is_range_<
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p)
-> decltype(std::tuple_size<U>::value,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
@ -119,26 +118,24 @@ template <typename T> class is_tuple_like_ {
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
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 size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, std::size_t N, T... Ns>
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
@ -186,11 +183,11 @@ FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
} // namespace detail
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;
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
@ -203,17 +200,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
@ -229,14 +226,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
size_t i = 0;
detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
detail::copy(formatting.postfix, out);
return ctx.out();
}
@ -244,10 +241,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
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 &&
detail::is_range_<T>::value && !detail::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;
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
@ -263,15 +259,17 @@ struct formatter<RangeT, Char,
template <typename FormatContext>
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) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
@ -280,11 +278,11 @@ struct formatter<RangeT, Char,
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
@ -302,14 +300,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
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)>{});
return format(value, ctx, detail::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...>) {
detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
@ -360,6 +358,29 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@ -1,293 +0,0 @@
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (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

@ -8,50 +8,62 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>;
namespace detail {
// 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;
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
} // namespace detail
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
#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;
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context>&);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
FMT_END_NAMESPACE

View File

@ -1,4 +1,4 @@
// A C++ interface to POSIX functions.
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@ -10,37 +10,43 @@
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/posix.h"
#include "fmt/os.h"
#include <climits>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# 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
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#endif
#ifdef fileno
# undef fileno
@ -66,6 +72,78 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int detail::utf16_to_utf8::convert(wstring_view s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
}
void windows_error::init(int err_code, string_view format_str,
format_args args) {
error_code_ = err_code;
memory_buffer buffer;
detail::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer));
}
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
wmemory_buffer buf;
buf.resize(inline_buffer_size);
for (;;) {
wchar_t* system_message = &buf[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
static_cast<uint32_t>(buf.size()), nullptr);
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
@ -94,14 +172,15 @@ int buffered_file::fileno() const {
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
# if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
# else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
# endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
@ -123,7 +202,7 @@ void file::close() {
}
long long file::size() const {
#ifdef _WIN32
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
@ -137,7 +216,7 @@ long long file::size() const {
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
# else
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
@ -145,21 +224,21 @@ long long file::size() const {
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
#endif
# endif
}
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"));
return internal::to_unsigned(result);
return detail::to_unsigned(result);
}
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"));
return internal::to_unsigned(result);
return detail::to_unsigned(result);
}
file file::dup(int fd) {
@ -192,15 +271,15 @@ void file::pipe(file& read_end, file& write_end) {
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
# endif
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.
@ -210,7 +289,11 @@ void file::pipe(file& read_end, file& write_end) {
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
#if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
#else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
#endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
@ -220,14 +303,15 @@ buffered_file file::fdopen(const char* mode) {
}
long getpagesize() {
#ifdef _WIN32
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
# else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
#endif
# endif
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

3
support/Vagrantfile vendored
View File

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

View File

@ -0,0 +1,26 @@
# This module provides function for joining paths
# known from from most languages
#
# Original license:
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Explicit permission given to distribute this module under
# the terms of the project as described in /LICENSE.rst.
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

View File

@ -48,17 +48,6 @@ endif ()
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
# 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; };
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
set (SUPPORTS_VARIADIC_TEMPLATES OFF)
endif ()
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);

View File

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

View File

@ -137,15 +137,40 @@ def update_site(env):
if not os.path.exists(contents):
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
# Fix issues in reference.rst/api.rst.
for filename in ['reference.rst', 'api.rst']:
for filename in ['reference.rst', 'api.rst', 'index.rst']:
pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
with rewrite(os.path.join(target_doc_dir, filename)) as b:
b.data = b.data.replace('std::ostream &', 'std::ostream&')
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned')
b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(', size_t', ', std::size_t')
#b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(
'format_to_n(OutputIt, size_t, string_view, Args&&',
'format_to_n(OutputIt, size_t, const S&, const Args&')
b.data = b.data.replace(
'format_to_n(OutputIt, std::size_t, string_view, Args&&',
'format_to_n(OutputIt, std::size_t, const S&, const Args&')
if version == ('3.0.2'):
b.data = b.data.replace(
'fprintf(std::ostream&', 'fprintf(std::ostream &')
if version == ('5.3.0'):
b.data = b.data.replace(
'format_to(OutputIt, const S&, const Args&...)',
'format_to(OutputIt, const S &, const Args &...)')
if version.startswith('5.') or version.startswith('6.'):
b.data = b.data.replace(', size_t', ', std::size_t')
if version.startswith('7.'):
b.data = b.data.replace(', std::size_t', ', size_t')
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
b.data = b.data.replace('aa long', 'a long')
b.data = b.data.replace('serveral', 'several')
if version.startswith('6.2.'):
b.data = b.data.replace(
'vformat(const S&, basic_format_args<' +
'buffer_context<Char>>)',
'vformat(const S&, basic_format_args<' +
'buffer_context<type_identity_t<Char>>>)')
# Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b:

View File

@ -17,9 +17,7 @@ else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
if (MSVC)
# Workaround a bug in implementation of variadic templates in MSVC11.
@ -47,8 +45,6 @@ target_compile_definitions(gmock
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
add_library(test-main STATIC ${TEST_MAIN_SRC})
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
target_link_libraries(test-main gmock fmt)
@ -113,12 +109,11 @@ add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
if (NOT MSVC_BUILD_STATIC)
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock)
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
if (FMT_PEDANTIC)
@ -128,7 +123,7 @@ if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
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)
add_fmt_test(os-test)
endif ()
add_fmt_executable(header-only-test
@ -174,18 +169,6 @@ if (FMT_PEDANTIC)
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)
target_include_directories(
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
if (FMT_PEDANTIC)
target_compile_options(no-windows-h-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(no-windows-h-test SYSTEM PUBLIC gtest gmock)
endif ()
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
@ -232,20 +215,22 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()
# Activate optional CUDA tests if CUDA is found. For version selection, see
# 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)
if (FMT_CUDA_TEST)
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 ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()

View File

@ -8,15 +8,21 @@
#include "fmt/core.h"
#include "gtest.h"
#if GTEST_HAS_DEATH_TEST
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEBUG_DEATH(statement, regex)
#else
# 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!");
#if GTEST_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!");
#else
fmt::print("warning: death tests are not supported\n");
#endif
}
bool test_condition = false;
TEST(AssertTest, DanglingElse) {
bool executed_else = false;
if (test_condition)
FMT_ASSERT(true, "");
else
executed_else = true;
EXPECT_TRUE(executed_else);
}

View File

@ -10,10 +10,11 @@
#endif
#include "fmt/chrono.h"
#include "gtest-extra.h"
#include <iomanip>
#include "gtest-extra.h"
std::tm make_tm() {
auto time = std::tm();
time.tm_mday = 1;
@ -145,6 +146,48 @@ TEST(ChronoTest, FormatDefault) {
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
}
TEST(ChronoTest, FormatWide) {
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
EXPECT_EQ(L"42as",
fmt::format(L"{}", std::chrono::duration<int, std::atto>(42)));
EXPECT_EQ(L"42fs",
fmt::format(L"{}", std::chrono::duration<int, std::femto>(42)));
EXPECT_EQ(L"42ps",
fmt::format(L"{}", std::chrono::duration<int, std::pico>(42)));
EXPECT_EQ(L"42ns", fmt::format(L"{}", std::chrono::nanoseconds(42)));
EXPECT_EQ(L"42\u00B5s", fmt::format(L"{}", std::chrono::microseconds(42)));
EXPECT_EQ(L"42ms", fmt::format(L"{}", std::chrono::milliseconds(42)));
EXPECT_EQ(L"42cs",
fmt::format(L"{}", std::chrono::duration<int, std::centi>(42)));
EXPECT_EQ(L"42ds",
fmt::format(L"{}", std::chrono::duration<int, std::deci>(42)));
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
EXPECT_EQ(L"42das",
fmt::format(L"{}", std::chrono::duration<int, std::deca>(42)));
EXPECT_EQ(L"42hs",
fmt::format(L"{}", std::chrono::duration<int, std::hecto>(42)));
EXPECT_EQ(L"42ks",
fmt::format(L"{}", std::chrono::duration<int, std::kilo>(42)));
EXPECT_EQ(L"42Ms",
fmt::format(L"{}", std::chrono::duration<int, std::mega>(42)));
EXPECT_EQ(L"42Gs",
fmt::format(L"{}", std::chrono::duration<int, std::giga>(42)));
EXPECT_EQ(L"42Ts",
fmt::format(L"{}", std::chrono::duration<int, std::tera>(42)));
EXPECT_EQ(L"42Ps",
fmt::format(L"{}", std::chrono::duration<int, std::peta>(42)));
EXPECT_EQ(L"42Es",
fmt::format(L"{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ(L"42m", fmt::format(L"{}", std::chrono::minutes(42)));
EXPECT_EQ(L"42h", fmt::format(L"{}", std::chrono::hours(42)));
EXPECT_EQ(
L"42[15]s",
fmt::format(L"{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ(
L"42[15/4]s",
fmt::format(L"{}", 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));

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include "fmt/color.h"
#include "gtest-extra.h"
TEST(ColorsTest, ColorsPrint) {
@ -50,6 +51,8 @@ TEST(ColorsTest, ColorsPrint) {
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::rgb(255, 20, 30)), L"rgb(255,20,30) wide"),
L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_EQ(

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <stdint.h>
#include <cctype>
#include <cfloat>
#include <climits>
@ -39,8 +40,8 @@ using testing::StrictMock;
#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>
typedef fmt::detail::compiled_format_base<decltype(format)> provider;
typedef fmt::detail::format_part<char>
expected_parts_type[EXPECTED_PARTS_COUNT];
static_assert(std::is_same<typename provider::parts_container,
expected_parts_type>::value,
@ -66,46 +67,37 @@ TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
#endif
TEST(CompileTest, PassStringLiteralFormat) {
const auto prepared = fmt::compile<int>("test {}");
const auto prepared = fmt::detail::compile<int>("test {}");
EXPECT_EQ("test 42", fmt::format(prepared, 42));
const auto wprepared = fmt::compile<int>(L"test {}");
const auto wprepared = fmt::detail::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);
const auto prepared = fmt::detail::compile<int>("4{}");
fmt::format_to(fmt::detail::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);
const auto wprepared = fmt::detail::compile<int>(L"4{}");
fmt::format_to(fmt::detail::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{}");
const auto prepared = fmt::detail::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{}");
const auto wprepared = fmt::detail::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 f = fmt::detail::compile<int>("{:10}");
auto result = fmt::format_to_n(buf, 5, f, 42);
EXPECT_EQ(result.size, 10);
EXPECT_EQ(result.out, buf + 5);
@ -113,12 +105,12 @@ TEST(CompileTest, FormatToN) {
}
TEST(CompileTest, FormattedSize) {
auto f = fmt::compile<int>("{:10}");
auto f = fmt::detail::compile<int>("{:10}");
EXPECT_EQ(fmt::formatted_size(f, 42), 10);
}
TEST(CompileTest, MultipleTypes) {
auto f = fmt::compile<int, int>("{} {}");
auto f = fmt::detail::compile<int, int>("{} {}");
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
}
@ -126,18 +118,49 @@ struct formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<formattable> : formatter<const char*> {
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
template <typename FormatContext>
auto format(formattable, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<const char*>::format("foo", ctx);
}
};
FMT_END_NAMESPACE
TEST(CompileTest, FormatUserDefinedType) {
auto f = fmt::compile<formattable>("{}");
auto f = fmt::detail::compile<formattable>("{}");
EXPECT_EQ(fmt::format(f, formattable()), "foo");
}
TEST(CompileTest, EmptyFormatString) {
auto f = fmt::compile<>("");
auto f = fmt::detail::compile<>("");
EXPECT_EQ(fmt::format(f), "");
}
#ifdef __cpp_if_constexpr
TEST(CompileTest, FormatDefault) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ull));
EXPECT_EQ("true", fmt::format(FMT_COMPILE("{}"), true));
EXPECT_EQ("x", fmt::format(FMT_COMPILE("{}"), 'x'));
EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{}"), 4.2));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), "foo"));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo")));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), formattable()));
}
TEST(CompileTest, FormatSpecs) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
}
TEST(CompileTest, FormatTo) {
char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
*end = '\0';
EXPECT_STREQ("42", buf);
}
TEST(CompileTest, TextAndArg) {
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
}
#endif

View File

@ -15,9 +15,8 @@
#include <string>
#include <type_traits>
#include "test-assert.h"
#include "gmock.h"
#include "test-assert.h"
// Check if fmt/core.h compiles with windows.h included before it.
#ifdef _WIN32
@ -31,8 +30,8 @@
using fmt::basic_format_arg;
using fmt::string_view;
using fmt::internal::buffer;
using fmt::internal::value;
using fmt::detail::buffer;
using fmt::detail::value;
using testing::_;
using testing::StrictMock;
@ -43,7 +42,7 @@ struct test_struct {};
template <typename Context, typename T>
basic_format_arg<Context> make_arg(const T& value) {
return fmt::internal::make_arg<Context>(value);
return fmt::detail::make_arg<Context>(value);
}
} // namespace
@ -84,26 +83,26 @@ TEST(BufferTest, Nonmoveable) {
// 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); }
void grow(size_t capacity) { this->set(nullptr, capacity); }
};
template <typename T> struct mock_buffer : buffer<T> {
MOCK_METHOD1(do_grow, void(std::size_t capacity));
MOCK_METHOD1(do_grow, void(size_t capacity));
void grow(std::size_t capacity) {
void grow(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); }
mock_buffer(T* data, size_t capacity) { this->set(data, capacity); }
};
TEST(BufferTest, Ctor) {
{
mock_buffer<int> buffer;
EXPECT_EQ(nullptr, &buffer[0]);
EXPECT_EQ(nullptr, buffer.data());
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
@ -116,7 +115,7 @@ TEST(BufferTest, Ctor) {
}
{
int dummy;
std::size_t capacity = std::numeric_limits<std::size_t>::max();
size_t capacity = std::numeric_limits<size_t>::max();
mock_buffer<int> buffer(&dummy, capacity);
EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
@ -127,8 +126,13 @@ TEST(BufferTest, Ctor) {
struct dying_buffer : test_buffer<int> {
MOCK_METHOD0(die, void());
~dying_buffer() { die(); }
private:
virtual void avoid_weak_vtable();
};
void dying_buffer::avoid_weak_vtable() {}
TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer;
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
@ -144,7 +148,7 @@ TEST(BufferTest, Access) {
EXPECT_EQ(11, buffer[0]);
buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3));
const fmt::internal::buffer<char>& const_buffer = buffer;
const fmt::detail::buffer<char>& const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]);
}
@ -206,7 +210,8 @@ TEST(ArgTest, FormatArgs) {
}
struct custom_context {
typedef char char_type;
using char_type = char;
using parse_context_type = fmt::format_parse_context;
template <typename T> struct formatter_type {
template <typename ParseContext>
@ -229,20 +234,20 @@ struct custom_context {
TEST(ArgTest, MakeValueWithCustomContext) {
test_struct t;
fmt::internal::value<custom_context> arg(
fmt::internal::arg_mapper<custom_context>().map(t));
fmt::detail::value<custom_context> arg(
fmt::detail::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 {
namespace detail {
template <typename Char>
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
return lhs.value == rhs.value;
}
} // namespace internal
} // namespace detail
FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable
@ -285,8 +290,6 @@ VISIT_TYPE(long, long long);
VISIT_TYPE(unsigned long, unsigned long long);
#endif
VISIT_TYPE(float, double);
#define CHECK_ARG_(Char, expected, value) \
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
@ -369,12 +372,12 @@ TEST(ArgTest, PointerArg) {
struct check_custom {
test_result operator()(
fmt::basic_format_arg<fmt::format_context>::handle h) const {
struct test_buffer : fmt::internal::buffer<char> {
struct test_buffer : fmt::detail::buffer<char> {
char data[10];
test_buffer() : fmt::internal::buffer<char>(data, 0, 10) {}
void grow(std::size_t) {}
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
void grow(size_t) {}
} buffer;
fmt::internal::buffer<char>& base = buffer;
fmt::detail::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);
@ -399,8 +402,174 @@ TEST(ArgTest, VisitInvalidArg) {
fmt::visit_format_arg(visitor, arg);
}
TEST(FormatDynArgsTest, Basic) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc1");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
EXPECT_EQ("42 and abc1 and 1.5", result);
}
TEST(FormatDynArgsTest, StringsAndRefs) {
// Unfortunately the tests are compiled with old ABI so strings use COW.
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[] = "1234567890";
store.push_back(str);
store.push_back(std::cref(str));
store.push_back(fmt::string_view{str});
str[0] = 'X';
std::string result = fmt::vformat("{} and {} and {}", store);
EXPECT_EQ("1234567890 and X234567890 and X234567890", result);
}
struct custom_type {
int i = 0;
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<custom_type> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const custom_type& p, FormatContext& ctx) -> decltype(ctx.out()) {
return format_to(ctx.out(), "cust={}", p.i);
}
};
FMT_END_NAMESPACE
TEST(FormatDynArgsTest, CustomFormat) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
custom_type c{};
store.push_back(c);
++c.i;
store.push_back(c);
++c.i;
store.push_back(std::cref(c));
++c.i;
std::string result = fmt::vformat("{} and {} and {}", store);
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
}
TEST(FormatDynArgsTest, NamedInt) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("a1", 42));
std::string result = fmt::vformat("{a1}", store);
EXPECT_EQ("42", result);
}
TEST(FormatDynArgsTest, NamedStrings) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[]{"1234567890"};
store.push_back(fmt::arg("a1", str));
store.push_back(fmt::arg("a2", std::cref(str)));
str[0] = 'X';
std::string result = fmt::vformat("{a1} and {a2}", store);
EXPECT_EQ("1234567890 and X234567890", result);
}
TEST(FormatDynArgsTest, NamedArgByRef) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
// Note: fmt::arg() constructs an object which holds a reference
// to its value. It's not an aggregate, so it doesn't extend the
// reference lifetime. As a result, it's a very bad idea passing temporary
// as a named argument value. Only GCC with optimization level >0
// complains about this.
//
// A real life usecase is when you have both name and value alive
// guarantee their lifetime and thus don't want them to be copied into
// storages.
int a1_val{42};
auto a1 = fmt::arg("a1_", a1_val);
store.push_back("abc");
store.push_back(1.5f);
store.push_back(std::cref(a1));
std::string result = fmt::vformat("{a1_} and {} and {} and {}", store);
EXPECT_EQ("42 and abc and 1.5 and 42", result);
}
TEST(FormatDynArgsTest, NamedCustomFormat) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
custom_type c{};
store.push_back(fmt::arg("c1", c));
++c.i;
store.push_back(fmt::arg("c2", c));
++c.i;
store.push_back(fmt::arg("c_ref", std::cref(c)));
++c.i;
std::string result = fmt::vformat("{c1} and {c2} and {c_ref}", store);
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
}
TEST(FormatDynArgsTest, Clear) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
std::string result = fmt::vformat("{}", store);
EXPECT_EQ("42", result);
store.push_back(43);
result = fmt::vformat("{} and {}", store);
EXPECT_EQ("42 and 43", result);
store.clear();
store.push_back(44);
result = fmt::vformat("{}", store);
EXPECT_EQ("44", result);
}
TEST(FormatDynArgsTest, Reserve) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.reserve(2, 1);
store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42));
std::string result = fmt::vformat("{a1} and {}", store);
EXPECT_EQ("42 and 1.5", result);
}
struct copy_throwable {
copy_throwable() {}
copy_throwable(const copy_throwable&) { throw "deal with it"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<copy_throwable> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(copy_throwable, format_context& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(FormatDynArgsTest, ThrowOnCopy) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(std::string("foo"));
try {
store.push_back(copy_throwable());
} catch (...) {
}
EXPECT_EQ(fmt::vformat("{}", store), "foo");
}
TEST(StringViewTest, ValueType) {
static_assert(std::is_same<string_view::value_type, char>::value, "");
}
TEST(StringViewTest, Length) {
// Test that StringRef::size() returns string length, not buffer size.
// Test that string_view::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));
@ -409,9 +578,9 @@ TEST(StringViewTest, Length) {
// 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) {
size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (size_t i = 0; i < num_inputs; ++i) {
for (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));
}
@ -450,17 +619,22 @@ template <> struct formatter<enabled_formatter> {
FMT_END_NAMESPACE
TEST(CoreTest, HasFormatter) {
using fmt::internal::has_formatter;
using fmt::has_formatter;
using context = fmt::format_context;
EXPECT_TRUE((has_formatter<enabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter_convertible, context>::value));
static_assert(has_formatter<enabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter_convertible, context>::value,
"");
}
struct convertible_to_int {
operator int() const { return 42; }
};
struct convertible_to_c_string {
operator const char*() const { return "foo"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_int> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
@ -470,10 +644,21 @@ template <> struct formatter<convertible_to_int> {
return std::copy_n("foo", 3, ctx.out());
}
};
template <> struct formatter<convertible_to_c_string> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_c_string, format_context& ctx)
-> decltype(ctx.out()) {
return std::copy_n("bar", 3, ctx.out());
}
};
FMT_END_NAMESPACE
TEST(CoreTest, FormatterOverridesImplicitConversion) {
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar");
}
namespace my_ns {
@ -481,7 +666,7 @@ 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(); }
size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); }
private:
@ -497,23 +682,6 @@ inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
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;
@ -525,21 +693,19 @@ 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::detail::is_string<TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<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>;
fmt::detail::is_string<derived_from_string_view<TypeParam>>::value);
using string_view = fmt::detail::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);
fmt::detail::is_string<string_view>::value);
EXPECT_TRUE(fmt::detail::is_string<my_ns::my_string<TypeParam>>::value);
EXPECT_FALSE(fmt::detail::is_string<my_ns::non_string>::value);
}
TEST(CoreTest, Format) {
@ -562,33 +728,16 @@ TEST(CoreTest, FormatTo) {
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;
fmt::detail::type type =
fmt::detail::mapped_type_constant<my_string<char>,
fmt::format_context>::value;
EXPECT_EQ(type, fmt::detail::type::string_type);
}
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 {
@ -613,25 +762,25 @@ 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"; }
# ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "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()));
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
# endif
#endif
struct disabled_rvalue_conversion {
operator const char*() const& { return "foo"; }
operator const char*() & { return "foo"; }
operator const char*() const&& = delete;
operator const char*() && = delete;
};
TEST(FormatterTest, DisabledRValueConversion) {
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
}

View File

@ -18,14 +18,14 @@
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class custom_arg_formatter
: public fmt::arg_formatter<fmt::buffer_range<char>> {
: public fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
public:
using range = fmt::buffer_range<char>;
typedef fmt::arg_formatter<range> base;
using base = fmt::detail::arg_formatter<fmt::format_context::iterator, char>;
custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx,
fmt::format_specs* s = nullptr)
fmt::format_specs* s = nullptr,
const char* = nullptr)
: base(ctx, parse_ctx, s) {}
using base::operator();
@ -39,8 +39,10 @@ class custom_arg_formatter
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer;
fmt::detail::buffer<char>& base = buffer;
// Pass custom argument formatter as a template arg to vwrite.
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args);
fmt::vformat_to<custom_arg_formatter>(std::back_inserter(base), format_str,
args);
return std::string(buffer.data(), buffer.size());
}

View File

@ -8,6 +8,7 @@
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <cassert>
#include <variant>
#include "fmt/format.h"
@ -37,9 +38,9 @@ namespace std {
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>;
/* unspecified */ std::back_insert_iterator<fmt::detail::buffer<char>>, char>;
using wformat_context = basic_format_context<
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<wchar_t>>, wchar_t>;
/* unspecified */ std::back_insert_iterator<fmt::detail::buffer<wchar_t>>, wchar_t>;
template<class T, class charT = char> struct formatter {
formatter() = delete;
@ -82,9 +83,9 @@ namespace std {
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);
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out>
struct format_to_n_result {
@ -218,7 +219,7 @@ namespace std {
// 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)
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::detail::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 {
@ -487,22 +488,21 @@ template<class... Args>
namespace std {
namespace detail {
template <typename Range>
template <typename OutputIt, typename Char>
class arg_formatter
: public fmt::internal::arg_formatter_base<Range, error_handler> {
: public fmt::detail::arg_formatter_base<OutputIt, Char, 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>;
using char_type = Char;
using base = fmt::detail::arg_formatter_base<OutputIt, Char, error_handler>;
using format_context = std::basic_format_context<OutputIt, Char>;
using parse_context = basic_format_parse_context<Char>;
parse_context* parse_ctx_;
format_context& ctx_;
public:
typedef Range range;
typedef typename base::iterator iterator;
typedef typename base::format_specs format_specs;
using iterator = OutputIt;
using format_specs = typename base::format_specs;
/**
\rst
@ -512,7 +512,7 @@ class arg_formatter
\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) {}
: base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator();
@ -528,36 +528,36 @@ class arg_formatter
};
template <typename Context>
inline fmt::internal::type get_type(basic_format_arg<Context> arg) {
inline fmt::detail::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;
return fmt::detail::type::none_type;
if (std::is_same_v<T, bool>)
return fmt::internal::bool_type;
return fmt::detail::type::bool_type;
if (std::is_same_v<T, char_type>)
return fmt::internal::char_type;
return fmt::detail::type::char_type;
if (std::is_same_v<T, int>)
return fmt::internal::int_type;
return fmt::detail::type::int_type;
if (std::is_same_v<T, unsigned int>)
return fmt::internal::uint_type;
return fmt::detail::type::uint_type;
if (std::is_same_v<T, long long int>)
return fmt::internal::long_long_type;
return fmt::detail::type::long_long_type;
if (std::is_same_v<T, unsigned long long int>)
return fmt::internal::ulong_long_type;
return fmt::detail::type::ulong_long_type;
if (std::is_same_v<T, double>)
return fmt::internal::double_type;
return fmt::detail::type::double_type;
if (std::is_same_v<T, long double>)
return fmt::internal::long_double_type;
return fmt::detail::type::long_double_type;
if (std::is_same_v<T, const char_type*>)
return fmt::internal::cstring_type;
return fmt::detail::type::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::internal::string_type;
return fmt::detail::type::string_type;
if (std::is_same_v<T, const void*>)
return fmt::internal::pointer_type;
return fmt::detail::type::pointer_type;
assert(get_value(arg).index() == 12);
return fmt::internal::custom_type;
return fmt::detail::type::custom_type;
}, arg);
}
@ -581,45 +581,42 @@ class custom_formatter {
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler {
typedef typename ArgFormatter::range range;
using iterator = typename ArgFormatter::iterator;
format_handler(range r, basic_string_view<Char> str,
format_handler(iterator out, 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) {}
fmt::detail::locale_ref loc)
: parse_ctx(str), context(out, format_args, loc) {}
void on_text(const Char* begin, const Char* end) {
auto size = fmt::internal::to_unsigned(end - begin);
auto size = fmt::detail::to_unsigned(end - begin);
auto out = context.out();
auto&& it = fmt::internal::reserve(out, size);
auto&& it = fmt::detail::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>) {}
int on_arg_id() { return parse_ctx.next_arg_id(); }
int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; }
int on_arg_id(fmt::basic_string_view<Char>) { return 0; }
void on_replacement_field(const Char* p) {
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
void on_replacement_field(int id, const Char* p) {
auto arg = context.arg(id);
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) {
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
auto arg = context.arg(id);
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 fmt::detail::specs_handler;
using parse_context = basic_format_parse_context<Char>;
fmt::internal::specs_checker<specs_handler<parse_context, Context>> handler(
fmt::detail::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");
@ -630,7 +627,6 @@ struct format_handler : detail::error_handler {
basic_format_parse_context<Char> parse_ctx;
Context context;
basic_format_arg<Context> arg;
};
template <typename T, typename Char>
@ -639,47 +635,45 @@ struct formatter {
// 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),
namespace detail = fmt::detail;
typedef detail::dynamic_specs_handler<ParseContext> handler_type;
auto type = detail::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
detail::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:
case detail::type::none_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:
case detail::type::int_type:
case detail::type::uint_type:
case detail::type::long_long_type:
case detail::type::ulong_long_type:
case detail::type::bool_type:
handle_int_type_spec(type_spec,
internal::int_type_checker<decltype(eh)>(eh));
detail::int_type_checker<decltype(eh)>(eh));
break;
case internal::char_type:
case detail::type::char_type:
handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(type_spec, eh));
&specs_, detail::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));
case detail::type::double_type:
case detail::type::long_double_type:
detail::parse_float_type_spec(specs_, eh);
break;
case internal::cstring_type:
internal::handle_cstring_type_spec(
type_spec, internal::cstring_type_checker<decltype(eh)>(eh));
case detail::type::cstring_type:
detail::handle_cstring_type_spec(
type_spec, detail::cstring_type_checker<decltype(eh)>(eh));
break;
case internal::string_type:
internal::check_string_type_spec(type_spec, eh);
case detail::type::string_type:
detail::check_string_type_spec(type_spec, eh);
break;
case internal::pointer_type:
internal::check_pointer_type_spec(type_spec, eh);
case detail::type::pointer_type:
detail::check_pointer_type_spec(type_spec, eh);
break;
case internal::custom_type:
case detail::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
@ -689,18 +683,18 @@ struct formatter {
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>(
specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
fmt::detail::handle_dynamic_spec<fmt::detail::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_),
using af = arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(af(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val));
}
private:
fmt::internal::dynamic_format_specs<Char> specs_;
fmt::detail::dynamic_format_specs<Char> specs_;
};
} // namespace detail
@ -717,11 +711,11 @@ template<class... 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);
fmt::detail::buffer<char>& buf = mbuf;
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
detail::format_handler<af, char, format_context>
h(std::back_inserter(buf), fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf);
}
@ -730,26 +724,26 @@ 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...)});
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...)});
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);
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using af = detail::arg_formatter<Out, char>;
detail::format_handler<af, char, basic_format_context<Out, char>>
h(out, fmt, args, {});
fmt::detail::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);
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_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,

View File

@ -0,0 +1,6 @@
// Copyright (c) 2020 Vladimir Solontsov
// SPDX-License-Identifier: MIT Licence
#include <fmt/core.h>
#include "gtest-extra.h"

View File

@ -10,21 +10,20 @@
#include "test-assert.h"
// Include format.cc instead of format.h to test implementation.
#include "../src/format.cc"
#include "fmt/printf.h"
#include <algorithm>
#include <cstring>
#include "../src/format.cc"
#include "fmt/printf.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "util.h"
#undef max
using fmt::internal::bigint;
using fmt::internal::fp;
using fmt::internal::max_value;
using fmt::detail::bigint;
using fmt::detail::fp;
using fmt::detail::max_value;
static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
@ -35,6 +34,46 @@ TEST(BigIntTest, Construct) {
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
}
TEST(BigIntTest, Compare) {
bigint n1(42);
bigint n2(42);
EXPECT_EQ(compare(n1, n2), 0);
n2 <<= 32;
EXPECT_LT(compare(n1, n2), 0);
bigint n3(43);
EXPECT_LT(compare(n1, n3), 0);
EXPECT_GT(compare(n3, n1), 0);
bigint n4(42 * 0x100000001);
EXPECT_LT(compare(n2, n4), 0);
EXPECT_GT(compare(n4, n2), 0);
}
TEST(BigIntTest, AddCompare) {
EXPECT_LT(
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
bigint(0x300000010)),
0);
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000000)),
0);
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000001)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000002)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000003)),
0);
}
TEST(BigIntTest, ShiftLeft) {
bigint n(0x42);
n <<= 0;
@ -47,30 +86,133 @@ TEST(BigIntTest, ShiftLeft) {
TEST(BigIntTest, Multiply) {
bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
auto max = max_value<uint32_t>();
bigint bigmax(max);
bigmax *= max;
bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
template <bool is_iec559> void test_construct_from_double() {
TEST(BigIntTest, Accumulator) {
fmt::detail::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += fmt::detail::max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(BigIntTest, Square) {
bigint n0(0);
n0.square();
EXPECT_EQ("0", fmt::format("{}", n0));
bigint n1(0x100);
n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1));
bigint n2(0xfffffffff);
n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
bigint n3(max_value<uint64_t>());
n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
bigint n4;
n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4));
}
TEST(BigIntTest, DivModAssignZeroDivisor) {
bigint zero(0);
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
}
TEST(BigIntTest, DivModAssignSelf) {
bigint n(100);
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
}
TEST(BigIntTest, DivModAssignUnaligned) {
// (42 << 340) / pow(10, 100):
bigint n1(42);
n1 <<= 340;
bigint n2;
n2.assign_pow10(100);
int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
fmt::format("{}", n1));
}
TEST(BigIntTest, DivModAssign) {
// 100 / 10:
bigint n1(100);
int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1));
// pow(10, 100) / (42 << 320):
n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
fmt::format("{}", n1));
// 42 / 100:
bigint n2(42);
n1.assign_pow10(2);
result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0);
EXPECT_EQ("2a", fmt::format("{}", n2));
}
template <bool is_iec559> void run_double_tests() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <> void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
template <> void run_double_tests<true>() {
// Construct from double.
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
// Compute boundaries:
fp value;
// Normalized & not power of 2 - equidistant boundaries:
auto b = value.assign_with_boundaries(1.23);
EXPECT_EQ(value, fp(0x0013ae147ae147ae, -52));
EXPECT_EQ(b.lower, 0x9d70a3d70a3d6c00);
EXPECT_EQ(b.upper, 0x9d70a3d70a3d7400);
// Normalized power of 2 - lower boundary is closer:
b = value.assign_with_boundaries(1.9807040628566084e+28); // 2**94
EXPECT_EQ(value, fp(0x0010000000000000, 42));
EXPECT_EQ(b.lower, 0x7ffffffffffffe00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Smallest normalized double - equidistant boundaries:
b = value.assign_with_boundaries(2.2250738585072014e-308);
EXPECT_EQ(value, fp(0x0010000000000000, -1074));
EXPECT_EQ(b.lower, 0x7ffffffffffffc00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Subnormal - equidistant boundaries:
b = value.assign_with_boundaries(4.9406564584124654e-324);
EXPECT_EQ(value, fp(0x0000000000000001, -1074));
EXPECT_EQ(b.lower, 0x4000000000000000);
EXPECT_EQ(b.upper, 0xc000000000000000);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
TEST(FPTest, DoubleTests) {
run_double_tests<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
@ -80,30 +222,31 @@ TEST(FPTest, Normalize) {
EXPECT_EQ(-6, normalized.e);
}
TEST(FPTest, ComputeBoundariesSubnormal) {
auto v = fp(0xbeef, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0xbeee800000000000, lower.f);
EXPECT_EQ(-6, lower.e);
EXPECT_EQ(0xbeef800000000000, upper.f);
EXPECT_EQ(-6, upper.e);
}
TEST(FPTest, ComputeBoundaries) {
auto v = fp(0x10000000000000, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
EXPECT_EQ(31, lower.e);
EXPECT_EQ(0x8000000000000400, upper.f);
EXPECT_EQ(31, upper.e);
}
TEST(FPTest, Subtract) {
auto v = fp(123, 1) - fp(102, 1);
EXPECT_EQ(v.f, 21u);
EXPECT_EQ(v.e, 1);
TEST(FPTest, ComputeFloatBoundaries) {
struct {
double x, lower, upper;
} tests[] = {
// regular
{1.5f, 1.4999999403953552, 1.5000000596046448},
// boundary
{1.0f, 0.9999999701976776, 1.0000000596046448},
// min normal
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
// max subnormal
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
// min subnormal
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
};
for (auto test : tests) {
fp vlower = normalize(fp(test.lower));
fp vupper = normalize(fp(test.upper));
vlower.f >>= vupper.e - vlower.e;
vlower.e = vupper.e;
fp value;
auto b = value.assign_float_with_boundaries(test.x);
EXPECT_EQ(vlower.f, b.lower);
EXPECT_EQ(vupper.f, b.upper);
}
}
TEST(FPTest, Multiply) {
@ -119,7 +262,7 @@ 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);
auto fp = fmt::detail::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));
@ -128,32 +271,33 @@ TEST(FPTest, GetCachedPower) {
}
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));
using fmt::detail::get_round_direction;
using fmt::detail::round_direction;
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10));
for (size_t i = 41; i < 60; ++i)
EXPECT_EQ(round_direction::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));
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 2));
// Check that 2 * (remainder + error) doesn't overflow.
EXPECT_EQ(fmt::internal::unknown,
EXPECT_EQ(round_direction::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));
EXPECT_EQ(round_direction::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));
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
}
TEST(FPTest, FixedHandler) {
struct handler : fmt::internal::fixed_handler {
struct handler : fmt::detail::fixed_handler {
char buffer[10];
handler(int prec = 0) : fmt::internal::fixed_handler() {
handler(int prec = 0) : fmt::detail::fixed_handler() {
buf = buffer;
precision = prec;
}
@ -162,7 +306,7 @@ TEST(FPTest, FixedHandler) {
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;
namespace digits = fmt::detail::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);
@ -174,8 +318,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
int exp = 0;
grisu_format(4.2f, buf, -1, false, exp);
format_float(0.42, -1, fmt::detail::float_specs(), buf);
}
template <typename T> struct value_extractor {
@ -185,23 +328,23 @@ template <typename T> struct value_extractor {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
}
#ifdef __apple_build_version__
#if FMT_USE_INT128
// 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()(fmt::detail::int128_t) {
throw std::runtime_error("invalid type __int128_t");
}
FMT_NORETURN T operator()(__uint128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__uint128_t"));
FMT_NORETURN T operator()(fmt::detail::uint128_t) {
throw std::runtime_error("invalid type __uint128_t");
}
#endif
};
TEST(FormatTest, ArgConverter) {
long long value = max_value<long long>();
auto arg = fmt::internal::make_arg<fmt::format_context>(value);
auto arg = fmt::detail::make_arg<fmt::format_context>(value);
fmt::visit_format_arg(
fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
fmt::detail::arg_converter<long long, fmt::format_context>(arg, 'd'),
arg);
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
}
@ -217,9 +360,9 @@ TEST(FormatTest, FormatNegativeNaN) {
TEST(FormatTest, StrError) {
char* message = nullptr;
char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = nullptr, 0),
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = nullptr, 0),
"invalid buffer");
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0),
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer");
buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
@ -231,18 +374,18 @@ TEST(FormatTest, StrError) {
#endif
int result =
fmt::internal::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
fmt::detail::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(result, 0);
std::size_t message_size = std::strlen(message);
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.
#if !defined(__MINGW32__) && !defined(__sun)
result =
fmt::internal::safe_strerror(error_code, message = buffer, message_size);
fmt::detail::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result);
result = fmt::internal::safe_strerror(error_code, message = buffer, 1);
result = fmt::detail::safe_strerror(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message);
@ -254,46 +397,48 @@ TEST(FormatTest, FormatErrorCode) {
{
fmt::memory_buffer buffer;
format_to(buffer, "garbage");
fmt::internal::format_error_code(buffer, 42, "test");
fmt::detail::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::internal::format_error_code(buffer, 42, prefix);
fmt::detail::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer));
}
int codes[] = {42, -1};
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
for (size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
// 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::internal::format_error_code(buffer, codes[i], prefix);
fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
std::size_t size = fmt::inline_buffer_size;
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::internal::format_error_code(buffer, codes[i], prefix);
fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(msg, to_string(buffer));
}
}
TEST(FormatTest, CountCodePoints) {
EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик")));
EXPECT_EQ(4,
fmt::detail::count_code_points(
fmt::basic_string_view<fmt::detail::char8_type>(
reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
}
// Tests fmt::internal::count_digits for integer type Int.
// Tests fmt::detail::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) {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::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));
EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
}
}
@ -302,11 +447,10 @@ TEST(UtilTest, CountDigits) {
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));
TEST(UtilTest, WriteFallbackUIntPtr) {
std::string s;
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
EXPECT_EQ(s, "0xface");
}

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,19 @@ in fmt. It is a part of the continous fuzzing at
The source code is modified to make the fuzzing possible without locking up on
resource exhaustion:
```cpp
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#ifdef FMT_FUZZ
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).
```
This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
practically possible. It is used in fmt code to prevent resource exhaustion in
fuzzing mode.
The macro `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` is the
defacto standard for making fuzzing practically possible to disable certain
fuzzing-unfriendly features (for example, randomness), see [the libFuzzer
documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
## Running the fuzzers locally

View File

@ -25,7 +25,7 @@ void invoke_inner(fmt::string_view formatstring, const Item item) {
// 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) {
void invoke_outer(const uint8_t* Data, size_t Size, const int scaling) {
// always use a fixed location of the data
using fmt_fuzzer::Nfixed;
@ -97,7 +97,7 @@ void invoke_outer(const uint8_t* Data, std::size_t Size, const int scaling) {
// doit_impl<Item,std::yotta>(buf.data(),item);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 4) {
return 0;
}

View File

@ -4,7 +4,7 @@
#include <vector>
#include "fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size);
int main(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
std::ifstream in(argv[i]);
@ -13,7 +13,7 @@ int main(int argc, char* argv[]) {
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));
std::vector<char> buf(static_cast<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());

View File

@ -10,7 +10,7 @@
#include "fuzzer_common.h"
template <typename Item1>
void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
void invoke_fmt(const uint8_t* Data, 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) {
@ -29,11 +29,11 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
// allocating buffers separately is slower, but increases chances
// of detecting memory errors
#if FMT_FUZZ_SEPARATE_ALLOCATION
std::vector<char> argnamebuffer(argsize);
std::vector<char> argnamebuffer(argsize + 1);
std::memcpy(argnamebuffer.data(), Data, argsize);
auto argname = fmt::string_view(argnamebuffer.data(), argsize);
auto argname = argnamebuffer.data();
#else
auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize);
auto argname = fmt_fuzzer::as_chars(Data);
#endif
Data += argsize;
Size -= argsize;
@ -105,7 +105,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -13,7 +13,7 @@
using fmt_fuzzer::Nfixed;
template <typename Item>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed is too small");
if (Size <= Nfixed) {
@ -40,7 +40,7 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size) {
#endif
}
void invoke_fmt_time(const uint8_t* Data, std::size_t Size) {
void invoke_fmt_time(const uint8_t* Data, size_t Size) {
using Item = std::time_t;
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed too small");
@ -69,7 +69,7 @@ void invoke_fmt_time(const uint8_t* Data, std::size_t Size) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -10,7 +10,7 @@
using fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded");
@ -90,7 +90,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -10,7 +10,7 @@
constexpr auto Nfixed = fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded");
@ -86,7 +86,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -344,6 +344,7 @@ class GTEST_API_ SingleFailureChecker {
# include <sys/mman.h> // NOLINT
# include <sys/time.h> // NOLINT
# include <unistd.h> // NOLINT
# include <string>
#elif GTEST_OS_SYMBIAN
@ -7728,6 +7729,7 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
# include <sys/syslimits.h>
#else
# include <limits.h>
# include <climits> // Some Linux distributions define PATH_MAX here.
#endif // GTEST_OS_WINDOWS_MOBILE
@ -8899,6 +8901,7 @@ const char* StringFromGTestEnv(const char* flag, const char* default_value) {
#include <ctype.h>
#include <stdio.h>
#include <ostream> // NOLINT
#include <string>
@ -9494,6 +9497,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
// This file implements cardinalities.
#include <limits.h>
#include <ostream> // NOLINT
#include <sstream>
#include <string>
@ -9646,6 +9650,7 @@ GTEST_API_ Cardinality Exactly(int n) { return Between(n, n); }
// USE THEM IN USER CODE.
#include <ctype.h>
#include <ostream> // NOLINT
#include <string>
@ -9808,6 +9813,7 @@ GTEST_API_ void Log(LogSeverity severity, const string& message,
// utilities for defining matchers.
#include <string.h>
#include <sstream>
#include <string>
@ -10291,6 +10297,7 @@ bool UnorderedElementsAreMatcherImplBase::
// EXPECT_CALL).
#include <stdlib.h>
#include <iostream> // NOLINT
#include <map>
#include <set>

View File

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

View File

@ -8,6 +8,7 @@
#include "gtest-extra.h"
#include <gtest/gtest-spi.h>
#include <algorithm>
#include <cstring>
#include <memory>
@ -74,14 +75,6 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
EXPECT_EQ(s_ + 1, p_);
}
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) {
// successful EXPECT_THROW_MSG
@ -163,30 +156,59 @@ TEST_F(SingleEvaluationTest, SystemErrorTests) {
EXPECT_EQ(4, b_);
}
#if FMT_USE_FCNTL
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE
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")),
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(
stdout,
{ // NOLINT
a_++;
std::printf("test");
},
(b_++, "other")),
"Actual: test");
EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_);
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
#endif // FMT_USE_FCNTL
// Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
@ -280,16 +302,6 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
format_system_error(EDOM, "test")));
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
<< "unexpected failure";
@ -308,20 +320,13 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
"expected failure");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
TEST(UtilTest, FormatSystemError) {
fmt::memory_buffer out;
fmt::format_system_error(out, EDOM, "test message");
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
}
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::buffered_file;
using fmt::error_code;
@ -415,16 +420,17 @@ TEST(OutputRedirectTest, ErrorInDtor) {
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"));
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
}

View File

@ -7,7 +7,7 @@
#include "gtest-extra.h"
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::file;
@ -57,7 +57,7 @@ std::string OutputRedirect::restore_and_read() {
if (read_end_.descriptor() == -1) return content; // Already read.
enum { BUFFER_SIZE = 4096 };
char buffer[BUFFER_SIZE];
std::size_t count = 0;
size_t count = 0;
do {
count = read_end_.read(buffer, BUFFER_SIZE);
content.append(buffer, count);
@ -66,9 +66,9 @@ std::string OutputRedirect::restore_and_read() {
return content;
}
std::string read(file& f, std::size_t count) {
std::string read(file& f, size_t count) {
std::string buffer(count, '\0');
std::size_t n = 0, offset = 0;
size_t n = 0, offset = 0;
do {
n = f.read(&buffer[offset], count - offset);
// We can't read more than size_t bytes since count has type size_t.
@ -78,7 +78,7 @@ std::string read(file& f, std::size_t count) {
return buffer;
}
#endif // FMT_USE_FILE_DESCRIPTORS
#endif // FMT_USE_FCNTL
std::string format_system_error(int error_code, fmt::string_view message) {
fmt::memory_buffer out;

View File

@ -9,18 +9,10 @@
#define FMT_GTEST_EXTRA_H_
#include <string>
#include "fmt/os.h"
#include "gmock.h"
#include "fmt/core.h"
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
# include "fmt/posix.h"
#endif
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
@ -65,7 +57,7 @@ std::string format_system_error(int error_code, fmt::string_view message);
EXPECT_THROW_MSG(statement, fmt::system_error, \
format_system_error(error_code, message))
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
// Captures file output by redirecting it to a pipe.
// The output it can handle is limited by the pipe capacity.
@ -146,12 +138,15 @@ class SuppressAssert {
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, size_t count);
# define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
EXPECT_EQ(expected_content, \
read(file, fmt::string_view(expected_content).size()))
#endif // FMT_USE_FILE_DESCRIPTORS
#else
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
#endif // FMT_USE_FCNTL
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; }

View File

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

View File

@ -16,8 +16,8 @@ template <typename T> class mock_allocator {
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*(size_t n));
MOCK_METHOD2_T(deallocate, void(T* p, size_t n));
};
template <typename Allocator> class allocator_ref {
@ -51,10 +51,10 @@ template <typename Allocator> class allocator_ref {
public:
Allocator* get() const { return alloc_; }
value_type* allocate(std::size_t n) {
value_type* allocate(size_t n) {
return std::allocator_traits<Allocator>::allocate(*alloc_, n);
}
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); }
};
#endif // FMT_MOCK_ALLOCATOR_H_

View File

@ -1,15 +1,16 @@
// Formatting library for C++ - tests of the C++ interface to POSIX functions
// Formatting library for C++ - tests of the OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/os.h"
#include <cstdlib> // std::exit
#include <cstring>
#include <memory>
#include "fmt/posix.h"
#include "gtest-extra.h"
#include "util.h"
@ -19,6 +20,124 @@
using fmt::buffered_file;
using fmt::error_code;
#ifdef _WIN32
# include <windows.h>
TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик";
fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
TEST(UtilTest, UTF16ToUTF8EmptyString) {
std::string s = "";
fmt::detail::utf16_to_utf8 u(L"");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
template <typename Converter, typename Char>
void check_utf_conversion_error(
const char* message,
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
fmt::memory_buffer out;
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
fmt::system_error error(0, "");
try {
(Converter)(str);
} catch (const fmt::system_error& e) {
error = e;
}
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
EXPECT_EQ(fmt::to_string(out), error.what());
}
TEST(UtilTest, UTF16ToUTF8Error) {
check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
"cannot convert string from UTF-16 to UTF-8");
}
TEST(UtilTest, UTF16ToUTF8Convert) {
fmt::detail::utf16_to_utf8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
EXPECT_EQ(ERROR_INVALID_PARAMETER,
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
}
TEST(UtilTest, FormatWindowsError) {
LPWSTR message = 0;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, ERROR_FILE_EXISTS,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::detail::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
actual_message.resize(0);
auto max_size = fmt::detail::max_value<size_t>();
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_size));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message));
}
TEST(UtilTest, FormatLongWindowsError) {
LPWSTR message = 0;
// this error code is not available on all Windows platforms and
// Windows SDKs, so do not fail the test if the error string cannot
// be retrieved.
const int provisioning_not_allowed =
0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, static_cast<DWORD>(provisioning_not_allowed),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
return;
}
fmt::detail::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
}
TEST(UtilTest, WindowsError) {
fmt::system_error error(0, "");
try {
throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error");
} catch (const fmt::system_error& e) {
error = e;
}
fmt::memory_buffer message;
fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
EXPECT_EQ(to_string(message), error.what());
EXPECT_EQ(ERROR_FILE_EXISTS, error.error_code());
}
TEST(UtilTest, ReportWindowsError) {
fmt::memory_buffer out;
fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
out.push_back('\n');
EXPECT_WRITE(stderr,
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
fmt::to_string(out));
}
#endif // _WIN32
#if FMT_USE_FCNTL
using fmt::file;
// Checks if the file is open by reading one character from it.
@ -45,10 +164,10 @@ static file open_file() {
// Attempts to write a string to a file.
static void write(file& f, fmt::string_view s) {
std::size_t num_chars_left = s.size();
size_t num_chars_left = s.size();
const char* ptr = s.data();
do {
std::size_t count = f.write(ptr, num_chars_left);
size_t count = f.write(ptr, num_chars_left);
ptr += count;
// We can't write more than size_t bytes since num_chars_left
// has type size_t.
@ -119,16 +238,17 @@ TEST(BufferedFileTest, CloseFileInDtor) {
TEST(BufferedFileTest, CloseErrorInDtor) {
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");
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) {
@ -148,7 +268,7 @@ TEST(BufferedFileTest, CloseError) {
TEST(BufferedFileTest, Fileno) {
buffered_file f;
#ifndef __COVERITY__
# ifndef __COVERITY__
// fileno on a null FILE pointer either crashes or returns an error.
// Disable Coverity because this is intentional.
EXPECT_DEATH_IF_SUPPORTED(
@ -160,13 +280,33 @@ TEST(BufferedFileTest, Fileno) {
}
},
"");
#endif
# endif
f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1);
file copy = file::dup(f.fileno());
EXPECT_READ(copy, FILE_CONTENT);
}
TEST(DirectBufferedFileTest, Print) {
fmt::direct_buffered_file out(
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
fmt::print(out, "The answer is {}.\n", 42);
out.close();
file in("test-file", file::RDONLY);
EXPECT_READ(in, "The answer is 42.\n");
}
TEST(DirectBufferedFileTest, BufferBoundary) {
auto str = std::string(4096, 'x');
fmt::direct_buffered_file out(
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
fmt::print(out, "{}", str);
fmt::print(out, "{}", str);
out.close();
file in("test-file", file::RDONLY);
EXPECT_READ(in, str + str);
}
TEST(FileTest, DefaultCtor) {
file f;
EXPECT_EQ(-1, f.descriptor());
@ -250,16 +390,17 @@ TEST(FileTest, CloseFileInDtor) {
TEST(FileTest, CloseErrorInDtor) {
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");
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) {
@ -312,13 +453,13 @@ TEST(FileTest, Dup) {
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
}
#ifndef __COVERITY__
# ifndef __COVERITY__
TEST(FileTest, DupError) {
int value = -1;
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
"cannot duplicate file descriptor -1");
}
#endif
# endif
TEST(FileTest, Dup2) {
file f = open_file();
@ -368,11 +509,12 @@ TEST(FileTest, Fdopen) {
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
}
#ifdef FMT_LOCALE
# ifdef FMT_LOCALE
TEST(LocaleTest, Strtod) {
fmt::Locale locale;
fmt::locale loc;
const char *start = "4.2", *ptr = start;
EXPECT_EQ(4.2, locale.strtod(ptr));
EXPECT_EQ(4.2, loc.strtod(ptr));
EXPECT_EQ(start + 3, ptr);
}
#endif
# endif
#endif // FMT_USE_FCNTL

View File

@ -21,9 +21,9 @@ template <> struct formatter<test> : formatter<int> {
};
} // namespace fmt
#include "fmt/ostream.h"
#include <sstream>
#include "fmt/ostream.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "util.h"
@ -64,22 +64,23 @@ TEST(OStreamTest, Enum) {
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
}
using range = fmt::buffer_range<char>;
struct test_arg_formatter : fmt::arg_formatter<range> {
struct test_arg_formatter
: fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
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("") {}
: fmt::detail::arg_formatter<fmt::format_context::iterator, char>(
ctx, &parse_ctx, &s),
parse_ctx("") {}
};
TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer;
fmt::internal::buffer<char>& base = buffer;
fmt::detail::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);
fmt::visit_format_arg(
af, fmt::internal::make_arg<fmt::format_context>(streamable_enum()));
af, fmt::detail::make_arg<fmt::format_context>(streamable_enum()));
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
}
@ -95,7 +96,7 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
#if FMT_NUMERIC_ALIGN
#if FMT_DEPRECATED_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
"format specifier requires numeric argument");
#endif
@ -140,18 +141,18 @@ TEST(OStreamTest, WriteToOStream) {
fmt::memory_buffer buffer;
const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo));
fmt::internal::write(os, buffer);
fmt::detail::write_buffer(os, buffer);
EXPECT_EQ("foo", os.str());
}
TEST(OStreamTest, WriteToOStreamMaxSize) {
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;
size_t max_size = fmt::detail::max_value<size_t>();
std::streamsize max_streamsize = fmt::detail::max_value<std::streamsize>();
if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
struct test_buffer : fmt::internal::buffer<char> {
explicit test_buffer(std::size_t size) { resize(size); }
void grow(std::size_t) {}
struct test_buffer : fmt::detail::buffer<char> {
explicit test_buffer(size_t size) { resize(size); }
void grow(size_t) {}
} buffer(max_size);
struct mock_streambuf : std::streambuf {
@ -171,13 +172,13 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize size = max_size;
do {
auto n = std::min(size, fmt::internal::to_unsigned(max_streamsize));
auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize));
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
size -= n;
} while (size != 0);
fmt::internal::write(os, buffer);
fmt::detail::write_buffer(os, buffer);
}
TEST(OStreamTest, Join) {
@ -187,8 +188,8 @@ TEST(OStreamTest, Join) {
#if FMT_USE_CONSTEXPR
TEST(OStreamTest, ConstexprString) {
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
EXPECT_EQ("a string", format(fmt("{0}"), TestString("a string")));
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
}
#endif
@ -243,8 +244,7 @@ TEST(FormatTest, UDL) {
}
#endif
template <typename T>
struct convertible {
template <typename T> struct convertible {
T value;
explicit convertible(const T& val) : value(val) {}
operator T() const { return value; }
@ -255,3 +255,55 @@ TEST(OStreamTest, DisableBuiltinOStreamOperators) {
EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(OStreamTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u};
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(OStreamTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct copyfmt_test {};
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
std::ios ios(nullptr);
ios.copyfmt(os);
return os << "foo";
}
TEST(OStreamTest, CopyFmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
}
TEST(OStreamTest, CompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
}

View File

@ -11,13 +11,15 @@
#endif
#include "posix-mock.h"
#include "../src/posix.cc"
#include <errno.h>
#include <fcntl.h>
#include <climits>
#include <memory>
#include "../src/os.cc"
#ifdef _WIN32
# include <io.h>
# undef max
@ -30,7 +32,6 @@
using fmt::buffered_file;
using fmt::error_code;
using fmt::file;
using testing::_;
using testing::Return;
@ -48,11 +49,11 @@ int pipe_count;
int fopen_count;
int fclose_count;
int fileno_count;
std::size_t read_nbyte;
std::size_t write_nbyte;
size_t read_nbyte;
size_t write_nbyte;
bool sysconf_error;
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
enum { NONE, MAX_SIZE, ERROR } fstat_sim;
} // namespace
#define EMULATE_EINTR(func, error_result) \
@ -198,29 +199,32 @@ static void write_file(fmt::cstring_view filename, fmt::string_view content) {
f.print("{}", content);
}
#if FMT_USE_FCNTL
using fmt::file;
TEST(UtilTest, GetPageSize) {
#ifdef _WIN32
# ifdef _WIN32
SYSTEM_INFO si = {};
GetSystemInfo(&si);
EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
#else
# else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true;
EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
"cannot get memory page size");
sysconf_error = false;
#endif
# endif
}
TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here");
write_file("temp", "there must be something here");
std::unique_ptr<file> f{nullptr};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
"cannot open file test");
#ifndef _WIN32
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
"cannot open file temp");
# ifndef _WIN32
char c = 0;
f->read(&c, 1);
#endif
# endif
}
TEST(FileTest, CloseNoRetryInDtor) {
@ -228,14 +232,15 @@ TEST(FileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_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);
}
@ -250,26 +255,26 @@ TEST(FileTest, CloseNoRetry) {
TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading";
write_file("test", content);
file f("test", file::RDONLY);
write_file("temp", content);
file f("temp", file::RDONLY);
EXPECT_GE(f.size(), 0);
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
#ifdef _WIN32
# ifdef _WIN32
fmt::memory_buffer message;
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
"cannot get file size");
fmt::detail::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;
#else
# else
f.close();
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
#endif
# endif
}
TEST(FileTest, MaxSize) {
write_file("test", "");
file f("test", file::RDONLY);
write_file("temp", "");
file f("temp", file::RDONLY);
fstat_sim = MAX_SIZE;
EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size());
@ -283,7 +288,7 @@ TEST(FileTest, ReadRetry) {
write_end.write("test", SIZE);
write_end.close();
char buffer[SIZE];
std::size_t count = 0;
size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
"cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
@ -293,26 +298,26 @@ TEST(FileTest, WriteRetry) {
file read_end, write_end;
file::pipe(read_end, write_end);
enum { SIZE = 4 };
std::size_t count = 0;
size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE), write,
"cannot write to file");
write_end.close();
#ifndef _WIN32
# ifndef _WIN32
EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
char buffer[SIZE + 1];
read_end.read(buffer, SIZE);
buffer[SIZE] = '\0';
EXPECT_STREQ("test", buffer);
#endif
# endif
}
#ifdef _WIN32
# ifdef _WIN32
TEST(FileTest, ConvertReadCount) {
file read_end, write_end;
file::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(size_t)) ++size;
read_count = 1;
read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
@ -324,15 +329,15 @@ TEST(FileTest, ConvertWriteCount) {
file read_end, write_end;
file::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(size_t)) ++size;
write_count = 1;
write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
write_count = 0;
EXPECT_EQ(UINT_MAX, write_nbyte);
}
#endif
# endif
TEST(FileTest, DupNoRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout));
@ -357,11 +362,11 @@ TEST(FileTest, Dup2NoExceptRetry) {
error_code ec;
dup2_count = 1;
f1.dup2(f2.descriptor(), ec);
#ifndef _WIN32
# ifndef _WIN32
EXPECT_EQ(4, dup2_count);
#else
# else
EXPECT_EQ(EINTR, ec.get());
#endif
# endif
dup2_count = 0;
}
@ -383,15 +388,15 @@ TEST(FileTest, FdopenNoRetry) {
}
TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here");
write_file("temp", "there must be something here");
std::unique_ptr<buffered_file> f{nullptr};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
"cannot open file test");
#ifndef _WIN32
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
"cannot open file temp");
# ifndef _WIN32
char c = 0;
if (fread(&c, 1, 1, f->get()) < 1)
throw fmt::system_error(errno, "fread failed");
#endif
# endif
}
TEST(BufferedFileTest, CloseNoRetryInDtor) {
@ -399,14 +404,15 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end);
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);
}
@ -429,34 +435,35 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
#endif // FMT_USE_FCNTL
struct TestMock {
static TestMock* instance;
} * TestMock::instance;
struct test_mock {
static test_mock* instance;
} * test_mock::instance;
TEST(ScopedMock, Scope) {
{
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
TestMock& copy = mock;
ScopedMock<test_mock> mock;
EXPECT_EQ(&mock, test_mock::instance);
test_mock& copy = mock;
static_cast<void>(copy);
}
EXPECT_EQ(nullptr, TestMock::instance);
EXPECT_EQ(nullptr, test_mock::instance);
}
#ifdef FMT_LOCALE
typedef fmt::Locale::type LocaleType;
typedef fmt::locale::type locale_type;
struct LocaleMock {
static LocaleMock* instance;
MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
LocaleType base));
MOCK_METHOD1(freelocale, void(LocaleType locale));
struct locale_mock {
static locale_mock* instance;
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
locale_type base));
MOCK_METHOD1(freelocale, void(locale_type locale));
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, LocaleType locale));
} * LocaleMock::instance;
double(const char* nptr, char** endptr, locale_type locale));
} * locale_mock::instance;
# ifdef _MSC_VER
# pragma warning(push)
@ -467,15 +474,15 @@ struct LocaleMock {
# endif
_locale_t _create_locale(int category, const char* locale) {
return LocaleMock::instance->newlocale(category, locale, 0);
return locale_mock::instance->newlocale(category, locale, 0);
}
void _free_locale(_locale_t locale) {
LocaleMock::instance->freelocale(locale);
locale_mock::instance->freelocale(locale);
}
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# ifdef __clang__
# pragma clang diagnostic pop
@ -489,11 +496,6 @@ double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
# define FMT_LOCALE_THROW
# endif
LocaleType newlocale(int category_mask, const char* locale,
LocaleType base) FMT_LOCALE_THROW {
return LocaleMock::instance->newlocale(category_mask, locale, base);
}
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult;
@ -501,49 +503,55 @@ typedef int FreeLocaleResult;
typedef void FreeLocaleResult;
# endif
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
LocaleMock::instance->freelocale(locale);
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
locale_mock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char* nptr, char** endptr,
LocaleType locale) FMT_LOCALE_THROW {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
locale_type locale) FMT_LOCALE_THROW {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# undef FMT_LOCALE_THROW
TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock;
LocaleType locale = reinterpret_cast<LocaleType>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale);
# ifndef _WIN32
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
return locale_mock::instance->newlocale(category_mask, locale, base);
}
TEST(LocaleTest, LocaleMock) {
ScopedMock<locale_mock> mock;
locale_type locale = reinterpret_cast<locale_type>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
FMT_SYSTEM(newlocale(222, "foo", locale));
}
# endif
TEST(LocaleTest, Locale) {
# ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
# endif
ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42);
ScopedMock<locale_mock> mock;
locale_type impl = reinterpret_cast<locale_type>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale;
EXPECT_EQ(impl, locale.get());
fmt::locale loc;
EXPECT_EQ(impl, loc.get());
}
TEST(LocaleTest, Strtod) {
ScopedMock<LocaleMock> mock;
ScopedMock<locale_mock> mock;
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
fmt::locale loc;
const char* str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, locale.strtod(str));
EXPECT_EQ(777, loc.strtod(str));
EXPECT_EQ(&end, str);
}

View File

@ -9,7 +9,11 @@
#define FMT_POSIX_TEST_H
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#ifdef __APPLE__
# include <xlocale.h>
#endif
#ifdef _WIN32
# include <windows.h>
@ -62,6 +66,10 @@ int pipe(int* pfds, unsigned psize, int textmode);
FILE* fopen(const char* filename, const char* mode);
int fclose(FILE* stream);
int(fileno)(FILE* stream);
#if defined(FMT_LOCALE) && !defined(_WIN32)
locale_t newlocale(int category_mask, const char* locale, locale_t base);
#endif
} // namespace test
#define FMT_SYSTEM(call) test::call

View File

@ -5,18 +5,19 @@
//
// For the license information refer to format.h.
#include "fmt/printf.h"
#include <cctype>
#include <climits>
#include <cstring>
#include "fmt/core.h"
#include "fmt/printf.h"
#include "gtest-extra.h"
#include "util.h"
using fmt::format;
using fmt::format_error;
using fmt::internal::max_value;
using fmt::detail::max_value;
const unsigned BIG_NUM = INT_MAX + 1u;
@ -112,14 +113,13 @@ TEST(PrintfTest, SwitchArgIndexing) {
TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
"number is too big");
}
@ -139,8 +139,11 @@ TEST(PrintfTest, ZeroFlag) {
EXPECT_PRINTF("+00042", "%00+6d", 42);
EXPECT_PRINTF(" 42", "%05.d", 42);
EXPECT_PRINTF(" 0042", "%05.4d", 42);
// '0' flag is ignored for non-numeric types.
EXPECT_PRINTF("0000x", "%05c", 'x');
EXPECT_PRINTF(" x", "%05c", 'x');
}
TEST(PrintfTest, PlusFlag) {
@ -151,11 +154,38 @@ TEST(PrintfTest, PlusFlag) {
// '+' flag is ignored for non-numeric types.
EXPECT_PRINTF("x", "%+c", 'x');
// '+' flag wins over space flag
EXPECT_PRINTF("+42", "%+ d", 42);
EXPECT_PRINTF("-42", "%+ d", -42);
EXPECT_PRINTF("+42", "% +d", 42);
EXPECT_PRINTF("-42", "% +d", -42);
EXPECT_PRINTF("+0042", "% +05d", 42);
EXPECT_PRINTF("+0042", "%0+ 5d", 42);
// '+' flag and space flag are both ignored for non-numeric types.
EXPECT_PRINTF("x", "%+ c", 'x');
EXPECT_PRINTF("x", "% +c", 'x');
}
TEST(PrintfTest, MinusFlag) {
EXPECT_PRINTF("abc ", "%-5s", "abc");
EXPECT_PRINTF("abc ", "%0--5s", "abc");
EXPECT_PRINTF("7 ", "%-5d", 7);
EXPECT_PRINTF("97 ", "%-5hhi", 'a');
EXPECT_PRINTF("a ", "%-5c", 'a');
// '0' flag is ignored if '-' flag is given
EXPECT_PRINTF("7 ", "%-05d", 7);
EXPECT_PRINTF("7 ", "%0-5d", 7);
EXPECT_PRINTF("a ", "%-05c", 'a');
EXPECT_PRINTF("a ", "%0-5c", 'a');
EXPECT_PRINTF("97 ", "%-05hhi", 'a');
EXPECT_PRINTF("97 ", "%0-5hhi", 'a');
// '-' and space flag don't interfere
EXPECT_PRINTF(" 42", "%- d", 42);
}
TEST(PrintfTest, SpaceFlag) {
@ -221,8 +251,7 @@ TEST(PrintfTest, DynamicWidth) {
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"), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big");
}
@ -258,6 +287,11 @@ TEST(PrintfTest, FloatPrecision) {
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
}
TEST(PrintfTest, StringPrecision) {
char test[] = {'H', 'e', 'l', 'l', 'o'};
EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell");
}
TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
EXPECT_PRINTF("abc", "%.5s", "abc");
}
@ -267,8 +301,7 @@ TEST(PrintfTest, DynamicPrecision) {
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"), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big");
if (sizeof(long long) != sizeof(int)) {
@ -297,18 +330,18 @@ void TestLength(const char* length_spec, U value) {
unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument.
unsigned long long max = max_value<U>();
using fmt::internal::const_check;
using fmt::detail::const_check;
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned long long>(value);
} else if (const_check(max <= max_value<unsigned>())) {
signed_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned long long>(value);
}
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
signed_value = static_cast<long long>(value);
unsigned_value =
static_cast<typename std::make_unsigned<unsigned>::type>(value);
unsigned_value = static_cast<unsigned long long>(
static_cast<typename std::make_unsigned<unsigned>::type>(value));
} else {
signed_value = static_cast<typename make_signed<T>::type>(value);
unsigned_value = static_cast<typename std::make_unsigned<T>::type>(value);
@ -337,7 +370,9 @@ template <typename T> void TestLength(const char* length_spec) {
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);
long long long_long_min = std::numeric_limits<long long>::min();
if (static_cast<long long>(min) > long_long_min)
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
unsigned long long long_long_max = max_value<long long>();
if (static_cast<unsigned long long>(max) < long_long_max)
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
@ -364,7 +399,7 @@ TEST(PrintfTest, Length) {
TestLength<long long>("ll");
TestLength<unsigned long long>("ll");
TestLength<intmax_t>("j");
TestLength<std::size_t>("z");
TestLength<size_t>("z");
TestLength<std::ptrdiff_t>("t");
long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
@ -408,6 +443,7 @@ TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.65", "%G", 392.65);
EXPECT_PRINTF("392", "%g", 392.0);
EXPECT_PRINTF("392", "%G", 392.0);
EXPECT_PRINTF("4.56e-07", "%g", 0.000000456);
safe_sprintf(buffer, "%a", -392.65);
EXPECT_EQ(buffer, format("{:a}", -392.65));
safe_sprintf(buffer, "%A", -392.65);
@ -443,6 +479,12 @@ TEST(PrintfTest, String) {
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
}
TEST(PrintfTest, UCharString) {
unsigned char str[] = "test";
unsigned char* pstr = str;
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
}
TEST(PrintfTest, Pointer) {
int n;
void* p = &n;
@ -470,11 +512,15 @@ TEST(PrintfTest, Location) {
// TODO: test %n
}
enum E { A = 42 };
enum test_enum { answer = 42 };
TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
TEST(PrintfTest, Enum) {
EXPECT_PRINTF("42", "%d", answer);
volatile test_enum volatile_enum = answer;
EXPECT_PRINTF("42", "%d", volatile_enum);
}
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
TEST(PrintfTest, Examples) {
const char* weekday = "Thursday";
const char* month = "August";
@ -561,51 +607,22 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
#endif
}
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t;
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
TEST(PrintfTest, PrintfDetermineOutputSize) {
using backit = std::back_insert_iterator<std::vector<char>>;
using truncated_printf_context =
fmt::basic_printf_context<fmt::detail::truncating_iterator<backit>, char>;
// 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;
auto v = std::vector<char>{};
auto it = std::back_inserter(v);
custom_printf_arg_formatter(formatter_t::iterator iter,
formatter_t::format_specs& specs, context_t& ctx)
: formatter_t(iter, specs, ctx) {}
const auto format_string = "%s";
const auto format_arg = "Hello";
const auto expected_size = fmt::sprintf(format_string, format_arg).size();
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));
EXPECT_EQ((truncated_printf_context(
fmt::detail::truncating_iterator<backit>(it, 0), format_string,
fmt::make_format_args<truncated_printf_context>(format_arg))
.format()
.count()),
expected_size);
}

View File

@ -10,6 +10,7 @@
// {fmt} support for ranges, containers and types tuple interface.
#include "fmt/ranges.h"
#include "gtest.h"
// Check if 'if constexpr' is supported.
@ -44,9 +45,10 @@ TEST(RangesTest, FormatPair) {
}
TEST(RangesTest, FormatTuple) {
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));
std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
'i'};
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", t));
EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
}
TEST(RangesTest, JoinTuple) {
@ -68,10 +70,16 @@ TEST(RangesTest, JoinTuple) {
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
}
TEST(RangesTest, JoinInitializerList) {
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
EXPECT_EQ("fmt rocks !",
fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
}
struct my_struct {
int32_t i;
std::string str; // can throw
template <std::size_t N> decltype(auto) get() const noexcept {
template <size_t N> decltype(auto) get() const noexcept {
if constexpr (N == 0)
return i;
else if constexpr (N == 1)
@ -79,16 +87,15 @@ struct my_struct {
}
};
template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept {
template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
return s.get<N>();
}
namespace std {
template <>
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
template <> struct tuple_size<my_struct> : std::integral_constant<size_t, 2> {};
template <std::size_t N> struct tuple_element<N, my_struct> {
template <size_t N> struct tuple_element<N, my_struct> {
using type = decltype(std::declval<my_struct>().get<N>());
};
@ -132,3 +139,17 @@ TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
};
TEST(RangesTest, JoinSentinel) {
zstring hello{"hello"};
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
}

View File

@ -5,12 +5,14 @@
//
// For the license information refer to format.h.
#include "scan.h"
#include <time.h>
#include <climits>
#include "gmock.h"
#include "gtest-extra.h"
#include "scan.h"
TEST(ScanTest, ReadText) {
fmt::string_view s = "foo";
@ -73,7 +75,7 @@ template <> struct scanner<tm> {
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
format.reserve(internal::to_unsigned(end - it + 1));
format.reserve(detail::to_unsigned(end - it + 1));
format.append(it, end);
format.push_back('\0');
return end;

View File

@ -6,6 +6,8 @@
// For the license information refer to format.h.
#include <array>
#include <cassert>
#include <climits>
#include "fmt/format.h"
@ -29,7 +31,7 @@ class scan_parse_context {
FMT_CONSTEXPR iterator end() const { return format_.end(); }
void advance_to(iterator it) {
format_.remove_prefix(internal::to_unsigned(it - begin()));
format_.remove_prefix(detail::to_unsigned(it - begin()));
}
};
@ -46,11 +48,11 @@ struct scan_context {
iterator end() const { return begin() + input_.size(); }
void advance_to(iterator it) {
input_.remove_prefix(internal::to_unsigned(it - begin()));
input_.remove_prefix(detail::to_unsigned(it - begin()));
}
};
namespace internal {
namespace detail {
enum class scan_type {
none_type,
int_type,
@ -105,20 +107,20 @@ class scan_arg {
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
}
};
} // namespace internal
} // namespace detail
struct scan_args {
int size;
const internal::scan_arg* data;
const detail::scan_arg* data;
template <size_t N>
scan_args(const std::array<internal::scan_arg, N>& store)
scan_args(const std::array<detail::scan_arg, N>& store)
: size(N), data(store.data()) {
static_assert(N < INT_MAX, "too many arguments");
}
};
namespace internal {
namespace detail {
struct scan_handler : error_handler {
private:
@ -135,21 +137,20 @@ struct scan_handler : error_handler {
char c = *it++;
if (c < '0' || c > '9') on_error("invalid input");
// TODO: check overflow
value = value * 10 + (c - '0');
value = value * 10 + static_cast<unsigned>(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;
const auto value = read_uint<typename std::make_unsigned<T>::type>();
if (negative) return -static_cast<T>(value);
return static_cast<T>(value);
}
public:
@ -159,7 +160,7 @@ struct scan_handler : error_handler {
const char* pos() const { return scan_ctx_.begin(); }
void on_text(const char* begin, const char* end) {
auto size = end - begin;
auto size = to_unsigned(end - begin);
auto it = scan_ctx_.begin();
if (it + size > scan_ctx_.end() ||
!std::equal(begin, end, make_checked(it, size))) {
@ -168,14 +169,15 @@ struct scan_handler : error_handler {
scan_ctx_.advance_to(it + size);
}
void on_arg_id() { on_arg_id(next_arg_id_++); }
void on_arg_id(int id) {
int on_arg_id() { return on_arg_id(next_arg_id_++); }
int on_arg_id(int id) {
if (id >= args_.size) on_error("argument index out of range");
arg_ = args_.data[id];
return id;
}
void on_arg_id(string_view) { on_error("invalid format"); }
int on_arg_id(string_view) { return on_error("invalid format"), 0; }
void on_replacement_field(const char*) {
void on_replacement_field(int, const char*) {
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
switch (arg_.type) {
case scan_type::int_type:
@ -197,33 +199,34 @@ struct scan_handler : error_handler {
case scan_type::string_view_type: {
auto s = it;
while (it != end && *it != ' ') ++it;
*arg_.string_view = fmt::string_view(s, it - s);
*arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
scan_ctx_.advance_to(it);
break;
}
default:
case scan_type::none_type:
case scan_type::custom_type:
assert(false);
}
}
const char* on_format_specs(const char* begin, const char*) {
const char* on_format_specs(int, 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
} // namespace detail
template <typename... Args>
std::array<internal::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
std::array<detail::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);
detail::scan_handler h(format_str, input, args);
detail::parse_format_string<false>(format_str, h);
return input.begin() + (h.pos() - &*input.begin());
}

View File

@ -1,4 +1,5 @@
#include <format>
#include "gtest.h"
TEST(StdFormatTest, Escaping) {
@ -29,8 +30,8 @@ TEST(StdFormatTest, Alignment) {
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 "
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*****");
@ -61,11 +62,11 @@ TEST(StdFormatTest, Int) {
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:n}", 1234); // s3 == "1,234" (depends on the locale)
string s3 = format("{:L}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A");
EXPECT_EQ(s3, "1,234");
EXPECT_EQ(s3, "1234");
}
#include <format>
@ -123,7 +124,7 @@ template <> struct std::formatter<S> {
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) ||
else if (fmt::detail::is_negative(value) ||
value > numeric_limits<int>::max())
throw format_error("invalid width");
else

View File

@ -9,13 +9,19 @@
#define FMT_TEST_ASSERT_H_
#include <stdexcept>
#include "gtest.h"
class assertion_failure : public std::logic_error {
public:
explicit assertion_failure(const char* message) : std::logic_error(message) {}
private:
virtual void avoid_weak_vtable();
};
void assertion_failure::avoid_weak_vtable() {}
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw assertion_failure(message);

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <cstdlib>
#include "gtest.h"
#ifdef _WIN32

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include "util.h"
#include <cstring>
void increment(char* s) {
@ -33,11 +34,17 @@ std::string get_system_error(int error_code) {
const char* const FILE_CONTENT = "Don't panic!";
fmt::buffered_file open_buffered_file(FILE** fp) {
#if FMT_USE_FCNTL
fmt::file read_end, write_end;
fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close();
fmt::buffered_file f = read_end.fdopen("r");
if (fp) *fp = f.get();
#else
fmt::buffered_file f("test-file", "w");
fputs(FILE_CONTENT, f.get());
if (fp) *fp = f.get();
#endif
return f;
}

View File

@ -9,7 +9,7 @@
#include <cstdio>
#include <string>
#include "fmt/posix.h"
#include "fmt/os.h"
enum { BUFFER_SIZE = 256 };
@ -19,7 +19,7 @@ enum { BUFFER_SIZE = 256 };
# define FMT_VSNPRINTF vsnprintf
#endif
template <std::size_t SIZE>
template <size_t SIZE>
void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) {
std::va_list args;
va_start(args, format);