Compare commits

...
339 Commits
Author SHA1 Message Date
Victor Zverovich 9bdd1596ce Update version 2020-04-05 06:46:41 -07:00
Victor Zverovich d151562bdd Fix punctuation in changelog 2020-04-04 12:49:56 -07:00
Victor Zverovich 346500e70b Fix gcc version check 2020-04-04 11:23:40 -07:00
Victor Zverovich a434a8f778 Update changelog 2020-04-04 06:34:23 -07:00
Victor Zverovich 9eb47d951a Fix markup 2020-04-03 08:49:29 -07:00
Victor Zverovich 51c58a56ba Bump version 2020-04-03 08:32:45 -07:00
Victor Zverovich 3fc33f6273 Update changelog 2020-04-03 08:29:02 -07:00
Victor Zverovich 2e32db5b99 Update changelog 2020-04-03 07:42:02 -07:00
Victor Zverovich c1ce6e01f7 Update changelog 2020-04-02 08:18:42 -07:00
Victor Zverovich 1c3c80dc1f Update changelog 2020-04-02 07:27:58 -07:00
Victor Zverovich d1d653d895 Implement the L specifier 2020-04-02 06:58:38 -07:00
Victor Zverovich 73c8437485 Follow naming conventions 2020-04-01 09:30:28 -07:00
Victor Zverovich e588b02b17 Fix posix-mock-test 2020-04-01 08:42:14 -07:00
Victor Zverovich 1a62711d01 Reduce binary size 2020-03-31 08:07:11 -07:00
Victor Zverovich 5b02881582 Merge branch 'master' of github.com:fmtlib/fmt 2020-03-28 09:46:44 -07:00
Victor Zverovich a133187a8c Update changelog 2020-03-28 09:44:27 -07:00
Victor Zverovich 80ce222ca6 Fix wide print overload (#1609) 2020-03-28 09:44:18 -07:00
Scott Ramsby 770a94edef Use FMT_THROW macro where applicable 2020-03-27 12:17:22 -07:00
Victor Zverovich 2864e8432a Update readme and add compatibility option 2020-03-26 07:18:27 -07:00
Victor Zverovich 21a295c272 Undo comment change 2020-03-25 08:14:31 -07:00
Victor Zverovich 96c68afe69 Fix -Wsign-conversion warnings 2020-03-25 07:46:00 -07:00
Scott Ramsby 664dd88e31 Enable FMT_STRING() use with types other than string literals 2020-03-24 12:56:21 -07:00
Victor Zverovich 69779b4ed6 Fix handling of small precision in general format 2020-03-24 09:01:57 -07:00
Scott Ramsby 01a172c969 Add .vs to .gitignore 2020-03-23 15:01:02 -07:00
Victor Zverovich 08ca40ea91 Detect /utf-8 in MSVC 2020-03-22 08:19:38 -07:00
Victor Zverovich dd97f4920c Improve exception safety in dynamic_format_arg_store 2020-03-21 08:58:50 -07:00
Victor Zverovich 2951169481 Move FMT_USE_FLOAT and friends to fmt/format.h 2020-03-20 07:55:43 -07:00
Alberto Aguirre 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
Victor Zverovich 52d0e1bbe3 Don't use properties when setting FMT_LIB_NAME 2020-03-19 08:35:09 -07:00
Attila M. Szilagyi 5d32ccfc31 Add back missing OUTPUT_NAME in target properties. (#1598) 2020-03-19 07:01:51 -07:00
Victor Zverovich 3cf619de55 Simplify dynamic_format_arg_store 2020-03-17 07:13:46 -07:00
Spirrwell 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
Victor Zverovich 026f99178e Simplify dynamic store 2020-03-16 19:10:41 -07:00
Victor Zverovich 9f70fc3e7a Minor tweaks for dynamic_format_arg_store 2020-03-16 07:58:15 -07:00
Vladimir Solontsov 6012dc9ab4 Dynamic arguments storage. Implementation of enhancement from issue #1170. (#1584) 2020-03-16 07:00:29 -07:00
Nikolay Rapotkin 85050aa2e6 Ability to join elements of std::initializer_list was added 2020-03-15 16:10:00 -07:00
Victor Zverovich ff486a72a7 Allow leading zeros in precision (#1579) 2020-03-14 11:37:38 -07:00
Victor Zverovich 678341275b Deprecate fmt::char8_t 2020-03-14 10:32:34 -07:00
Victor Zverovich 6f01b6ebb6 Fix a typo in CMake config: STRINGS -> STRING 2020-03-14 09:50:25 -07:00
Victor Zverovich 61c5a51604 Fix handling of empty tuples (#1588) 2020-03-14 07:41:08 -07:00
Dair Grant 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
Victor Zverovich 3c24052cf1 Workaround 'cannot call member function without object' error on gcc 4.9 2020-03-11 17:39:32 -07:00
Victor Zverovich f72a905eb3 Fix handling of volatile enums 2020-03-11 08:40:57 -07:00
Victor Zverovich 941d5e147a Workaround broken fallthrough attribute in the PGI compiler (#1583) 2020-03-11 07:56:23 -07:00
Victor Zverovich ee2b828b9a Tweak a comment 2020-03-09 11:27:14 -07:00
Pramod Kumbhar 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
Laurent Stacul 1c0c59d4a0 Fix empty debug postfix 2020-03-09 08:34:02 -07:00
Victor Zverovich b1adaa9881 Remove gcc 4.4 workaround 2020-03-07 14:50:52 -08:00
Victor Zverovich 48e8d0ebef set_doc -> set_verbose 2020-03-07 14:31:39 -08:00
Victor Zverovich ce00979152 Cleanup CMake config 2020-03-07 08:18:01 -08:00
Victor Zverovich db4a6cfbf9 is_static_compiled_format -> is_compiled_format 2020-03-06 07:25:34 -08:00
Dair Grant 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
Victor Zverovich 8a06ca84c7 Fix ambiguous overloads of format & format_to 2020-03-05 12:22:43 -08:00
Victor Zverovich 153f753bde Deprecate undocumented _u suffix 2020-03-04 19:20:19 -08:00
Victor Zverovich eafd079868 Improve width computation 2020-03-04 17:17:34 -08:00
Victor Zverovich 0c6919ec72 Make FMT_DEBUG_POSTFIX a cache variable (#1566) 2020-03-04 08:37:11 -08:00
Victor Zverovich 197a5c3721 Apply clang-format 2020-03-04 08:37:00 -08:00
refnum 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
Victor Zverovich 1e8493196e Make compile-time checks in format_to handle references 2020-03-01 07:57:34 -08:00
refnum 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
refnum 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
refnum 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
Victor Zverovich bed134a4aa Tentative fix for default template param in friend error 2020-02-27 15:29:46 -08:00
Victor Zverovich b2d3a86ec0 Make FMT_ASSERT work in constexpr on clang 4.0.1 2020-02-26 06:26:46 -08:00
Victor Zverovich 13d82e32bd Don't use internal GTest API 2020-02-24 11:32:34 -08:00
fghzxm 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
IkarusDeveloper 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
dominicpoeschko 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
Victor Zverovich 3bafd0749b Fix to_string docs 2020-02-15 09:51:35 +01:00
Victor Zverovich f733882b55 Remove misleading FMT_USE_WINDOWS_H 2020-02-14 14:09:27 +01:00
Victor Zverovich dc22360c34 Workaround broken UDL templates in GCC < 6.4 2020-02-10 17:08:14 +01:00
Victor Zverovich 1f1b50707c Make formatter override implicit conversion to a C string 2020-02-07 19:24:36 -08:00
Victor Zverovich 24924128e3 Fix a link error in gcc8 (#1548) 2020-02-07 18:34:05 -08:00
Jordan Williams c54cd71800 only modify CMAKE_RUNTIME_OUTPUT_DIRECTORY if it is not already set 2020-02-07 07:10:03 -08:00
Victor Zverovich 43e9b29e50 Only use compiler features if available 2020-02-06 15:43:33 -08:00
Victor Zverovich b55ea58705 string_view::char_type -> value_type (#1539) 2020-02-01 12:27:44 -08:00
Victor Zverovich 4098970db2 Update README.rst 2020-01-31 11:33:51 -08:00
Milian Wolff 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
Victor Zverovich f499b393d1 Apply coding conventions 2020-01-26 19:48:48 -08:00
Attila Mark 6c30f41443 Configure fmt.pc library name correctly.
Simplify getting library name.

Add FMT_DEBUG_SUFFIX variable.
2020-01-26 19:48:14 -08:00
zeffy 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
Victor Zverovich 09a13244c8 Disallow passing non-string-literals to FMT_STRING 2020-01-22 21:05:46 -08:00
Victor Zverovich 419db8baa1 Fix length computation of constexpr C strings 2020-01-22 18:25:07 -08:00
dspc-douglas 9fc4161f5e fix interal compiler error when building with mingw 2020-01-22 18:14:46 -08:00
Greg Sjaardema 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
Victor Zverovich 0b2eb6501c Add locale example 2020-01-20 08:42:16 -08:00
Victor Zverovich fd1cabe464 Workaround a bogus MSVC warning 2020-01-20 06:56:22 -08:00
Victor Zverovich a844d7ab81 Add namespaces 2020-01-19 19:20:48 -08:00
Victor Zverovich 47d3968092 Add more examples 2020-01-19 19:15:54 -08:00
Victor Zverovich 7800173eb1 Update fill docs 2020-01-19 18:57:32 -08:00
Victor Zverovich b4218aa0f8 Test invalid fill 2020-01-19 16:52:36 -08:00
Victor Zverovich 8a3a8177d6 Bump version 2020-01-19 15:34:30 -08:00
Victor Zverovich e5f2f8ce7a Add variable-width fill support (#1109) 2020-01-19 14:49:51 -08:00
Victor Zverovich 75765bfad5 Avoid unnecessary unsigned overflows (#1515) 2020-01-18 08:19:27 -08:00
Victor Zverovich 9bd9738da0 Remove static and simplify names 2020-01-18 07:43:59 -08:00
Victor Zverovich bd5f903f28 Add a locale example 2020-01-18 07:11:45 -08:00
Victor Zverovich 06e437fd98 Move docs to the proper place 2020-01-18 06:59:21 -08:00
JackBoosY 1bd4f54fa6 update format 2020-01-17 11:07:55 -08:00
JackBoosY 11cc2903e4 re-fix url link 2020-01-17 11:07:55 -08:00
JackBoosY b124e3e8e7 fix url link 2020-01-17 11:07:55 -08:00
JackBoosY ffd5f3469f Correct display format 2020-01-17 11:07:55 -08:00
JackBoosY 0f0e5ddf5f Add vcpkg installation instructions 2020-01-17 11:07:55 -08:00
Victor Zverovich 1f110702a1 Remove redundant braces 2020-01-15 15:07:30 -08:00
Jason Turner 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
Greg Sjaardema 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
Victor Zverovich c8dd9cc99d Use type_identity to block unnecessary template argument deduction (thanks Tim Song) 2020-01-15 10:27:50 -08:00
Greg Sjaardema 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
Greg Sjaardema 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
Greg Sjaardema 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
Tobias Hammer 77165fdf85 Use FMT_NOEXCEPT instead of noexcept directly
Otherwise breaks on compilers without noexcept support
2020-01-14 09:31:31 -08:00
Victor Zverovich 65ac626c58 Improve join docs 2020-01-12 07:26:16 -08:00
torsten cd0b3f9695 check if _SECURE_SCL is defined not equal to 0 2020-01-09 15:01:48 -08:00
Victor Zverovich cef1e4354b Optimize grisu_gen_digits 2020-01-07 16:11:18 -10:00
Victor Zverovich 0201c8db21 Restructure float_format 2020-01-07 14:41:30 -10:00
Victor Zverovich 9e3f3e8cff Fix handling of output iterators in format_to_n (#1506) 2020-01-05 09:59:01 -10:00
Victor Zverovich aa07c57654 Move vprint_mojibake to the internal namespace 2020-01-04 08:31:18 -10:00
Victor Zverovich a73d89e9c7 Catch invalid uses of fmt::arg 2020-01-04 07:11:35 -10:00
Daniela Engert 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
Victor Zverovich b3fd0005dd Suppress a bogus -Wdouble-promotion warning 2020-01-03 06:57:14 -10:00
Victor Zverovich 7b478f9dec Simplify example 2020-01-02 07:31:45 -10:00
Victor Zverovich c85efef312 More showpoint fixes and tests (#1498) 2019-12-30 13:08:35 -10:00
Victor Zverovich 455a7c0787 Clarify lifetime of basic_format_args 2019-12-30 08:51:47 -10:00
Victor Zverovich 674c326d7c Update syntax.rst 2019-12-29 07:25:34 -10:00
Victor Zverovich 061a9897fe Update syntax.rst 2019-12-26 16:49:40 -10:00
Ivan Shynkarenka d2d1c9c560 warning C4267: 'argument': conversion from 'size_t' to 'DWORD', possible loss of data 2019-12-26 08:07:24 -08:00
Victor Zverovich b6e19e5953 Update apidoc 2019-12-24 12:08:37 -08:00
Victor Zverovich f219dcd59b Add fmt::bytes 2019-12-24 10:45:15 -08:00
Victor Zverovich dea7fde8b7 Deprecate u8string_view 2019-12-24 09:44:57 -08:00
Victor Zverovich 5390e29d42 Enable mojibake 2019-12-24 08:42:29 -08:00
Victor Zverovich 9f6434dcde Improve UTF-8 handling on Windows 2019-12-23 16:19:11 -08:00
Victor Zverovich dac9a7f99d Improve UTF-8 handling on Windows 2019-12-22 12:05:28 -08:00
Victor Zverovich 3ca9533f38 Flatten forward 2019-12-22 08:23:54 -08:00
Victor Zverovich 7eec036d9a Improve UTF-8 support 2019-12-21 19:53:52 -08:00
Victor Zverovich e6b37b4aff Handle block boundaries in utf8_to_utf16 2019-12-21 16:33:34 -08:00
Victor Zverovich 8cf4c52068 Apply clang-format 2019-12-21 13:10:45 -08:00
Victor Zverovich 74532c23a3 Make type a scoped enum 2019-12-21 12:22:17 -08:00
Victor Zverovich b308159be5 Make round_direction a scoped enum 2019-12-21 09:24:42 -08:00
Victor Zverovich 162995fedd Add os.h to docs 2019-12-18 14:17:49 -08:00
Victor Zverovich 8b41362a0a Add trailing decimal point if # is specified (#1476) 2019-12-18 12:41:56 -08:00
Victor Zverovich 1b1c70108a trailing_zeros -> showpoint 2019-12-18 12:12:09 -08:00
Victor Zverovich d7e72a09e0 Simplify FMT_STRING_IMPL 2019-12-18 11:50:47 -08:00
Victor Zverovich 2201890d7a Apply clang-format and update inclusion guards 2019-12-18 11:17:36 -08:00
Greg Sjaardema 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
Victor Zverovich 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
parkertomatoes 9acf89fef6 Mitigate MSVC issue with min/max macros (#1480) 2019-12-16 08:24:00 -08:00
Victor Zverovich 9ea42fb26e Rename posix-test to os-test 2019-12-15 16:43:40 -08:00
Victor Zverovich da2569827e posix.cc -> os.cc 2019-12-15 12:36:15 -08:00
Victor Zverovich 35959a31d7 Move OS-specific APIs to a separate header 2019-12-15 11:46:45 -08:00
Victor Zverovich ec2463c905 Implement utf8_to_utf16 using utf8_decode 2019-12-15 09:35:26 -08:00
Victor Zverovich 0012917f69 Add a UTF-8 decoder 2019-12-15 07:28:19 -08:00
Victor Zverovich 9e450911fa Give an error on precision overflow 2019-12-14 07:45:27 -08:00
Victor Zverovich 068d20bc31 Avoid shadowing warnings in FMT_STRING 2019-12-14 07:00:08 -08:00
Victor Zverovich a99fbe67b9 Apply a typo fix retroactively 2019-12-14 06:50:53 -08:00
Victor Zverovich adbed11ed4 Fix a typo 2019-12-13 15:49:40 -08:00
iPherian 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
Victor Zverovich a770009fcc Improve error reporting 2019-12-13 11:34:03 -08:00
Ivan Shynkarenka 598e6042d1 warning C4468: 'fallthrough': attribute must be followed by a case label or a default label 2019-12-12 06:25:33 -08:00
Victor Zverovich e09814dc93 Merge branch 'master' of github.com:fmtlib/fmt 2019-12-11 15:44:44 -08:00
Federico b272fb3605 Extend FMT_FALLTHROUGH compatibily to gcc and clang pre-C++17 (#1469) 2019-12-11 14:28:20 -08:00
Victor Zverovich f94b7364b9 Update version 2019-12-11 06:16:42 -08:00
Victor Zverovich 7abec071b5 Update changelog 2019-12-11 06:15:06 -08:00
Victor Zverovich b7eb8c8921 Prepare for the next release 2019-12-10 21:50:14 -08:00
Victor Zverovich ae7c50185d Reintroduce sprintf_format for ABI compatibility 2019-12-10 20:44:08 -08:00
Victor Zverovich 9f2e7edaeb Fix handling of types convertible to std::string_view 2019-12-09 13:25:08 -08:00
Victor Zverovich fd52de0c6b Add FMT_CUDA_TEST CMake option to enable cuda-test 2019-12-09 07:30:34 -08:00
Victor Zverovich f675cb887e Remove redundant cast 2019-12-08 18:01:59 -08:00
Victor Zverovich 73a16b827f Fix handling of int128_t in format-impl-test (#1461) 2019-12-08 17:07:20 -08:00
Florin Iucha 72879db40e Clean-up sign-conversion warnings in public headers 2019-12-08 16:07:55 -08:00
Florin Iucha d3aa0c3a28 Clean-up sign-conversion warnings in test code 2019-12-08 16:07:43 -08:00
Victor Zverovich 31de9a1b80 Revert "Clean-up sign-conversion warnings in test code"
This reverts commit 227bfe62dd.
2019-12-08 15:47:24 -08:00
Florin Iucha 227bfe62dd Clean-up sign-conversion warnings in test code 2019-12-08 15:21:38 -08:00
Victor Zverovich 95dfdc6cc4 Update README.rst 2019-12-07 10:12:56 -08:00
Victor Zverovich 5916ff63c4 Update README.rst 2019-12-07 10:12:15 -08:00
Deniz Evrenci 1ab80aa92c Fix handling of types with custom formatters that are convertible to std::string_view 2019-12-06 11:40:21 -08:00
Victor Zverovich 4f4d876616 Remove '%' from the docs 2019-12-06 07:06:19 -08:00
Victor Zverovich f443bd3baf Ditch decimal_formatter (#1363) 2019-12-05 19:07:45 -08:00
Victor Zverovich 1219b65f21 Relax fallthrough attribute detection 2019-12-05 10:40:15 -08:00
Victor Zverovich 071794ec65 Update version 2019-12-04 12:21:48 -08:00
Victor Zverovich d22e4ad85b Remove trailing comma 2019-12-04 12:20:52 -08:00
Victor Zverovich 983806b0c1 Update changelog 2019-12-04 12:03:44 -08:00
Victor Zverovich 02af5beb8a Bump version and update changelog 2019-12-04 10:22:07 -08:00
Victor Zverovich 123e7f7fc3 Revert #1433 because of build failures (#1450) 2019-12-03 09:24:15 -08:00
Victor Zverovich 168460f02c Remove TYPES 2019-12-03 06:45:00 -08:00
Egor Pugin a64f60c849 Remove unneeded FMT_API. 2019-12-03 05:55:04 -08:00
Egor Pugin 1a599117d8 Export assert_fail with FMT_API. This fixes dll build. 2019-12-03 05:55:04 -08:00
Victor Zverovich b160123e39 Update ChangeLog.rst 2019-12-02 16:18:06 -08:00
Beat Bolli 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
Victor Zverovich 8bbe76af3a Add a missing decimal point in exponent notation with trailing zeros 2019-12-02 11:36:33 -08:00
Victor Zverovich 4ca6821e8f Update version 2019-12-01 16:58:40 -08:00
Victor Zverovich 7111a1eb9f Bump version 2019-12-01 16:50:07 -08:00
Victor Zverovich ae00bbdc91 Update changelog 2019-12-01 16:39:58 -08:00
Victor Zverovich e71e07d9fb Update changlog 2019-12-01 16:26:07 -08:00
Victor Zverovich 0184df7020 Update docs 2019-12-01 14:49:56 -08:00
Victor Zverovich 1cbae6e9ba Put vprint declarations in one place 2019-12-01 14:43:51 -08:00
Robert Franke 159f89e2b8 Fixing installation directory of '*.dll' files on Windows 2019-12-01 14:36:21 -08:00
Victor Zverovich 4b120b68ae Clean up includes 2019-12-01 10:19:07 -08:00
Victor Zverovich 186b225d9d Update changlog 2019-12-01 10:16:01 -08:00
Victor Zverovich 4cbf4888ea Update changelog 2019-12-01 10:04:38 -08:00
Victor Zverovich e31f2b3d03 Update changelog 2019-12-01 09:27:28 -08:00
Victor Zverovich 62da1db62a Avoid wchar_t instantiations 2019-12-01 07:34:09 -08:00
Chris Martin 3bc28fcc6b Squelch MSVC warning exporting subclasses of runtime_error
When compiling {fmt} as a DLL, MSVC complains that we are exporting
classes that inherit from "std::runtime_error", which we are not
exporting.

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

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

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

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

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

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

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

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

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

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

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

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

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

* More fixes for VS2019 pedantic warnings

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

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

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

* Add defaulted copy and move operations to format_error and system_error

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

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

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

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

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

* Fix undefined in printf-test

Fixes "signed integer overflow" in PrintfTest.Length

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

* Fix undefined in printf %0$

Printf counts arguments from 1.

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

`do_get` is called with index -1 when `basic_printf_context.arg` is called with
id 4294967295 when basic_printf_context::get_arg subtracts 1 from arg_index 0 in
the format string "%0$d".
2019-10-08 06:28:39 -07:00
Victor Zverovich b60114533f Implement more bigint operations 2019-10-06 12:49:23 -07:00
Victor Zverovich c41cea8b18 Initial implementation of square 2019-10-05 16:37:52 -07:00
Victor Zverovich 0c7650373c Fix handling of types convertible to std::string_view 2019-10-05 06:58:37 -07:00
Jack Andersen 0571013709 Repoint one more Python 2 link to Python 3
One more python reference, this one acknowledging the `str.format` function.
2019-10-01 11:44:36 -07:00
Paul Dreik d2c9276fcd let README point to python 3 instead of 2 2019-09-29 08:30:47 -07:00
55 changed files with 5004 additions and 2970 deletions
+1
View File
@@ -1,4 +1,5 @@
.vscode/
.vs/
*.iml
.idea/
-39
View File
@@ -92,45 +92,6 @@ matrix:
# g++ 4.8 on Linux with C++11
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
compiler: gcc
- name: Android NDK (Gradle)
language: android
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- ninja-build
- curl
- tree
android:
components:
- tools
- platform-tools
- android-25 # 7.0
- android-27 # 8.1
- android-28 # 9.0
- build-tools-28.0.3
before_install:
# Install Gradle from https://sdkman.io/
- curl -s "https://get.sdkman.io" | bash > /dev/null
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- sdk version
- sdk install gradle
- sdk use gradle
- gradle --version
install:
# Accept SDK Licenses + Install NDK
- yes | sdkmanager --update > /dev/null 2>&1
- sdkmanager ndk-bundle > /dev/null 2>&1
before_script:
- pushd ./support
script:
- gradle clean
- gradle assemble
after_success:
- popd;
- tree ./libs
before_script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi
+58 -29
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
@@ -78,7 +98,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion
-Wconversion -Wswitch-enum
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
@@ -98,7 +118,7 @@ endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion)
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@@ -122,7 +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}\" %*")
@@ -135,10 +157,8 @@ endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(open fcntl.h HAVE_OPEN)
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
@@ -152,13 +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 printf.h ranges.h
safe-duration-cast.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
locale.h 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)
@@ -180,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")
@@ -200,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
@@ -211,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)
@@ -223,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(
@@ -259,7 +286,9 @@ if (FMT_INSTALL)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
@@ -285,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}")
+495
View File
@@ -1,3 +1,498 @@
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
------------------
* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal
representation with correct rounding by default:
.. code:: c++
#include <cmath>
#include <fmt/core.h>
int main() {
fmt::print("{}", M_PI);
}
prints ``3.141592653589793``.
* Made the fast binary to decimal floating-point formatter the default,
simplified it and improved performance. {fmt} is now 15 times faster than
libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10%
faster than double-conversion on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_:
================== ========= =======
Function Time (ns) Speedup
================== ========= =======
ostringstream 1,346.30 1.00x
ostrstream 1,195.74 1.13x
sprintf 995.08 1.35x
doubleconv 99.10 13.59x
fmt 88.34 15.24x
================== ========= =======
.. image:: https://user-images.githubusercontent.com/576385/
69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
* {fmt} no longer converts ``float`` arguments to ``double``. In particular this
improves the default (shortest) representation of floats and makes
``fmt::format`` consistent with ``std::format`` specs
(`#1336 <https://github.com/fmtlib/fmt/issues/1336>`_,
`#1353 <https://github.com/fmtlib/fmt/issues/1353>`_,
`#1360 <https://github.com/fmtlib/fmt/pull/1360>`_,
`#1361 <https://github.com/fmtlib/fmt/pull/1361>`_):
.. code:: c++
fmt::print("{}", 0.1f);
prints ``0.1`` instead of ``0.10000000149011612``.
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_.
* Made floating-point formatting output consistent with ``printf``/iostreams
(`#1376 <https://github.com/fmtlib/fmt/issues/1376>`_,
`#1417 <https://github.com/fmtlib/fmt/issues/1417>`_).
* Added support for 128-bit integers
(`#1287 <https://github.com/fmtlib/fmt/pull/1287>`_):
.. code:: c++
fmt::print("{}", std::numeric_limits<__int128_t>::max());
prints ``170141183460469231731687303715884105727``.
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the
output from different threads doesn't interleave
(`#1351 <https://github.com/fmtlib/fmt/pull/1351>`_).
Thanks `@tankiJong (Tanki Zhang) <https://github.com/tankiJong>`_.
* Made compile time in the header-only mode ~20% faster by reducing the number
of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from
``fmt/core.h`` to ``fmt/format.h``.
* Added an overload of ``fmt::join`` that works with tuples
(`#1322 <https://github.com/fmtlib/fmt/issues/1322>`_,
`#1330 <https://github.com/fmtlib/fmt/pull/1330>`_):
.. code:: c++
#include <tuple>
#include <fmt/ranges.h>
int main() {
std::tuple<char, int, float> t{'a', 1, 2.0f};
fmt::print("{}", t);
}
prints ``('a', 1, 2.0)``.
Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_.
* Changed formatting of octal zero with prefix from "00" to "0":
.. code:: c++
fmt::print("{:#o}", 0);
prints ``0``.
* The locale is now passed to ostream insertion (``<<``) operators
(`#1406 <https://github.com/fmtlib/fmt/pull/1406>`_):
.. code:: c++
#include <fmt/locale.h>
#include <fmt/ostream.h>
struct S {
double value;
};
std::ostream& operator<<(std::ostream& os, S s) {
return os << s.value;
}
int main() {
auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42});
// s == "0,42"
}
Thanks `@dlaugt (Daniel Laügt) <https://github.com/dlaugt>`_.
* Locale-specific number formatting now uses grouping
(`#1393 <https://github.com/fmtlib/fmt/issues/1393>`_
`#1394 <https://github.com/fmtlib/fmt/pull/1394>`_).
Thanks `@skrdaniel <https://github.com/skrdaniel>`_.
* Fixed handling of types with deleted implicit rvalue conversion to
``const char**`` (`#1421 <https://github.com/fmtlib/fmt/issues/1421>`_):
.. code:: c++
struct mystring {
operator const char*() const&;
operator const char*() &;
operator const char*() const&& = delete;
operator const char*() && = delete;
};
mystring str;
fmt::print("{}", str); // now compiles
* Enums are now mapped to correct underlying types instead of ``int``
(`#1286 <https://github.com/fmtlib/fmt/pull/1286>`_).
Thanks `@agmt (Egor Seredin) <https://github.com/agmt>`_.
* Enum classes are no longer implicitly converted to ``int``
(`#1424 <https://github.com/fmtlib/fmt/issues/1424>`_).
* Added ``basic_format_parse_context`` for consistency with C++20
``std::format`` and deprecated ``basic_parse_context``.
* Fixed handling of UTF-8 in precision
(`#1389 <https://github.com/fmtlib/fmt/issues/1389>`_,
`#1390 <https://github.com/fmtlib/fmt/pull/1390>`_).
Thanks `@tajtiattila (Attila Tajti) <https://github.com/tajtiattila>`_.
* {fmt} can now be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__ using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__
(`#1410 <https://github.com/fmtlib/fmt/pull/1410>`_)::
conda install -c conda-forge fmt
Thanks `@tdegeus (Tom de Geus) <https://github.com/tdegeus>`_.
* Added a CUDA test (`#1285 <https://github.com/fmtlib/fmt/pull/1285>`_,
`#1317 <https://github.com/fmtlib/fmt/pull/1317>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and
`@risa2000 <https://github.com/risa2000>`_.
* Improved documentation (`#1276 <https://github.com/fmtlib/fmt/pull/1276>`_,
`#1291 <https://github.com/fmtlib/fmt/issues/1291>`_,
`#1296 <https://github.com/fmtlib/fmt/issues/1296>`_,
`#1315 <https://github.com/fmtlib/fmt/pull/1315>`_,
`#1332 <https://github.com/fmtlib/fmt/pull/1332>`_,
`#1337 <https://github.com/fmtlib/fmt/pull/1337>`_,
`#1395 <https://github.com/fmtlib/fmt/issues/1395>`_
`#1418 <https://github.com/fmtlib/fmt/pull/1418>`_).
Thanks
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Various code improvements
(`#1358 <https://github.com/fmtlib/fmt/pull/1358>`_,
`#1407 <https://github.com/fmtlib/fmt/pull/1407>`_).
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@dpacbach (David P. Sicilia) <https://github.com/dpacbach>`_,
* Fixed compile-time format string checks for user-defined types
(`#1292 <https://github.com/fmtlib/fmt/issues/1292>`_).
* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer
(`#1377 <https://github.com/fmtlib/fmt/issues/1377>`_).
* Fixed various warnings and compilation issues
(`#1273 <https://github.com/fmtlib/fmt/issues/1273>`_,
`#1278 <https://github.com/fmtlib/fmt/pull/1278>`_,
`#1280 <https://github.com/fmtlib/fmt/pull/1280>`_,
`#1281 <https://github.com/fmtlib/fmt/issues/1281>`_,
`#1288 <https://github.com/fmtlib/fmt/issues/1288>`_,
`#1290 <https://github.com/fmtlib/fmt/pull/1290>`_,
`#1301 <https://github.com/fmtlib/fmt/pull/1301>`_,
`#1305 <https://github.com/fmtlib/fmt/issues/1305>`_,
`#1306 <https://github.com/fmtlib/fmt/issues/1306>`_,
`#1309 <https://github.com/fmtlib/fmt/issues/1309>`_,
`#1312 <https://github.com/fmtlib/fmt/pull/1312>`_,
`#1313 <https://github.com/fmtlib/fmt/issues/1313>`_,
`#1316 <https://github.com/fmtlib/fmt/issues/1316>`_,
`#1319 <https://github.com/fmtlib/fmt/issues/1319>`_,
`#1320 <https://github.com/fmtlib/fmt/pull/1320>`_,
`#1326 <https://github.com/fmtlib/fmt/pull/1326>`_,
`#1328 <https://github.com/fmtlib/fmt/pull/1328>`_,
`#1344 <https://github.com/fmtlib/fmt/issues/1344>`_,
`#1345 <https://github.com/fmtlib/fmt/pull/1345>`_,
`#1347 <https://github.com/fmtlib/fmt/pull/1347>`_,
`#1349 <https://github.com/fmtlib/fmt/pull/1349>`_,
`#1354 <https://github.com/fmtlib/fmt/issues/1354>`_,
`#1362 <https://github.com/fmtlib/fmt/issues/1362>`_,
`#1366 <https://github.com/fmtlib/fmt/issues/1366>`_,
`#1364 <https://github.com/fmtlib/fmt/pull/1364>`_,
`#1370 <https://github.com/fmtlib/fmt/pull/1370>`_,
`#1371 <https://github.com/fmtlib/fmt/pull/1371>`_,
`#1385 <https://github.com/fmtlib/fmt/issues/1385>`_,
`#1388 <https://github.com/fmtlib/fmt/issues/1388>`_,
`#1397 <https://github.com/fmtlib/fmt/pull/1397>`_,
`#1414 <https://github.com/fmtlib/fmt/pull/1414>`_,
`#1416 <https://github.com/fmtlib/fmt/pull/1416>`_,
`#1422 <https://github.com/fmtlib/fmt/issues/1422>`_
`#1427 <https://github.com/fmtlib/fmt/pull/1427>`_,
`#1431 <https://github.com/fmtlib/fmt/issues/1431>`_,
`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_).
Thanks `@hhb <https://github.com/hhb>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@neheb (Rosen Penev) <https://github.com/neheb>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_
`@leonklingele <https://github.com/leonklingele>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@kent-tri <https://github.com/kent-tri>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_,
`@marti4d (Chris Martin) <https://github.com/marti4d>`_.
6.0.0 - 2019-08-26
------------------
+36 -55
View File
@@ -7,20 +7,20 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: http://stackoverflow.com/questions/tagged/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: 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
--------
@@ -28,15 +28,15 @@ Features
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization.
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python.
* Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
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
@@ -81,16 +81,15 @@ Check a format string at compile time:
.. code:: c++
// test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{2}"), 42);
.. code::
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message);
@@ -122,11 +121,10 @@ Format objects of user-defined types via a simple `extension API
template <>
struct fmt::formatter<date> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) {
auto format(const date& d, FormatContext& ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
}
};
@@ -142,12 +140,12 @@ which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++
// Prints formatted error message.
void vreport_error(const char *format, fmt::format_args args) {
void vreport_error(const char* format, fmt::format_args args) {
fmt::print("Error: ");
fmt::vprint(format, args);
}
template <typename... Args>
void report_error(const char *format, const Args & ... args) {
void report_error(const char* format, const Args & ... args) {
vreport_error(format, fmt::make_format_args(args...));
}
@@ -166,18 +164,17 @@ Speed tests
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.01
libc++ std::ostream 3.04
{fmt} 1632f72 fmt::print 0.86
tinyformat 2.0.1 tfm::printf 3.23
Boost Format 1.67 boost::format 7.98
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~17% faster than ``printf``.
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.3 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
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
@@ -187,7 +184,7 @@ further details refer to the `source
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
@@ -211,7 +208,6 @@ printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
@@ -232,14 +228,13 @@ printf 2.2 33 30
printf+string 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
tinyformat 32.6 88 82
Boost Format 54.1 365 303
Folly Format 79.9 445 430
============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
compare formatting function overhead only. Boost Format and tinyformat are
header-only libraries so they don't provide any linkage options.
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
@@ -269,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>`_:
@@ -290,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
@@ -311,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,
@@ -331,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++
@@ -373,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
@@ -425,20 +420,6 @@ arguments. However it has significant limitations, citing its author:
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses ``printf``-like format strings and
is type safe. It doesn't support user-defined types or positional arguments and
makes unconventional use of ``operator()`` for passing format arguments.
Tinyformat
~~~~~~~~~~
This library supports ``printf``-like format strings and is very small .
It doesn't support positional arguments and wrapping it in C++98 is somewhat
difficult. Tinyformat relies on iostreams which limits its performance.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
@@ -505,10 +486,10 @@ 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
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
+1 -1
View File
@@ -84,7 +84,7 @@
<div class="jumbotron">
<div class="tb-container">
<h1>{fmt}</h1>
<p class="lead">Small, safe and fast formatting library</p>
<p class="lead">A modern formatting library</p>
<div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success"
+64 -22
View File
@@ -17,7 +17,7 @@ The {fmt} library API consists of the following parts:
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
All functions and types provided by the library reside in namespace ``fmt`` and
macros have prefix ``FMT_`` or ``fmt``.
macros have prefix ``FMT_``.
.. _core-api:
@@ -52,7 +52,6 @@ participate in an overload resolution if the latter is not a string.
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
Named Arguments
---------------
@@ -86,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
@@ -97,8 +109,11 @@ string checks, output iterator and user-defined type support.
Compile-time Format String Checks
---------------------------------
Compile-time checks are supported for built-in and string types as well as
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
specializations.
.. doxygendefine:: FMT_STRING
.. doxygendefine:: fmt
Formatting User-defined Types
-----------------------------
@@ -110,32 +125,56 @@ template and implement ``parse`` and ``format`` methods::
struct point { double x, y; };
namespace fmt {
template <>
struct formatter<point> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) {
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". The formatter should
// parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'.
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range:
if (it != end && *it != '}')
throw format_error("invalid format");
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point &p, FormatContext &ctx) {
return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
auto format(const point& p, FormatContext& ctx) {
// ctx.out() is an output iterator to write to.
return format_to(
ctx.out(),
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
p.x, p.y);
}
};
}
Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2};
std::string s = fmt::format("{}", p);
std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)"
In the example above the ``formatter<point>::parse`` function ignores the
contents of the format string referred to by ``ctx.begin()`` so the object will
always be formatted in the same way. See ``formatter<tm>::parse`` in
:file:`fmt/chrono.h` for an advanced example of how to parse the format string and
customize the formatted output.
You can also reuse existing formatters, for example::
You can also reuse existing formatters via inheritance or composition, for
example::
enum class color {red, green, blue};
@@ -143,7 +182,7 @@ You can also reuse existing formatters, for example::
struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext &ctx) {
auto format(color c, FormatContext& ctx) {
string_view name = "unknown";
switch (c) {
case color::red: name = "red"; break;
@@ -183,6 +222,9 @@ You can also write a formatter for a hierarchy of classes::
fmt::print("{}", a); // prints "B"
}
.. doxygenclass:: fmt::basic_format_parse_context
:members:
Output Iterator Support
-----------------------
@@ -266,7 +308,7 @@ allocator::
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args & ... args) {
const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_args(args...));
}
@@ -310,7 +352,7 @@ custom argument formatter class::
template <typename ...Args>
inline std::string custom_format(
fmt::string_view format_str, const Args &... args) {
fmt::string_view format_str, const Args&... args) {
return custom_vformat(format_str, fmt::make_format_args(args...));
}
@@ -377,7 +419,7 @@ user-defined types that have overloaded ``operator<<``::
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const date &d) {
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
+2 -2
View File
@@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0']
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0']
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
+20 -30
View File
@@ -1,17 +1,18 @@
Overview
========
**fmt** is an open-source formatting library.
It can be used as a fast and safe alternative to printf and IOStreams.
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
.. raw:: html
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. It’s been a hole in C++ for a long
time. I’ve used both boost::format and loki::SPrintf, and neither felt
like the right answer. This does.
Thanks for creating this library. It’s been a hole in C++ for
a long time. I’ve used both <code>boost::format</code> and
<code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
</div>
</div>
@@ -20,12 +21,13 @@ It can be used as a fast and safe alternative to printf and IOStreams.
Format API
----------
The replacement-based Format API provides a safe alternative to ``printf``,
``sprintf`` and friends with comparable or `better performance
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
The format API is similar in spirit to the C ``printf`` family of function but
is safer, simpler and serveral times `faster
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
than common standard library implementations.
The `format string syntax <syntax.html>`_ is similar to the one used by
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python:
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
Python:
.. code:: c++
@@ -60,7 +62,7 @@ The Format API also supports positional arguments useful for localization:
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
Named arguments can be created with ``fmt::arg``. This makes it easier to track
what goes where when multiple values are being inserted:
what goes where when multiple arguments are being formatted:
.. code:: c++
@@ -72,21 +74,10 @@ an alternative, slightly terser syntax for named arguments:
.. code:: c++
using namespace fmt::literals;
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
"name"_a="World", "number"_a=42);
The ``_format`` suffix may be used to format string literals similar to Python:
.. code:: c++
std::string message = "{0}{1}{0}"_format("abra", "cad");
Other than the placement of the format string on the left of the operator,
``_format`` is functionally identical to ``fmt::format``. In order to use the
literal operators, they must be made visible with the directive
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
``_format`` but nothing else from the ``fmt`` namespace.
.. _safety:
Safety
@@ -106,11 +97,10 @@ string", because the argument ``"forty-two"`` is a string while the format code
.. code:: c++
format(fmt("The answer is {:d}"), "forty-two");
format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error for the same reason on compilers that support
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for how to enable
compile-time checks.
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
The following code
@@ -178,13 +168,13 @@ The library is highly portable and relies only on a small set of C++11 features:
* alias templates
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
compilers use fmt `version 4.x
compilers use {fmt} `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98.
The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the
output of ``printf`` is platform-dependent in this case. For example,
output of ``printf`` is platform-dependent. For example,
.. code::
@@ -197,9 +187,9 @@ always prints ``inf``.
Ease of Use
-----------
fmt has a small self-contained code base with the core library consisting of
{fmt} has a small self-contained code base with the core library consisting of
just three header files and no external dependencies.
A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects.
.. raw:: html
+66 -55
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']):
+27 -1
View File
@@ -103,7 +103,33 @@ the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documentation in ``doc/html``.
Conda
=====
fmt can be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__, using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows::
conda install -c conda-forge fmt
Vcpkg
=====
You can download and install fmt using the `vcpkg
<https://github.com/Microsoft/vcpkg>`__ dependency manager::
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install fmt
The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please `create an issue or pull
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
Android NDK
===========
+334 -43
View File
@@ -8,24 +8,299 @@
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
@@ -220,12 +495,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
const Char newline[] = {'\n'};
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
const Char tab[] = {'\t'};
handler.on_text(tab, tab + 1);
break;
}
@@ -421,7 +696,7 @@ inline int to_nonnegative_int(T value, int upper) {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
return x % static_cast<T>(y);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
@@ -484,18 +759,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,
@@ -518,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;
@@ -582,8 +872,8 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n = to_unsigned(
to_nonnegative_int(value, max_value<int>()));
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
@@ -593,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);
}
@@ -629,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) {
@@ -638,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) {
@@ -647,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) {
@@ -672,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() {
@@ -702,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
@@ -728,7 +1019,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@@ -746,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) {
@@ -761,13 +1052,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
using iterator = typename basic_parse_context<Char>::iterator;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
@@ -788,7 +1079,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
@@ -810,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);
+32 -50
View File
@@ -299,15 +299,15 @@ class text_style {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@@ -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:
@@ -470,58 +470,39 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
auto background =
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buffer, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
}
return fmt::to_string(buffer);
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
}
} // namespace internal
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/**
@@ -536,7 +517,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >;
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
@@ -554,11 +535,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
basic_format_args<buffer_context<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);
}
/**
@@ -573,12 +556,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S> >
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return internal::vformat(
ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
return vformat(ts, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
+38 -24
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*) {}
@@ -101,7 +102,7 @@ class format_string_compiler : public error_handler {
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_parse_context<Char> parse_context_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
@@ -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);
}
@@ -136,8 +137,8 @@ class format_string_compiler : public error_handler {
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_parse_context<Char>> handler(repl.specs,
parse_context_);
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
@@ -160,8 +161,9 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
@@ -172,7 +174,8 @@ template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_parse_context<char_type> parse_ctx(to_string_view(cf.format_str_));
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
@@ -348,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;
@@ -359,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) {
@@ -373,6 +381,13 @@ OutputIt format_default(OutputIt out, T value) {
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
@@ -397,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;
@@ -409,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};
@@ -499,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...);
@@ -542,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);
}
@@ -554,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,
@@ -570,10 +587,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
return format_to(internal::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE
+485 -156
View File
File diff suppressed because it is too large Load Diff
+706 -419
View File
File diff suppressed because it is too large Load Diff
+777 -742
View File
File diff suppressed because it is too large Load Diff
+10 -9
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
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_
+8 -4
View File
@@ -89,9 +89,13 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !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());
@@ -104,7 +108,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
@@ -113,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);
@@ -132,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
+2 -311
View File
@@ -1,311 +1,2 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
+48 -41
View File
@@ -1,4 +1,4 @@
// Formatting library for C++
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
@@ -16,10 +16,6 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
@@ -32,7 +28,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
@@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@@ -307,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();
@@ -324,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>;
@@ -332,17 +331,17 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = 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:
/**
@@ -359,9 +358,11 @@ 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 {}; }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
@@ -401,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);
@@ -410,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;
@@ -440,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;
@@ -468,7 +470,8 @@ 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.
if (it != end && *it == '.') {
@@ -476,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;
}
@@ -599,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);
@@ -618,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();
@@ -646,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);
}
@@ -669,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);
@@ -685,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;
@@ -707,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
+26 -4
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_
-293
View File
@@ -1,293 +0,0 @@
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
FMT_END_NAMESPACE
+131 -12
View File
@@ -8,11 +8,125 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>;
namespace internal {
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned,
int&) = internal::grisu_format;
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef 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,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
@@ -21,6 +135,7 @@ template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
@@ -35,23 +150,27 @@ template FMT_API std::string internal::vformat<char>(
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context>&);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE
+123 -40
View File
@@ -1,4 +1,4 @@
// A C++ interface to POSIX functions.
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@@ -10,37 +10,43 @@
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/posix.h"
#include "fmt/os.h"
#include <climits>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#endif
#ifdef fileno
# undef fileno
@@ -66,6 +72,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");
@@ -94,14 +175,15 @@ int buffered_file::fileno() const {
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
# if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
# else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
# endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
@@ -123,7 +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.
@@ -137,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)
@@ -145,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) {
@@ -192,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.
@@ -220,14 +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
+1 -1
View File
@@ -3,7 +3,7 @@
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.box = "ubuntu/xenial64"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
-11
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);
+1 -1
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}
+2 -1
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:
+19 -34
View File
@@ -17,9 +17,7 @@ else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
if (MSVC)
# Workaround a bug in implementation of variadic templates in MSVC11.
@@ -47,8 +45,6 @@ target_compile_definitions(gmock
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
add_library(test-main STATIC ${TEST_MAIN_SRC})
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
target_link_libraries(test-main gmock fmt)
@@ -113,12 +109,11 @@ add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
if (NOT MSVC_BUILD_STATIC)
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock)
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
if (FMT_PEDANTIC)
@@ -128,7 +123,7 @@ if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
endif ()
add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(posix-test)
add_fmt_test(os-test)
endif ()
add_fmt_executable(header-only-test
@@ -174,18 +169,6 @@ if (FMT_PEDANTIC)
target_compile_definitions(
nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1)
# Test that the library compiles without windows.h.
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_library(no-windows-h-test ../src/format.cc)
target_include_directories(
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
if (FMT_PEDANTIC)
target_compile_options(no-windows-h-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(no-windows-h-test SYSTEM PUBLIC gtest gmock)
endif ()
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
@@ -232,20 +215,22 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()
# Activate optional CUDA tests if CUDA is found. For version selection, see
# Activate optional CUDA tests if CUDA is found. For version selection see
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
if (FMT_CUDA_TEST)
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()
+16 -10
View File
@@ -8,15 +8,21 @@
#include "fmt/core.h"
#include "gtest.h"
#if GTEST_HAS_DEATH_TEST
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEBUG_DEATH(statement, regex)
#else
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
#endif
TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"),
"don't panic!");
#if GTEST_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!");
#else
fmt::print("warning: death tests are not supported\n");
#endif
}
bool test_condition = false;
TEST(AssertTest, DanglingElse) {
bool executed_else = false;
if (test_condition)
FMT_ASSERT(true, "");
else
executed_else = true;
EXPECT_TRUE(executed_else);
}
+42
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));
+2
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(
+156 -23
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
@@ -103,7 +102,7 @@ template <typename T> struct mock_buffer : buffer<T> {
TEST(BufferTest, Ctor) {
{
mock_buffer<int> buffer;
EXPECT_EQ(nullptr, &buffer[0]);
EXPECT_EQ(nullptr, buffer.data());
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
@@ -127,8 +126,13 @@ TEST(BufferTest, Ctor) {
struct dying_buffer : test_buffer<int> {
MOCK_METHOD0(die, void());
~dying_buffer() { die(); }
private:
virtual void avoid_weak_vtable();
};
void dying_buffer::avoid_weak_vtable() {}
TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer;
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
@@ -285,8 +289,6 @@ VISIT_TYPE(long, long long);
VISIT_TYPE(unsigned long, unsigned long long);
#endif
VISIT_TYPE(float, double);
#define CHECK_ARG_(Char, expected, value) \
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
@@ -399,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));
@@ -450,17 +558,22 @@ template <> struct formatter<enabled_formatter> {
FMT_END_NAMESPACE
TEST(CoreTest, HasFormatter) {
using fmt::internal::has_formatter;
using fmt::has_formatter;
using context = fmt::format_context;
EXPECT_TRUE((has_formatter<enabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter_convertible, context>::value));
static_assert(has_formatter<enabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter_convertible, context>::value,
"");
}
struct convertible_to_int {
operator int() const { return 42; }
};
struct convertible_to_c_string {
operator const char*() const { return "foo"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_int> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
@@ -470,10 +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 {
@@ -569,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;
@@ -613,6 +737,17 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
# ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
# endif
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
@@ -621,17 +756,15 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
#endif
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
struct disabled_rvalue_conversion {
operator const char*() const& { return "foo"; }
operator const char*() & { return "foo"; }
operator const char*() const&& = delete;
operator const char*() && = delete;
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
TEST(FormatterTest, DisabledRValueConversion) {
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
}
#endif
+35 -35
View File
@@ -8,6 +8,7 @@
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <cassert>
#include <variant>
#include "fmt/format.h"
@@ -82,9 +83,9 @@ namespace std {
Out format_to(Out out, wstring_view fmt, const Args&... args);
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args);
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out>
struct format_to_n_result {
@@ -533,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);
}
@@ -648,38 +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:
handle_float_type_spec(type_spec,
internal::float_type_checker<decltype(eh)>(eh));
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,
+6
View File
@@ -0,0 +1,6 @@
// Copyright (c) 2020 Vladimir Solontsov
// SPDX-License-Identifier: MIT Licence
#include <fmt/core.h>
#include "gtest-extra.h"
+208 -60
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"
@@ -35,6 +34,46 @@ TEST(BigIntTest, Construct) {
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
}
TEST(BigIntTest, Compare) {
bigint n1(42);
bigint n2(42);
EXPECT_EQ(compare(n1, n2), 0);
n2 <<= 32;
EXPECT_LT(compare(n1, n2), 0);
bigint n3(43);
EXPECT_LT(compare(n1, n3), 0);
EXPECT_GT(compare(n3, n1), 0);
bigint n4(42 * 0x100000001);
EXPECT_LT(compare(n2, n4), 0);
EXPECT_GT(compare(n4, n2), 0);
}
TEST(BigIntTest, AddCompare) {
EXPECT_LT(
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
bigint(0x300000010)),
0);
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000000)),
0);
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000001)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000002)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000003)),
0);
}
TEST(BigIntTest, ShiftLeft) {
bigint n(0x42);
n <<= 0;
@@ -47,30 +86,133 @@ TEST(BigIntTest, ShiftLeft) {
TEST(BigIntTest, Multiply) {
bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
auto max = max_value<uint32_t>();
bigint bigmax(max);
bigmax *= max;
bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
template <bool is_iec559> void test_construct_from_double() {
TEST(BigIntTest, Accumulator) {
fmt::internal::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += fmt::internal::max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(BigIntTest, Square) {
bigint n0(0);
n0.square();
EXPECT_EQ("0", fmt::format("{}", n0));
bigint n1(0x100);
n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1));
bigint n2(0xfffffffff);
n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
bigint n3(max_value<uint64_t>());
n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
bigint n4;
n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4));
}
TEST(BigIntTest, DivModAssignZeroDivisor) {
bigint zero(0);
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
}
TEST(BigIntTest, DivModAssignSelf) {
bigint n(100);
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
}
TEST(BigIntTest, DivModAssignUnaligned) {
// (42 << 340) / pow(10, 100):
bigint n1(42);
n1 <<= 340;
bigint n2;
n2.assign_pow10(100);
int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
fmt::format("{}", n1));
}
TEST(BigIntTest, DivModAssign) {
// 100 / 10:
bigint n1(100);
int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1));
// pow(10, 100) / (42 << 320):
n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
fmt::format("{}", n1));
// 42 / 100:
bigint n2(42);
n1.assign_pow10(2);
result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0);
EXPECT_EQ("2a", fmt::format("{}", n2));
}
template <bool is_iec559> void run_double_tests() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <> void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
template <> void run_double_tests<true>() {
// Construct from double.
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
// Compute boundaries:
fp value;
// Normalized & not power of 2 - equidistant boundaries:
auto b = value.assign_with_boundaries(1.23);
EXPECT_EQ(value, fp(0x0013ae147ae147ae, -52));
EXPECT_EQ(b.lower, 0x9d70a3d70a3d6c00);
EXPECT_EQ(b.upper, 0x9d70a3d70a3d7400);
// Normalized power of 2 - lower boundary is closer:
b = value.assign_with_boundaries(1.9807040628566084e+28); // 2**94
EXPECT_EQ(value, fp(0x0010000000000000, 42));
EXPECT_EQ(b.lower, 0x7ffffffffffffe00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Smallest normalized double - equidistant boundaries:
b = value.assign_with_boundaries(2.2250738585072014e-308);
EXPECT_EQ(value, fp(0x0010000000000000, -1074));
EXPECT_EQ(b.lower, 0x7ffffffffffffc00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Subnormal - equidistant boundaries:
b = value.assign_with_boundaries(4.9406564584124654e-324);
EXPECT_EQ(value, fp(0x0000000000000001, -1074));
EXPECT_EQ(b.lower, 0x4000000000000000);
EXPECT_EQ(b.upper, 0xc000000000000000);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
TEST(FPTest, DoubleTests) {
run_double_tests<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
@@ -80,30 +222,31 @@ TEST(FPTest, Normalize) {
EXPECT_EQ(-6, normalized.e);
}
TEST(FPTest, ComputeBoundariesSubnormal) {
auto v = fp(0xbeef, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0xbeee800000000000, lower.f);
EXPECT_EQ(-6, lower.e);
EXPECT_EQ(0xbeef800000000000, upper.f);
EXPECT_EQ(-6, upper.e);
}
TEST(FPTest, ComputeBoundaries) {
auto v = fp(0x10000000000000, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
EXPECT_EQ(31, lower.e);
EXPECT_EQ(0x8000000000000400, upper.f);
EXPECT_EQ(31, upper.e);
}
TEST(FPTest, Subtract) {
auto v = fp(123, 1) - fp(102, 1);
EXPECT_EQ(v.f, 21u);
EXPECT_EQ(v.e, 1);
TEST(FPTest, ComputeFloatBoundaries) {
struct {
double x, lower, upper;
} tests[] = {
// regular
{1.5f, 1.4999999403953552, 1.5000000596046448},
// boundary
{1.0f, 0.9999999701976776, 1.0000000596046448},
// min normal
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
// max subnormal
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
// min subnormal
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
};
for (auto test : tests) {
fp vlower = normalize(fp(test.lower));
fp vupper = normalize(fp(test.upper));
vlower.f >>= vupper.e - vlower.e;
vlower.e = vupper.e;
fp value;
auto b = value.assign_float_with_boundaries(test.x);
EXPECT_EQ(vlower.f, b.lower);
EXPECT_EQ(vupper.f, b.upper);
}
}
TEST(FPTest, Multiply) {
@@ -129,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) {
@@ -174,8 +318,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
int exp = 0;
grisu_format(4.2f, buf, -1, false, exp);
format_float(0.42, -1, fmt::internal::float_specs(), buf);
}
template <typename T> struct value_extractor {
@@ -185,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
};
@@ -283,14 +426,19 @@ 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.
template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i));
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end;
++i) {
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10;
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
@@ -305,8 +453,8 @@ TEST(UtilTest, CountDigits) {
TEST(UtilTest, WriteUIntPtr) {
fmt::memory_buffer buf;
fmt::internal::writer writer(buf);
writer.write_pointer(fmt::internal::bit_cast<fmt::internal::fallback_uintptr>(
reinterpret_cast<void*>(0xface)),
nullptr);
writer.write_pointer(
fmt::internal::fallback_uintptr(reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ("0xface", to_string(buf));
}
+201 -231
View File
@@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <stdint.h>
#include <cctype>
#include <cfloat>
#include <climits>
@@ -20,8 +21,14 @@
# include <windows.h>
#endif
// Check if fmt/format.h compiles with the X11 index macro defined.
#define index(x, y) no nice things
#include "fmt/color.h"
#include "fmt/format.h"
#undef index
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
@@ -34,13 +41,14 @@
using std::size_t;
using fmt::basic_memory_buffer;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using fmt::format;
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;
using testing::Return;
using testing::StrictMock;
@@ -163,8 +171,7 @@ TEST(UtilTest, Increment) {
}
TEST(UtilTest, ParseNonnegativeInt) {
if (max_value<int>() !=
static_cast<int>(static_cast<unsigned>(1) << 31)) {
if (max_value<int>() != static_cast<int>(static_cast<unsigned>(1) << 31)) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
}
@@ -182,7 +189,7 @@ TEST(UtilTest, ParseNonnegativeInt) {
}
TEST(IteratorTest, CountingIterator) {
fmt::internal::counting_iterator<char> it;
fmt::internal::counting_iterator it;
auto prev = it++;
EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1);
@@ -271,7 +278,7 @@ static void check_move_buffer(
EXPECT_EQ(alloc, buffer2.get_allocator().get());
}
TEST(MemoryBufferTest, MoveCtor) {
TEST(MemoryBufferTest, MoveCtorInlineBuffer) {
std::allocator<char> alloc;
basic_memory_buffer<char, 5, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
@@ -281,15 +288,22 @@ TEST(MemoryBufferTest, MoveCtor) {
// dynamic allocation.
buffer.push_back('a');
check_move_buffer("testa", buffer);
}
TEST(MemoryBufferTest, MoveCtorDynamicBuffer) {
std::allocator<char> alloc;
basic_memory_buffer<char, 4, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
buffer.append(test, test + 4);
const char* inline_buffer_ptr = &buffer[0];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer.push_back('b');
basic_memory_buffer<char, 5, TestAllocator> buffer2(std::move(buffer));
buffer.push_back('a');
basic_memory_buffer<char, 4, TestAllocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 5u);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 4u);
}
static void check_move_assign_buffer(const char* str,
@@ -388,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) {
@@ -417,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");
@@ -498,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) {
@@ -509,70 +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());
@@ -674,9 +572,7 @@ TEST(WriterTest, WriteString) {
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) {
CHECK_WRITE_WCHAR(L"abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) {
std::string s;
@@ -921,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) {
@@ -1031,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,
@@ -1086,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) {
@@ -1189,12 +1094,39 @@ TEST(FormatterTest, Precision) {
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2%}", 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:3.0}", 'x'), format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", format("{0:.2}", 1.2345));
EXPECT_EQ("1.2", format("{0:.2}", 1.2345l));
EXPECT_EQ("1.2e+56", format("{:.2}", 1.234e56));
EXPECT_EQ("1e+00", format("{:.0e}", 1.0L));
EXPECT_EQ(" 0.0e+00", format("{:9.1e}", 0.0));
EXPECT_EQ(
"4.9406564584124654417656879286822137236505980261432476442558568250067550"
"727020875186529983636163599237979656469544571773092665671035593979639877"
"479601078187812630071319031140452784581716784898210368871863605699873072"
"305000638740915356498438731247339727316961514003171538539807412623856559"
"117102665855668676818703956031062493194527159149245532930545654440112748"
"012970999954193198940908041656332452475714786901472678015935523861155013"
"480352649347201937902681071074917033322268447533357208324319361e-324",
format("{:.494}", 4.9406564584124654E-324));
EXPECT_EQ(
"-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000P+127",
format("{:.838A}", -2.14001164E+38));
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
EXPECT_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,
@@ -1202,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"));
}
@@ -1322,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) {
@@ -1462,11 +1396,8 @@ TEST(FormatterTest, FormatOct) {
}
TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("123", format("{:n}", 123));
EXPECT_EQ("1,234", format("{:n}", 1234));
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
EXPECT_EQ("4,294,967,295",
format("{:n}", max_value<uint32_t>()));
EXPECT_EQ("1234", format("{:n}", 1234));
EXPECT_EQ("1234", format("{:L}", 1234));
}
struct ConvertibleToLongLong {
@@ -1479,7 +1410,6 @@ TEST(FormatterTest, FormatConvertibleToLongLong) {
TEST(FormatterTest, FormatFloat) {
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
EXPECT_EQ("12.500000%", format("{0:%}", 0.125f));
}
TEST(FormatterTest, FormatDouble) {
@@ -1492,8 +1422,6 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ("392.65", format("{:G}", 392.65));
EXPECT_EQ("392.650000", format("{:f}", 392.65));
EXPECT_EQ("392.650000", format("{:F}", 392.65));
EXPECT_EQ("12.500000%", format("{:%}", 0.125));
EXPECT_EQ("12.34%", format("{:.2%}", 0.1234432));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, format("{0:e}", 392.65));
@@ -1533,7 +1461,6 @@ TEST(FormatterTest, FormatNaN) {
EXPECT_EQ("nan ", format("{:<7}", nan));
EXPECT_EQ(" nan ", format("{:^7}", nan));
EXPECT_EQ(" nan", format("{:>7}", nan));
EXPECT_EQ("nan%", format("{:%}", nan));
}
TEST(FormatterTest, FormatInfinity) {
@@ -1546,7 +1473,6 @@ TEST(FormatterTest, FormatInfinity) {
EXPECT_EQ("inf ", format("{:<7}", inf));
EXPECT_EQ(" inf ", format("{:^7}", inf));
EXPECT_EQ(" inf", format("{:>7}", inf));
EXPECT_EQ("inf%", format("{:%}", inf));
}
TEST(FormatterTest, FormatLongDouble) {
@@ -1557,8 +1483,6 @@ TEST(FormatterTest, FormatLongDouble) {
EXPECT_EQ("392.65", format("{0:G}", 392.65l));
EXPECT_EQ("392.650000", format("{0:f}", 392.65l));
EXPECT_EQ("392.650000", format("{0:F}", 392.65l));
EXPECT_EQ("12.500000%", format("{:%}", 0.125l));
EXPECT_EQ("12.34%", format("{:.2%}", 0.1234432l));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%Le", 392.65l);
EXPECT_EQ(buffer, format("{0:e}", 392.65l));
@@ -1568,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'));
@@ -1662,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
@@ -1805,16 +1749,17 @@ TEST(FormatIntTest, FormatInt) {
EXPECT_EQ("-42", fmt::format_int(-42ll).str());
std::ostringstream os;
os << max_value<int64_t>();
EXPECT_EQ(os.str(),
fmt::format_int(max_value<int64_t>()).str());
EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str());
}
TEST(FormatTest, Print) {
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!");
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
"Don't panic!");
#endif
// Check that the wide print overload compiles.
if (fmt::internal::const_check(false)) fmt::print(L"test");
}
TEST(FormatTest, Variadic) {
@@ -1830,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};
@@ -1907,17 +1858,32 @@ 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));
const Answer const_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));
}
@@ -1965,8 +1931,9 @@ enum TestEnum { A };
TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); }
TEST(FormatTest, FormatterNotSpecialized) {
EXPECT_FALSE((fmt::internal::has_formatter<fmt::formatter<TestEnum>,
fmt::format_context>::value));
static_assert(
!fmt::has_formatter<fmt::formatter<TestEnum>, fmt::format_context>::value,
"");
}
#if FMT_HAS_FEATURE(cxx_strong_enums)
@@ -2140,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;
}
@@ -2185,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;
@@ -2206,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() {}
@@ -2257,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; }
@@ -2286,9 +2278,9 @@ struct test_context {
template <size_t N>
FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
fmt::format_specs specs;
test_parse_context parse_ctx;
test_context ctx{};
auto specs = fmt::format_specs();
auto parse_ctx = test_parse_context();
auto ctx = test_context();
fmt::internal::specs_handler<test_parse_context, test_context> h(
specs, parse_ctx, ctx);
parse_format_specs(s, s + N, h);
@@ -2342,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;
}
@@ -2448,11 +2440,11 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:10000000000}", "number is too big", int);
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
#if FMT_NUMERIC_ALIGN
# if FMT_NUMERIC_ALIGN
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
const char*);
#endif
# endif
EXPECT_ERROR("{:+}", "format specifier requires numeric argument",
const char*);
EXPECT_ERROR("{:-}", "format specifier requires numeric argument",
@@ -2531,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");
}
@@ -2608,3 +2560,21 @@ TEST(FormatTest, FormatCustomChar) {
EXPECT_EQ(result.size(), 1);
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<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(from_u8str(result), from_u8str(str.substr(0, 5)));
}
+19 -1
View File
@@ -48,10 +48,28 @@ TEST(GrisuTest, Prettify) {
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
EXPECT_EQ("1e+11", fmt::format("{}", 1e11));
EXPECT_EQ("100000000000.0", fmt::format("{}", 1e11));
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f)));
}
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }
TEST(GrisuTest, Fallback) {
EXPECT_EQ("1e+23", fmt::format("{}", 1e23));
EXPECT_EQ("9e-265", fmt::format("{}", 9e-265));
EXPECT_EQ("5.423717798060526e+125",
fmt::format("{}", 5.423717798060526e+125));
EXPECT_EQ("1.372371880954233e-288",
fmt::format("{}", 1.372371880954233e-288));
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
EXPECT_EQ("2.2506787569811123e-253",
fmt::format("{}", 2.2506787569811123e-253));
EXPECT_EQ("1103618912042992.8", fmt::format("{}", 1103618912042992.8));
// pow(2, -25) - assymetric boundaries:
EXPECT_EQ("2.9802322387695312e-08",
fmt::format("{}", 2.9802322387695312e-08));
}
+28 -26
View File
@@ -74,14 +74,6 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
EXPECT_EQ(s_ + 1, p_);
}
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) {
// successful EXPECT_THROW_MSG
@@ -163,6 +155,15 @@ TEST_F(SingleEvaluationTest, SystemErrorTests) {
EXPECT_EQ(4, b_);
}
#if FMT_USE_FCNTL
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE
@@ -187,6 +188,24 @@ TEST_F(SingleEvaluationTest, WriteTests) {
EXPECT_EQ(2, b_);
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
#endif // FMT_USE_FCNTL
// Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
@@ -280,16 +299,6 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
format_system_error(EDOM, "test")));
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
<< "unexpected failure";
@@ -308,20 +317,13 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
"expected failure");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
TEST(UtilTest, FormatSystemError) {
fmt::memory_buffer out;
fmt::format_system_error(out, EDOM, "test message");
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
}
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::buffered_file;
using fmt::error_code;
+2 -2
View File
@@ -7,7 +7,7 @@
#include "gtest-extra.h"
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::file;
@@ -78,7 +78,7 @@ std::string read(file& f, std::size_t count) {
return buffer;
}
#endif // FMT_USE_FILE_DESCRIPTORS
#endif // FMT_USE_FCNTL
std::string format_system_error(int error_code, fmt::string_view message) {
fmt::memory_buffer out;
+5 -12
View File
@@ -10,16 +10,7 @@
#include <string>
#include "gmock.h"
#include "fmt/core.h"
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
# include "fmt/posix.h"
#endif
#include "fmt/os.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
@@ -65,7 +56,7 @@ std::string format_system_error(int error_code, fmt::string_view message);
EXPECT_THROW_MSG(statement, fmt::system_error, \
format_system_error(error_code, message))
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
// Captures file output by redirecting it to a pipe.
// The output it can handle is limited by the pipe capacity.
@@ -151,7 +142,9 @@ std::string read(fmt::file& f, std::size_t count);
# define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
#endif // FMT_USE_FILE_DESCRIPTORS
#else
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
#endif // FMT_USE_FCNTL
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; }
+50 -13
View File
@@ -8,45 +8,82 @@
#include "fmt/locale.h"
#include "gmock.h"
using fmt::internal::max_value;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '?'; }
std::string do_grouping() const FMT_OVERRIDE { return "\03"; }
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
};
template <typename Char> struct no_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return ""; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
template <typename Char> struct special_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return "\03\02"; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
template <typename Char> struct small_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return "\01"; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
TEST(LocaleTest, DoubleDecimalPoint) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23));
// Test with Grisu disabled.
fmt::memory_buffer buf;
fmt::internal::writer w(buf, fmt::internal::locale_ref(loc));
auto specs = fmt::format_specs();
specs.type = 'n';
w.write_double<double, false>(1.23, specs);
EXPECT_EQ(fmt::to_string(buf), "1?23");
}
TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567));
fmt::format_arg_store<fmt::format_context, int> as{1234567};
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as)));
std::string s;
fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567);
EXPECT_EQ("1~234~567", s);
std::locale no_grouping_loc(std::locale(), new no_grouping<char>());
EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:n}", 1234567));
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:n}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<char>());
EXPECT_EQ("4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, "{:n}", max_value<uint32_t>()));
}
TEST(LocaleTest, WFormat) {
std::locale loc(std::locale(), new numpunct<wchar_t>());
EXPECT_EQ(L"1,234,567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567));
fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:n}", fmt::wformat_args(as)));
auto sep =
std::use_facet<std::numpunct<wchar_t>>(std::locale("C")).thousands_sep();
auto result = sep == ',' ? L"1,234,567" : L"1234567";
EXPECT_EQ(result, fmt::format(std::locale("C"), L"{:n}", 1234567));
EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:n}", 1234567));
std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>());
EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:n}", 1234567));
std::locale special_grouping_loc(std::locale(),
new special_grouping<wchar_t>());
EXPECT_EQ(L"1,23,45,678",
fmt::format(special_grouping_loc, L"{:n}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<wchar_t>());
EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, L"{:n}", max_value<uint32_t>()));
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
+130 -10
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"
@@ -19,6 +19,125 @@
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;
// Checks if the file is open by reading one character from it.
@@ -148,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(
@@ -160,7 +279,7 @@ TEST(BufferedFileTest, Fileno) {
}
},
"");
#endif
# endif
f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1);
file copy = file::dup(f.fileno());
@@ -312,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();
@@ -368,11 +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
+39 -4
View File
@@ -187,8 +187,8 @@ TEST(OStreamTest, Join) {
#if FMT_USE_CONSTEXPR
TEST(OStreamTest, ConstexprString) {
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
EXPECT_EQ("a string", format(fmt("{0}"), TestString("a string")));
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
}
#endif
@@ -243,8 +243,7 @@ TEST(FormatTest, UDL) {
}
#endif
template <typename T>
struct convertible {
template <typename T> struct convertible {
T value;
explicit convertible(const T& val) : value(val) {}
operator T() const { return value; }
@@ -255,3 +254,39 @@ TEST(OStreamTest, DisableBuiltinOStreamOperators) {
EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(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
+95 -87
View File
@@ -11,13 +11,15 @@
#endif
#include "posix-mock.h"
#include "../src/posix.cc"
#include <errno.h>
#include <fcntl.h>
#include <climits>
#include <memory>
#include "../src/os.cc"
#ifdef _WIN32
# include <io.h>
# undef max
@@ -30,7 +32,6 @@
using fmt::buffered_file;
using fmt::error_code;
using fmt::file;
using testing::_;
using testing::Return;
@@ -52,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) \
@@ -198,29 +199,32 @@ static void write_file(fmt::cstring_view filename, fmt::string_view content) {
f.print("{}", content);
}
#if FMT_USE_FCNTL
using fmt::file;
TEST(UtilTest, GetPageSize) {
#ifdef _WIN32
# ifdef _WIN32
SYSTEM_INFO si = {};
GetSystemInfo(&si);
EXPECT_EQ(si.dwPageSize, fmt::getpagesize());
#else
# else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true;
EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
"cannot get memory page size");
sysconf_error = false;
#endif
# endif
}
TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here");
write_file("temp", "there must be something here");
std::unique_ptr<file> f{nullptr};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
"cannot open file test");
#ifndef _WIN32
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
"cannot open file temp");
# ifndef _WIN32
char c = 0;
f->read(&c, 1);
#endif
# endif
}
TEST(FileTest, CloseNoRetryInDtor) {
@@ -228,14 +232,15 @@ TEST(FileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end);
std::unique_ptr<file> f(new file(std::move(read_end)));
int saved_close_count = 0;
EXPECT_WRITE(stderr,
{
close_count = 1;
f.reset(nullptr);
saved_close_count = close_count;
close_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_WRITE(
stderr,
{
close_count = 1;
f.reset(nullptr);
saved_close_count = close_count;
close_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_close_count);
}
@@ -250,26 +255,26 @@ TEST(FileTest, CloseNoRetry) {
TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading";
write_file("test", content);
file f("test", file::RDONLY);
write_file("temp", content);
file f("temp", file::RDONLY);
EXPECT_GE(f.size(), 0);
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
#ifdef _WIN32
# ifdef _WIN32
fmt::memory_buffer message;
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
"cannot get file size");
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());
@@ -297,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);
@@ -332,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));
@@ -357,11 +362,11 @@ TEST(FileTest, Dup2NoExceptRetry) {
error_code ec;
dup2_count = 1;
f1.dup2(f2.descriptor(), ec);
#ifndef _WIN32
# ifndef _WIN32
EXPECT_EQ(4, dup2_count);
#else
# else
EXPECT_EQ(EINTR, ec.get());
#endif
# endif
dup2_count = 0;
}
@@ -383,15 +388,15 @@ TEST(FileTest, FdopenNoRetry) {
}
TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here");
write_file("temp", "there must be something here");
std::unique_ptr<buffered_file> f{nullptr};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
"cannot open file test");
#ifndef _WIN32
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
"cannot open file temp");
# ifndef _WIN32
char c = 0;
if (fread(&c, 1, 1, f->get()) < 1)
throw fmt::system_error(errno, "fread failed");
#endif
# endif
}
TEST(BufferedFileTest, CloseNoRetryInDtor) {
@@ -399,14 +404,15 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end);
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
int saved_fclose_count = 0;
EXPECT_WRITE(stderr,
{
fclose_count = 1;
f.reset(nullptr);
saved_fclose_count = fclose_count;
fclose_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_WRITE(
stderr,
{
fclose_count = 1;
f.reset(nullptr);
saved_fclose_count = fclose_count;
fclose_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_fclose_count);
}
@@ -429,34 +435,35 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
#endif // FMT_USE_FCNTL
struct TestMock {
static TestMock* instance;
} * TestMock::instance;
struct test_mock {
static test_mock* instance;
} * test_mock::instance;
TEST(ScopedMock, Scope) {
{
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
TestMock& copy = mock;
ScopedMock<test_mock> mock;
EXPECT_EQ(&mock, test_mock::instance);
test_mock& copy = mock;
static_cast<void>(copy);
}
EXPECT_EQ(nullptr, TestMock::instance);
EXPECT_EQ(nullptr, test_mock::instance);
}
#ifdef FMT_LOCALE
typedef fmt::Locale::type LocaleType;
typedef fmt::locale::type locale_type;
struct LocaleMock {
static LocaleMock* instance;
MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
LocaleType base));
MOCK_METHOD1(freelocale, void(LocaleType locale));
struct locale_mock {
static locale_mock* instance;
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
locale_type base));
MOCK_METHOD1(freelocale, void(locale_type locale));
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, LocaleType locale));
} * LocaleMock::instance;
double(const char* nptr, char** endptr, locale_type locale));
} * locale_mock::instance;
# ifdef _MSC_VER
# pragma warning(push)
@@ -467,15 +474,15 @@ struct LocaleMock {
# endif
_locale_t _create_locale(int category, const char* locale) {
return LocaleMock::instance->newlocale(category, locale, 0);
return locale_mock::instance->newlocale(category, locale, 0);
}
void _free_locale(_locale_t locale) {
LocaleMock::instance->freelocale(locale);
locale_mock::instance->freelocale(locale);
}
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# ifdef __clang__
# pragma clang diagnostic pop
@@ -489,11 +496,6 @@ double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
# define FMT_LOCALE_THROW
# endif
LocaleType newlocale(int category_mask, const char* locale,
LocaleType base) FMT_LOCALE_THROW {
return LocaleMock::instance->newlocale(category_mask, locale, base);
}
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult;
@@ -501,49 +503,55 @@ typedef int FreeLocaleResult;
typedef void FreeLocaleResult;
# endif
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
LocaleMock::instance->freelocale(locale);
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
locale_mock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char* nptr, char** endptr,
LocaleType locale) FMT_LOCALE_THROW {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
locale_type locale) FMT_LOCALE_THROW {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# undef FMT_LOCALE_THROW
TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock;
LocaleType locale = reinterpret_cast<LocaleType>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale);
# ifndef _WIN32
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
return locale_mock::instance->newlocale(category_mask, locale, base);
}
TEST(LocaleTest, LocaleMock) {
ScopedMock<locale_mock> mock;
locale_type locale = reinterpret_cast<locale_type>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
FMT_SYSTEM(newlocale(222, "foo", locale));
}
# endif
TEST(LocaleTest, Locale) {
# ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
# endif
ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42);
ScopedMock<locale_mock> mock;
locale_type impl = reinterpret_cast<locale_type>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale;
EXPECT_EQ(impl, locale.get());
fmt::locale loc;
EXPECT_EQ(impl, loc.get());
}
TEST(LocaleTest, Strtod) {
ScopedMock<LocaleMock> mock;
ScopedMock<locale_mock> mock;
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
fmt::locale loc;
const char* str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, locale.strtod(str));
EXPECT_EQ(777, loc.strtod(str));
EXPECT_EQ(&end, str);
}
+8
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
+20 -10
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);
@@ -337,7 +338,9 @@ template <typename T> void TestLength(const char* length_spec) {
TestLength<T>(length_spec, -42);
TestLength<T>(length_spec, min);
TestLength<T>(length_spec, max);
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
long long long_long_min = std::numeric_limits<long long>::min();
if (static_cast<long long>(min) > long_long_min)
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
unsigned long long long_long_max = max_value<long long>();
if (static_cast<unsigned long long>(max) < long_long_max)
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
@@ -408,6 +411,7 @@ TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.65", "%G", 392.65);
EXPECT_PRINTF("392", "%g", 392.0);
EXPECT_PRINTF("392", "%G", 392.0);
EXPECT_PRINTF("4.56e-07", "%g", 0.000000456);
safe_sprintf(buffer, "%a", -392.65);
EXPECT_EQ(buffer, format("{:a}", -392.65));
safe_sprintf(buffer, "%A", -392.65);
@@ -470,11 +474,15 @@ TEST(PrintfTest, Location) {
// TODO: test %n
}
enum E { A = 42 };
enum test_enum { answer = 42 };
TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
TEST(PrintfTest, Enum) {
EXPECT_PRINTF("42", "%d", answer);
volatile test_enum volatile_enum = answer;
EXPECT_PRINTF("42", "%d", volatile_enum);
}
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
TEST(PrintfTest, Examples) {
const char* weekday = "Thursday";
const char* month = "August";
@@ -494,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) {
+11 -3
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
+10 -8
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,11 +198,12 @@ struct scan_handler : error_handler {
case scan_type::string_view_type: {
auto s = it;
while (it != end && *it != ' ') ++it;
*arg_.string_view = fmt::string_view(s, it - s);
*arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
scan_ctx_.advance_to(it);
break;
}
default:
case scan_type::none_type:
case scan_type::custom_type:
assert(false);
}
}
+4 -4
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*****");
@@ -61,11 +61,11 @@ TEST(StdFormatTest, Int) {
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:n}", 1234); // s3 == "1,234" (depends on the locale)
string s3 = format("{:n}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A");
EXPECT_EQ(s3, "1,234");
EXPECT_EQ(s3, "1234");
}
#include <format>
+5
View File
@@ -14,8 +14,13 @@
class assertion_failure : public std::logic_error {
public:
explicit assertion_failure(const char* message) : std::logic_error(message) {}
private:
virtual void avoid_weak_vtable();
};
void assertion_failure::avoid_weak_vtable() {}
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw assertion_failure(message);
+6
View File
@@ -33,11 +33,17 @@ std::string get_system_error(int error_code) {
const char* const FILE_CONTENT = "Don't panic!";
fmt::buffered_file open_buffered_file(FILE** fp) {
#if FMT_USE_FCNTL
fmt::file read_end, write_end;
fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close();
fmt::buffered_file f = read_end.fdopen("r");
if (fp) *fp = f.get();
#else
fmt::buffered_file f("test-file", "w");
fputs(FILE_CONTENT, f.get());
if (fp) *fp = f.get();
#endif
return f;
}
+1 -1
View File
@@ -9,7 +9,7 @@
#include <cstdio>
#include <string>
#include "fmt/posix.h"
#include "fmt/os.h"
enum { BUFFER_SIZE = 256 };