Compare commits

..

89 Commits
5.1.0 ... 5.2.0

Author SHA1 Message Date
Victor Zverovich
295a0d84d9 Update version 2018-09-13 07:28:20 -07:00
Victor Zverovich
d62f4c3bc1 Formatting 2018-09-12 19:00:26 -07:00
Victor Zverovich
a243490ad7 Add more methods to benchmark results 2018-09-12 18:28:26 -07:00
Victor Zverovich
9e12ca6069 Update changelog 2018-09-12 18:07:29 -07:00
Victor Zverovich
fbca830dd1 Update changelog, readme and improve compat 2018-09-12 12:44:04 -07:00
Victor Zverovich
6146248cf4 Update changelog 2018-09-12 10:42:50 -07:00
Victor Zverovich
bc26fbf1b0 Move experimental color API to fmt/color.h 2018-09-12 09:04:26 -07:00
Victor Zverovich
97cc889374 Workaround a visit lookup issue in gcc 8 (#851) 2018-09-12 08:45:14 -07:00
Victor Zverovich
7110b46076 Optimize default formatting 2018-09-12 08:45:14 -07:00
Victor Zverovich
c8a8464f7d Optimize buffer construction 2018-09-12 08:45:14 -07:00
medithe
8cbfb6e727 Get rid of conversion warning in gcc-4.8 (#854)
Get rid of conversion warning in gcc-4.8 

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

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

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

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

This reverts commit f9e9bf0231.
2018-07-08 08:08:50 -07:00
Victor Zverovich
07b690a679 Update README.rst 2018-07-07 18:18:56 -07:00
Victor Zverovich
f9e9bf0231 Optimize format string parsing 2018-07-07 16:50:22 -07:00
26 changed files with 1667 additions and 955 deletions

View File

@@ -1,12 +1,18 @@
message(STATUS "CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 3.1.0)
# Use newer policies if available, up to most recent tested version of CMake.
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
message(STATUS "CMake version: ${CMAKE_VERSION}")
endif ()
# Joins arguments and places the results in ${result_var}.
@@ -67,7 +73,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
-Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Wmissing-declarations -Woverloaded-virtual
-Winvalid-pch -Woverloaded-virtual
-Wno-ctor-dtor-privacy -Wno-dangling-else -Wno-float-equal
-Wno-format-nonliteral -Wno-sign-conversion -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
@@ -86,28 +92,24 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(WERROR_FLAG -Werror)
endif ()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Weverything -Wpedantic
-Wno-weak-vtables -Wno-padded -Wno-gnu-statement-expression
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
-Wno-global-constructors -Wno-disabled-macro-expansion
-Wno-switch-enum -Wno-documentation-unknown-command
-Wno-gnu-string-literal-operator-template -Wno-unused-member-function
-Wno-unused-member-function
-Wno-format-nonliteral -Wno-missing-noreturn -Wno-undefined-func-template
-Wno-shadow -Wno-sign-conversion -Wno-used-but-marked-unused
-Wno-covered-switch-default -Wno-missing-variable-declarations
-Wno-double-promotion)
-Wno-covered-switch-default -Wno-missing-prototypes
-Wno-missing-variable-declarations -Wno-double-promotion)
set(WERROR_FLAG -Werror)
check_cxx_compiler_flag(-Wno-zero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-zero-as-null-pointer-constant)
endif ()
check_cxx_compiler_flag(-Wno-gnu-string-literal-operator-template HAS_GNU_UDL_WARNING)
if (HAS_GNU_UDL_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-gnu-string-literal-operator-template)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
-Wno-zero-as-null-pointer-constant)
endif ()
endif ()
@@ -148,8 +150,8 @@ function(add_headers VAR)
endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS core.h format.h format-inl.h ostream.h printf.h time.h
ranges.h)
add_headers(FMT_HEADERS color.h core.h format.h format-inl.h ostream.h printf.h
time.h ranges.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)

View File

@@ -1,3 +1,112 @@
5.2.0 - 2018-09-13
------------------
* Optimized format string parsing and argument processing which resulted in up
to 5x speed up on long format strings and significant performance boost on
various benchmarks. For example, version 5.2 is 2.22x faster than 5.1 on
decimal integer formatting with ``format_to`` (macOS, clang-902.0.39.2):
================== ======= =======
Method Time, s Speedup
================== ======= =======
fmt::format 5.1 0.58
fmt::format 5.2 0.35 1.66x
fmt::format_to 5.1 0.51
fmt::format_to 5.2 0.23 2.22x
sprintf 0.71
std::to_string 1.01
std::stringstream 1.73
================== ======= =======
* Changed the ``fmt`` macro from opt-out to opt-in to prevent name collisions.
To enable it define the ``FMT_STRING_ALIAS`` macro to 1 before including
``fmt/format.h``:
.. code:: c++
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string answer = format(fmt("{}"), 42);
* Added compile-time format string checks to ``format_to`` overload that takes
``fmt::memory_buffer`` (`#783 <https://github.com/fmtlib/fmt/issues/783>`_):
.. code:: c++
fmt::memory_buffer buf;
// Compile-time error: invalid type specifier.
fmt::format_to(buf, fmt("{:d}"), "foo");
* Moved experimental color support to ``fmt/color.h`` and enabled the
new API by default. The old API can be enabled by defining the
``FMT_DEPRECATED_COLORS`` macro.
* Added formatting support for types explicitly convertible to
``fmt::string_view``:
.. code:: c++
struct foo {
explicit operator fmt::string_view() const { return "foo"; }
};
auto s = format("{}", foo());
In particular, this makes formatting function work with
``folly::StringPiece``.
* Implemented preliminary support for ``char*_t`` by replacing the ``format``
function overloads with a single function template parameterized on the string
type.
* Added support for dynamic argument lists
(`#814 <https://github.com/fmtlib/fmt/issues/814>`_,
`#819 <https://github.com/fmtlib/fmt/pull/819>`_).
Thanks `@MikePopoloski (Michael Popoloski)
<https://github.com/MikePopoloski>`_.
* Reduced executable size overhead for embedded targets using newlib nano by
making locale dependency optional
(`#839 <https://github.com/fmtlib/fmt/pull/839>`_).
Thanks `@teajay-fr (Thomas Benard) <https://github.com/teajay-fr>`_.
* Keep ``noexcept`` specifier when exceptions are disabled
(`#801 <https://github.com/fmtlib/fmt/issues/801>`_,
`#810 <https://github.com/fmtlib/fmt/pull/810>`_).
Thanks `@qis (Alexej Harm) <https://github.com/qis>`_.
* Fixed formatting of user-defined types providing ``operator<<`` with
``format_to_n``
(`#806 <https://github.com/fmtlib/fmt/pull/806>`_).
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
* Fixed dynamic linkage of new symbols
(`#808 <https://github.com/fmtlib/fmt/issues/808>`_).
* Fixed global initialization issue
(`#807 <https://github.com/fmtlib/fmt/issues/807>`_):
.. code:: c++
// This works on compilers with constexpr support.
static const std::string answer = fmt::format("{}", 42);
* Fixed various compiler warnings and errors
(`#804 <https://github.com/fmtlib/fmt/pull/804>`_,
`#809 <https://github.com/fmtlib/fmt/issues/809>`_,
`#811 <https://github.com/fmtlib/fmt/pull/811>`_,
`#822 <https://github.com/fmtlib/fmt/issues/822>`_,
`#827 <https://github.com/fmtlib/fmt/pull/827>`_,
`#830 <https://github.com/fmtlib/fmt/issues/830>`_,
`#838 <https://github.com/fmtlib/fmt/pull/838>`_,
`#843 <https://github.com/fmtlib/fmt/issues/843>`_,
`#844 <https://github.com/fmtlib/fmt/pull/844>`_,
`#851 <https://github.com/fmtlib/fmt/issues/851>`_,
`#852 <https://github.com/fmtlib/fmt/pull/852>`_,
`#854 <https://github.com/fmtlib/fmt/pull/854>`_).
Thanks `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_,
`@medithe <https://github.com/medithe>`_, and
`@eliasdaler (Elias Daler) <https://github.com/eliasdaler>`_.
5.1.0 - 2018-07-05
------------------
@@ -16,7 +125,7 @@
``color`` enum) is now deprecated.
(`#762 <https://github.com/fmtlib/fmt/issues/762>`_
`#767 <https://github.com/fmtlib/fmt/pull/767>`_).
Thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
thanks `@remotion (remo) <https://github.com/remotion>`_.
* Added quotes to strings in ranges and tuples
(`#766 <https://github.com/fmtlib/fmt/pull/766>`_).

View File

@@ -80,6 +80,7 @@ Format strings can be checked at compile time:
.. code:: c++
// test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string s = format(fmt("{2}"), 42);
@@ -87,7 +88,7 @@ Format strings can be checked at compile time:
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:3:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
@@ -272,7 +273,7 @@ Boost Format library
~~~~~~~~~~~~~~~~~~~~
This is a very powerful library which supports both printf-like format
strings and positional arguments. The main its drawback is performance.
strings and positional arguments. Its main drawback is performance.
According to various benchmarks it is much slower than other methods
considered here. Boost Format also has excessive build times and severe
code bloat issues (see `Benchmarks`_).
@@ -369,18 +370,18 @@ macOS Sierra, best of three) is shown in the following tables.
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.7 29 26
printf+string 18.4 29 26
IOStreams 34.6 59 55
fmt 22.0 37 34
tinyformat 51.8 103 97
Boost Format 120.5 762 739
Folly Format 158.7 102 87
printf 2.6 29 26
printf+string 16.4 29 26
IOStreams 31.1 59 55
fmt 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
As you can see, fmt has 60% less overhead in terms of resulting binary code
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
has by far the largest overheads.
and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
@@ -390,13 +391,13 @@ include to measure the overhead of the latter.
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.4 33 30
printf+string 18.5 33 30
IOStreams 31.9 56 52
fmt 20.9 56 51
tinyformat 38.9 88 82
Boost Format 64.8 366 304
Folly Format 113.5 442 428
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
@@ -429,6 +430,29 @@ or the bloat test::
$ make bloat-test
FAQ
---
Q: how can I capture formatting arguments and format them later?
A: use ``std::tuple``:
.. code:: c++
template <typename... Args>
auto capture(const Args&... args) {
return std::make_tuple(args...);
}
auto print_message = [](const auto&... args) {
fmt::print(args...);
};
// Capture and store arguments:
auto args = capture("{} {}", 42, "foo");
// Do formatting:
std::apply(print_message, args);
License
-------

View File

@@ -38,8 +38,8 @@ arguments in the resulting string.
.. _format:
.. doxygenfunction:: format(string_view, const Args&...)
.. doxygenfunction:: vformat(string_view, format_args)
.. doxygenfunction:: format(const String&, const Args&...)
.. doxygenfunction:: vformat(const String&, basic_format_args<typename buffer_context<Char>::type>)
.. _print:
@@ -130,6 +130,25 @@ always be formatted in the same way. See ``formatter<tm>::parse`` in
:file:`fmt/time.h` for an advanced example of how to parse the format string and
customize the formatted output.
You can also reuse existing formatters, for example::
enum color {red, green, blue};
template <>
struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext &ctx) {
string_view name = "unknown";
switch (c) {
case red: name = "red"; break;
case green: name = "green"; break;
case blue: name = "blue"; break;
}
return formatter<string_view>::format(name, ctx);
}
};
This section shows how to define a custom format function for a user-defined
type. The next section describes how to get ``fmt`` to use a conventional stream
output ``operator<<`` when one is defined for a user-defined type.
@@ -138,7 +157,7 @@ Output iterator support
-----------------------
.. doxygenfunction:: fmt::format_to(OutputIt, string_view, const Args&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, string_view, const Args&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...)
.. doxygenstruct:: fmt::format_to_n_result
:members:
@@ -234,11 +253,10 @@ custom argument formatter class::
using arg_formatter::operator();
void operator()(int value) {
auto operator()(int value) {
if (spec().type() == 'x')
(*this)(static_cast<unsigned>(value)); // convert to unsigned and format
else
arg_formatter::operator()(value);
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value);
}
};

View File

@@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0']
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."

View File

@@ -76,7 +76,7 @@ The general form of a *standard format specifier* is:
.. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
fill: <a character other than '{' or '}'>
fill: <a character other than '{', '}' or '\0'>
align: "<" | ">" | "=" | "^"
sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}"
@@ -84,11 +84,11 @@ The general form of a *standard format specifier* is:
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
The *fill* character can be any character other than '{' or '}'. The presence
of a fill character is signaled by the character following it, which must be
one of the alignment options. If the second character of *format_spec* is not
a valid alignment option, then it is assumed that both the fill character and
the alignment option are absent.
The *fill* character can be any character other than '{', '}' or '\\0'. The
presence of a fill character is signaled by the character following it, which
must be one of the alignment options. If the second character of *format_spec*
is not a valid alignment option, then it is assumed that both the fill character
and the alignment option are absent.
The meaning of the various alignment options is as follows:

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

@@ -0,0 +1,278 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
#ifdef FMT_DEPRECATED_COLORS
// color and (v)print_colored are deprecated.
enum color { black, red, green, yellow, blue, magenta, cyan, white };
FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
template <typename... Args>
inline void print_colored(color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args(args...));
}
template <typename... Args>
inline void print_colored(color c, wstring_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
}
inline void vprint_colored(color c, string_view format, format_args args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
wchar_t escape[] = L"\x1b[30m";
escape[3] = static_cast<wchar_t>('0' + c);
std::fputws(escape, stdout);
vprint(format, args);
std::fputws(internal::data::WRESET_COLOR, stdout);
}
#else
// Experimental color support.
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32, // rgb(154,205,50)
}; // enum class color
// rgb is a struct for red, green and blue colors.
// We use rgb as name because some editors will show it as color direct in the
// editor.
struct rgb {
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
: r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
FMT_CONSTEXPR_DECL rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
void vprint_rgb(rgb fd, string_view format, format_args args);
void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args);
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify foreground color 'fd'.
Example:
fmt::print(fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... Args>
inline void print(rgb fd, string_view format_str, const Args & ... args) {
vprint_rgb(fd, format_str, make_format_args(args...));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify foreground color 'fd' and background color 'bg'.
Example:
fmt::print(fmt::color::red, fmt::color::black,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... Args>
inline void print(rgb fd, rgb bg, string_view format_str,
const Args & ... args) {
vprint_rgb(fd, bg, format_str, make_format_args(args...));
}
namespace internal {
FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset) {
out[offset + 0] = static_cast<char>('0' + c / 100);
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
out[offset + 2] = static_cast<char>('0' + c % 10);
}
} // namespace internal
inline void vprint_rgb(rgb fd, string_view format, format_args args) {
char escape_fd[] = "\x1b[38;2;000;000;000m";
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
std::fputs(escape_fd, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
inline void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) {
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
internal::to_esc(bg.r, escape_bg, 7);
internal::to_esc(bg.g, escape_bg, 11);
internal::to_esc(bg.b, escape_bg, 15);
std::fputs(escape_fd, stdout);
std::fputs(escape_bg, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
#endif
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View File

@@ -9,14 +9,14 @@
#define FMT_CORE_H_
#include <cassert>
#include <cstdio>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
#include <string>
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 50100
#define FMT_VERSION 50200
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@@ -24,7 +24,8 @@
# define FMT_HAS_FEATURE(x) 0
#endif
#ifdef __has_include
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
(!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1600)
# define FMT_HAS_INCLUDE(x) __has_include(x)
#else
# define FMT_HAS_INCLUDE(x) 0
@@ -54,7 +55,7 @@
# define FMT_MSC_VER 0
#endif
// Check if relaxed c++14 constexpr is supported.
// Check if relaxed C++14 constexpr is supported.
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
@@ -69,18 +70,26 @@
# define FMT_CONSTEXPR_DECL
#endif
#ifndef FMT_USE_CONSTEXPR11
# define FMT_USE_CONSTEXPR11 \
(FMT_MSC_VER >= 1900 || FMT_GCC_VERSION >= 406 || FMT_USE_CONSTEXPR)
#endif
#if FMT_USE_CONSTEXPR11
# define FMT_CONSTEXPR11 constexpr
#else
# define FMT_CONSTEXPR11
#endif
#ifndef FMT_OVERRIDE
# if FMT_HAS_FEATURE(cxx_override) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1900
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
# define FMT_OVERRIDE override
# else
# define FMT_OVERRIDE
# endif
#endif
#if FMT_HAS_FEATURE(cxx_explicit_conversions) || \
FMT_MSC_VER >= 1800
#if FMT_HAS_FEATURE(cxx_explicit_conversions) || FMT_MSC_VER >= 1800
# define FMT_EXPLICIT explicit
#else
# define FMT_EXPLICIT
@@ -88,8 +97,7 @@
#ifndef FMT_NULL
# if FMT_HAS_FEATURE(cxx_nullptr) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1600
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600
# define FMT_NULL nullptr
# define FMT_USE_NULLPTR 1
# else
@@ -123,15 +131,16 @@
#endif
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1900
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
# define FMT_DETECTED_NOEXCEPT noexcept
# define FMT_HAS_CXX11_NOEXCEPT 1
#else
# define FMT_DETECTED_NOEXCEPT throw()
# define FMT_HAS_CXX11_NOEXCEPT 0
#endif
#ifndef FMT_NOEXCEPT
# if FMT_EXCEPTIONS
# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
# else
# define FMT_NOEXCEPT
@@ -173,13 +182,6 @@
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
#define FMT_DELETED = delete
// A macro to disallow the copy construction and assignment.
#define FMT_DISALLOW_COPY_AND_ASSIGN(Type) \
Type(const Type &) FMT_DELETED; \
void operator=(const Type &) FMT_DELETED
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@@ -198,13 +200,22 @@
#endif
FMT_BEGIN_NAMESPACE
namespace internal {
// An implementation of declval for pre-C++11 compilers such as gcc 4.
template <typename T>
typename std::add_rvalue_reference<T>::type declval() FMT_NOEXCEPT;
template <typename>
struct result_of;
template <typename F, typename... Args>
struct result_of<F(Args...)> {
// A workaround for gcc 4.4 that doesn't allow F to be a reference.
typedef typename std::result_of<
typename std::remove_reference<F>::type(Args...)>::type type;
};
// Casts nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
@@ -212,7 +223,17 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
// A constexpr std::char_traits::length replacement for pre-C++17.
template <typename Char>
FMT_CONSTEXPR size_t length(const Char *s) {
const Char *start = s;
while (*s) ++s;
return s - start;
}
#if FMT_GCC_VERSION
FMT_CONSTEXPR size_t length(const char *s) { return std::strlen(s); }
#endif
} // namespace internal
/**
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
@@ -255,20 +276,20 @@ class basic_string_view {
the size with ``std::char_traits<Char>::length``.
\endrst
*/
basic_string_view(const Char *s)
: data_(s), size_(std::char_traits<Char>::length(s)) {}
FMT_CONSTEXPR basic_string_view(const Char *s)
: data_(s), size_(internal::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Alloc>
FMT_CONSTEXPR basic_string_view(
const std::basic_string<Char, Alloc> &s) FMT_NOEXCEPT
: data_(s.c_str()), size_(s.size()) {}
: data_(s.data()), size_(s.size()) {}
FMT_CONSTEXPR basic_string_view(type s) FMT_NOEXCEPT
: data_(s.data()), size_(s.size()) {}
/** Returns a pointer to the string data. */
const Char *data() const { return data_; }
FMT_CONSTEXPR const Char *data() const { return data_; }
/** Returns the string size. */
FMT_CONSTEXPR size_t size() const { return size_; }
@@ -323,19 +344,30 @@ class basic_format_args;
template <typename T, typename Char = char, typename Enable = void>
struct formatter;
template <typename T, typename Char, typename Enable = void>
struct convert_to_int {
enum {
value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
};
};
namespace internal {
/** A contiguous memory buffer with an optional growing ability. */
template <typename T>
class basic_buffer {
private:
FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer);
basic_buffer(const basic_buffer &) = delete;
void operator=(const basic_buffer &) = delete;
T *ptr_;
std::size_t size_;
std::size_t capacity_;
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
basic_buffer(std::size_t sz) FMT_NOEXCEPT: size_(sz), capacity_(sz) {}
basic_buffer(T *p = FMT_NULL, std::size_t sz = 0, std::size_t cap = 0)
FMT_NOEXCEPT: ptr_(p), size_(sz), capacity_(cap) {}
@@ -377,6 +409,9 @@ class basic_buffer {
size_ = new_size;
}
/** Clears this buffer. */
void clear() { size_ = 0; }
/** Reserves space to store at least *capacity* elements. */
void reserve(std::size_t new_capacity) {
if (new_capacity > capacity_)
@@ -413,8 +448,7 @@ class container_buffer : public basic_buffer<typename Container::value_type> {
public:
explicit container_buffer(Container &c)
: basic_buffer<typename Container::value_type>(&c[0], c.size(), c.size()),
container_(c) {}
: basic_buffer<typename Container::value_type>(c.size()), container_(c) {}
};
struct error_handler {
@@ -468,13 +502,6 @@ FMT_CONSTEXPR bool is_arithmetic(type t) {
return t > internal::none_type && t <= internal::last_numeric_type;
}
template <typename T, typename Char, bool ENABLE = true>
struct convert_to_int {
enum {
value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
};
};
template <typename Char>
struct string_value {
const Char *value;
@@ -554,12 +581,14 @@ class value {
}
};
template <typename Context, type TYPE>
struct typed_value : value<Context> {
// Value initializer used to delay conversion to value and reduce memory churn.
template <typename Context, typename T, type TYPE>
struct init {
T val;
static const type type_tag = TYPE;
template <typename T>
FMT_CONSTEXPR typed_value(const T &val) : value<Context>(val) {}
FMT_CONSTEXPR init(const T &v) : val(v) {}
FMT_CONSTEXPR operator value<Context>() const { return value<Context>(val); }
};
template <typename Context, typename T>
@@ -567,15 +596,13 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T &value);
#define FMT_MAKE_VALUE(TAG, ArgType, ValueType) \
template <typename C> \
FMT_CONSTEXPR typed_value<C, TAG> make_value(ArgType val) { \
FMT_CONSTEXPR init<C, ValueType, TAG> make_value(ArgType val) { \
return static_cast<ValueType>(val); \
}
#define FMT_MAKE_VALUE_SAME(TAG, Type) \
template <typename C> \
FMT_CONSTEXPR typed_value<C, TAG> make_value(Type val) { \
return val; \
}
FMT_CONSTEXPR init<C, Type, TAG> make_value(Type val) { return val; }
FMT_MAKE_VALUE(bool_type, bool, int)
FMT_MAKE_VALUE(int_type, short, int)
@@ -603,7 +630,7 @@ FMT_MAKE_VALUE(char_type, char, int)
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
template <typename C>
inline typed_value<C, char_type> make_value(wchar_t val) {
inline init<C, int, char_type> make_value(wchar_t val) {
require_wchar<typename C::char_type>();
return static_cast<int>(val);
}
@@ -650,20 +677,27 @@ typename std::enable_if<!std::is_same<T, typename C::char_type>::value>::type
template <typename C, typename T>
inline typename std::enable_if<
std::is_enum<T>::value && convert_to_int<T, typename C::char_type>::value,
typed_value<C, int_type>>::type
init<C, int, int_type>>::type
make_value(const T &val) { return static_cast<int>(val); }
template <typename C, typename T, typename Char = typename C::char_type>
inline typename std::enable_if<
std::is_constructible<basic_string_view<Char>, T>::value,
init<C, basic_string_view<Char>, string_type>>::type
make_value(const T &val) { return basic_string_view<Char>(val); }
template <typename C, typename T, typename Char = typename C::char_type>
inline typename std::enable_if<
!convert_to_int<T, Char>::value &&
!std::is_convertible<T, basic_string_view<Char>>::value,
!std::is_convertible<T, basic_string_view<Char>>::value &&
!std::is_constructible<basic_string_view<Char>, T>::value,
// Implicit conversion to std::string is not handled here because it's
// unsafe: https://github.com/fmtlib/fmt/issues/729
typed_value<C, custom_type>>::type
init<C, const T &, custom_type>>::type
make_value(const T &val) { return val; }
template <typename C, typename T>
typed_value<C, named_arg_type>
init<C, const void*, named_arg_type>
make_value(const named_arg<T, typename C::char_type> &val) {
basic_format_arg<C> arg = make_arg<C>(val.value);
std::memcpy(val.data, &arg, sizeof(arg));
@@ -675,17 +709,7 @@ enum { max_packed_args = 15 };
template <typename Context>
class arg_map;
template <typename>
struct result_of;
template <typename F, typename... Args>
struct result_of<F(Args...)> {
// A workaround for gcc 4.4 that doesn't allow F to be a reference.
typedef typename std::result_of<
typename std::remove_reference<F>::type(Args...)>::type type;
};
}
} // namespace internal
// A formatting argument. It is a trivially copyable/constructible type to
// allow storage in basic_memory_buffer.
@@ -701,7 +725,7 @@ class basic_format_arg {
template <typename Visitor, typename Ctx>
friend FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
visit(Visitor &&vis, basic_format_arg<Ctx> arg);
visit(Visitor &&vis, const basic_format_arg<Ctx> &arg);
friend class basic_format_args<Context>;
friend class internal::arg_map<Context>;
@@ -789,7 +813,8 @@ namespace internal {
template <typename Context>
class arg_map {
private:
FMT_DISALLOW_COPY_AND_ASSIGN(arg_map);
arg_map(const arg_map &) = delete;
void operator=(const arg_map &) = delete;
typedef typename Context::char_type char_type;
@@ -904,7 +929,8 @@ class basic_format_context :
private:
internal::arg_map<basic_format_context> map_;
FMT_DISALLOW_COPY_AND_ASSIGN(basic_format_context);
basic_format_context(const basic_format_context &) = delete;
void operator=(const basic_format_context &) = delete;
typedef internal::context_base<OutputIt, basic_format_context, Char> base;
typedef typename base::format_arg format_arg;
@@ -948,10 +974,10 @@ struct get_type {
};
template <typename Context>
FMT_CONSTEXPR unsigned long long get_types() { return 0; }
FMT_CONSTEXPR11 unsigned long long get_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
FMT_CONSTEXPR unsigned long long get_types() {
FMT_CONSTEXPR11 unsigned long long get_types() {
return get_type<Context, Arg>::value | (get_types<Context, Args...>() << 4);
}
@@ -974,7 +1000,7 @@ inline typename std::enable_if<!IS_PACKED, basic_format_arg<Context>>::type
make_arg(const T &value) {
return make_arg<Context>(value);
}
}
} // namespace internal
/**
\rst
@@ -1001,15 +1027,15 @@ class format_arg_store {
friend class basic_format_args<Context>;
static FMT_CONSTEXPR long long get_types() {
static FMT_CONSTEXPR11 long long get_types() {
return IS_PACKED ?
static_cast<long long>(internal::get_types<Context, Args...>()) :
-static_cast<long long>(NUM_ARGS);
}
public:
#if FMT_USE_CONSTEXPR
static constexpr long long TYPES = get_types();
#if FMT_USE_CONSTEXPR11
static FMT_CONSTEXPR11 long long TYPES = get_types();
#else
static const long long TYPES;
#endif
@@ -1028,7 +1054,7 @@ class format_arg_store {
#endif
};
#if !FMT_USE_CONSTEXPR
#if !FMT_USE_CONSTEXPR11
template <typename Context, typename ...Args>
const long long format_arg_store<Context, Args...>::TYPES = get_types();
#endif
@@ -1036,8 +1062,8 @@ const long long format_arg_store<Context, Args...>::TYPES = get_types();
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context` can
be omitted in which case it defaults to `~fmt::context`.
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::context`.
\endrst
*/
template <typename Context, typename ...Args>
@@ -1086,12 +1112,15 @@ class basic_format_args {
void set_data(const format_arg *args) { args_ = args; }
format_arg do_get(size_type index) const {
format_arg arg;
long long signed_types = static_cast<long long>(types_);
if (signed_types < 0) {
unsigned long long num_args = static_cast<unsigned long long>(-signed_types);
return index < num_args ? args_[index] : format_arg();
unsigned long long num_args =
static_cast<unsigned long long>(-signed_types);
if (index < num_args)
arg = args_[index];
return arg;
}
format_arg arg;
if (index > internal::max_packed_args)
return arg;
arg.type_ = type(index);
@@ -1116,11 +1145,22 @@ class basic_format_args {
set_data(store.data_);
}
/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
\endrst
*/
basic_format_args(const format_arg *args, size_type count)
: types_(-static_cast<int64_t>(count)) {
set_data(args);
}
/** Returns the argument at specified index. */
format_arg get(size_type index) const {
format_arg arg = do_get(index);
return arg.type_ == internal::named_arg_type ?
arg.value_.as_named_arg().template deserialize<Context>() : arg;
if (arg.type_ == internal::named_arg_type)
arg = arg.value_.as_named_arg().template deserialize<Context>();
return arg;
}
unsigned max_size() const {
@@ -1193,24 +1233,69 @@ inline internal::named_arg<T, wchar_t> arg(wstring_view name, const T &arg) {
// This function template is deleted intentionally to disable nested named
// arguments as in ``format("{}", arg("a", arg("b", 42)))``.
template <typename S, typename T, typename Char>
void arg(S, internal::named_arg<T, Char>) FMT_DELETED;
void arg(S, internal::named_arg<T, Char>) = delete;
#ifndef FMT_EXTENDED_COLORS
// color and (v)print_colored are deprecated.
enum color { black, red, green, yellow, blue, magenta, cyan, white };
FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
template <typename... Args>
inline void print_colored(color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args(args...));
}
template <typename... Args>
inline void print_colored(color c, wstring_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
}
#endif
// A base class for compile-time strings. It is defined in the fmt namespace to
// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
struct compile_string {};
namespace internal {
// If S is a format string type, format_string_traints<S>::char_type gives its
// character type.
template <typename S, typename Enable = void>
struct format_string_traits {
private:
// Use constructability as a way to detect if format_string_traits is
// specialized because other methods are broken on MSVC2013.
format_string_traits();
};
template <typename Char>
struct format_string_traits_base { typedef Char char_type; };
template <typename Char>
struct format_string_traits<Char *> : format_string_traits_base<Char> {};
template <typename Char>
struct format_string_traits<const Char *> : format_string_traits_base<Char> {};
template <typename Char, std::size_t N>
struct format_string_traits<Char[N]> : format_string_traits_base<Char> {};
template <typename Char, std::size_t N>
struct format_string_traits<const Char[N]> : format_string_traits_base<Char> {};
template <typename Char>
struct format_string_traits<std::basic_string<Char>> :
format_string_traits_base<Char> {};
template <typename S>
struct format_string_traits<
S, typename std::enable_if<std::is_base_of<
basic_string_view<typename S::char_type>, S>::value>::type> :
format_string_traits_base<typename S::char_type> {};
template <typename S>
struct is_format_string :
std::integral_constant<
bool, std::is_constructible<format_string_traits<S>>::value> {};
template <typename S>
struct is_compile_string :
std::integral_constant<bool, std::is_base_of<compile_string, S>::value> {};
template <typename... Args, typename S>
inline typename std::enable_if<!is_compile_string<S>::value>::type
check_format_string(const S &) {}
template <typename... Args, typename S>
typename std::enable_if<is_compile_string<S>::value>::type
check_format_string(S);
template <typename Char>
std::basic_string<Char> vformat(
basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args);
} // namespace internal
format_context::iterator vformat_to(
internal::buffer &buf, string_view format_str, format_args args);
@@ -1232,10 +1317,9 @@ typename std::enable_if<
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
vformat_to(std::back_insert_iterator<Container> out,
string_view format_str, format_args args) {
auto& container = internal::get_container(out);
internal::container_buffer<Container> buf(container);
internal::container_buffer<Container> buf(internal::get_container(out));
vformat_to(buf, format_str, args);
return std::back_inserter(container);
return out;
}
template <typename Container>
@@ -1243,14 +1327,38 @@ typename std::enable_if<
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
vformat_to(std::back_insert_iterator<Container> out,
wstring_view format_str, wformat_args args) {
auto& container = internal::get_container(out);
internal::container_buffer<Container> buf(container);
internal::container_buffer<Container> buf(internal::get_container(out));
vformat_to(buf, format_str, args);
return std::back_inserter(container);
return out;
}
std::string vformat(string_view format_str, format_args args);
std::wstring vformat(wstring_view format_str, wformat_args args);
template <typename Container, typename... Args>
inline typename std::enable_if<
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
format_to(std::back_insert_iterator<Container> out,
string_view format_str, const Args & ... args) {
format_arg_store<format_context, Args...> as{args...};
return vformat_to(out, format_str, as);
}
template <typename Container, typename... Args>
inline typename std::enable_if<
is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
format_to(std::back_insert_iterator<Container> out,
wstring_view format_str, const Args & ... args) {
return vformat_to(out, format_str,
make_format_args<wformat_context>(args...));
}
template <
typename String,
typename Char = typename internal::format_string_traits<String>::char_type>
inline std::basic_string<Char> vformat(
const String &format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
// Convert format string to string_view to reduce the number of overloads.
return internal::vformat(basic_string_view<Char>(format_str), args);
}
/**
\rst
@@ -1262,18 +1370,20 @@ std::wstring vformat(wstring_view format_str, wformat_args args);
std::string message = fmt::format("The answer is {}", 42);
\endrst
*/
template <typename... Args>
inline std::string format(string_view format_str, const Args & ... args) {
template <typename String, typename... Args>
inline std::basic_string<
typename internal::format_string_traits<String>::char_type>
format(const String &format_str, const Args & ... args) {
internal::check_format_string<Args...>(format_str);
// This should be just
// return vformat(format_str, make_format_args(args...));
// return vformat(format_str, make_format_args(args...));
// but gcc has trouble optimizing the latter, so break it down.
format_arg_store<format_context, Args...> as{args...};
return vformat(format_str, as);
}
template <typename... Args>
inline std::wstring format(wstring_view format_str, const Args & ... args) {
format_arg_store<wformat_context, Args...> as{args...};
return vformat(format_str, as);
typedef typename internal::format_string_traits<String>::char_type char_t;
typedef typename buffer_context<char_t>::type context_t;
format_arg_store<context_t, Args...> as{args...};
return internal::vformat(
basic_string_view<char_t>(format_str),
basic_format_args<context_t>(as));
}
FMT_API void vprint(std::FILE *f, string_view format_str, format_args args);
@@ -1294,11 +1404,12 @@ inline void print(std::FILE *f, string_view format_str, const Args & ... args) {
vprint(f, format_str, as);
}
/**
Prints formatted data to the file *f* which should be in wide-oriented mode set
via ``fwide(f, 1)`` or ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
Prints formatted data to the file *f* which should be in wide-oriented mode
set via ``fwide(f, 1)`` or ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
*/
template <typename... Args>
inline void print(std::FILE *f, wstring_view format_str, const Args & ... args) {
inline void print(std::FILE *f, wstring_view format_str,
const Args & ... args) {
format_arg_store<wformat_context, Args...> as(args...);
vprint(f, format_str, as);
}

View File

@@ -18,10 +18,9 @@
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#include <locale>
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#include <cstring> // for std::memmove
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
#endif
#if FMT_USE_WINDOWS_H
@@ -193,6 +192,7 @@ void report_error(FormatFunc func, int error_code,
}
} // namespace
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
class locale {
private:
std::locale locale_;
@@ -202,11 +202,27 @@ class locale {
std::locale get() { return locale_; }
};
FMT_FUNC size_t internal::count_code_points(u8string_view s) {
const char8_t *data = s.data();
int num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i].value & 0xc0) != 0x80)
++num_code_points;
}
return num_code_points;
}
template <typename Char>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
std::locale loc = lp ? lp->locale().get() : std::locale();
return std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
}
#else
template <typename Char>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
return FMT_STATIC_THOUSANDS_SEPARATOR;
}
#endif
FMT_FUNC void system_error::init(
int err_code, string_view format_str, format_args args) {
@@ -256,11 +272,16 @@ const char basic_data<T>::DIGITS[] =
template <typename T>
const uint32_t basic_data<T>::POWERS_OF_10_32[] = {
1, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t basic_data<T>::POWERS_OF_10_64[] = {
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(1000000000ull),
@@ -271,28 +292,35 @@ const uint64_t basic_data<T>::POWERS_OF_10_64[] = {
// These are generated by support/compute-powers.py.
template <typename T>
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea,
0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f,
0xbe5691ef416bd60c, 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637,
0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5,
0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8,
0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd,
0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3,
0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c,
0x9c40000000000000, 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245,
0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a,
0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3,
0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece,
0x88fcf317f22241e2, 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a,
0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429,
0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
};
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
@@ -312,6 +340,96 @@ const int16_t basic_data<T>::POW10_EXPONENTS[] = {
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
// A handmade floating-point number f * pow(2, e).
class fp {
private:
typedef uint64_t significand_type;
// All sizes are in bits.
static FMT_CONSTEXPR_DECL const int char_size =
std::numeric_limits<unsigned char>::digits;
// Subtract 1 to account for an implicit most significant bit in the
// normalized form.
static FMT_CONSTEXPR_DECL const int double_significand_size =
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ull << double_significand_size;
public:
significand_type f;
int e;
static FMT_CONSTEXPR_DECL const int significand_size =
sizeof(significand_type) * char_size;
fp(): f(0), e(0) {}
fp(uint64_t f, int e): f(f), e(e) {}
// Constructs fp from an IEEE754 double. It is a template to prevent compile
// errors on platforms where double is not IEEE754.
template <typename Double>
explicit fp(Double d) {
// Assume double is in the format [sign][exponent][significand].
typedef std::numeric_limits<Double> limits;
const int double_size = static_cast<int>(sizeof(Double) * char_size);
const int exponent_size =
double_size - double_significand_size - 1; // -1 for sign
const uint64_t significand_mask = implicit_bit - 1;
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d);
auto biased_e = (u & exponent_mask) >> double_significand_size;
f = u & significand_mask;
if (biased_e != 0)
f += implicit_bit;
else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
}
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0>
void normalize() {
// Handle subnormals.
auto shifted_implicit_bit = implicit_bit << SHIFT;
while ((f & shifted_implicit_bit) == 0) {
f <<= 1;
--e;
}
// Subtract 1 to account for hidden bit.
auto offset = significand_size - double_significand_size - SHIFT - 1;
f <<= offset;
e -= offset;
}
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
// a boundary is a value half way between the number and its predecessor
// (lower) or successor (upper). The upper boundary is normalized and lower
// has the same exponent but may be not normalized.
void compute_boundaries(fp &lower, fp &upper) const {
lower = f == implicit_bit ?
fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
upper = fp((f << 1) + 1, e - 1);
upper.normalize<1>(); // 1 is to account for the exponent shift above.
lower.f <<= lower.e - upper.e;
lower.e = upper.e;
}
};
// Returns an fp number representing x - y. Result may not be normalized.
inline fp operator-(fp x, fp y) {
FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
return fp(x.f - y.f, x.e);
}
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized.
FMT_API fp operator*(fp x, fp y);
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
FMT_API fp get_cached_power(int min_exponent, int &pow10_exponent);
FMT_FUNC fp operator*(fp x, fp y) {
// Multiply 32-bit parts of significands.
uint64_t mask = (1ULL << 32) - 1;
@@ -329,12 +447,248 @@ FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
// Difference between two consecutive decimal exponents in cached powers of 10.
// Difference between 2 consecutive decimal exponents in cached powers of 10.
const int dec_exp_step = 8;
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
pow10_exponent = first_dec_exp + index * dec_exp_step;
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
}
// Generates output using Grisu2 digit-gen algorithm.
FMT_FUNC void grisu2_gen_digits(
const fp &scaled_value, const fp &scaled_upper, uint64_t delta,
char *buffer, size_t &size, int &dec_exp) {
internal::fp one(1ull << -scaled_upper.e, scaled_upper.e);
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
// hi = floor(scaled_upper / one).
uint32_t hi = static_cast<uint32_t>(scaled_upper.f >> -one.e);
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
// lo = scaled_upper mod 1.
uint64_t lo = scaled_upper.f & (one.f - 1);
size = 0;
auto exp = count_digits(hi); // kappa in Grisu.
while (exp > 0) {
uint32_t digit = 0;
// This optimization by miloyip reduces the number of integer divisions by
// one per iteration.
switch (exp) {
case 10: digit = hi / 1000000000; hi %= 1000000000; break;
case 9: digit = hi / 100000000; hi %= 100000000; break;
case 8: digit = hi / 10000000; hi %= 10000000; break;
case 7: digit = hi / 1000000; hi %= 1000000; break;
case 6: digit = hi / 100000; hi %= 100000; break;
case 5: digit = hi / 10000; hi %= 10000; break;
case 4: digit = hi / 1000; hi %= 1000; break;
case 3: digit = hi / 100; hi %= 100; break;
case 2: digit = hi / 10; hi %= 10; break;
case 1: digit = hi; hi = 0; break;
default:
FMT_ASSERT(false, "invalid number of digits");
}
if (digit != 0 || size != 0)
buffer[size++] = static_cast<char>('0' + digit);
--exp;
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
if (remainder <= delta) {
dec_exp += exp;
// TODO: use scaled_value
(void)scaled_value;
return;
}
}
for (;;) {
lo *= 10;
delta *= 10;
char digit = static_cast<char>(lo >> -one.e);
if (digit != 0 || size != 0)
buffer[size++] = static_cast<char>('0' + digit);
lo &= one.f - 1;
--exp;
if (lo < delta) {
dec_exp += exp;
return;
}
}
}
FMT_FUNC void grisu2_format_positive(double value, char *buffer, size_t &size,
int &dec_exp) {
FMT_ASSERT(value > 0, "value is nonpositive");
fp fp_value(value);
fp lower, upper; // w^- and w^+ in the Grisu paper.
fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 close to 1 / upper.
const int min_exp = -60; // alpha in Grisu.
auto dec_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
min_exp - (upper.e + fp::significand_size), dec_exp);
dec_exp = -dec_exp;
fp_value.normalize();
fp scaled_value = fp_value * dec_pow;
fp scaled_lower = lower * dec_pow; // \tilde{M}^- in Grisu.
fp scaled_upper = upper * dec_pow; // \tilde{M}^+ in Grisu.
++scaled_lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
--scaled_upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
uint64_t delta = scaled_upper.f - scaled_lower.f;
grisu2_gen_digits(scaled_value, scaled_upper, delta, buffer, size, dec_exp);
}
FMT_FUNC void round(char *buffer, size_t &size, int &exp,
int digits_to_remove) {
size -= to_unsigned(digits_to_remove);
exp += digits_to_remove;
int digit = buffer[size] - '0';
// TODO: proper rounding and carry
if (digit > 5 || (digit == 5 && (digits_to_remove > 1 ||
(buffer[size - 1] - '0') % 2) != 0)) {
++buffer[size - 1];
}
}
// Writes the exponent exp in the form "[+-]d{1,3}" to buffer.
FMT_FUNC char *write_exponent(char *buffer, int exp) {
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
if (exp < 0) {
*buffer++ = '-';
exp = -exp;
} else {
*buffer++ = '+';
}
if (exp >= 100) {
*buffer++ = static_cast<char>('0' + exp / 100);
exp %= 100;
const char *d = data::DIGITS + exp * 2;
*buffer++ = d[0];
*buffer++ = d[1];
} else {
const char *d = data::DIGITS + exp * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
return buffer;
}
FMT_FUNC void format_exp_notation(
char *buffer, size_t &size, int exp, int precision, bool upper) {
// Insert a decimal point after the first digit and add an exponent.
std::memmove(buffer + 2, buffer + 1, size - 1);
buffer[1] = '.';
exp += static_cast<int>(size) - 1;
int num_digits = precision - static_cast<int>(size) + 1;
if (num_digits > 0) {
std::uninitialized_fill_n(buffer + size + 1, num_digits, '0');
size += to_unsigned(num_digits);
} else if (num_digits < 0) {
round(buffer, size, exp, -num_digits);
}
char *p = buffer + size + 1;
*p++ = upper ? 'E' : 'e';
size = to_unsigned(write_exponent(p, exp) - buffer);
}
// Prettifies the output of the Grisu2 algorithm.
// The number is given as v = buffer * 10^exp.
FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
int precision, bool upper) {
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int int_size = static_cast<int>(size);
int full_exp = int_size + exp;
const int exp_threshold = 21;
if (int_size <= full_exp && full_exp <= exp_threshold) {
// 1234e7 -> 12340000000[.0+]
std::uninitialized_fill_n(buffer + int_size, full_exp - int_size, '0');
char *p = buffer + full_exp;
if (precision > 0) {
*p++ = '.';
std::uninitialized_fill_n(p, precision, '0');
p += precision;
}
size = to_unsigned(p - buffer);
} else if (0 < full_exp && full_exp <= exp_threshold) {
// 1234e-2 -> 12.34[0+]
int fractional_size = -exp;
std::memmove(buffer + full_exp + 1, buffer + full_exp,
to_unsigned(fractional_size));
buffer[full_exp] = '.';
int num_zeros = precision - fractional_size;
if (num_zeros > 0) {
std::uninitialized_fill_n(buffer + size + 1, num_zeros, '0');
size += to_unsigned(num_zeros);
}
++size;
} else if (-6 < full_exp && full_exp <= 0) {
// 1234e-6 -> 0.001234
int offset = 2 - full_exp;
std::memmove(buffer + offset, buffer, size);
buffer[0] = '0';
buffer[1] = '.';
std::uninitialized_fill_n(buffer + 2, -full_exp, '0');
size = to_unsigned(int_size + offset);
} else {
format_exp_notation(buffer, size, exp, precision, upper);
}
}
#if FMT_CLANG_VERSION
# define FMT_FALLTHROUGH [[clang::fallthrough]];
#elif FMT_GCC_VERSION >= 700
# define FMT_FALLTHROUGH [[gnu::fallthrough]];
#else
# define FMT_FALLTHROUGH
#endif
// Formats a nonnegative value using Grisu2 algorithm. Grisu2 doesn't give any
// guarantees on the shortness of the result.
FMT_FUNC void grisu2_format(double value, char *buffer, size_t &size, char type,
int precision, bool write_decimal_point) {
FMT_ASSERT(value >= 0, "value is negative");
int dec_exp = 0; // K in Grisu.
if (value > 0) {
grisu2_format_positive(value, buffer, size, dec_exp);
} else {
*buffer = '0';
size = 1;
}
const int default_precision = 6;
if (precision < 0)
precision = default_precision;
bool upper = false;
switch (type) {
case 'G':
upper = true;
FMT_FALLTHROUGH
case '\0': case 'g': {
int digits_to_remove = static_cast<int>(size) - precision;
if (digits_to_remove > 0) {
round(buffer, size, dec_exp, digits_to_remove);
// Remove trailing zeros.
while (size > 0 && buffer[size - 1] == '0') {
--size;
++dec_exp;
}
}
precision = 0;
break;
}
case 'F':
upper = true;
FMT_FALLTHROUGH
case 'f': {
int digits_to_remove = -dec_exp - precision;
if (digits_to_remove > 0) {
if (digits_to_remove >= static_cast<int>(size))
digits_to_remove = static_cast<int>(size) - 1;
round(buffer, size, dec_exp, digits_to_remove);
}
break;
}
case 'e': case 'E':
format_exp_notation(buffer, size, dec_exp, precision, type == 'E');
return;
}
if (write_decimal_point && precision < 1)
precision = 1;
grisu2_prettify(buffer, size, dec_exp, precision, upper);
}
} // namespace internal
#if FMT_USE_WINDOWS_H
@@ -499,61 +853,9 @@ FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
vprint(stdout, format_str, args);
}
#ifndef FMT_EXTENDED_COLORS
FMT_FUNC void vprint_colored(color c, string_view format, format_args args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
FMT_FUNC void vprint_colored(color c, wstring_view format, wformat_args args) {
wchar_t escape[] = L"\x1b[30m";
escape[3] = static_cast<wchar_t>('0' + c);
std::fputws(escape, stdout);
vprint(format, args);
std::fputws(internal::data::WRESET_COLOR, stdout);
}
#else
namespace internal {
FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset) {
out[offset + 0] = static_cast<char>('0' + c / 100);
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
out[offset + 2] = static_cast<char>('0' + c % 10);
}
} // namespace internal
FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args) {
char escape_fd[] = "\x1b[38;2;000;000;000m";
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
std::fputs(escape_fd, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) {
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
internal::to_esc(bg.r, escape_bg, 7);
internal::to_esc(bg.g, escape_bg, 11);
internal::to_esc(bg.b, escape_bg, 15);
std::fputs(escape_fd, stdout);
std::fputs(escape_bg, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
#endif
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
FMT_FUNC locale locale_provider::locale() { return fmt::locale(); }
#endif
FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@@ -68,18 +68,7 @@ class is_streamable {
typedef decltype(test<T>(0)) result;
public:
// std::string operator<< is not considered user-defined because we handle strings
// specially.
static const bool value = result::value && !std::is_same<T, std::string>::value;
};
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template <typename T, typename Char>
class convert_to_int<T, Char, true> {
public:
static const bool value =
convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
static const bool value = result::value;
};
// Write the content of buf to os.
@@ -106,17 +95,24 @@ void format_value(basic_buffer<Char> &buffer, const T &value) {
output << value;
buffer.resize(buffer.size());
}
// Disable builtin formatting of enums and use operator<< instead.
template <typename T>
struct format_enum<T,
typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type {};
} // namespace internal
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template <typename T, typename Char>
struct convert_to_int<T, Char, void> {
static const bool value =
convert_to_int<T, Char, int>::value &&
!internal::is_streamable<T, Char>::value;
};
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct formatter<T, Char,
typename std::enable_if<internal::is_streamable<T, Char>::value>::type>
typename std::enable_if<
internal::is_streamable<T, Char>::value &&
!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
@@ -124,8 +120,7 @@ struct formatter<T, Char,
basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size());
formatter<basic_string_view<Char>, Char>::format(str, ctx);
return ctx.out();
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
};

View File

@@ -139,52 +139,10 @@ class buffered_file {
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
FILE *file;
};
buffered_file(const buffered_file &) = delete;
void operator=(const buffered_file &) = delete;
public:
// A "move constructor" for moving from a temporary.
buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// A "move constructor" for moving from an lvalue.
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary.
buffered_file &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
buffered_file &operator=(buffered_file &other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
// Returns a proxy object for moving from a temporary:
// buffered_file file = buffered_file(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
public:
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
@@ -197,7 +155,6 @@ public:
other.file_ = FMT_NULL;
return *this;
}
#endif
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
@@ -249,52 +206,9 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
int fd;
};
public:
// A "move constructor" for moving from a temporary.
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
// A "move constructor" for moving from an lvalue.
file(file &other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
file &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
file &operator=(file &other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// file f = file(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(file);
file(const file &) = delete;
void operator=(const file &) = delete;
public:
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
@@ -307,7 +221,6 @@ class file {
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_DTOR_NOEXCEPT;
@@ -353,7 +266,8 @@ class file {
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__)
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
!defined(__NEWLIB_H__)
# define FMT_LOCALE
#endif
@@ -381,7 +295,8 @@ class Locale {
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
Locale(const Locale &) = delete;
void operator=(const Locale &) = delete;
public:
typedef locale_t Type;
@@ -406,12 +321,4 @@ class Locale {
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; }
inline fmt::file &move(fmt::file &f) { return f; }
}
#endif
#endif // FMT_POSIX_H_

View File

@@ -142,8 +142,6 @@ class char_converter: public function<void> {
private:
basic_format_arg<Context> &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(char_converter);
public:
explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
@@ -169,8 +167,6 @@ class printf_width_handler: public function<unsigned> {
format_specs &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(printf_width_handler);
public:
explicit printf_width_handler(format_specs &spec) : spec_(spec) {}
@@ -226,12 +222,12 @@ class printf_arg_formatter:
context_type &context_;
void write_null_pointer(char) {
this->spec().type_ = 0;
this->spec()->type_ = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
this->spec().type_ = 0;
this->spec()->type_ = 0;
this->write(L"(nil)");
}
@@ -247,7 +243,7 @@ class printf_arg_formatter:
*/
printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
format_specs &spec, context_type &ctx)
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), spec),
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec),
context_(ctx) {}
template <typename T>
@@ -256,13 +252,13 @@ class printf_arg_formatter:
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
format_specs &fmt_spec = this->spec();
format_specs &fmt_spec = *this->spec();
if (fmt_spec.type_ != 's')
return base::operator()(value ? 1 : 0);
fmt_spec.type_ = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs &fmt_spec = this->spec();
format_specs &fmt_spec = *this->spec();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
return (*this)(static_cast<int>(value));
fmt_spec.flags_ = 0;
@@ -284,7 +280,7 @@ class printf_arg_formatter:
iterator operator()(const char *value) {
if (value)
base::operator()(value);
else if (this->spec().type_ == 'p')
else if (this->spec()->type_ == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
@@ -295,7 +291,7 @@ class printf_arg_formatter:
iterator operator()(const wchar_t *value) {
if (value)
base::operator()(value);
else if (this->spec().type_ == 'p')
else if (this->spec()->type_ == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
@@ -314,7 +310,7 @@ class printf_arg_formatter:
iterator operator()(const void *value) {
if (value)
return base::operator()(value);
this->spec().type_ = 0;
this->spec()->type_ = 0;
write_null_pointer(char_type());
return this->out();
}
@@ -341,7 +337,9 @@ struct printf_formatter {
/** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char, typename ArgFormatter>
class basic_printf_context :
private internal::context_base<
// Inherit publicly as a workaround for the icc bug
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
public internal::context_base<
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
public:
/** The character type for the output. */

View File

@@ -13,8 +13,12 @@
FMT_BEGIN_NAMESPACE
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal{
inline null<> localtime_r(...) { return null<>(); }
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }

View File

@@ -8,12 +8,6 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Force linking of inline functions into the library.
std::string (*vformat_ref)(string_view, format_args) = vformat;
std::wstring (*vformat_wref)(wstring_view, wformat_args) = vformat;
}
template struct internal::basic_data<void>;
// Explicit instantiations for char.

View File

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

View File

@@ -247,7 +247,8 @@ def release(args):
package = 'fmt-{}.zip'.format(version)
r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package),
params=params, files={package: open('build/fmt/' + package, 'rb')})
headers={'Content-Type': 'application/zip'},
params=params, data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))

View File

@@ -85,13 +85,13 @@ function(add_fmt_test name)
endfunction()
add_fmt_test(assert-test)
add_fmt_test(core-test mock-allocator.h)
add_fmt_test(gtest-extra-test)
add_fmt_test(format-test)
add_fmt_test(format-impl-test)
add_fmt_test(ostream-test)
add_fmt_test(printf-test)
add_fmt_test(time-test)
add_fmt_test(util-test mock-allocator.h)
add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
@@ -123,7 +123,10 @@ else ()
endif ()
# Test that the library can be compiled with exceptions disabled.
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
endif ()
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../src/format.cc)
target_include_directories(

View File

@@ -36,7 +36,6 @@ using fmt::basic_format_arg;
using fmt::internal::basic_buffer;
using fmt::basic_memory_buffer;
using fmt::string_view;
using fmt::internal::fp;
using fmt::internal::value;
using testing::_;
@@ -235,8 +234,6 @@ TEST(MemoryBufferTest, Ctor) {
EXPECT_EQ(123u, buffer.capacity());
}
#if FMT_USE_RVALUE_REFERENCES
typedef AllocatorRef< std::allocator<char> > TestAllocator;
static void check_move_buffer(const char *str,
@@ -304,8 +301,6 @@ TEST(MemoryBufferTest, MoveAssignment) {
EXPECT_GT(buffer2.capacity(), 5u);
}
#endif // FMT_USE_RVALUE_REFERENCES
TEST(MemoryBufferTest, Grow) {
typedef AllocatorRef< MockAllocator<int> > Allocator;
typedef basic_memory_buffer<int, 10, Allocator> Base;
@@ -453,7 +448,7 @@ struct custom_context {
TEST(UtilTest, MakeValueWithCustomFormatter) {
::Test t;
fmt::internal::value<custom_context> arg =
fmt::internal::make_value<custom_context>(t);
fmt::internal::make_value<custom_context>(t);
custom_context ctx = {false};
arg.custom.format(&t, ctx);
EXPECT_TRUE(ctx.called);
@@ -855,20 +850,21 @@ TEST(UtilTest, ReportWindowsError) {
enum TestEnum2 {};
TEST(UtilTest, ConvertToInt) {
EXPECT_FALSE((fmt::internal::convert_to_int<char, char>::value));
EXPECT_FALSE((fmt::internal::convert_to_int<const char *, char>::value));
EXPECT_TRUE((fmt::internal::convert_to_int<TestEnum2, char>::value));
EXPECT_FALSE((fmt::convert_to_int<char, char>::value));
EXPECT_FALSE((fmt::convert_to_int<const char *, char>::value));
EXPECT_TRUE((fmt::convert_to_int<TestEnum2, char>::value));
}
#if FMT_USE_ENUM_BASE
enum TestEnum : char {TestValue};
TEST(UtilTest, IsEnumConvertibleToInt) {
EXPECT_TRUE((fmt::internal::convert_to_int<TestEnum, char>::value));
EXPECT_TRUE((fmt::convert_to_int<TestEnum, char>::value));
}
#endif
TEST(UtilTest, ParseNonnegativeInt) {
if (std::numeric_limits<int>::max() != static_cast<int>(static_cast<unsigned>(1) << 31)) {
if (std::numeric_limits<int>::max() !=
static_cast<int>(static_cast<unsigned>(1) << 31)) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
}
@@ -882,56 +878,6 @@ TEST(UtilTest, ParseNonnegativeInt) {
fmt::format_error, "number is too big");
}
template <bool is_iec559>
void test_construct_from_double() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <>
void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
auto v = fp(0xbeef, 42);
v.normalize();
EXPECT_EQ(0xbeef000000000000, v.f);
EXPECT_EQ(-6, v.e);
}
TEST(FPTest, Subtract) {
auto v = fp(123, 1) - fp(102, 1);
EXPECT_EQ(v.f, 21u);
EXPECT_EQ(v.e, 1);
}
TEST(FPTest, Multiply) {
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
EXPECT_EQ(v.f, 123u * 56u);
EXPECT_EQ(v.e, 4 + 7 + 64);
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
EXPECT_EQ(v.e, 4 + 8 + 64);
}
TEST(FPTest, GetCachedPower) {
typedef std::numeric_limits<double> limits;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
EXPECT_LE(exp, fp.e);
int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e));
}
}
TEST(IteratorTest, CountingIterator) {
fmt::internal::counting_iterator<char> it;
auto prev = it++;

View File

@@ -19,14 +19,15 @@ class custom_arg_formatter :
typedef fmt::back_insert_range<fmt::internal::buffer> range;
typedef fmt::arg_formatter<range> base;
custom_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
custom_arg_formatter(
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
: base(ctx, s) {}
using base::operator();
iterator operator()(double value) {
// Comparing a float to 0.0 is safe
if (round(value * pow(10, spec().precision())) == 0.0)
if (round(value * pow(10, spec()->precision())) == 0.0)
value = 0;
return base::operator()(value);
}

17
test/folly/Range.h Normal file
View File

@@ -0,0 +1,17 @@
// A folly::StringPiece stub.
#include <cstring>
namespace folly {
class StringPiece {
public:
explicit StringPiece(const char *s) : data_(s), size_(std::strlen(s)) {}
const char* data() const { return "foo"; }
size_t size() const { return 3; }
private:
const char *data_;
size_t size_;
};
}

View File

@@ -5,13 +5,13 @@
//
// For the license information refer to format.h.
#define FMT_EXTENDED_COLORS
#define FMT_NOEXCEPT
#undef FMT_SHARED
#include "test-assert.h"
// Include format.cc instead of format.h to test implementation.
#include "../src/format.cc"
#include "fmt/color.h"
#include "fmt/printf.h"
#include <algorithm>
@@ -24,6 +24,78 @@
#undef min
#undef max
using fmt::internal::fp;
template <bool is_iec559>
void test_construct_from_double() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <>
void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
auto v = fp(0xbeef, 42);
v.normalize();
EXPECT_EQ(0xbeef000000000000, v.f);
EXPECT_EQ(-6, v.e);
}
TEST(FPTest, ComputeBoundariesSubnormal) {
auto v = fp(0xbeef, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0xbeee800000000000, lower.f);
EXPECT_EQ(-6, lower.e);
EXPECT_EQ(0xbeef800000000000, upper.f);
EXPECT_EQ(-6, upper.e);
}
TEST(FPTest, ComputeBoundaries) {
auto v = fp(0x10000000000000, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
EXPECT_EQ(31, lower.e);
EXPECT_EQ(0x8000000000000400, upper.f);
EXPECT_EQ(31, upper.e);
}
TEST(FPTest, Subtract) {
auto v = fp(123, 1) - fp(102, 1);
EXPECT_EQ(v.f, 21u);
EXPECT_EQ(v.e, 1);
}
TEST(FPTest, Multiply) {
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
EXPECT_EQ(v.f, 123u * 56u);
EXPECT_EQ(v.e, 4 + 7 + 64);
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
EXPECT_EQ(v.e, 4 + 8 + 64);
}
TEST(FPTest, GetCachedPower) {
typedef std::numeric_limits<double> limits;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
EXPECT_LE(exp, fp.e);
int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e));
}
}
template <typename T>
struct ValueExtractor: fmt::internal::function<T> {
T operator()(T value) {
@@ -118,9 +190,13 @@ TEST(FormatTest, FormatErrorCode) {
}
}
TEST(FormatTest, CountCodePoints) {
EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик")));
}
TEST(ColorsTest, Colors) {
EXPECT_WRITE(stdout, fmt::print(fmt::rgb(255,20,30), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::color::blue,"blue"),
EXPECT_WRITE(stdout, fmt::print(fmt::color::blue, "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
}

View File

@@ -555,7 +555,10 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("0x42", format("{0:#x}", 0x42ull));
EXPECT_EQ("042", format("{0:#o}", 042ull));
EXPECT_EQ("-42.0000", format("{0:#}", -42.0));
if (fmt::internal::use_grisu())
EXPECT_EQ("-42.0", format("{0:#}", -42.0));
else
EXPECT_EQ("-42.0000", format("{0:#}", -42.0));
EXPECT_EQ("-42.0000", format("{0:#}", -42.0l));
EXPECT_THROW_MSG(format("{0:#", 'c'),
format_error, "missing '}' in format string");
@@ -1085,14 +1088,45 @@ TEST(FormatterTest, FormatStdStringView) {
}
#endif
struct ConvertibleToStringView {
struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatConvertibleToStringView) {
EXPECT_EQ("foo", format("{}", ConvertibleToStringView()));
TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", format("{}", implicitly_convertible_to_string_view()));
}
// std::is_constructible is broken in MSVC until version 2015.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", format("{}", explicitly_convertible_to_string_view()));
}
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo", format(L"{}", explicitly_convertible_to_wstring_view()));
}
struct explicitly_convertible_to_string_like {
template <
typename String,
typename = typename std::enable_if<
std::is_constructible<String, const char*, std::size_t>::value>::type>
explicit operator String() const { return String("foo", 3u); }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", format("{}", explicitly_convertible_to_string_like()));
}
#endif
FMT_BEGIN_NAMESPACE
template <>
struct formatter<Date> {
@@ -1285,6 +1319,21 @@ TEST(FormatTest, Variadic) {
EXPECT_EQ(L"abc1", format(L"{}c{}", L"ab", 1));
}
TEST(FormatTest, Dynamic) {
using ctx = fmt::format_context;
std::vector<fmt::basic_format_arg<ctx>> args;
args.emplace_back(fmt::internal::make_arg<ctx>(42));
args.emplace_back(fmt::internal::make_arg<ctx>("abc1"));
args.emplace_back(fmt::internal::make_arg<ctx>(1.2f));
std::string result = fmt::vformat("{} and {} and {}",
fmt::basic_format_args<ctx>(
args.data(),
static_cast<unsigned>(args.size())));
EXPECT_EQ("42 and abc1 and 1.2", result);
}
TEST(FormatTest, JoinArg) {
using fmt::join;
int v1[3] = { 1, 2, 3 };
@@ -1397,6 +1446,10 @@ TEST(FormatTest, Enum) {
EXPECT_EQ("0", fmt::format("{}", A));
}
TEST(FormatTest, EnumFormatterUnambiguous) {
fmt::formatter<TestEnum> f;
}
#if FMT_HAS_FEATURE(cxx_strong_enums)
enum TestFixedEnum : short { B };
@@ -1412,13 +1465,13 @@ class mock_arg_formatter:
fmt::internal::arg_formatter_base<buffer_range>::iterator>,
public fmt::internal::arg_formatter_base<buffer_range> {
private:
MOCK_METHOD1(call, void (int value));
MOCK_METHOD1(call, void (long long value));
public:
typedef fmt::internal::arg_formatter_base<buffer_range> base;
typedef buffer_range range;
mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
: base(fmt::internal::get_container(ctx.out()), s) {
EXPECT_CALL(*this, call(42));
}
@@ -1760,18 +1813,20 @@ struct test_format_string_handler {
template <typename T>
FMT_CONSTEXPR void on_arg_id(T) {}
FMT_CONSTEXPR void on_replacement_field(const char *) {}
template <typename Iterator>
FMT_CONSTEXPR void on_replacement_field(Iterator) {}
FMT_CONSTEXPR const char *on_format_specs(const char *s) { return s; }
template <typename Iterator>
FMT_CONSTEXPR Iterator on_format_specs(Iterator it) { return it; }
FMT_CONSTEXPR void on_error(const char *) { error = true; }
bool error = false;
};
FMT_CONSTEXPR bool parse_string(const char *s) {
FMT_CONSTEXPR bool parse_string(fmt::string_view s) {
test_format_string_handler h;
fmt::internal::parse_format_string(s, h);
fmt::internal::parse_format_string<true>(s, h);
return !h.error;
}
@@ -1884,3 +1939,30 @@ TEST(FormatTest, FormatStringErrors) {
}
#endif // FMT_USE_CONSTEXPR
TEST(FormatTest, ConstructU8StringViewFromCString) {
fmt::u8string_view s("ab");
EXPECT_EQ(s.size(), 2u);
const fmt::char8_t *data = s.data();
EXPECT_EQ(data[0].value, 'a');
EXPECT_EQ(data[1].value, 'b');
}
TEST(FormatTest, ConstructU8StringViewFromDataAndSize) {
fmt::u8string_view s("foobar", 3);
EXPECT_EQ(s.size(), 3u);
const fmt::char8_t *data = s.data();
EXPECT_EQ(data[0].value, 'f');
EXPECT_EQ(data[1].value, 'o');
EXPECT_EQ(data[2].value, '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 fmt::char8_t *data = s.data();
EXPECT_EQ(data[0].value, 'a');
EXPECT_EQ(data[1].value, 'b');
}
#endif

View File

@@ -25,37 +25,31 @@ class AllocatorRef {
private:
Allocator *alloc_;
public:
typedef typename Allocator::value_type value_type;
explicit AllocatorRef(Allocator *alloc = nullptr) : alloc_(alloc) {}
AllocatorRef(const AllocatorRef &other) : alloc_(other.alloc_) {}
AllocatorRef& operator=(const AllocatorRef &other) {
alloc_ = other.alloc_;
return *this;
}
#if FMT_USE_RVALUE_REFERENCES
private:
void move(AllocatorRef &other) {
alloc_ = other.alloc_;
other.alloc_ = nullptr;
}
public:
AllocatorRef(AllocatorRef &&other) {
move(other);
}
typedef typename Allocator::value_type value_type;
explicit AllocatorRef(Allocator *alloc = nullptr) : alloc_(alloc) {}
AllocatorRef(const AllocatorRef &other) : alloc_(other.alloc_) {}
AllocatorRef(AllocatorRef &&other) { move(other); }
AllocatorRef& operator=(AllocatorRef &&other) {
assert(this != &other);
move(other);
return *this;
}
#endif
AllocatorRef& operator=(const AllocatorRef &other) {
alloc_ = other.alloc_;
return *this;
}
public:
Allocator *get() const { return alloc_; }
value_type* allocate(std::size_t n) {

View File

@@ -5,6 +5,7 @@
//
// For the license information refer to format.h.
#define FMT_STRING_ALIAS 1
#include "fmt/ostream.h"
#include <sstream>
@@ -37,10 +38,10 @@ static std::wostream &operator<<(std::wostream &os, TestEnum) {
enum TestEnum2 {A};
TEST(OStreamTest, Enum) {
EXPECT_FALSE((fmt::internal::convert_to_int<TestEnum, char>::value));
EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value));
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
EXPECT_FALSE((fmt::internal::convert_to_int<TestEnum, wchar_t>::value));
EXPECT_FALSE((fmt::convert_to_int<TestEnum, wchar_t>::value));
EXPECT_EQ(L"TestEnum", fmt::format(L"{}", TestEnum()));
EXPECT_EQ(L"0", fmt::format(L"{}", A));
}
@@ -49,7 +50,7 @@ typedef fmt::back_insert_range<fmt::internal::buffer> range;
struct test_arg_formatter: fmt::arg_formatter<range> {
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
: fmt::arg_formatter<range>(ctx, s) {}
: fmt::arg_formatter<range>(ctx, &s) {}
};
TEST(OStreamTest, CustomArg) {
@@ -169,3 +170,32 @@ TEST(OStreamTest, ConstexprString) {
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
}
#endif
namespace fmt_test {
struct ABC {};
template <typename Output> Output &operator<<(Output &out, ABC) {
out << "ABC";
return out;
}
} // namespace fmt_test
TEST(FormatTest, FormatToN) {
char buffer[4];
buffer[3] = 'x';
auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::ABC());
EXPECT_EQ(3u, result.size);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("ABCx", fmt::string_view(buffer, 4));
result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::ABC());
EXPECT_EQ(5u, result.size);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
}
#if FMT_USE_USER_DEFINED_LITERALS
TEST(FormatTest, UDL) {
using namespace fmt::literals;
EXPECT_EQ("{}"_format("test"), "test");
}
#endif

View File

@@ -180,8 +180,13 @@ TEST(PrintfTest, HashFlag) {
safe_sprintf(buffer, "%#E", -42.0);
EXPECT_PRINTF(buffer, "%#E", -42.0);
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
if (fmt::internal::use_grisu()) {
EXPECT_PRINTF("-42.0", "%#g", -42.0);
EXPECT_PRINTF("-42.0", "%#G", -42.0);
} else {
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
}
safe_sprintf(buffer, "%#a", 16.0);
EXPECT_PRINTF(buffer, "%#a", 16.0);