Compare commits

...

189 Commits
6.1.0 ... 6.2.0

Author SHA1 Message Date
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
45 changed files with 2819 additions and 1597 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
.vscode/
.vs/
*.iml
.idea/

View File

@ -24,13 +24,24 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE)
endfunction()
# 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(SET_VERBOSE "" "" "CACHE" ${ARGN})
list(GET SET_VERBOSE_CACHE 0 type)
list(REMOVE_AT SET_VERBOSE_CACHE 0)
join(doc ${SET_VERBOSE_CACHE})
set(${SET_VERBOSE_UNPARSED_ARGUMENTS} 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 ()
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
@ -42,6 +53,7 @@ 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)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
project(FMT CXX)
@ -60,7 +72,9 @@ 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")
@ -68,7 +82,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14)
include(CheckCXXCompilerFlag)
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
@ -122,7 +142,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}\" %*")
@ -150,8 +172,8 @@ endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
locale.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc src/posix.cc)
locale.h os.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc src/os.cc)
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
@ -173,9 +195,18 @@ target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
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")
@ -193,7 +224,6 @@ 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
@ -204,8 +234,9 @@ target_include_directories(fmt-header-only INTERFACE
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, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
@ -216,14 +247,17 @@ if (FMT_INSTALL)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(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_verbose(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, relative to "
"${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
@ -280,7 +314,7 @@ 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,247 @@
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 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
------------------
@ -92,7 +336,7 @@
Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_.
* Changed formatting of octal zero with prefix from "0o0" to "0":
* Changed formatting of octal zero with prefix from "00" to "0":
.. code:: c++

View File

@ -13,14 +13,14 @@
.. 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
: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
--------
@ -33,10 +33,10 @@ Features
* 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>`__.
* Implementation of `C++20 std::format <https://en.cppreference.com/w/cpp/utility/format>`__.
* 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
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_ and
iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
* Small code size both in terms of source code (the minimum configuration
@ -164,9 +164,9 @@ Speed tests
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.03
libc++ std::ostream 2.98
{fmt} 4de41a fmt::print 0.76
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
================= ============= ===========
@ -174,7 +174,7 @@ Folly Format folly::format 2.23
{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
10.14.6 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source
@ -264,7 +264,7 @@ or the bloat test::
Projects using this library
---------------------------
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform real-time
strategy game
* `AMPL/MP <https://github.com/ampl/mp>`_:
@ -285,15 +285,15 @@ Projects using this library
* `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
* `KBEngine <https://kbengine.org/>`_: An open-source MMOG server engine
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
* `Keypirinha <https://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,10 +306,10 @@ Projects using this library
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization
* `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
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
@ -326,10 +326,10 @@ Projects using this 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++
@ -368,7 +368,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
@ -486,8 +486,8 @@ 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/>`_.
<https://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <https://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable

View File

@ -85,6 +85,19 @@ 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("{:n}", 1000000); // s == "1,000,000"
.. _format-api:
Format API

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', '6.1.0']
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."
@ -74,7 +74,7 @@ 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 \
INPUT = {0}/core.h {0}/format.h {0}/os.h {0}/ostream.h \
{0}/printf.h {0}/time.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES

View File

@ -10,7 +10,7 @@ alternative to C stdio and C++ iostreams.
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for
aa long time. Ive used both <code>boost::format</code> and
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>

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

@ -114,6 +114,22 @@ fmt can be installed on Linux, macOS and Windows with
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,14 +8,14 @@
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
@ -495,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;
}
@ -696,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) {
@ -759,18 +759,30 @@ 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 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 Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) {
string_view s(unit);
if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (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,
@ -793,7 +805,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;
@ -868,13 +883,13 @@ struct chrono_formatter {
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);
}
@ -904,7 +919,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) {
@ -913,7 +928,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) {
@ -922,7 +937,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) {
@ -947,13 +962,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() {
@ -977,16 +991,18 @@ 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
@ -1021,10 +1037,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
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) {
@ -1085,8 +1101,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);

View File

@ -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:
@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, format_str, args);
if (has_style) {
internal::reset_color<Char>(buf);
}
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
}
} // namespace internal
@ -540,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
@ -562,7 +560,7 @@ 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 vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -9,6 +9,7 @@
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
@ -26,11 +27,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 +41,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) {
@ -62,7 +63,7 @@ template <typename Char> struct part_counter {
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
@ -119,7 +120,7 @@ class format_string_compiler : public error_handler {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
FMT_CONSTEXPR void on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
@ -350,6 +351,8 @@ 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;
@ -361,6 +364,9 @@ template <typename Char> struct text {
}
};
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) {
@ -406,6 +412,9 @@ template <typename Char, typename T, int N> struct field {
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
@ -418,6 +427,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};
@ -508,19 +520,15 @@ constexpr auto compile(S format_str) {
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
FMT_ENABLE_IF(internal::is_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)>
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
@ -551,7 +559,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
make_format_args<context>(args...));
return to_string(buffer);
}
@ -563,8 +571,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf,
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 internal::cf::vformat_to<context>(range(out), cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,

View File

@ -10,12 +10,15 @@
#include <cstdio> // std::FILE
#include <cstring>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60100
#define FMT_VERSION 60200
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@ -36,6 +39,18 @@
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
#endif
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
(__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
(__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
@ -117,16 +132,25 @@
# endif
#endif
// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings.
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
// warnings.
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
!FMT_NVCC
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
#ifndef FMT_MAYBE_UNUSED
# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
# define FMT_MAYBE_UNUSED [[maybe_unused]]
# else
# define FMT_MAYBE_UNUSED
# endif
#endif
#ifndef FMT_DEPRECATED
# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \
FMT_MSC_VER >= 1900
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if defined(__GNUC__) || defined(__clang__)
@ -139,8 +163,8 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
@ -166,19 +190,37 @@
#endif
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# if FMT_MSC_VER
# define FMT_NO_W4275 __pragma(warning(suppress : 4275))
# else
# define FMT_NO_W4275
# endif
# define FMT_CLASS_API FMT_NO_W4275
# ifdef FMT_EXPORT
# define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllexport)
# define FMT_API __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllimport)
# define FMT_API __declspec(dllimport)
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
#endif
#ifndef FMT_CLASS_API
# define FMT_CLASS_API
#endif
#ifndef FMT_API
# define FMT_API
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_API __attribute__((visibility("default")))
# define FMT_EXTERN_TEMPLATE_API FMT_API
# define FMT_INSTANTIATION_DEF_API
# else
# define FMT_API
# endif
#endif
#ifndef FMT_EXTERN_TEMPLATE_API
# define FMT_EXTERN_TEMPLATE_API
#endif
#ifndef FMT_INSTANTIATION_DEF_API
# define FMT_INSTANTIATION_DEF_API FMT_API
#endif
#ifndef FMT_HEADER_ONLY
# define FMT_EXTERN extern
@ -197,9 +239,16 @@
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
#endif
#ifndef FMT_UNICODE
# define FMT_UNICODE !FMT_MSC_VER
#endif
#if FMT_UNICODE && FMT_MSC_VER
# pragma execution_character_set("utf-8")
#endif
FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other types for pre-C++14 systems.
// Implementations of enable_if_t and other metafunctions for older systems.
template <bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, class T, class F>
@ -211,6 +260,8 @@ template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; };
template <typename T> using type_identity_t = typename type_identity<T>::type;
struct monostate {};
@ -221,19 +272,25 @@ struct monostate {};
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
void assert_fail(const char* file, int line, const char* message);
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
# define FMT_ASSERT(condition, message) ((void)0)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
@ -248,7 +305,7 @@ template <typename T> struct std_string_view {};
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
@ -266,6 +323,19 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
constexpr unsigned char micro[] = "\u00B5";
template <typename Char> constexpr bool is_unicode() {
return FMT_UNICODE || sizeof(Char) != 1 ||
(sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
}
#ifdef __cpp_char8_t
using char8_type = char8_t;
#else
enum char8_type : unsigned char {};
#endif
} // namespace internal
template <typename... Ts>
@ -284,7 +354,8 @@ template <typename Char> class basic_string_view {
size_t size_;
public:
using char_type = Char;
using char_type FMT_DEPRECATED_ALIAS = Char;
using value_type = Char;
using iterator = const Char*;
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
@ -300,6 +371,9 @@ template <typename Char> class basic_string_view {
the size with ``std::char_traits<Char>::length``.
\endrst
*/
#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr.
FMT_CONSTEXPR
#endif
basic_string_view(const Char* s)
: data_(s), size_(std::char_traits<Char>::length(s)) {}
@ -365,15 +439,15 @@ using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
#ifndef __cpp_char8_t
// A UTF-8 code unit type.
enum char8_t : unsigned char {};
// char8_t is deprecated; use char instead.
using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
#endif
/** Specifies if ``T`` is a character type. Can be specialized by users. */
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<char8_t> : std::true_type {};
template <> struct is_char<internal::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
@ -442,7 +516,7 @@ struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
template <typename S, typename = void> struct char_t_impl {};
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
using result = decltype(to_string_view(std::declval<S>()));
using type = typename result::char_type;
using type = typename result::value_type;
};
struct error_handler {
@ -603,6 +677,9 @@ template <typename T> class buffer {
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
const T* begin() const FMT_NOEXCEPT { return ptr_; }
const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
/** Returns the size of this buffer. */
std::size_t size() const FMT_NOEXCEPT { return size_; }
@ -639,8 +716,10 @@ template <typename T> class buffer {
/** Appends data to the end of the buffer. */
template <typename U> void append(const U* begin, const U* end);
T& operator[](std::size_t index) { return ptr_[index]; }
const T& operator[](std::size_t index) const { return ptr_[index]; }
template <typename I> T& operator[](I index) { return ptr_[index]; }
template <typename I> const T& operator[](I index) const {
return ptr_[index];
}
};
// A container-backed buffer.
@ -684,7 +763,7 @@ using has_fallback_formatter =
template <typename Char> struct named_arg_base;
template <typename T, typename Char> struct named_arg;
enum type {
enum class type {
none_type,
named_arg_type,
// Integer types should go first,
@ -710,11 +789,12 @@ enum type {
// Maps core type T to the corresponding type enum constant.
template <typename T, typename Char>
struct type_constant : std::integral_constant<type, custom_type> {};
struct type_constant : std::integral_constant<type, type::custom_type> {};
#define FMT_TYPE_CONSTANT(Type, constant) \
template <typename Char> \
struct type_constant<Type, Char> : std::integral_constant<type, constant> {}
struct type_constant<Type, Char> \
: std::integral_constant<type, type::constant> {}
FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
FMT_TYPE_CONSTANT(int, int_type);
@ -733,13 +813,13 @@ FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_integer_type;
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
return t > type::none_type && t <= type::last_integer_type;
}
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_numeric_type;
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
return t > type::none_type && t <= type::last_numeric_type;
}
template <typename Char> struct string_value {
@ -869,7 +949,8 @@ template <typename Context> struct arg_mapper {
template <typename T,
FMT_ENABLE_IF(
std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
@ -878,7 +959,8 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
@ -907,14 +989,13 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR auto map(const T& val) -> decltype(
map(static_cast<typename std::underlying_type<T>::type>(val))) {
FMT_CONSTEXPR auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map(
static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>,
T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
@ -928,6 +1009,16 @@ template <typename Context> struct arg_mapper {
std::memcpy(val.data, &arg, sizeof(arg));
return val;
}
int map(...) {
constexpr bool formattable = sizeof(Context) == 0;
static_assert(
formattable,
"Cannot format argument. To make type T formattable provide a "
"formatter<T> specialization: "
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
return 0;
}
};
// A type constant after applying arg_mapper<Context>.
@ -979,10 +1070,10 @@ template <typename Context> class basic_format_arg {
internal::custom_value<Context> custom_;
};
FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {}
FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
return type_ != internal::none_type;
return type_ != internal::type::none_type;
}
internal::type type() const { return type_; }
@ -1004,47 +1095,47 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
-> decltype(vis(0)) {
using char_type = typename Context::char_type;
switch (arg.type_) {
case internal::none_type:
case internal::type::none_type:
break;
case internal::named_arg_type:
case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
case internal::int_type:
case internal::type::int_type:
return vis(arg.value_.int_value);
case internal::uint_type:
case internal::type::uint_type:
return vis(arg.value_.uint_value);
case internal::long_long_type:
case internal::type::long_long_type:
return vis(arg.value_.long_long_value);
case internal::ulong_long_type:
case internal::type::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
case internal::int128_type:
case internal::type::int128_type:
return vis(arg.value_.int128_value);
case internal::uint128_type:
case internal::type::uint128_type:
return vis(arg.value_.uint128_value);
#else
case internal::int128_type:
case internal::uint128_type:
case internal::type::int128_type:
case internal::type::uint128_type:
break;
#endif
case internal::bool_type:
case internal::type::bool_type:
return vis(arg.value_.bool_value);
case internal::char_type:
case internal::type::char_type:
return vis(arg.value_.char_value);
case internal::float_type:
case internal::type::float_type:
return vis(arg.value_.float_value);
case internal::double_type:
case internal::type::double_type:
return vis(arg.value_.double_value);
case internal::long_double_type:
case internal::type::long_double_type:
return vis(arg.value_.long_double_value);
case internal::cstring_type:
case internal::type::cstring_type:
return vis(arg.value_.string.data);
case internal::string_type:
case internal::type::string_type:
return vis(basic_string_view<char_type>(arg.value_.string.data,
arg.value_.string.size));
case internal::pointer_type:
case internal::type::pointer_type:
return vis(arg.value_.pointer);
case internal::custom_type:
case internal::type::custom_type:
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
}
return vis(monostate());
@ -1104,7 +1195,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value |
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
}
@ -1127,6 +1218,43 @@ template <bool IS_PACKED, typename Context, typename T,
inline basic_format_arg<Context> make_arg(const T& value) {
return make_arg<Context>(value);
}
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = node->value;
node->next = std::move(head_);
head_ = std::move(node);
return value;
}
};
} // namespace internal
// Formatting context.
@ -1189,7 +1317,13 @@ using wformat_context = buffer_context<wchar_t>;
such as `~fmt::vformat`.
\endrst
*/
template <typename Context, typename... Args> class format_arg_store {
template <typename Context, typename... Args>
class format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
static const size_t num_args = sizeof...(Args);
static const bool is_packed = num_args < internal::max_packed_args;
@ -1206,10 +1340,14 @@ template <typename Context, typename... Args> class format_arg_store {
static constexpr unsigned long long types =
is_packed ? internal::encode_types<Context, Args...>()
: internal::is_unpacked_bit | num_args;
FMT_DEPRECATED static constexpr unsigned long long TYPES = types;
format_arg_store(const Args&... args)
: data_{internal::make_arg<is_packed, Context>(args)...} {}
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{internal::make_arg<is_packed, Context>(args)...} {
}
};
/**
@ -1226,7 +1364,112 @@ inline format_arg_store<Context, Args...> make_format_args(
return {args...};
}
/** Formatting arguments. */
/**
\rst
A dynamic version of `fmt::format_arg_store<>`.
It's equipped with a storage to potentially temporary objects which lifetime
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr internal::type mapped_type =
internal::mapped_type_constant<T, Context>::value;
enum {
value = !(internal::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, internal::std_string_view<char_type>>::value ||
(mapped_type != internal::type::cstring_type &&
mapped_type != internal::type::string_type &&
mapped_type != internal::type::custom_type &&
mapped_type != internal::type::named_arg_type))
};
};
template <typename T>
using stored_type = conditional_t<internal::is_string<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
internal::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
return internal::is_unpacked_bit | data_.size();
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(internal::make_arg<Context>(arg));
}
public:
/**
\rst
Adds an argument into the dynamic store for later passing to a formating
function.
Note that custom types and string types (but not string views!) are copied
into the store with dynamic memory (in addition to resizing vector).
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
static_assert(
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
"named arguments are not supported yet");
if (internal::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(arg);
}
/**
Adds a reference to the argument into the dynamic store for later passing to
a formating function.
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
};
/**
\rst
A view of a collection of formatting arguments. To avoid lifetime issues it
should only be used as a parameter type in type-erased functions such as
``vformat``::
void vlog(string_view format_str, format_args args); // OK
format_args args = make_format_args(42); // Error: dangling reference
\endrst
*/
template <typename Context> class basic_format_args {
public:
using size_type = int;
@ -1268,7 +1511,7 @@ template <typename Context> class basic_format_args {
}
if (index > internal::max_packed_args) return arg;
arg.type_ = type(index);
if (arg.type_ == internal::none_type) return arg;
if (arg.type_ == internal::type::none_type) return arg;
internal::value<Context>& val = arg.value_;
val = values_[index];
return arg;
@ -1284,10 +1527,21 @@ template <typename Context> class basic_format_args {
*/
template <typename... Args>
basic_format_args(const format_arg_store<Context, Args...>& store)
: types_(static_cast<unsigned long long>(store.types)) {
: types_(store.types) {
set_data(store.data_);
}
/**
\rst
Constructs a `basic_format_args` object from
`~fmt::dynamic_format_arg_store`.
\endrst
*/
basic_format_args(const dynamic_format_arg_store<Context>& store)
: types_(store.get_types()) {
set_data(store.data_.data());
}
/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
@ -1301,7 +1555,7 @@ template <typename Context> class basic_format_args {
/** Returns the argument at specified index. */
format_arg get(int index) const {
format_arg arg = do_get(index);
if (arg.type_ == internal::named_arg_type)
if (arg.type_ == internal::type::named_arg_type)
arg = arg.value_.named_arg->template deserialize<Context>();
return arg;
}
@ -1318,12 +1572,12 @@ template <typename Context> class basic_format_args {
struct format_args : basic_format_args<format_context> {
template <typename... Args>
format_args(Args&&... args)
: basic_format_args<format_context>(std::forward<Args>(args)...) {}
: basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
};
struct wformat_args : basic_format_args<wformat_context> {
template <typename... Args>
wformat_args(Args&&... args)
: basic_format_args<wformat_context>(std::forward<Args>(args)...) {}
: basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
};
template <typename Container> struct is_contiguous : std::false_type {};
@ -1357,7 +1611,10 @@ template <typename Char> struct named_arg_base {
}
};
template <typename T, typename Char> struct named_arg : named_arg_base<Char> {
struct view {};
template <typename T, typename Char>
struct named_arg : view, named_arg_base<Char> {
const T& value;
named_arg(basic_string_view<Char> name, const T& val)
@ -1375,7 +1632,6 @@ inline void check_format_string(const S&) {
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
struct view {};
template <bool...> struct bool_pack;
template <bool... Args>
using all_true =
@ -1385,32 +1641,38 @@ template <typename... Args, typename S, typename Char = char_t<S>>
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args) {
static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() ||
!std::is_reference<Args>())...>::value,
"passing views as lvalues is disallowed");
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
static_assert(
all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
!std::is_reference<Args>::value)...>::value,
"passing views as lvalues is disallowed");
check_format_string<Args...>(format_str);
return {args...};
}
template <typename Char>
std::basic_string<Char> vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args);
std::basic_string<Char> vformat(
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args);
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args);
basic_format_args<buffer_context<type_identity_t<Char>>> args);
template <typename Char, typename Args,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
#ifndef _WIN32
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
#endif
} // namespace internal
/**
\rst
Returns a named argument to be used in a formatting function.
The named argument holds a reference and does not extend the lifetime
of its arguments.
Consequently, a dangling reference can accidentally be created.
The user should take care to only pass this function temporaries when
the named argument is itself a temporary, as per the following example.
Returns a named argument to be used in a formatting function. It should only
be used in a call to a formatting function.
**Example**::
@ -1433,8 +1695,9 @@ void arg(S, internal::named_arg<T, Char>) = delete;
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(
internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
OutputIt vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
OutputIt vformat_to(
OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using container = remove_reference_t<decltype(internal::get_container(out))>;
internal::container_buffer<container> buf((internal::get_container(out)));
internal::vformat_to(buf, to_string_view(format_str), args);
@ -1447,14 +1710,14 @@ template <typename Container, typename S, typename... Args,
inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str,
Args&&... args) {
return vformat_to(
out, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
return vformat_to(out, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const S& format_str, basic_format_args<buffer_context<Char>> args) {
const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(to_string_view(format_str), args);
}
@ -1474,44 +1737,52 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
return internal::vformat(
to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
FMT_API void vprint(string_view format_str, format_args args);
FMT_API void vprint(string_view, format_args);
FMT_API void vprint(std::FILE*, string_view, format_args);
/**
\rst
Prints formatted data to the file *f*. For wide format strings,
*f* should be in wide-oriented mode set via ``fwide(f, 1)`` or
``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
Formats ``args`` according to specifications in ``format_str`` and writes the
output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
``FMT_UNICODE`` macro is set to 0.
**Example**::
fmt::print(stderr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
vprint(f, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
return internal::is_unicode<Char>()
? vprint(f, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...))
: internal::vprint_mojibake(
f, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
/**
\rst
Prints formatted data to ``stdout``.
Formats ``args`` according to specifications in ``format_str`` and writes
the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
the ``FMT_UNICODE`` macro is set to 0.
**Example**::
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(const S& format_str, Args&&... args) {
vprint(to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
return internal::is_unicode<Char>()
? vprint(to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...))
: internal::vprint_mojibake(
stdout, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -8,8 +8,6 @@
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#include "format.h"
#include <cassert>
#include <cctype>
#include <climits>
@ -17,29 +15,15 @@
#include <cstdarg>
#include <cstring> // for std::memmove
#include <cwchar>
#include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
#endif
#if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#ifdef _WIN32
# include <io.h>
# include <windows.h>
#endif
#ifdef _MSC_VER
@ -73,8 +57,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
using format_func = void (*)(internal::buffer<char>&, int, string_view);
// A portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
@ -104,6 +86,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
}
// Handle the result of GNU-specific version of strerror_r.
FMT_MAYBE_UNUSED
int handle(char* message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
@ -113,11 +96,13 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
}
// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(internal::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
FMT_MAYBE_UNUSED
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
@ -168,15 +153,6 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
assert(out.size() <= inline_buffer_size);
}
// A wrapper around fwrite that throws on error.
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) {
FMT_THROW(system_error(errno, "cannot write to file"));
}
}
FMT_FUNC void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT {
memory_buffer full_message;
@ -185,6 +161,13 @@ FMT_FUNC void report_error(format_func func, int error_code,
(void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
// A wrapper around fwrite that throws on error.
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
}
} // namespace internal
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
@ -356,6 +339,10 @@ class fp {
private:
using significand_type = uint64_t;
public:
significand_type f;
int e;
// All sizes are in bits.
// Subtract 1 to account for an implicit most significant bit in the
// normalized form.
@ -363,11 +350,6 @@ class fp {
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ULL << double_significand_size;
public:
significand_type f;
int e;
static FMT_CONSTEXPR_DECL const int significand_size =
bits<significand_type>::value;
@ -378,22 +360,6 @@ class fp {
// errors on platforms where double is not IEEE754.
template <typename Double> explicit fp(Double d) { assign(d); }
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> friend fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
// Assigns d to this and return true iff predecessor is closer than successor.
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
bool assign(Double d) {
@ -406,7 +372,8 @@ class fp {
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d);
f = u & significand_mask;
auto biased_e = (u & exponent_mask) >> double_significand_size;
int biased_e =
static_cast<int>((u & exponent_mask) >> double_significand_size);
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
// the smallest normalized number (biased_e > 1).
bool is_predecessor_closer = f == 0 && biased_e > 1;
@ -414,7 +381,7 @@ class fp {
f += implicit_bit;
else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
e = biased_e - exponent_bias - double_significand_size;
return is_predecessor_closer;
}
@ -453,6 +420,22 @@ class fp {
}
};
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
@ -477,14 +460,12 @@ inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
int index = static_cast<int>(
static_cast<int64_t>(
(min_exponent + fp::significand_size - 1) * one_over_log2_10 +
((uint64_t(1) << 32) - 1) // ceil
) >>
32 // arithmetic shift
((min_exponent + fp::significand_size - 1) * one_over_log2_10 +
((int64_t(1) << 32) - 1)) // ceil
>> 32 // arithmetic shift
);
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
@ -526,20 +507,23 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
void subtract_bigits(int index, bigit other, bigit& borrow) {
auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
bigits_[index] = static_cast<bigit>(result);
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
(*this)[index] = static_cast<bigit>(result);
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
}
void remove_leading_zeros() {
int num_bigits = static_cast<int>(bigits_.size()) - 1;
while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
bigits_.resize(num_bigits + 1);
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
bigits_.resize(to_unsigned(num_bigits + 1));
}
// Computes *this -= other assuming aligned bigints and *this >= other.
@ -548,8 +532,7 @@ class bigint {
FMT_ASSERT(compare(*this, other) >= 0, "");
bigit borrow = 0;
int i = other.exp_ - exp_;
for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
++i, ++j) {
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) {
subtract_bigits(i, other.bigits_[j], borrow);
}
while (borrow > 0) subtract_bigits(i, 0, borrow);
@ -600,7 +583,7 @@ class bigint {
}
void assign(uint64_t n) {
int num_bigits = 0;
size_t num_bigits = 0;
do {
bigits_[num_bigits++] = n & ~bigit(0);
n >>= bigit_bits;
@ -641,7 +624,7 @@ class bigint {
int end = i - j;
if (end < 0) end = 0;
for (; i >= end; --i, --j) {
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
}
if (i != j) return i > j ? 1 : -1;
@ -656,7 +639,7 @@ class bigint {
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
if (max_lhs_bigits > num_rhs_bigits) return 1;
auto get_bigit = [](const bigint& n, int i) -> bigit {
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
};
double_bigit borrow = 0;
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
@ -696,7 +679,7 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
int num_bigits = static_cast<int>(bigits_.size());
int num_result_bigits = 2 * num_bigits;
bigits_.resize(num_result_bigits);
bigits_.resize(to_unsigned(num_result_bigits));
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
auto sum = accumulator_t();
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
@ -706,7 +689,7 @@ class bigint {
// Most terms are multiplied twice which can be optimized in the future.
sum += static_cast<double_bigit>(n[i]) * n[j];
}
bigits_[bigit_index] = static_cast<bigit>(sum);
(*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value; // Compute the carry.
}
// Do the same for the top half.
@ -714,7 +697,7 @@ class bigint {
++bigit_index) {
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
sum += static_cast<double_bigit>(n[i++]) * n[j--];
bigits_[bigit_index] = static_cast<bigit>(sum);
(*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value;
}
--num_result_bigits;
@ -728,11 +711,11 @@ class bigint {
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
int num_bigits = static_cast<int>(bigits_.size());
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
int exp_difference = exp_ - divisor.exp_;
if (exp_difference > 0) {
// Align bigints by adding trailing zeros to simplify subtraction.
bigits_.resize(num_bigits + exp_difference);
bigits_.resize(to_unsigned(num_bigits + exp_difference));
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
@ -747,7 +730,7 @@ class bigint {
}
};
enum round_direction { unknown, up, down };
enum class round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
// some number v and the error, returns whether v should be rounded up, down, or
@ -760,13 +743,13 @@ inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
// Round down if (remainder + error) * 2 <= divisor.
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
return down;
return round_direction::down;
// Round up if (remainder - error) * 2 >= divisor.
if (remainder >= error &&
remainder - error >= divisor - (remainder - error)) {
return up;
return round_direction::up;
}
return unknown;
return round_direction::unknown;
}
namespace digits {
@ -777,6 +760,20 @@ enum result {
};
}
// A version of count_digits optimized for grisu_gen_digits.
inline int grisu_count_digits(uint32_t n) {
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
if (n < 1000000000) return 9;
return 10;
}
// Generates output using the Grisu digit-gen algorithm.
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
@ -792,7 +789,7 @@ FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
FMT_ASSERT(integral == value.f >> -one.e, "");
// The fractional part of scaled value (p2 in Grisu) c = value % one.
uint64_t fractional = value.f & (one.f - 1);
exp = count_digits(integral); // kappa in Grisu.
exp = grisu_count_digits(integral); // kappa in Grisu.
// Divide by 10 to prevent overflow.
auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
value.f / 10, error * 10, exp);
@ -882,8 +879,8 @@ struct fixed_handler {
if (precision > 0) return digits::more;
if (precision < 0) return digits::done;
auto dir = get_round_direction(divisor, remainder, error);
if (dir == unknown) return digits::error;
buf[size++] = dir == up ? '1' : '0';
if (dir == round_direction::unknown) return digits::error;
buf[size++] = dir == round_direction::up ? '1' : '0';
return digits::done;
}
@ -901,7 +898,8 @@ struct fixed_handler {
FMT_ASSERT(error == 1 && divisor > 2, "");
}
auto dir = get_round_direction(divisor, remainder, error);
if (dir != up) return dir == down ? digits::done : digits::error;
if (dir != round_direction::up)
return dir == round_direction::down ? digits::done : digits::error;
++buf[size - 1];
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
buf[i] = '0';
@ -1028,7 +1026,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
if (result > 0 || (result == 0 && (digit % 2) != 0))
++data[num_digits - 1];
}
buf.resize(num_digits);
buf.resize(to_unsigned(num_digits));
exp10 -= num_digits - 1;
return;
}
@ -1043,7 +1041,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template <typename T>
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
static_assert(!std::is_same<T, float>(), "");
static_assert(!std::is_same<T, float>::value, "");
FMT_ASSERT(value >= 0, "value is negative");
const bool fixed = specs.format == float_format::fixed;
@ -1062,25 +1060,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
int exp = 0;
const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu.
if (precision != -1) {
if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value));
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return snprintf_float(value, precision, specs, buf);
int num_digits = handler.size;
if (!fixed) {
// Remove trailing zeros.
while (num_digits > 0 && buf[num_digits - 1] == '0') {
--num_digits;
++exp;
}
}
buf.resize(to_unsigned(num_digits));
} else {
if (precision < 0) {
fp fp_value;
auto boundaries = specs.binary32
? fp_value.assign_float_with_boundaries(value)
@ -1109,6 +1089,24 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
return exp;
}
buf.resize(to_unsigned(handler.size));
} else {
if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value));
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return snprintf_float(value, precision, specs, buf);
int num_digits = handler.size;
if (!fixed) {
// Remove trailing zeros.
while (num_digits > 0 && buf[num_digits - 1] == '0') {
--num_digits;
++exp;
}
}
buf.resize(to_unsigned(num_digits));
}
return exp - cached_exp10;
}
@ -1118,7 +1116,7 @@ int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
static_assert(!std::is_same<T, float>(), "");
static_assert(!std::is_same<T, float>::value, "");
// Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format.
@ -1131,7 +1129,7 @@ int snprintf_float(T value, int precision, float_specs specs,
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.trailing_zeros) *format_ptr++ = '#';
if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
if (precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
@ -1153,7 +1151,8 @@ int snprintf_float(T value, int precision, float_specs specs,
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about a nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
// Cannot use auto becase of a bug in MinGW (#1532).
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value)
: snprintf_ptr(begin, capacity, format, value);
@ -1161,7 +1160,7 @@ int snprintf_float(T value, int precision, float_specs specs,
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
continue;
}
unsigned size = to_unsigned(result);
auto size = to_unsigned(result);
// Size equal to capacity means that the last character was truncated.
if (size >= capacity) {
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
@ -1179,7 +1178,7 @@ int snprintf_float(T value, int precision, float_specs specs,
--p;
} while (is_digit(*p));
int fraction_size = static_cast<int>(end - p - 1);
std::memmove(p, p + 1, fraction_size);
std::memmove(p, p + 1, to_unsigned(fraction_size));
buf.resize(size - 1);
return -fraction_size;
}
@ -1208,12 +1207,67 @@ int snprintf_float(T value, int precision, float_specs specs,
while (*fraction_end == '0') --fraction_end;
// Move the fractional part left to get rid of the decimal point.
fraction_size = static_cast<int>(fraction_end - begin - 1);
std::memmove(begin + 1, begin + 2, fraction_size);
std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
}
buf.resize(fraction_size + offset + 1);
buf.resize(to_unsigned(fraction_size) + offset + 1);
return exp - fraction_size;
}
}
// A public domain branchless UTF-8 decoder by Christopher Wellons:
// https://github.com/skeeto/branchless-utf8
/* Decode the next character, c, from buf, reporting errors in e.
*
* Since this is a branchless decoder, four bytes will be read from the
* buffer regardless of the actual length of the next character. This
* means the buffer _must_ have at least three bytes of zero padding
* following the end of the data stream.
*
* Errors are reported in e, which will be non-zero if the parsed
* character was somehow invalid: invalid byte sequence, non-canonical
* encoding, or a surrogate half.
*
* The function returns a pointer to the next character. When an error
* occurs, this pointer will be a guess that depends on the particular
* error, but it will always advance at least one byte.
*/
FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
static const int shiftc[] = {0, 18, 12, 6, 0};
static const int shifte[] = {0, 6, 4, 2, 0};
auto s = reinterpret_cast<const unsigned char*>(buf);
int len = lengths[s[0] >> 3];
// Compute the pointer to the next character early so that the next
// iteration can start working on the next character. Neither Clang
// nor GCC figure out this reordering on their own.
const char* next = buf + len + !len;
// Assume a four-byte character and load four bytes. Unused bits are
// shifted out.
*c = uint32_t(s[0] & masks[len]) << 18;
*c |= uint32_t(s[1] & 0x3f) << 12;
*c |= uint32_t(s[2] & 0x3f) << 6;
*c |= uint32_t(s[3] & 0x3f) << 0;
*c >>= shiftc[len];
// Accumulate the various error conditions.
*e = (*c < mins[len]) << 6; // non-canonical encoding
*e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
*e |= (*c > 0x10FFFF) << 8; // out of range?
*e |= (s[1] & 0xc0) >> 2;
*e |= (s[2] & 0xc0) >> 4;
*e |= (s[3]) >> 6;
*e ^= 0x2a; // top two bits of each tail byte correct?
*e >>= shifte[len];
return next;
}
} // namespace internal
template <> struct formatter<internal::bigint> {
@ -1226,7 +1280,7 @@ template <> struct formatter<internal::bigint> {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1];
auto value = n.bigits_[i - 1u];
if (first) {
out = format_to(out, "{:x}", value);
first = false;
@ -1240,101 +1294,37 @@ template <> struct formatter<internal::bigint> {
}
};
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// MultiByteToWideChar does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return;
}
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(),
s_size, nullptr, 0);
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size,
&buffer_[0], length);
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::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"));
}
}
FMT_FUNC int internal::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;
}
FMT_FUNC void windows_error::init(int err_code, string_view format_str,
format_args args) {
error_code_ = err_code;
memory_buffer buffer;
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer));
}
FMT_FUNC void internal::format_windows_error(internal::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) {
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
auto transcode = [this](const char* p) {
auto cp = uint32_t();
auto error = 0;
p = utf8_decode(p, &cp, &error);
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
if (cp <= 0xFFFF) {
buffer_.push_back(static_cast<wchar_t>(cp));
} else {
cp -= 0x10000;
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
}
return p;
};
auto p = s.data();
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
if (s.size() >= block_size) {
for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
if (auto num_chars_left = s.data() + s.size() - p) {
char buf[2 * block_size - 1] = {};
memcpy(buf, p, to_unsigned(num_chars_left));
p = buf;
do {
p = transcode(p);
} while (p - buf < num_chars_left);
}
buffer_.push_back(0);
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
@ -1369,20 +1359,37 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
#ifdef _WIN32
auto fd = _fileno(f);
if (_isatty(fd)) {
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
auto written = DWORD();
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
nullptr)) {
FMT_THROW(format_error("failed to write to console"));
}
return;
}
#endif
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
#endif
FMT_FUNC void vprint(string_view format_str, format_args args) {
vprint(stdout, format_str, args);
}

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
@ -18,16 +19,16 @@ 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) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
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);
return fmt::to_string(buffer);
@ -37,7 +38,7 @@ std::basic_string<Char> vformat(const std::locale& loc,
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) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
@ -46,15 +47,15 @@ inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_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));

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

@ -0,0 +1,392 @@
// 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 internal {
// 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 internal
/** 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.
};
// 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 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();
#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

@ -93,7 +93,9 @@ void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
@ -115,7 +117,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
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);
@ -134,7 +136,7 @@ template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -1,321 +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 <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
#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_; }
};
// 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.
};
// 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 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();
#endif // FMT_USE_FCNTL
#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_;
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~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

@ -28,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; }
@ -303,6 +303,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();
@ -320,6 +322,7 @@ 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>;
template <typename T> using formatter_type = printf_formatter<T>;
@ -333,12 +336,12 @@ template <typename OutputIt, typename Char> class basic_printf_context {
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:
/**
@ -355,7 +358,9 @@ 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); }
internal::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
@ -397,8 +402,8 @@ 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);
@ -406,15 +411,16 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
}
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = 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);
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
@ -436,8 +442,8 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
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(
internal::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -464,7 +470,7 @@ 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 index out of range");
// Parse precision.
@ -473,11 +479,11 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
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));
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(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
@ -596,7 +602,8 @@ 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);
return to_string(buffer);
@ -615,12 +622,13 @@ template <typename S, typename... Args,
typename Char = enable_if_t<internal::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();
@ -643,12 +651,13 @@ template <typename S, typename... Args,
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);
}
@ -666,12 +675,13 @@ template <typename S, typename... Args,
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);
@ -682,9 +692,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(
internal::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;
@ -704,7 +714,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.
@ -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:
@ -360,6 +359,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<internal::iterator_t<const std::initializer_list<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<internal::iterator_t<const std::initializer_list<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

@ -8,7 +8,120 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>;
namespace internal {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
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);
}
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
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 struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,

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,39 +10,43 @@
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/posix.h"
#include "fmt/os.h"
#include <climits>
#if FMT_USE_FCNTL
#include <sys/stat.h>
#include <sys/types.h>
# 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>
# 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 // FMT_USE_FCNTL
#endif
#ifdef fileno
# undef fileno
@ -68,6 +72,81 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
internal::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 internal::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;
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer));
}
void internal::format_windows_error(internal::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) {
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(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(internal::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");
@ -99,12 +178,12 @@ int buffered_file::fileno() const {
#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()));
}
@ -126,7 +205,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.
@ -140,7 +219,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)
@ -148,7 +227,7 @@ 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) {
@ -195,15 +274,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.
@ -223,15 +302,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

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

@ -6,6 +6,6 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
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,7 +137,7 @@ 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&')
@ -146,6 +146,7 @@ def update_site(env):
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('aa long', 'a long')
# 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.
@ -125,7 +123,7 @@ if (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
@ -171,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"
@ -229,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,17 +8,12 @@
#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;

View File

@ -145,6 +145,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

@ -50,6 +50,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

@ -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
@ -402,8 +401,114 @@ 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, 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(std::cref(a1));
std::string result = fmt::vformat("{a1_}", // and {} and {}",
store);
EXPECT_EQ("42", 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));
@ -457,13 +562,18 @@ TEST(CoreTest, HasFormatter) {
using context = fmt::format_context;
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, "");
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()) {
@ -473,10 +583,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 {
@ -572,13 +693,13 @@ TEST(CoreTest, ToStringViewForeignStrings) {
fmt::internal::type type =
fmt::internal::mapped_type_constant<my_string<char>,
fmt::format_context>::value;
EXPECT_EQ(type, fmt::internal::string_type);
EXPECT_EQ(type, fmt::internal::type::string_type);
type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::string_type);
EXPECT_EQ(type, fmt::internal::type::string_type);
type =
fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::string_type);
EXPECT_EQ(type, fmt::internal::type::string_type);
// Does not compile: only wide format contexts are compatible with QString!
// type = fmt::internal::mapped_type_constant<QString,
// fmt::format_context>::value;
@ -616,7 +737,7 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
#ifdef FMT_USE_STRING_VIEW
# ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
@ -625,7 +746,7 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
#endif
# endif
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
@ -639,9 +760,9 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
struct disabled_rvalue_conversion {
operator const char*() const& { return "foo"; }
operator const char*()& { return "foo"; }
operator const char*() & { return "foo"; }
operator const char*() const&& = delete;
operator const char*()&& = delete;
operator const char*() && = delete;
};
TEST(FormatterTest, DisabledRValueConversion) {

View File

@ -83,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 {
@ -534,31 +534,31 @@ inline fmt::internal::type get_type(basic_format_arg<Context> arg) {
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::internal::type::none_type;
if (std::is_same_v<T, bool>)
return fmt::internal::bool_type;
return fmt::internal::type::bool_type;
if (std::is_same_v<T, char_type>)
return fmt::internal::char_type;
return fmt::internal::type::char_type;
if (std::is_same_v<T, int>)
return fmt::internal::int_type;
return fmt::internal::type::int_type;
if (std::is_same_v<T, unsigned int>)
return fmt::internal::uint_type;
return fmt::internal::type::uint_type;
if (std::is_same_v<T, long long int>)
return fmt::internal::long_long_type;
return fmt::internal::type::long_long_type;
if (std::is_same_v<T, unsigned long long int>)
return fmt::internal::ulong_long_type;
return fmt::internal::type::ulong_long_type;
if (std::is_same_v<T, double>)
return fmt::internal::double_type;
return fmt::internal::type::double_type;
if (std::is_same_v<T, long double>)
return fmt::internal::long_double_type;
return fmt::internal::type::long_double_type;
if (std::is_same_v<T, const char_type*>)
return fmt::internal::cstring_type;
return fmt::internal::type::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::internal::string_type;
return fmt::internal::type::string_type;
if (std::is_same_v<T, const void*>)
return fmt::internal::pointer_type;
return fmt::internal::type::pointer_type;
assert(get_value(arg).index() == 12);
return fmt::internal::custom_type;
return fmt::internal::type::custom_type;
}, arg);
}
@ -649,37 +649,37 @@ struct formatter {
auto type_spec = specs_.type;
auto eh = ctx.error_handler();
switch (type) {
case internal::none_type:
case internal::named_arg_type:
case internal::type::none_type:
case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
case internal::int_type:
case internal::uint_type:
case internal::long_long_type:
case internal::ulong_long_type:
case internal::bool_type:
case internal::type::int_type:
case internal::type::uint_type:
case internal::type::long_long_type:
case internal::type::ulong_long_type:
case internal::type::bool_type:
handle_int_type_spec(type_spec,
internal::int_type_checker<decltype(eh)>(eh));
break;
case internal::char_type:
case internal::type::char_type:
handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(type_spec, eh));
break;
case internal::double_type:
case internal::long_double_type:
case internal::type::double_type:
case internal::type::long_double_type:
internal::parse_float_type_spec(specs_, eh);
break;
case internal::cstring_type:
case internal::type::cstring_type:
internal::handle_cstring_type_spec(
type_spec, internal::cstring_type_checker<decltype(eh)>(eh));
break;
case internal::string_type:
case internal::type::string_type:
internal::check_string_type_spec(type_spec, eh);
break;
case internal::pointer_type:
case internal::type::pointer_type:
internal::check_pointer_type_spec(type_spec, eh);
break;
case internal::custom_type:
case internal::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
@ -730,17 +730,17 @@ 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) {
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_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, {});
@ -749,7 +749,7 @@ template<class 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,12 +10,11 @@
#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"
@ -273,25 +272,26 @@ 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::internal::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) {
@ -328,14 +328,14 @@ 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::internal::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::internal::uint128_t) {
throw std::runtime_error("invalid type __uint128_t");
}
#endif
};
@ -426,7 +426,13 @@ TEST(FormatTest, FormatErrorCode) {
}
TEST(FormatTest, CountCodePoints) {
EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик")));
#ifndef __cpp_char8_t
using fmt::char8_t;
#endif
EXPECT_EQ(
4, fmt::internal::count_code_points(
fmt::basic_string_view<fmt::internal::char8_type>(
reinterpret_cast<const fmt::internal::char8_type*>("ёжик"))));
}
// Tests fmt::internal::count_digits for integer type Int.
@ -447,8 +453,8 @@ TEST(UtilTest, CountDigits) {
TEST(UtilTest, WriteUIntPtr) {
fmt::memory_buffer buf;
fmt::internal::writer writer(buf);
writer.write_pointer(fmt::internal::fallback_uintptr(
reinterpret_cast<void*>(0xface)),
nullptr);
writer.write_pointer(
fmt::internal::fallback_uintptr(reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ("0xface", to_string(buf));
}

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <stdint.h>
#include <cctype>
#include <cfloat>
#include <climits>
@ -45,6 +46,7 @@ using fmt::format_error;
using fmt::memory_buffer;
using fmt::string_view;
using fmt::wmemory_buffer;
using fmt::wstring_view;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
@ -400,26 +402,17 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
}
#ifdef _WIN32
TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик";
fmt::internal::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
TEST(UtilTest, UTF16ToUTF8EmptyString) {
std::string s = "";
fmt::internal::utf16_to_utf8 u(L"");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
TEST(UtilTest, UTF8ToUTF16) {
std::string s = "лошадка";
fmt::internal::utf8_to_utf16 u(s.c_str());
fmt::internal::utf8_to_utf16 u("лошадка");
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
EXPECT_EQ(7, u.size());
// U+10437 { DESERET SMALL LETTER YEE }
EXPECT_EQ(L"\xD801\xDC37", fmt::internal::utf8_to_utf16("𐐷").str());
EXPECT_THROW_MSG(fmt::internal::utf8_to_utf16("\xc3\x28"), std::runtime_error,
"invalid utf8");
EXPECT_THROW_MSG(fmt::internal::utf8_to_utf16(fmt::string_view("л", 1)),
std::runtime_error, "invalid utf8");
EXPECT_EQ(L"123456", fmt::internal::utf8_to_utf16("123456").str());
}
TEST(UtilTest, UTF8ToUTF16EmptyString) {
@ -429,59 +422,6 @@ TEST(UtilTest, UTF8ToUTF16EmptyString) {
EXPECT_EQ(s.size(), u.size());
}
template <typename Converter, typename Char>
void check_utf_conversion_error(
const char* message,
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
fmt::system_error error(0, "");
try {
(Converter)(str);
} catch (const fmt::system_error& e) {
error = e;
}
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
EXPECT_EQ(fmt::to_string(out), error.what());
}
TEST(UtilTest, UTF16ToUTF8Error) {
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>(
"cannot convert string from UTF-16 to UTF-8");
}
TEST(UtilTest, UTF8ToUTF16Error) {
const char* message = "cannot convert string from UTF-8 to UTF-16";
check_utf_conversion_error<fmt::internal::utf8_to_utf16, char>(message);
check_utf_conversion_error<fmt::internal::utf8_to_utf16, char>(
message, fmt::string_view("foo", INT_MAX + 1u));
}
TEST(UtilTest, UTF16ToUTF8Convert) {
fmt::internal::utf16_to_utf8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
EXPECT_EQ(ERROR_INVALID_PARAMETER,
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
}
#endif // _WIN32
typedef void (*FormatErrorMessage)(fmt::internal::buffer<char>& out,
int error_code, string_view message);
template <typename Error>
void check_throw_error(int error_code, FormatErrorMessage format) {
fmt::system_error error(0, "");
try {
throw Error(error_code, "test {}", "error");
} catch (const fmt::system_error& e) {
error = e;
}
fmt::memory_buffer message;
format(message, error_code, "test error");
EXPECT_EQ(to_string(message), error.what());
EXPECT_EQ(error_code, error.error_code());
}
TEST(UtilTest, FormatSystemError) {
fmt::memory_buffer message;
fmt::format_system_error(message, EDOM, "test");
@ -510,7 +450,17 @@ TEST(UtilTest, SystemError) {
fmt::system_error e(EDOM, "test");
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), e.what());
EXPECT_EQ(EDOM, e.error_code());
check_throw_error<fmt::system_error>(EDOM, fmt::format_system_error);
fmt::system_error error(0, "");
try {
throw fmt::system_error(EDOM, "test {}", "error");
} catch (const fmt::system_error& e) {
error = e;
}
fmt::memory_buffer message;
fmt::format_system_error(message, EDOM, "test error");
EXPECT_EQ(to_string(message), error.what());
EXPECT_EQ(EDOM, error.error_code());
}
TEST(UtilTest, ReportSystemError) {
@ -521,69 +471,6 @@ TEST(UtilTest, ReportSystemError) {
to_string(out));
}
#ifdef _WIN32
TEST(UtilTest, FormatWindowsError) {
LPWSTR message = 0;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, ERROR_FILE_EXISTS,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::internal::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
actual_message.resize(0);
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_value<size_t>()));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message));
}
TEST(UtilTest, FormatLongWindowsError) {
LPWSTR message = 0;
// this error code is not available on all Windows platforms and
// Windows SDKs, so do not fail the test if the error string cannot
// be retrieved.
const int provisioning_not_allowed =
0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, static_cast<DWORD>(provisioning_not_allowed),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
return;
}
fmt::internal::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, provisioning_not_allowed,
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
}
TEST(UtilTest, WindowsError) {
check_throw_error<fmt::windows_error>(ERROR_FILE_EXISTS,
fmt::internal::format_windows_error);
}
TEST(UtilTest, ReportWindowsError) {
fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
out.push_back('\n');
EXPECT_WRITE(stderr,
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
fmt::to_string(out));
}
#endif // _WIN32
TEST(StringViewTest, Ctor) {
EXPECT_STREQ("abc", string_view("abc").data());
EXPECT_EQ(3u, string_view("abc").size());
@ -930,6 +817,9 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("foo=", format("{:}=", "foo"));
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
EXPECT_EQ("жж42", format("{0:ж>4}", 42));
EXPECT_THROW_MSG(format("{:\x80\x80\x80\x80\x80>}", 0), format_error,
"invalid fill");
}
TEST(FormatterTest, PlusSign) {
@ -1040,6 +930,10 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("-42.0", format("{0:#}", -42.0));
EXPECT_EQ("-42.0", format("{0:#}", -42.0l));
EXPECT_EQ("4.e+01", format("{:#.0e}", 42.0));
EXPECT_EQ("0.", format("{:#.0f}", 0.01));
auto s = format("{:#.0f}", 0.5); // MSVC's printf uses wrong rounding mode.
EXPECT_TRUE(s == "0." || s == "1.");
EXPECT_THROW_MSG(format("{0:#", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error,
@ -1095,7 +989,9 @@ TEST(FormatterTest, Width) {
EXPECT_EQ(" 0xcafe", format("{0:10}", reinterpret_cast<void*>(0xcafe)));
EXPECT_EQ("x ", format("{0:11}", 'x'));
EXPECT_EQ("str ", format("{0:12}", "str"));
EXPECT_EQ(fmt::format("{:*^5}", "🤡"), "**🤡**");
}
template <typename T> inline T const_check(T value) { return value; }
TEST(FormatterTest, RuntimeWidth) {
@ -1204,6 +1100,7 @@ TEST(FormatterTest, Precision) {
EXPECT_EQ("1.2", format("{0:.2}", 1.2345l));
EXPECT_EQ("1.2e+56", format("{:.2}", 1.234e56));
EXPECT_EQ("1e+00", format("{:.0e}", 1.0L));
EXPECT_EQ(" 0.0e+00", format("{:9.1e}", 0.0));
EXPECT_EQ(
"4.9406564584124654417656879286822137236505980261432476442558568250067550"
"727020875186529983636163599237979656469544571773092665671035593979639877"
@ -1228,6 +1125,8 @@ TEST(FormatterTest, Precision) {
"000000000000000000000000000000000000000000000000000P+127",
format("{:.838A}", -2.14001164E+38));
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", format("{:.02f}", 1.234));
EXPECT_EQ("0.001", format("{:.1g}", 0.001));
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
format_error,
@ -1235,6 +1134,8 @@ TEST(FormatterTest, Precision) {
EXPECT_THROW_MSG(format("{0:.2f}", reinterpret_cast<void*>(0xcafe)),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{:.{}e}", 42.0, fmt::internal::max_value<int>()),
format_error, "number is too big");
EXPECT_EQ("st", format("{0:.2}", "str"));
}
@ -1355,7 +1256,7 @@ TEST(FormatterTest, FormatShort) {
TEST(FormatterTest, FormatInt) {
EXPECT_THROW_MSG(format("{0:v", 42), format_error,
"missing '}' in format string");
check_unknown_types(42, "bBdoxXn", "integer");
check_unknown_types(42, "bBdoxXnL", "integer");
}
TEST(FormatterTest, FormatBin) {
@ -1496,6 +1397,7 @@ TEST(FormatterTest, FormatOct) {
TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("1234", format("{:n}", 1234));
EXPECT_EQ("1234", format("{:L}", 1234));
}
struct ConvertibleToLongLong {
@ -1590,7 +1492,7 @@ TEST(FormatterTest, FormatLongDouble) {
}
TEST(FormatterTest, FormatChar) {
const char types[] = "cbBdoxXn";
const char types[] = "cbBdoxXnL";
check_unknown_types('a', types, "char");
EXPECT_EQ("a", format("{0}", 'a'));
EXPECT_EQ("z", format("{0:c}", 'z'));
@ -1684,6 +1586,26 @@ TEST(FormatterTest, FormatStdStringView) {
EXPECT_EQ("test", format("{}", std::string_view("test")));
EXPECT_EQ("foo", format("{}", string_viewable()));
}
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
namespace fmt {
template <>
struct formatter<explicitly_convertible_to_std_string_view>
: formatter<std::string_view> {
auto format(const explicitly_convertible_to_std_string_view& v,
format_context& ctx) -> decltype(ctx.out()) {
return format_to(ctx.out(), "'{}'", std::string_view(v));
}
};
} // namespace fmt
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("'foo'",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
#endif
FMT_BEGIN_NAMESPACE
@ -1836,6 +1758,8 @@ TEST(FormatTest, Print) {
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
"Don't panic!");
#endif
// Check that the wide print overload compiles.
if (fmt::internal::const_check(false)) fmt::print(L"test");
}
TEST(FormatTest, Variadic) {
@ -1851,12 +1775,18 @@ TEST(FormatTest, Dynamic) {
args.emplace_back(fmt::internal::make_arg<ctx>(1.5f));
std::string result = fmt::vformat(
"{} and {} and {}", fmt::basic_format_args<ctx>(
args.data(), static_cast<unsigned>(args.size())));
"{} and {} and {}",
fmt::basic_format_args<ctx>(args.data(), static_cast<int>(args.size())));
EXPECT_EQ("42 and abc1 and 1.5", result);
}
TEST(FormatTest, Bytes) {
auto s = fmt::format("{:10}", fmt::bytes("ёжик"));
EXPECT_EQ("ёжик ", s);
EXPECT_EQ(10, s.size());
}
TEST(FormatTest, JoinArg) {
using fmt::join;
int v1[3] = {1, 2, 3};
@ -1928,16 +1858,31 @@ TEST(FormatTest, UnpackedArgs) {
struct string_like {};
fmt::string_view to_string_view(string_like) { return "foo"; }
constexpr char with_null[3] = {'{', '}', '\0'};
constexpr char no_null[2] = {'{', '}'};
TEST(FormatTest, CompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
(void)with_null;
(void)no_null;
#if __cplusplus >= 201703L
EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42));
EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
#endif
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
#endif
}
TEST(FormatTest, CustomFormatCompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer()));
Answer answer;
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer));
char buf[10] = {};
fmt::format_to(buf, FMT_STRING("{}"), answer);
const Answer const_answer = Answer();
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer));
}
@ -2162,16 +2107,43 @@ TEST(FormatTest, WideFormatToN) {
EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4));
}
struct test_output_iterator {
char* data;
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
test_output_iterator& operator++() {
++data;
return *this;
}
test_output_iterator operator++(int) {
auto tmp = *this;
++data;
return tmp;
}
char& operator*() { return *data; }
};
TEST(FormatTest, FormatToNOutputIterator) {
char buf[10] = {};
fmt::format_to_n(test_output_iterator{buf}, 10, "{}", 42);
EXPECT_STREQ(buf, "42");
}
#if FMT_USE_CONSTEXPR
struct test_arg_id_handler {
enum result { NONE, EMPTY, INDEX, NAME, ERROR };
result res = NONE;
unsigned index = 0;
int index = 0;
string_view name;
FMT_CONSTEXPR void operator()() { res = EMPTY; }
FMT_CONSTEXPR void operator()(unsigned i) {
FMT_CONSTEXPR void operator()(int i) {
res = INDEX;
index = i;
}
@ -2207,9 +2179,9 @@ struct test_format_specs_handler {
fmt::align_t align = fmt::align::none;
char fill = 0;
unsigned width = 0;
int width = 0;
fmt::internal::arg_ref<char> width_ref;
unsigned precision = 0;
int precision = 0;
fmt::internal::arg_ref<char> precision_ref;
char type = 0;
@ -2228,23 +2200,21 @@ struct test_format_specs_handler {
type(other.type) {}
FMT_CONSTEXPR void on_align(fmt::align_t a) { align = a; }
FMT_CONSTEXPR void on_fill(char f) { fill = f; }
FMT_CONSTEXPR void on_fill(fmt::string_view f) { fill = f[0]; }
FMT_CONSTEXPR void on_plus() { res = PLUS; }
FMT_CONSTEXPR void on_minus() { res = MINUS; }
FMT_CONSTEXPR void on_space() { res = SPACE; }
FMT_CONSTEXPR void on_hash() { res = HASH; }
FMT_CONSTEXPR void on_zero() { res = ZERO; }
FMT_CONSTEXPR void on_width(unsigned w) { width = w; }
FMT_CONSTEXPR void on_width(int w) { width = w; }
FMT_CONSTEXPR void on_dynamic_width(fmt::internal::auto_id) {}
FMT_CONSTEXPR void on_dynamic_width(unsigned index) { width_ref = index; }
FMT_CONSTEXPR void on_dynamic_width(int index) { width_ref = index; }
FMT_CONSTEXPR void on_dynamic_width(string_view) {}
FMT_CONSTEXPR void on_precision(unsigned p) { precision = p; }
FMT_CONSTEXPR void on_precision(int p) { precision = p; }
FMT_CONSTEXPR void on_dynamic_precision(fmt::internal::auto_id) {}
FMT_CONSTEXPR void on_dynamic_precision(unsigned index) {
precision_ref = index;
}
FMT_CONSTEXPR void on_dynamic_precision(int index) { precision_ref = index; }
FMT_CONSTEXPR void on_dynamic_precision(string_view) {}
FMT_CONSTEXPR void end_precision() {}
@ -2279,7 +2249,7 @@ TEST(FormatTest, ConstexprParseFormatSpecs) {
struct test_parse_context {
typedef char char_type;
FMT_CONSTEXPR unsigned next_arg_id() { return 11; }
FMT_CONSTEXPR int next_arg_id() { return 11; }
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
FMT_CONSTEXPR const char* begin() { return nullptr; }
@ -2364,7 +2334,7 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) {
template <size_t N>
FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::internal::specs_checker<test_format_specs_handler> checker(
test_format_specs_handler(), fmt::internal::double_type);
test_format_specs_handler(), fmt::internal::type::double_type);
parse_format_specs(s, s + N, checker);
return checker;
}
@ -2553,48 +2523,8 @@ TEST(FormatTest, FmtStringInTemplate) {
#endif // FMT_USE_CONSTEXPR
// C++20 feature test, since r346892 Clang considers char8_t a fundamental
// type in this mode. If this is the case __cpp_char8_t will be defined.
#ifndef __cpp_char8_t
// Locally provide type char8_t defined in format.h
using fmt::char8_t;
#endif
TEST(FormatTest, ConstructU8StringViewFromCString) {
fmt::u8string_view s("ab");
EXPECT_EQ(s.size(), 2u);
const char8_t* data = s.data();
EXPECT_EQ(data[0], 'a');
EXPECT_EQ(data[1], 'b');
}
TEST(FormatTest, ConstructU8StringViewFromDataAndSize) {
fmt::u8string_view s("foobar", 3);
EXPECT_EQ(s.size(), 3u);
const char8_t* data = s.data();
EXPECT_EQ(data[0], 'f');
EXPECT_EQ(data[1], 'o');
EXPECT_EQ(data[2], 'o');
}
#if FMT_USE_USER_DEFINED_LITERALS
TEST(FormatTest, U8StringViewLiteral) {
using namespace fmt::literals;
fmt::u8string_view s = "ab"_u;
EXPECT_EQ(s.size(), 2u);
const char8_t* data = s.data();
EXPECT_EQ(data[0], 'a');
EXPECT_EQ(data[1], 'b');
EXPECT_EQ(format("{:*^5}"_u, "🤡"_u), "**🤡**"_u);
}
#endif
TEST(FormatTest, FormatU8String) {
EXPECT_EQ(format(fmt::u8string_view("{}"), 42), fmt::u8string_view("42"));
}
TEST(FormatTest, EmphasisNonHeaderOnly) {
// ensure this compiles even if FMT_HEADER_ONLY is not defined.
// Ensure this compiles even if FMT_HEADER_ONLY is not defined.
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m");
}
@ -2631,12 +2561,20 @@ TEST(FormatTest, FormatCustomChar) {
EXPECT_EQ(result[0], mychar('x'));
}
// Convert a char8_t string to std::string. Otherwise GTest will insist on
// inserting `char8_t` NTBS into a `char` stream which is disabled by P1423.
template <typename S> std::string from_u8str(const S& str) {
return std::string(str.begin(), str.end());
}
TEST(FormatTest, FormatUTF8Precision) {
using str_type = std::basic_string<char8_t>;
str_type format(reinterpret_cast<const char8_t*>(u8"{:.4}"));
str_type str(reinterpret_cast<const char8_t*>(u8"caf\u00e9s")); // cafés
using str_type = std::basic_string<fmt::internal::char8_type>;
str_type format(
reinterpret_cast<const fmt::internal::char8_type*>(u8"{:.4}"));
str_type str(reinterpret_cast<const fmt::internal::char8_type*>(
u8"caf\u00e9s")); // cafés
auto result = fmt::format(format, str);
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
EXPECT_EQ(result.size(), 5);
EXPECT_EQ(result, str.substr(0, 5));
EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
}

View File

@ -10,7 +10,7 @@
#include <string>
#include "gmock.h"
#include "fmt/posix.h"
#include "fmt/os.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \

View File

@ -1,4 +1,4 @@
// 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.
@ -9,7 +9,7 @@
#include <cstring>
#include <memory>
#include "fmt/posix.h"
#include "fmt/os.h"
#include "gtest-extra.h"
#include "util.h"
@ -20,6 +20,122 @@
using fmt::buffered_file;
using fmt::error_code;
#ifdef _WIN32
# include <windows.h>
TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик";
fmt::internal::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
TEST(UtilTest, UTF16ToUTF8EmptyString) {
std::string s = "";
fmt::internal::utf16_to_utf8 u(L"");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
template <typename Converter, typename Char>
void check_utf_conversion_error(
const char* message,
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
fmt::system_error error(0, "");
try {
(Converter)(str);
} catch (const fmt::system_error& e) {
error = e;
}
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
EXPECT_EQ(fmt::to_string(out), error.what());
}
TEST(UtilTest, UTF16ToUTF8Error) {
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>(
"cannot convert string from UTF-16 to UTF-8");
}
TEST(UtilTest, UTF16ToUTF8Convert) {
fmt::internal::utf16_to_utf8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
EXPECT_EQ(ERROR_INVALID_PARAMETER,
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
}
TEST(UtilTest, FormatWindowsError) {
LPWSTR message = 0;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, ERROR_FILE_EXISTS,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::internal::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
actual_message.resize(0);
auto max_size = fmt::internal::max_value<size_t>();
fmt::internal::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::internal::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, provisioning_not_allowed,
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
}
TEST(UtilTest, WindowsError) {
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::internal::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::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
out.push_back('\n');
EXPECT_WRITE(stderr,
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
fmt::to_string(out));
}
#endif // _WIN32
#if FMT_USE_FCNTL
using fmt::file;
@ -151,7 +267,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(
@ -163,7 +279,7 @@ TEST(BufferedFileTest, Fileno) {
}
},
"");
#endif
# endif
f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1);
file copy = file::dup(f.fileno());
@ -315,13 +431,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();
@ -371,12 +487,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

@ -264,15 +264,29 @@ struct explicitly_convertible_to_string_like {
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLikeIgnoreInserter) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
TEST(FormatterTest, 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::internal::std_string_view<char>() const {
return {"foo", 3u};
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW

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
@ -51,7 +53,7 @@ std::size_t read_nbyte;
std::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) \
@ -201,28 +203,28 @@ static void write_file(fmt::cstring_view filename, fmt::string_view content) {
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) {
@ -230,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);
}
@ -252,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");
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());
@ -299,16 +302,16 @@ TEST(FileTest, WriteRetry) {
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);
@ -334,7 +337,7 @@ TEST(FileTest, ConvertWriteCount) {
write_count = 0;
EXPECT_EQ(UINT_MAX, write_nbyte);
}
#endif
# endif
TEST(FileTest, DupNoRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout));
@ -359,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;
}
@ -385,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) {
@ -401,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);
}
@ -433,33 +437,33 @@ TEST(BufferedFileTest, FilenoNoRetry) {
}
#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)
@ -470,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
@ -492,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;
@ -504,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,12 +5,13 @@
//
// 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"
@ -300,15 +301,15 @@ void TestLength(const char* length_spec, U value) {
using fmt::internal::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);
@ -473,9 +474,13 @@ 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_FCNTL
TEST(PrintfTest, Examples) {
@ -497,7 +502,9 @@ TEST(PrintfTest, PrintfError) {
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
TEST(PrintfTest, PrintfCustom) {
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
// The test is disabled for now because it requires decoupling
// fallback_formatter::format from format_context.
//EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
}
TEST(PrintfTest, OStream) {
@ -589,7 +596,8 @@ class custom_printf_arg_formatter : public formatter_t {
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
return formatter_t::operator()(value);
}
};
}
;
typedef fmt::basic_format_args<context_t> format_args_t;

View File

@ -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,6 +70,12 @@ 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

View File

@ -6,6 +6,8 @@
// For the license information refer to format.h.
#include <array>
#include <cassert>
#include <climits>
#include "fmt/format.h"
@ -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))) {
@ -197,7 +198,7 @@ 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;
}

View File

@ -29,8 +29,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*****");

View File

@ -9,7 +9,7 @@
#include <cstdio>
#include <string>
#include "fmt/posix.h"
#include "fmt/os.h"
enum { BUFFER_SIZE = 256 };