Compare commits

..

67 Commits
6.0.0 ... text

Author SHA1 Message Date
Victor Zverovich
48f76dbb52 Remove unneeded stuff 2019-09-29 09:51:22 -07:00
Victor Zverovich
ca3dacba47 Remove algorithm.hpp 2019-09-29 09:43:23 -07:00
Victor Zverovich
6d47e093c5 Remove utility.hpp 2019-09-29 09:42:03 -07:00
Victor Zverovich
d1626e96ef Remove unused overloads 2019-09-29 09:29:19 -07:00
Victor Zverovich
160bcb723c Remove lazy_segment_range 2019-09-29 09:25:29 -07:00
Victor Zverovich
c7ea093c27 Handle Supplemental Symbols and Pictographs 2019-09-28 11:47:22 -07:00
Victor Zverovich
00434c93ef Implement correct width computation 2019-09-28 11:47:22 -07:00
Victor Zverovich
b12033fd68 Handle grapheme clusters when computing width 2019-09-28 11:47:22 -07:00
Victor Zverovich
e5ab813ffb Add a subset of the text library as an optional component 2019-09-28 11:47:22 -07:00
Victor Zverovich
0fc7bd1573 Fix ambiguity for types with dodgy conversions 2019-09-28 11:35:20 -07:00
Jeremy Ong
b4f1988c4b Provide overload for fmt::join that handles std::tuples
Address enhancement request #1322.

The overload is provided in `ranges` (original `fmt::join` exists
currently in `format.h` for historical reasons.

Tests for prvalue and lvalue tuple arguments as well as the empty
tuple are provided in `ranges-test.cc`.
2019-09-27 14:05:35 -07:00
Victor Zverovich
4b8f8fac96 Update README.rst 2019-09-25 07:14:45 -07:00
Richard Musil
3b2fc033d1 Making CUDA test work with CMAKE_MSVC_RUNTIME_LIBRARY
CMake 3.15 introduced a new way of handling MSVC CRT type definition for
the build: CMAKE_MSVC_RUNTIME_LIBRARY variable.
(https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html)

This is supposed to be the way to go with MSVC CRT selection in new
projects. Using this method however breaks the current CMake script for
CUDA test. The reason is the CUDA test uses "FindCUDA" CMake module to
detect and set up CUDA support in CMake, which is deprecated since CMake
version 3.10, and which does not support CMAKE_MSVC_RUNTIME_LIBRARY
selector correctly (i.e. it does not propagate the compiler option
related to the CRT).

I did not find a way to "patch" in the correct compiler options, so
(while knowing this feature is only available from CMake 3.15 on) I
decided to change also the way CUDA is handled and instead of using
FindCUDA, used enable_language. Apart from having some nice additional
side-effects, it also fixed the problem with CRT selection.

However, the propagation of the compiler options (and in particular the
options related to C++ standard selection) is still a bit flaky on
Windows+MSVC platform, so it had to be done manually.

The patch makes two things in parallel:

1) Introduces MSVC_BUILD_STATIC, which, together with CMake version >=
3.15, allows building static version of the 'fmt' lib (and all the
tests).

2) At the same time, for CMake >= 3.15 it switches handling of CUDA
support from (old) FindCUDA to (new) enable_language, to fix the
problems which the old method has with the new CRT selector for MSVC in
a new CMake.

Added a check for CUDA before enabling it.

Using VERSION_LESS instead of VERSION_GREATER_EQUAL

Since apparently VERSION_GREATER_EQUAL exists only from CMake 3.7, while
Android is using CMake 3.6.

Removed MSVC_RUNTIME_LIBRARY logic from the CMake file.

The static build can be set on the command line with CMake >= 3.15
by defining the policy and the CMAKE_MSVC_RUNTIME_LIBARY this way:

cmake -G <gen> <options>
    -DCMAKE_POLICY_DEFAULT_CMP0091=NEW
    -DMSVC_BUILD_STATIC=ON
    -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$<CONFIG:Debug>:Debug>"

When MSVC_BUILD_DEBUG is set the test 'posix-mock-test' is skipped as it
does not build with the static runtime.
2019-09-25 06:46:11 -07:00
mwinterb
ac59d9f3a4 workaround VS2019 Update 3 compiler bug. (#1328) 2019-09-24 11:29:29 -07:00
Daumantas Kavolis
8f27ce4d8b add test for multiple compilation types 2019-09-24 06:25:21 -07:00
Daumantas Kavolis
89b0c71fa9 fix name clash in header-only mode 2019-09-24 06:25:21 -07:00
Daumantas Kavolis
f6a783ad2e fix get 2019-09-24 06:25:21 -07:00
Victor Zverovich
ccc8f5db02 Disable integral operator<< (#1316) 2019-09-23 20:34:08 -07:00
Victor Zverovich
20fdb88a1c Remove redundant and nonportable test (#1313) 2019-09-23 13:36:12 -07:00
Victor Zverovich
f29901097f Don't use const char* overload of operator<< (#1309) 2019-09-23 12:35:08 -07:00
Victor Zverovich
758446c80d Fix a warning (#1319) and simplify code 2019-09-19 16:00:40 -07:00
Vedran Miletić
f7aedc5fc4 Fix shared build on Solaris 2019-09-19 14:12:03 -07:00
Paul Dreik
840a817ed2 add oss fuzz badge 2019-09-19 08:23:05 -07:00
Rosen Penev
79c923ba2c fmt.pc.in: Fix for cross compilation
These variables get set to /usr/lib and /usr/include , which is totally wrong in a cross compilation
environment.

Changed to standard pkgconfig values consistent with most other ones.
2019-09-16 17:18:21 -07:00
Victor Zverovich
5dc577c064 Update ChangeLog.rst 2019-09-12 18:20:48 -07:00
Victor Zverovich
c6d1a94a9f Fix fallback_format (#1306) 2019-09-09 17:28:53 -07:00
Victor Zverovich
0656045d02 Fix format overload that takes text_style (#1305) 2019-09-08 18:41:02 -07:00
Victor Zverovich
c85ae23c73 Add max_value 2019-09-08 09:21:30 -07:00
Victor Zverovich
b3bf665764 Implement multiplication and part of assignment from pow of 10 2019-09-08 08:27:00 -07:00
Victor Zverovich
0887887e23 Implement left shift 2019-09-07 18:10:36 -07:00
Victor Zverovich
ac71d853be Refactor normalize and clean up 2019-09-07 17:28:59 -07:00
Victor Zverovich
6649b8e0ca value -> bigit 2019-09-07 14:23:19 -07:00
Victor Zverovich
56b5c192a0 Add a bigint stub and reenable grisu 2019-09-07 13:50:22 -07:00
Victor Zverovich
b2f0b6e44e Tweak comment 2019-09-07 08:43:35 -07:00
Victor Zverovich
3d9f3c163b Integrate new format string compilation 2019-09-07 07:01:51 -07:00
Victor Zverovich
19547d5148 Update index.rst 2019-09-06 13:27:28 -07:00
Victor Zverovich
972ffd3151 Update README.rst 2019-09-06 13:13:30 -07:00
Victor Zverovich
2ed412fa38 Update README.rst 2019-09-06 13:03:14 -07:00
gabime
df4dcf2ece Fixed vs 2015 warning about unused variable when grisu is off 2019-09-06 11:29:56 -07:00
Victor Zverovich
f7a4b4ab91 Make numeric alignment optional 2019-09-06 09:43:53 -07:00
Victor Zverovich
611cf0b3c6 Format octal 0 as 0 2019-09-06 07:03:47 -07:00
Victor Zverovich
58a8f2f539 Add ccache to the list of projects 2019-09-06 06:26:09 -07:00
Victor Zverovich
1882b9687b Reduce the numer of ifdefs with an empty (u)int128_t fallback 2019-09-05 18:03:47 -07:00
Deniz Evrenci
6de0454b42 Add support for built-in __int128 when available 2019-09-04 07:05:08 -07:00
Deniz Evrenci
16e3c48bb0 Move definition of FMT_USE_INT128 to core.h 2019-09-04 07:05:08 -07:00
Deniz Evrenci
8ce5f680f2 Rename internal::is_integral and internal::is_arithmetic
To reserve space for the type traits sharing the same name as the ones
from standard library.
2019-09-04 07:05:08 -07:00
Victor Zverovich
2fd8f9ec8a Initial implementation of optimal compile-time formatter generation 2019-09-02 19:50:30 -07:00
Victor Zverovich
fe642d7648 Clarify use of the core API in header-only mode (#1296) 2019-09-02 17:14:30 -07:00
Victor Zverovich
a128b5b2cb Simplify format string compilation 2019-09-02 06:48:19 -07:00
Victor Zverovich
466128de00 Remove unused code and refactor 2019-09-01 13:08:53 -07:00
Victor Zverovich
22e98a5b6a Make compile work with user-defined types 2019-09-01 11:49:27 -07:00
Victor Zverovich
f18a3f36a7 Remove string_view_metadata
string_view_metadata was introduced to make compiled format relocatable.
However, format string compilation is an optimization and therefore
adding overhead and extra complexity for relocation is undesirable.
In most cases the string will be either static or outlive compilation
and formatting and if it doesn't, it's possible to make compiled
representation relocatale by other means.
2019-09-01 11:10:49 -07:00
Victor Zverovich
7cad33563c Refactor format string compilation 2019-09-01 10:28:41 -07:00
Victor Zverovich
e1ab6bc006 Simplify format string compilation 2019-09-01 08:31:32 -07:00
Victor Zverovich
24a88545d9 Add -Werror to tests 2019-08-31 09:26:45 -07:00
Victor Zverovich
422e7b9d70 Fix compile-time checks for user-defined types (#1292) 2019-08-31 08:35:38 -07:00
Victor Zverovich
d1dd9d5327 Document floating-point n specifier (#1291) 2019-08-31 08:16:47 -07:00
Victor Zverovich
9a56a608ee Fix warnings (#1288) 2019-08-31 08:16:47 -07:00
Victor Zverovich
c76957565c FixedEnum -> StrongEnum and make it a regression test 2019-08-31 08:16:47 -07:00
Rosen Penev
200ee6f108 Fix minor clang-tidy warnings
using instead of typedef.

climits instead of limits.h

Added missing cast to size_t.
2019-08-31 07:02:21 -07:00
Egor Seredin
bcd9b9331a Map not int enum to correct underlying_type (#1286)
* Map not int enum to correct underlying_type

* Use non-zero constant in TestFixedEnum
2019-08-31 06:25:38 -07:00
luncliff
345ba07f1d Add a CUDA test
test cuda: import fmt in CUDA source code

Current test is only for Windows(cl.exe).
Need to test more with the other host compilers...

* Activate the test when `find_package(CUDA)` worked
* The test runs with C++14

Detailed comments in 'test/cuda-test'

test cuda: add more comment / macro check

* checks both `__NVCC__` and `__CUDACC__`

More comments for CMake and CUDA source file.

test cuda: <fmt/core.h> checks NVCC and CUDA

The header file checks 2 things.

* __NVCC__: if the compiler is from NVIDIA
* __CUDACC__: if the source code is CUDA(.cu) file

Since we can't sure all users prefer latest, Version for
`find_pacakge(CUDA)` is downgraded to 9.0.
This is the minimum version for C++14 in CUDA
2019-08-30 17:45:50 -07:00
hhb
9e2490be4c Rename precision parameter
To avoid clang warning:

fmtlib/include/fmt/chrono.h:753:32: error: declaration shadows a field of 'formatter<duration<type-parameter-0-0, type-parameter-0-1>, type-parameter-0-2, void>' [-Werror,-Wshadow]
2019-08-28 07:29:04 -07:00
Victor Zverovich
3f75e2b69e Make buffer_range public and update custom formatting docs (#1281) 2019-08-28 06:50:20 -07:00
Victor Zverovich
744302add0 Workaround broken [[deprecated]] in Intel compiler (#1273) 2019-08-28 05:58:09 -07:00
Greg Sjaardema
f5556225a4 Eliminate shadow variable warning
gcc-9 gives the following shadow warning:
```
In file included from /Users/libraries/ioss/src/fmt/ostream.h:12,
                 from /Users/libraries/ioss/src/Ioss_DatabaseIO.C:59:
/Users/libraries/ioss/src/fmt/format.h: In function 'void fmt::v6::internal::parse_format_string(fmt::v6::basic_string_view<Char>, Handler&&)':
/Users/libraries/ioss/src/fmt/format.h:2442:10: warning: declaration of 'struct fmt::v6::internal::parse_format_string(fmt::v6::basic_string_view<Char>, Handler&&)::writer' shadows a global declaration [-Wshadow]
 2442 |   struct writer {
      |          ^~~~~~
/Users/libraries/ioss/src/fmt/format.h:1703:7: note: shadowed declaration is here
 1703 | using writer = basic_writer<buffer_range<char>>;
      |       ^~~~~~
```

Since the `writer` struct is only used internally in the `parse_format_string` function, its name can be changed somewhat aribtrarily to avoid conflicts with names in an outer scope.

Note that this warning is also present in the 6.0.0 release.
2019-08-27 14:29:39 -07:00
Bruce Mitchener
ad3c7855e2 Fix typos. 2019-08-27 06:26:17 -07:00
42 changed files with 8040 additions and 872 deletions

View File

@@ -33,6 +33,8 @@ if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
endif ()
option(FMT_USE_TEXT "Use the text library." OFF)
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
@@ -160,6 +162,10 @@ if (HAVE_OPEN)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
if (FMT_USE_TEXT)
set(FMT_SOURCES ${FMT_SOURCES} src/text/grapheme_break.cpp)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
@@ -180,12 +186,17 @@ target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
if (FMT_USE_TEXT)
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/text>)
endif ()
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX d)
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed)

View File

@@ -183,6 +183,7 @@
* Modernized the codebase using more C++11 features and removing workarounds.
Most importantly, ``buffer_context`` is now an alias template, so
use ``buffer_context<T>`` instead of ``buffer_context<T>::type``.
These features require GCC 4.8 or later.
* ``formatter`` specializations now always take precedence over implicit
conversions to ``int`` and the undocumented ``convert_to_int`` trait
@@ -298,7 +299,7 @@
`#1222 <https://github.com/fmtlib/fmt/issues/1222>`_).
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* Fixed bugs discovered by fuzzing or during fuzzing integation
* Fixed bugs discovered by fuzzing or during fuzzing integration
(`#1124 <https://github.com/fmtlib/fmt/issues/1124>`_,
`#1127 <https://github.com/fmtlib/fmt/issues/1127>`_,
`#1132 <https://github.com/fmtlib/fmt/issues/1132>`_,

View File

@@ -11,6 +11,10 @@
:alt: Ask questions at StackOverflow with the tag fmt
:target: http://stackoverflow.com/questions/tagged/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1
**{fmt}** is an open-source formatting library for C++.
It can be used as a safe and fast alternative to (s)printf and iostreams.
@@ -39,12 +43,12 @@ Features
consists of just three header files, ``core.h``, ``format.h`` and
``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests
<https://github.com/fmtlib/fmt/tree/master/test>`_.
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed.
* Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow
errors.
* Ease of use: small self-contained code base, no external dependencies,
permissive BSD `license
permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers.
@@ -278,6 +282,8 @@ Projects using this library
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
* `ccache <https://ccache.dev/>`_: A compiler cache
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
@@ -343,7 +349,7 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework
`More... <https://github.com/search?q=cppformat&type=Code>`_
`More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
@@ -469,7 +475,7 @@ A: use ``std::tuple``:
License
-------
{fmt} is distributed under the BSD `license
{fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax

View File

@@ -10,6 +10,8 @@ The {fmt} library API consists of the following parts:
facilities and a lightweight subset of formatting functions
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
format string checks, output iterator and user-defined type support
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
@@ -23,7 +25,8 @@ Core API
========
``fmt/core.h`` defines the core API which provides argument handling facilities
and a lightweight subset of formatting functions.
and a lightweight subset of formatting functions. In the header-only mode
include ``fmt/format.h`` instead of ``fmt/core.h``.
The following functions use :ref:`format string syntax <syntax>`
similar to that of Python's `str.format
@@ -278,21 +281,21 @@ Custom Formatting of Built-in Types
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
using arg_formatter =
fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>>;
using arg_formatter = fmt::arg_formatter<fmt::buffer_range<char>>;
// A custom argument formatter that formats negative integers as unsigned
// with the ``x`` format specifier.
class custom_arg_formatter : public arg_formatter {
public:
custom_arg_formatter(fmt::format_context &ctx,
fmt::format_specs *spec = nullptr)
: arg_formatter(ctx, spec) {}
custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx = nullptr,
fmt::format_specs* spec = nullptr)
: arg_formatter(ctx, parse_ctx, spec) {}
using arg_formatter::operator();
auto operator()(int value) {
if (spec().type() == 'x')
if (specs() && specs()->type == 'x')
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value);
}
@@ -316,6 +319,31 @@ custom argument formatter class::
.. doxygenclass:: fmt::arg_formatter
:members:
.. _ranges-api:
Ranges and Tuple Formatting
===========================
The library also supports convenient formatting of ranges and tuples::
#include <fmt/ranges.h>
std::tuple<char, int, float> t{'a', 1, 2.0f};
// Prints "('a', 1, 2.0)"
fmt::print("{}", t);
NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main
``format.h`` header, but expect this to change in the future.
Using ``fmt::join``, you can separate tuple elements with a custom separator::
#include <fmt/ranges.h>
std::tuple<int, char> t = {1, 'a'};
// Prints "1, a"
fmt::print("{}", fmt::join(t, ", "));
.. _chrono-api:
Date and Time Formatting

View File

@@ -1,7 +1,7 @@
Overview
========
**fmt** (formerly cppformat) is an open-source formatting library.
**fmt** is an open-source formatting library.
It can be used as a fast and safe alternative to printf and IOStreams.
.. raw:: html

View File

@@ -101,11 +101,6 @@ The meaning of the various alignment options is as follows:
| ``'>'`` | Forces the field to be right-aligned within the |
| | available space (this is the default for numbers). |
+---------+----------------------------------------------------------+
| ``'='`` | Forces the padding to be placed after the sign (if any) |
| | but before the digits. This is used for printing fields |
| | in the form '+000000120'. This alignment option is only |
| | valid for numeric types. |
+---------+----------------------------------------------------------+
| ``'^'`` | Forces the field to be centered within the available |
| | space. |
+---------+----------------------------------------------------------+
@@ -154,9 +149,11 @@ conversions, trailing zeros are not removed from the result.
*width* is a decimal integer defining the minimum field width. If not
specified, then the field width will be determined by the content.
Preceding the *width* field by a zero (``'0'``) character enables
sign-aware zero-padding for numeric types. This is equivalent to a *fill*
character of ``'0'`` with an *alignment* type of ``'='``.
Preceding the *width* field by a zero (``'0'``) character enables sign-aware
zero-padding for numeric types. It forces the padding to be placed after the
sign or base (if any) but before the digits. This is used for printing fields in
the form '+000000120'. This option is only valid for numeric types and it has no
effect on formatting of infinity and NaN.
The *precision* is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted with
@@ -264,6 +261,10 @@ The available presentation types for floating-point values are:
| | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
+---------+----------------------------------------------------------+
| ``'%'`` | Fixed point as a percentage. This is similar to ``'f'``, |
| | but the argument is multiplied by 100 and a percent sign |
| | ``%`` is appended. |

View File

@@ -403,7 +403,7 @@ inline bool isfinite(T value) {
return std::isfinite(value);
}
// Convers value to int and checks that it's in the range [0, upper).
// Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
@@ -582,8 +582,8 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_t<int> n = to_unsigned(
to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
uint32_or_64_or_128_t<int> n = to_unsigned(
to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
@@ -738,8 +738,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id);
return arg_ref_type(str_val);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
@@ -750,7 +749,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; }
void on_precision(unsigned _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
@@ -806,10 +805,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
specs.width, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin());
precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);

View File

@@ -576,8 +576,9 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S> >
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return internal::vformat(ts, to_string_view(format_str),
{internal::make_args_checked(format_str, args...)});
return internal::vformat(
ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE

View File

@@ -14,250 +14,44 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
public:
struct named_argument_id {
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
: id(id) {}
internal::string_view_metadata id;
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
struct argument_id {
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
FMT_CONSTEXPR argument_id(unsigned id)
: which(which_arg_id::index), val(id) {}
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
: which(which_arg_id::named_index), val(id) {}
enum class which_arg_id { index, named_index };
which_arg_id which;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(internal::string_view_metadata id)
: named_index(id) {}
unsigned index;
internal::string_view_metadata named_index;
} val;
};
struct specification {
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
argument_id arg_id;
internal::dynamic_format_specs<Char> parsed_specs;
};
FMT_CONSTEXPR format_part()
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
: which(kind::text), end_of_argument_id(0u), val(text) {}
FMT_CONSTEXPR format_part(unsigned id)
: which(kind::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
FMT_CONSTEXPR format_part(specification spec)
: which(kind::specification), end_of_argument_id(0u), val(spec) {}
enum class kind { argument_id, named_argument_id, text, specification };
kind which;
std::size_t end_of_argument_id;
kind part_kind;
union value {
FMT_CONSTEXPR value() : arg_id(0u) {}
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR value(named_argument_id named_id)
: named_arg_id(named_id.id) {}
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
FMT_CONSTEXPR value(specification s) : spec(s) {}
unsigned arg_id;
internal::string_view_metadata named_arg_id;
internal::string_view_metadata text;
specification spec;
unsigned arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
};
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
template <typename Char, typename PartsContainer>
class format_preparation_handler : public internal::error_handler {
private:
using part = format_part<Char>;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
public:
using iterator = typename basic_string_view<Char>::iterator;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin == end) return;
const auto offset = begin - format_.data();
const auto size = end - begin;
parts_.push_back(part(string_view_metadata(offset, size)));
static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
return format_part(kind::arg_index, index);
}
FMT_CONSTEXPR void on_arg_id() {
parts_.push_back(part(parse_context_.next_arg_id()));
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
parts_.push_back(part(id));
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
const auto view = string_view_metadata(format_, id);
const auto arg_id = typename part::named_argument_id(view);
parts_.push_back(part(arg_id));
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
parts_.back().end_of_argument_id = ptr - format_.begin();
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
const auto specs_offset = to_unsigned(begin - format_.begin());
using parse_context = basic_parse_context<Char>;
internal::dynamic_format_specs<Char> parsed_specs;
dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler);
if (*begin != '}') on_error("missing '}' in format string");
auto& last_part = parts_.back();
auto specs = last_part.which == part::kind::argument_id
? typename part::specification(last_part.val.arg_id)
: typename part::specification(last_part.val.named_arg_id);
specs.parsed_specs = parsed_specs;
last_part = part(specs);
last_part.end_of_argument_id = specs_offset;
return begin;
}
private:
PartsContainer& parts_;
basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_;
};
template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format {
public:
using char_type = char_t<Format>;
using format_part_t = format_part<char_type>;
constexpr prepared_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete;
using context = buffer_context<char_type>;
template <typename Range, typename Context>
auto vformat_to(Range out, basic_format_args<Context> args) const ->
typename Context::iterator {
const auto format_view = internal::to_string_view(format_);
basic_parse_context<char_type> parse_ctx(format_view);
Context ctx(out.begin(), args);
const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
switch (part.which) {
case format_part_t::kind::text: {
const auto text = value.text.to_view(format_view.data());
auto output = ctx.out();
auto&& it = internal::reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
} break;
case format_part_t::kind::argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(parse_ctx, ctx, value.arg_id);
} break;
case format_part_t::kind::named_argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id =
value.named_arg_id.to_view(format_view.data());
format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break;
case format_part_t::kind::specification: {
const auto& arg_id_value = value.spec.arg_id.val;
const auto arg = value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.named_index.to_view(
to_string_view(format_).data()));
auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(
specs.width, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx, format_view.begin());
check_prepared_specs(specs, arg.type());
advance_parse_context_to_specification(parse_ctx, part);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
} break;
}
}
return ctx.out();
}
private:
void advance_parse_context_to_specification(
basic_parse_context<char_type>& parse_ctx,
const format_part_t& part) const {
const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id;
advance_to(parse_ctx, specification_begin);
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
Id arg_id) const {
parse_ctx.check_arg_id(arg_id);
const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
ctx.advance_to(stopped_at);
}
template <typename Char>
void check_prepared_specs(const basic_format_specs<Char>& specs,
internal::type arg_type) const {
internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
}
private:
Format format_;
PreparedPartsProvider parts_provider_;
};
template <typename Char> struct part_counter {
@@ -276,13 +70,13 @@ template <typename Char> struct part_counter {
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned braces_counter = 0;
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++braces_counter;
++brace_counter;
} else if (*begin == '}') {
if (braces_counter == 0u) break;
--braces_counter;
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
@@ -291,156 +85,477 @@ template <typename Char> struct part_counter {
FMT_CONSTEXPR void on_error(const char*) {}
};
template <typename Format> class compiletime_prepared_parts_type_provider {
private:
using char_type = char_t<Format>;
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
static FMT_CONSTEXPR unsigned count_parts() {
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
part_counter<char_type> counter;
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, counter);
return counter.num_parts;
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
// Workaround for old compilers. Compiletime parts preparation will not be
// performed with them anyway.
FMT_CONSTEXPR void on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_parse_context<Char>> handler(repl.specs,
parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_parse_context<char_type> parse_ctx(to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
compiletime_prepared_parts_type_provider::count_parts();
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned number_of_format_parts = 0u;
static const unsigned num_format_parts = 1;
#endif
public:
template <unsigned N> struct format_parts_array {
using value_type = format_part<char_type>;
using parts_container = format_part<char_type>[num_format_parts];
FMT_CONSTEXPR format_parts_array() : arr{} {}
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
FMT_CONSTEXPR const value_type* begin() const { return arr; }
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
private:
value_type arr[N];
};
struct empty {
// Parts preparator will search for it
using value_type = format_part<char_type>;
};
using type = conditional_t<number_of_format_parts != 0,
format_parts_array<number_of_format_parts>, empty>;
};
template <typename Parts> class compiletime_prepared_parts_collector {
private:
using format_part = typename Parts::value_type;
public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
: parts_{parts}, counter_{0u} {}
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
private:
Parts& parts_;
unsigned counter_;
};
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) {
using collector = compiletime_prepared_parts_collector<PartsContainer>;
PartsContainer parts;
collector c(parts);
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
format, format_preparation_handler<Char, collector>(format, c));
return parts;
}
template <typename PartsContainer> class runtime_parts_provider {
public:
runtime_parts_provider() = delete;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {}
const PartsContainer& parts() const { return parts_; }
private:
PartsContainer parts_;
};
template <typename Format, typename PartsContainer>
struct compiletime_parts_provider {
compiletime_parts_provider() = delete;
template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{}));
return prepared_parts;
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format<
S,
internal::compiletime_parts_provider<
S,
typename internal::compiletime_prepared_parts_type_provider<S>::type>,
Args...> {
return format_str;
}
#endif
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) -> internal::prepared_format<
std::basic_string<Char>,
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
Args...> {
return std::basic_string<Char>(format_str, N - 1);
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type>
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = internal::buffer_range<Char>;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
cf.template vformat_to<range, context>(range(buffer),
{make_format_args<context>(args...)});
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return cf.template vformat_to<range, context>(
range(out), {make_format_args<context>(args...)});
return internal::cf::vformat_to<context>(
range(out), cf, {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
@@ -455,7 +570,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return fmt::format_to(
return format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();

View File

@@ -133,6 +133,13 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || defined(__NVCC__) || defined(__CUDACC__)
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
@@ -200,6 +207,8 @@ template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
struct monostate {};
@@ -222,7 +231,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
template <typename T> struct std_string_view {};
#endif
// Casts nonnegative integer to unsigned.
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
#else
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
struct int128_t {};
struct uint128_t {};
#endif
// Casts a nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
@@ -286,6 +309,8 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR iterator begin() const { return data_; }
FMT_CONSTEXPR iterator end() const { return data_ + size_; }
FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
FMT_CONSTEXPR void remove_prefix(size_t n) {
data_ += n;
size_ -= n;
@@ -474,8 +499,8 @@ class basic_parse_context : private ErrorHandler {
using format_parse_context = basic_parse_context<char>;
using wformat_parse_context = basic_parse_context<wchar_t>;
using parse_context FMT_DEPRECATED = basic_parse_context<char>;
using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>;
using parse_context FMT_DEPRECATED_ALIAS = basic_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_parse_context<wchar_t>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
@@ -626,6 +651,8 @@ enum type {
uint_type,
long_long_type,
ulong_long_type,
int128_type,
uint128_type,
bool_type,
char_type,
last_integer_type = char_type,
@@ -652,6 +679,8 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(int128_t, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(double, double_type);
@@ -660,12 +689,12 @@ FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral(type t) {
FMT_CONSTEXPR bool is_integral_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_integer_type;
}
FMT_CONSTEXPR bool is_arithmetic(type t) {
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_numeric_type;
}
@@ -691,6 +720,8 @@ template <typename Context> class value {
unsigned uint_value;
long long long_long_value;
unsigned long long ulong_long_value;
int128_t int128_value;
uint128_t uint128_value;
bool bool_value;
char_type char_value;
double double_value;
@@ -705,6 +736,8 @@ template <typename Context> class value {
FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) : long_long_value(val) {}
value(unsigned long long val) : ulong_long_value(val) {}
value(int128_t val) : int128_value(val) {}
value(uint128_t val) : uint128_value(val) {}
value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {}
value(bool val) : bool_value(val) {}
@@ -764,6 +797,8 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
FMT_CONSTEXPR long long map(long long val) { return val; }
FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
FMT_CONSTEXPR bool map(bool val) { return val; }
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
@@ -818,8 +853,9 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR int map(const T& val) {
return static_cast<int>(val);
FMT_CONSTEXPR auto map(const T& val) -> decltype(
map(static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
@@ -893,8 +929,8 @@ template <typename Context> class basic_format_arg {
internal::type type() const { return type_; }
bool is_integral() const { return internal::is_integral(type_); }
bool is_arithmetic() const { return internal::is_arithmetic(type_); }
bool is_integral() const { return internal::is_integral_type(type_); }
bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
};
/**
@@ -923,6 +959,16 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(arg.value_.long_long_value);
case internal::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
case internal::int128_type:
return vis(arg.value_.int128_value);
case internal::uint128_type:
return vis(arg.value_.uint128_value);
#else
case internal::int128_type:
case internal::uint128_type:
break;
#endif
case internal::bool_type:
return vis(arg.value_.bool_value);
case internal::char_type:

View File

@@ -49,9 +49,6 @@
# pragma warning(push)
# pragma warning(disable : 4127) // conditional expression is constant
# pragma warning(disable : 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable : 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
@@ -81,7 +78,7 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
using format_func = void (*)(internal::buffer<char>&, int, string_view);
// Portable thread-safe version of strerror.
// A portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
@@ -158,7 +155,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
auto abs_value = static_cast<uint32_or_64_t<int>>(error_code);
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
@@ -352,6 +349,9 @@ template <typename T> struct bits {
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
};
class fp;
template <int SHIFT = 0> fp normalize(fp value);
// A handmade floating-point number f * pow(2, e).
class fp {
private:
@@ -396,28 +396,30 @@ class fp {
}
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0> void normalize() {
template <int SHIFT> friend fp normalize(fp value) {
// Handle subnormals.
auto shifted_implicit_bit = implicit_bit << SHIFT;
while ((f & shifted_implicit_bit) == 0) {
f <<= 1;
--e;
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
auto offset = significand_size - double_significand_size - SHIFT - 1;
f <<= offset;
e -= offset;
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
// Computes 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.
// 1 in normalize accounts for the exponent shift above.
upper = normalize<1>(fp((f << 1) + 1, e - 1));
lower.f <<= lower.e - upper.e;
lower.e = upper.e;
}
@@ -451,8 +453,8 @@ FMT_FUNC fp operator*(fp x, fp y) {
#endif
}
// 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 + 28.
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
int index = static_cast<int>(
@@ -466,6 +468,88 @@ FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
return fp(data::pow10_significands[index], data::pow10_exponents[index]);
}
class bigint {
private:
// A bigint is stored as an array of bigits (big digits), with bigit at index
// 0 being the least significant one.
using bigit = uint32_t;
using double_bigit = uint64_t;
basic_memory_buffer<bigit> bigits_;
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
void assign(uint64_t n) {
int num_bigits = bits<uint64_t>::value / bigit_bits;
bigits_.resize(num_bigits);
for (int i = 0; i < num_bigits; ++i) {
bigits_[i] = n & ~bigit(0);
n >>= bigit_bits;
}
}
public:
bigint() {}
explicit bigint(uint64_t n) { assign(n); }
// Assigns pow(10, exp) to this bigint.
void assign_pow10(int exp) {
assert(exp >= 0);
if (exp == 0) return assign(1);
// Find the top bit.
int bitmask = 1;
while (exp >= bitmask) bitmask <<= 1;
bitmask >>= 1;
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
// repeated squaring and multiplication.
assign(5);
bitmask >>= 1;
while (bitmask != 0) {
square();
if ((exp & bitmask) != 0) *this *= 5;
bitmask >>= 1;
}
*this <<= exp; // Multiply by pow(2, exp) by shifting.
}
bigint(const bigint&) = delete;
void operator=(const bigint&) = delete;
void square() {
// TODO
}
bigint& operator<<=(int shift) {
assert(shift >= 0 && shift < bigit_bits);
bigit carry = 0;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
bigit c = shift != 0 ? bigits_[i] >> (bigit_bits - shift) : 0;
bigits_[i] = (bigits_[i] << shift) + carry;
carry = c;
}
if (carry != 0) bigits_.push_back(carry);
return *this;
}
bigint& operator*=(uint32_t value) {
assert(value > 0);
// Verify that the computation doesn't overflow.
constexpr double_bigit max32 = max_value<bigit>();
constexpr double_bigit max64 = max_value<double_bigit>();
static_assert(max32 * max32 <= max64 - max32, "");
bigit carry = 0;
const double_bigit wide_value = value;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
double_bigit result = bigits_[i] * wide_value + carry;
bigits_[i] = static_cast<bigit>(result);
carry = static_cast<bigit>(result >> bigit_bits);
}
if (carry != 0) bigits_.push_back(carry);
return *this;
}
};
enum round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
@@ -686,35 +770,40 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
}
};
FMT_FUNC void fallback_format(const fp& value, int exp10) {
(void)value; // TODO
(void)exp10;
}
template <typename Double,
enable_if_t<(sizeof(Double) == sizeof(uint64_t)), int>>
FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
unsigned options, int& exp) {
FMT_ASSERT(value >= 0, "value is negative");
bool fixed = (options & grisu_options::fixed) != 0;
const bool fixed = (options & grisu_options::fixed) != 0;
if (value <= 0) { // <= instead of == to silence a warning.
if (precision <= 0 || !fixed) {
exp = 0;
buf.push_back('0');
} else {
exp = -precision;
buf.resize(precision);
buf.resize(to_unsigned(precision));
std::uninitialized_fill_n(buf.data(), precision, '0');
}
return true;
}
fp fp_value(value);
const fp fp_value(value);
const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu.
if (precision != -1) {
if (precision > 17) return false;
fp_value.normalize();
auto cached_pow = get_cached_power(
min_exp - (fp_value.e + fp::significand_size), cached_exp10);
fp_value = fp_value * cached_pow;
fp normalized = normalize(fp_value);
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(fp_value, 1, exp, handler) == digits::error)
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return false;
buf.resize(to_unsigned(handler.size));
} else {
@@ -722,10 +811,10 @@ FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 such that multiplying upper by it will bring
// the exponent in the range [min_exp, -32].
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
const auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
min_exp - (upper.e + fp::significand_size), cached_exp10);
fp_value.normalize();
fp_value = fp_value * cached_pow;
fp normalized = normalize(fp_value);
normalized = normalized * cached_pow;
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
assert(min_exp <= upper.e && upper.e <= -32);
@@ -735,17 +824,21 @@ FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
--lower.f; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
++upper.f; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
// Numbers outside of (lower, upper) definitely do not round to value.
grisu_shortest_handler<3> handler{buf.data(), 0, (upper - fp_value).f};
grisu_shortest_handler<3> handler{buf.data(), 0, (upper - normalized).f};
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
size = handler.size;
if (result == digits::error) {
fallback_format(fp_value, exp - cached_exp10);
return false;
}
} else {
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
grisu_shortest_handler<2> handler{buf.data(), 0, (upper - fp_value).f};
grisu_shortest_handler<2> handler{buf.data(), 0, (upper - normalized).f};
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
size = handler.size;
assert(result != digits::error);
}
if (result == digits::error) return false;
buf.resize(to_unsigned(size));
}
exp -= cached_exp10;
@@ -831,6 +924,29 @@ char* sprintf_format(Double value, internal::buffer<char>& buf,
}
} // namespace internal
template <> struct formatter<internal::bigint> {
format_parse_context::iterator parse(format_parse_context& ctx) {
return ctx.begin();
}
format_context::iterator format(const internal::bigint& n,
format_context& ctx) {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1];
if (first) {
if (value == 0 && i > 1) continue;
out = format_to(out, "{:x}", value);
first = false;
continue;
}
out = format_to(out, "{:08x}", value);
}
return out;
}
};
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {

View File

@@ -45,6 +45,14 @@
#include "core.h"
#ifndef FMT_USE_TEXT
# define FMT_USE_TEXT 0
#endif
#if FMT_USE_TEXT
# include <boost/text/grapheme_break.hpp>
# include <boost/text/transcode_iterator.hpp>
#endif
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
@@ -123,14 +131,6 @@ FMT_END_NAMESPACE
# endif
#endif
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
#else
# define FMT_USE_INT128 0
#endif
// __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
@@ -193,6 +193,10 @@ inline uint32_t clzll(uint64_t x) {
FMT_END_NAMESPACE
#endif
#ifndef FMT_NUMERIC_ALIGN
# define FMT_NUMERIC_ALIGN 1
#endif
FMT_BEGIN_NAMESPACE
namespace internal {
@@ -217,6 +221,12 @@ inline Dest bit_cast(const Source& source) {
return dest;
}
// Returns the largest possible value for type T. Same as
// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
template <typename T> constexpr T max_value() {
return (std::numeric_limits<T>::max)();
}
// An approximation of iterator_t for pre-C++20 systems.
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T&>()));
@@ -413,22 +423,6 @@ class output_range {
sentinel end() const { return {}; } // Sentinel is not used yet.
};
// A range with an iterator appending to a buffer.
template <typename T>
class buffer_range
: public output_range<std::back_insert_iterator<buffer<T>>, T> {
public:
using iterator = std::back_insert_iterator<buffer<T>>;
using output_range<iterator, T>::output_range;
buffer_range(buffer<T>& buf)
: output_range<iterator, T>(std::back_inserter(buf)) {}
};
template <typename Char>
inline size_t count_code_points(basic_string_view<Char> s) {
return s.size();
}
// Counts the number of code points in a UTF-8 string.
inline size_t count_code_points(basic_string_view<char8_t> s) {
const char8_t* data = s.data();
@@ -460,7 +454,7 @@ OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
}
#ifndef FMT_USE_GRISU
# define FMT_USE_GRISU 0
# define FMT_USE_GRISU 1
#endif
template <typename T> constexpr bool use_grisu() {
@@ -478,6 +472,17 @@ void buffer<T>::append(const U* begin, const U* end) {
}
} // namespace internal
// A range with an iterator appending to a buffer.
template <typename T>
class buffer_range : public internal::output_range<
std::back_insert_iterator<internal::buffer<T>>, T> {
public:
using iterator = std::back_insert_iterator<internal::buffer<T>>;
using internal::output_range<iterator, T>::output_range;
buffer_range(internal::buffer<T>& buf)
: internal::output_range<iterator, T>(std::back_inserter(buf)) {}
};
// A UTF-8 string view.
class u8string_view : public basic_string_view<char8_t> {
public:
@@ -644,11 +649,12 @@ FMT_CONSTEXPR bool is_negative(T) {
return false;
}
// Smallest of uint32_t and uint64_t that is large enough to represent all
// values of T.
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of T.
template <typename T>
using uint32_or_64_t =
conditional_t<std::numeric_limits<T>::digits <= 32, uint32_t, uint64_t>;
using uint32_or_64_or_128_t = conditional_t<
std::numeric_limits<T>::digits <= 32, uint32_t,
conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
// Static data is placed in this class template for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
@@ -697,6 +703,23 @@ inline int count_digits(uint64_t n) {
}
#endif
#if FMT_USE_INT128
inline int count_digits(uint128_t n) {
int count = 1;
for (;;) {
// Integer division is slow so do it for a group of four digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
if (n < 10) return count;
if (n < 100) return count + 1;
if (n < 1000) return count + 2;
if (n < 10000) return count + 3;
n /= 10000u;
count += 4;
}
}
#endif
// Counts the number of digits in n. BITS = log2(radix).
template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
int num_digits = 0;
@@ -826,12 +849,18 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
return end;
}
template <typename Int> constexpr int digits10() noexcept {
return std::numeric_limits<Int>::digits10;
}
template <> constexpr int digits10<int128_t>() noexcept { return 38; }
template <> constexpr int digits10<uint128_t>() noexcept { return 38; }
template <typename Char, typename UInt, typename Iterator, typename F>
inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
F add_thousands_sep) {
FMT_ASSERT(num_digits >= 0, "invalid digit count");
// Buffer should be large enough to hold all digits (<= digits10 + 1).
enum { max_size = std::numeric_limits<UInt>::digits10 + 1 };
enum { max_size = digits10<UInt>() + 1 };
Char buffer[max_size + max_size / 3];
auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
return internal::copy_str<Char>(buffer, end, out);
@@ -886,6 +915,59 @@ inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
return internal::copy_str<Char>(buffer, buffer + num_digits, out);
}
template <typename Char>
inline size_t compute_width(basic_string_view<Char> s) {
return s.size();
}
inline size_t compute_width(string_view s) {
#if FMT_USE_TEXT
basic_memory_buffer<uint32_t> code_points;
const char* s_end = s.data() + s.size();
boost::text::utf_8_to_32_iterator<const char*> begin(s.data(), s.data(),
s_end),
end(s.data(), s_end, s_end);
for (auto it = begin; it != end; ++it) code_points.push_back(*it);
size_t width = 0;
for (auto it = code_points.begin(), end = code_points.end(); it != end;
it = boost::text::next_grapheme_break(it, end)) {
auto cp = *it;
// Based on http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c by Markus Kuhn.
width +=
1 + (cp >= 0x1100 &&
(cp <= 0x115f || // Hangul Jamo init. consonants
cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET〈
cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET 〉
// CJK ... Yi except Unicode Character “〿”:
(cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
(cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables
(cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs
(cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms
(cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms
(cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms
(cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms
(cp >= 0x20000 && cp <= 0x2fffd) || // CJK
(cp >= 0x30000 && cp <= 0x3fffd) ||
// Miscellaneous Symbols and Pictographs + Emoticons:
(cp >= 0x1f300 && cp <= 0x1f64f) ||
// Supplemental Symbols and Pictographs:
(cp >= 0x1f900 && cp <= 0x1f9ff)));
}
return width;
#else
return s.size();
#endif // FMT_USE_TEXT
}
inline size_t compute_width(basic_string_view<char8_t> s) {
#if FMT_USE_TEXT
return compute_width(
string_view(reinterpret_cast<const char*>(s.data(), s.size())));
#else
return count_code_points(s);
#endif // FMT_USE_TEXT
}
#ifndef _WIN32
# define FMT_USE_WINDOWS_H 0
#elif !defined(FMT_USE_WINDOWS_H)
@@ -1332,7 +1414,7 @@ template <typename Range> class basic_writer {
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
auto abs_value = static_cast<uint32_or_64_t<Int>>(value);
auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
bool is_negative = internal::is_negative(value);
if (is_negative) abs_value = 0 - abs_value;
int num_digits = internal::count_digits(abs_value);
@@ -1344,7 +1426,7 @@ template <typename Range> class basic_writer {
// The handle_int_type_spec handler that writes an integer.
template <typename Int, typename Specs> struct int_writer {
using unsigned_type = uint32_or_64_t<Int>;
using unsigned_type = uint32_or_64_or_128_t<Int>;
basic_writer<Range>& writer;
const Specs& specs;
@@ -1425,7 +1507,7 @@ template <typename Range> class basic_writer {
void on_oct() {
int num_digits = internal::count_digits<3>(abs_value);
if (specs.alt && specs.precision <= num_digits) {
if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix[prefix_size++] = '0';
@@ -1557,7 +1639,7 @@ template <typename Range> class basic_writer {
size_t size() const { return size_; }
size_t width() const {
return internal::count_code_points(basic_string_view<Char>(s, size_));
return internal::compute_width(basic_string_view<Char>(s, size_));
}
template <typename It> void operator()(It&& it) const {
@@ -1621,6 +1703,11 @@ template <typename Range> class basic_writer {
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
#if FMT_USE_INT128
void write(int128_t value) { write_decimal(value); }
void write(uint128_t value) { write_decimal(value); }
#endif
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec) {
@@ -1702,6 +1789,10 @@ template <typename Range> class basic_writer {
using writer = basic_writer<buffer_range<char>>;
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_t> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
public:
@@ -1764,7 +1855,7 @@ class arg_formatter_base {
return out();
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
iterator operator()(T value) {
if (specs_)
writer_.write_int(value, *specs_);
@@ -1859,7 +1950,7 @@ FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
constexpr unsigned max_int = (std::numeric_limits<int>::max)();
constexpr unsigned max_int = max_value<int>();
unsigned big = max_int / 10;
do {
// Check for overflow.
@@ -1896,7 +1987,7 @@ template <typename Context> class custom_formatter {
template <typename T>
using is_integer =
bool_constant<std::is_integral<T>::value && !std::is_same<T, bool>::value &&
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
@@ -1981,20 +2072,20 @@ template <typename ErrorHandler> class numeric_specs_checker {
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic(arg_type_))
if (!is_arithmetic_type(arg_type_))
error_handler_.on_error("format specifier requires numeric argument");
}
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
if (is_integral(arg_type_) && arg_type_ != int_type &&
if (is_integral_type(arg_type_) && arg_type_ != int_type &&
arg_type_ != long_long_type && arg_type_ != internal::char_type) {
error_handler_.on_error("format specifier requires signed argument");
}
}
FMT_CONSTEXPR void check_precision() {
if (is_integral(arg_type_) || arg_type_ == internal::pointer_type)
if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type)
error_handler_.on_error("precision not allowed for this argument type");
}
@@ -2049,14 +2140,12 @@ template <typename Handler> class specs_checker : public Handler {
numeric_specs_checker<Handler> checker_;
};
template <template <typename> class Handler, typename T, typename FormatArg,
template <template <typename> class Handler, typename FormatArg,
typename ErrorHandler>
FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) {
unsigned long long big_value =
visit_format_arg(Handler<ErrorHandler>(eh), arg);
if (big_value > to_unsigned((std::numeric_limits<int>::max)()))
eh.on_error("number is too big");
value = static_cast<T>(big_value);
FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
return static_cast<int>(value);
}
struct auto_id {};
@@ -2081,13 +2170,13 @@ class specs_handler : public specs_setter<typename Context::char_type> {
context_(ctx) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width, get_arg(arg_id),
context_.error_handler());
this->specs_.width = get_dynamic_spec<width_checker>(
get_arg(arg_id), context_.error_handler());
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
set_dynamic_spec<precision_checker>(this->specs_.precision, get_arg(arg_id),
context_.error_handler());
this->specs_.precision = get_dynamic_spec<precision_checker>(
get_arg(arg_id), context_.error_handler());
}
void on_error(const char* message) { context_.on_error(message); }
@@ -2114,24 +2203,6 @@ class specs_handler : public specs_setter<typename Context::char_type> {
Context& context_;
};
struct string_view_metadata {
FMT_CONSTEXPR string_view_metadata() : offset_(0u), size_(0u) {}
template <typename Char>
FMT_CONSTEXPR string_view_metadata(basic_string_view<Char> primary_string,
basic_string_view<Char> view)
: offset_(to_unsigned(view.data() - primary_string.data())),
size_(view.size()) {}
FMT_CONSTEXPR string_view_metadata(std::size_t offset, std::size_t size)
: offset_(offset), size_(size) {}
template <typename Char>
FMT_CONSTEXPR basic_string_view<Char> to_view(const Char* str) const {
return {str + offset_, size_};
}
std::size_t offset_;
std::size_t size_;
};
enum class arg_id_kind { none, index, name };
// An argument reference.
@@ -2139,7 +2210,7 @@ template <typename Char> struct arg_ref {
FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
FMT_CONSTEXPR explicit arg_ref(int index)
: kind(arg_id_kind::index), val(index) {}
FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
: kind(arg_id_kind::name), val(name) {}
FMT_CONSTEXPR arg_ref& operator=(int idx) {
@@ -2152,10 +2223,10 @@ template <typename Char> struct arg_ref {
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(int id) : index(id) {}
FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
int index;
string_view_metadata name;
basic_string_view<Char> name;
} val;
};
@@ -2213,8 +2284,7 @@ class dynamic_specs_handler
context_.check_arg_id(arg_id);
basic_string_view<char_type> format_str(
context_.begin(), to_unsigned(context_.end() - context_.begin()));
const auto id_metadata = string_view_metadata(format_str, arg_id);
return arg_ref_type(id_metadata);
return arg_ref_type(arg_id);
}
dynamic_format_specs<char_type>& specs_;
@@ -2294,9 +2364,11 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
case '>':
align = align::right;
break;
#if FMT_NUMERIC_ALIGN
case '=':
align = align::numeric;
break;
#endif
case '^':
align = align::center;
break;
@@ -2439,7 +2511,7 @@ template <typename Handler, typename Char> struct id_adapter {
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) {
struct writer {
struct pfs_writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
@@ -2509,7 +2581,7 @@ class format_string_checker {
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
: arg_id_((std::numeric_limits<unsigned>::max)()),
: arg_id_(max_value<unsigned>()),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
@@ -2573,32 +2645,29 @@ void check_format_string(S format_str) {
(void)invalid_format;
}
template <template <typename> class Handler, typename Spec, typename Context>
void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
Context& ctx,
const typename Context::char_type* format_str) {
template <template <typename> class Handler, typename Context>
void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
Context& ctx) {
switch (ref.kind) {
case arg_id_kind::none:
break;
case arg_id_kind::index:
internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
ctx.error_handler());
value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
ctx.error_handler());
break;
case arg_id_kind::name: {
const auto arg_id = ref.val.name.to_view(format_str);
internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id),
ctx.error_handler());
case arg_id_kind::name:
value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
ctx.error_handler());
break;
}
}
}
} // namespace internal
template <typename Range>
using basic_writer FMT_DEPRECATED = internal::basic_writer<Range>;
using writer FMT_DEPRECATED = internal::writer;
using wwriter FMT_DEPRECATED =
internal::basic_writer<internal::buffer_range<wchar_t>>;
using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
using writer FMT_DEPRECATED_ALIAS = internal::writer;
using wwriter FMT_DEPRECATED_ALIAS =
internal::basic_writer<buffer_range<wchar_t>>;
/** The default argument formatter. */
template <typename Range>
@@ -2777,12 +2846,12 @@ void internal::basic_writer<Range>::write_double(T value,
memory_buffer buffer;
int exp = 0;
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
unsigned options = handler.fixed ? internal::grisu_options::fixed : 0;
bool use_grisu = USE_GRISU &&
(specs.type != 'a' && specs.type != 'A' &&
specs.type != 'e' && specs.type != 'E') &&
internal::grisu_format(static_cast<double>(value), buffer,
precision, options, exp);
internal::grisu_format(
static_cast<double>(value), buffer, precision,
handler.fixed ? internal::grisu_options::fixed : 0, exp);
char* decimal_point_pos = nullptr;
if (!use_grisu)
decimal_point_pos = internal::sprintf_format(value, buffer, specs);
@@ -2954,13 +3023,12 @@ template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<internal::type_constant<T, Char>::value !=
internal::custom_type>> {
FMT_CONSTEXPR formatter() : format_str_(nullptr) {}
FMT_CONSTEXPR formatter() {}
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin();
using handler_type = internal::dynamic_specs_handler<ParseContext>;
auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
@@ -2976,6 +3044,8 @@ struct formatter<T, Char,
case internal::uint_type:
case internal::long_long_type:
case internal::ulong_long_type:
case internal::int128_type:
case internal::uint128_type:
case internal::bool_type:
handle_int_type_spec(specs_.type,
internal::int_type_checker<decltype(eh)>(eh));
@@ -3010,9 +3080,9 @@ struct formatter<T, Char,
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
internal::handle_dynamic_spec<internal::width_checker>(
specs_.width, specs_.width_ref, ctx, format_str_);
specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_);
specs_.precision, specs_.precision_ref, ctx);
using range_type =
internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type>;
@@ -3022,7 +3092,6 @@ struct formatter<T, Char,
private:
internal::dynamic_format_specs<Char> specs_;
const Char* format_str_;
};
#define FMT_FORMAT_AS(Type, Base) \
@@ -3123,9 +3192,9 @@ template <typename Char = char> class dynamic_formatter {
private:
template <typename Context> void handle_specs(Context& ctx) {
internal::handle_dynamic_spec<internal::width_checker>(
specs_.width, specs_.width_ref, ctx, format_str_);
specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_);
specs_.precision, specs_.precision_ref, ctx);
}
internal::dynamic_format_specs<Char> specs_;
@@ -3466,7 +3535,7 @@ template <typename Char, Char... CHARS> class udl_formatter {
std::basic_string<Char> operator()(Args&&... args) const {
FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
FMT_CONSTEXPR_DECL bool invalid_format =
do_check_format_string<Char, error_handler, Args...>(
do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
basic_string_view<Char>(s, sizeof...(CHARS)));
(void)invalid_format;
return format(s, std::forward<Args>(args)...);

View File

@@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
@@ -56,9 +60,9 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype((void)(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
std::true_type())
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
@@ -75,8 +79,7 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size =
to_unsigned((std::numeric_limits<std::streamsize>::max)());
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));

View File

@@ -24,7 +24,7 @@ template <typename T> inline T const_check(T value) { return value; }
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max();
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
@@ -33,7 +33,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
@@ -158,12 +158,12 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value);
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
@@ -339,7 +339,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
@@ -368,8 +368,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter =
printf_arg_formatter<internal::buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format();
};
@@ -403,7 +402,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max())
if (arg_index == internal::max_value<unsigned>())
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
@@ -413,7 +412,7 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
unsigned arg_index = internal::max_value<unsigned>();
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly

View File

@@ -246,7 +246,8 @@ template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value;
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
@@ -283,6 +284,82 @@ struct formatter<RangeT, Char,
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@@ -62,7 +62,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (from < 0) {
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
@@ -161,7 +161,8 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
@@ -182,14 +183,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 =
std::numeric_limits<IntermediateRep>::max() / Factor::num;
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 =
std::numeric_limits<IntermediateRep>::min() / Factor::num;
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
@@ -234,7 +233,8 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
@@ -257,7 +257,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;

View File

@@ -12,7 +12,7 @@
#include "fmt/posix.h"
#include <limits.h>
#include <climits>
#include <sys/stat.h>
#include <sys/types.h>
@@ -49,7 +49,7 @@
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
@@ -58,7 +58,7 @@ inline unsigned convert_rwcount(std::size_t count) {
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
@@ -138,7 +138,7 @@ long long file::size() const {
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

3589
src/text/grapheme_break.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: fmt
Description: A modern formatting library

View File

@@ -84,6 +84,9 @@ function(add_fmt_test name)
if (FMT_PEDANTIC)
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (FMT_WERROR)
target_compile_options(${name} PRIVATE ${WERROR_FLAG})
endif ()
target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
add_test(NAME ${name} COMMAND ${name})
endfunction()
@@ -96,6 +99,9 @@ add_fmt_test(grisu-test)
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
add_fmt_test(gtest-extra-test)
add_fmt_test(format-test mock-allocator.h)
if (MSVC)
target_compile_options(format-test PRIVATE /bigobj)
endif ()
if (NOT (MSVC AND BUILD_SHARED_LIBS))
add_fmt_test(format-impl-test)
endif ()
@@ -107,7 +113,7 @@ add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
if (HAVE_OPEN)
if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
@@ -225,3 +231,21 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()
# Activate optional CUDA tests if CUDA is found. For version selection, see
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()

View File

@@ -47,7 +47,7 @@ TEST(ColorsTest, ColorsPrint) {
"\x1b[105mtbmagenta\x1b[0m");
}
TEST(ColorsTest, ColorsFormat) {
TEST(ColorsTest, Format) {
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
@@ -78,4 +78,6 @@ TEST(ColorsTest, ColorsFormat) {
"\x1b[92mtbgreen\x1b[0m");
EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
"\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m");
}

View File

@@ -34,205 +34,17 @@
using testing::Return;
using testing::StrictMock;
FMT_BEGIN_NAMESPACE
namespace internal {
bool operator==(const internal::string_view_metadata& lhs,
const internal::string_view_metadata& rhs) {
return std::tie(lhs.offset_, lhs.size_) == std::tie(rhs.offset_, rhs.size_);
}
bool operator!=(const internal::string_view_metadata& lhs,
const internal::string_view_metadata& rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char>::specification& lhs,
const format_part<char>::specification& rhs) {
if (lhs.arg_id.which != rhs.arg_id.which) {
return false;
}
typedef format_part<char>::argument_id::which_arg_id which_arg_id;
switch (lhs.arg_id.which) {
case which_arg_id::index: {
if (lhs.arg_id.val.index != rhs.arg_id.val.index) {
return false;
}
} break;
case which_arg_id::named_index: {
if (lhs.arg_id.val.named_index != rhs.arg_id.val.named_index) {
return false;
}
} break;
}
return std::tie(lhs.parsed_specs.width, lhs.parsed_specs.fill[0],
lhs.parsed_specs.align, lhs.parsed_specs.precision,
lhs.parsed_specs.sign, lhs.parsed_specs.type) ==
std::tie(rhs.parsed_specs.width, rhs.parsed_specs.fill[0],
rhs.parsed_specs.align, rhs.parsed_specs.precision,
rhs.parsed_specs.sign, rhs.parsed_specs.type);
}
bool operator!=(const format_part<char>::specification& lhs,
const format_part<char>::specification& rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char>& lhs,
const fmt::internal::format_part<char>& rhs) {
typedef format_part<char>::kind kind;
if (lhs.which != rhs.which ||
lhs.end_of_argument_id != rhs.end_of_argument_id) {
return false;
}
switch (lhs.which) {
case kind::argument_id: {
return lhs.val.arg_id == rhs.val.arg_id;
}
case kind::named_argument_id: {
return lhs.val.named_arg_id == rhs.val.named_arg_id;
}
case kind::text: {
return lhs.val.text == rhs.val.text;
}
case kind::specification: {
return lhs.val.spec == rhs.val.spec;
}
}
return false;
}
bool operator!=(const fmt::internal::format_part<char>& lhs,
const fmt::internal::format_part<char>& rhs) {
return !(lhs == rhs);
}
}
FMT_END_NAMESPACE
TEST(CompileTest, FormatPart_ComparisonOperators) {
typedef fmt::internal::format_part<char> format_part;
typedef fmt::internal::dynamic_format_specs<char> prepared_specs;
{
const auto part = format_part(0u);
const auto other = format_part(0u);
EXPECT_EQ(part, other);
}
{
const auto lhs = format_part(0u);
const auto rhs = format_part(1u);
EXPECT_NE(lhs, rhs);
}
{
const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42));
const auto rhs = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_EQ(lhs, rhs);
}
{
const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42));
const auto rhs = format_part(fmt::internal::string_view_metadata(0, 4422));
EXPECT_NE(lhs, rhs);
}
{
auto lhs = format_part(0u);
auto rhs = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_NE(lhs, rhs);
rhs = format_part(fmt::internal::string_view_metadata(0, 0));
EXPECT_NE(lhs, rhs);
}
{
auto lhs = format_part(0u);
lhs.end_of_argument_id = 42;
auto rhs = format_part(0u);
rhs.end_of_argument_id = 42;
EXPECT_EQ(lhs, rhs);
rhs.end_of_argument_id = 13;
EXPECT_NE(lhs, rhs);
}
{
const auto specs_argument_id = 0u;
const auto specs_named_argument_id =
fmt::internal::string_view_metadata(0, 42);
auto specs = format_part::specification(0u);
auto lhs = format_part(specs);
auto rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs = format_part::specification(specs_named_argument_id);
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
auto lhs_spec = format_part::specification(specs_argument_id);
auto rhs_spec = format_part::specification(specs_named_argument_id);
lhs = format_part(lhs_spec);
rhs = format_part(rhs_spec);
EXPECT_NE(lhs, rhs);
lhs_spec = format_part::specification(specs_argument_id);
rhs_spec = format_part::specification(specs_argument_id);
lhs_spec.parsed_specs.precision = 1;
rhs_spec.parsed_specs.precision = 2;
lhs = format_part(lhs_spec);
rhs = format_part(rhs_spec);
EXPECT_NE(lhs, rhs);
}
{
const auto specs_argument_id = 0u;
const auto specs_named_argument_id =
fmt::internal::string_view_metadata(0, 42);
auto specs = format_part::specification(specs_argument_id);
auto lhs = format_part(specs);
auto rhs = format_part(0u);
auto rhs2 = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs.parsed_specs = prepared_specs();
lhs = format_part{specs};
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs = format_part::specification(specs_named_argument_id);
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
}
}
// compiletime_prepared_parts_type_provider is useful only with relaxed
// constexpr.
#if FMT_USE_CONSTEXPR
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
void check_prepared_parts_type(Format format) {
typedef fmt::internal::compiletime_prepared_parts_type_provider<decltype(
format)>
provider;
typedef typename provider::template format_parts_array<EXPECTED_PARTS_COUNT>
expected_parts_type;
static_assert(
std::is_same<typename provider::type, expected_parts_type>::value,
"CompileTimePreparedPartsTypeProvider test failed");
typedef fmt::internal::compiled_format_base<decltype(format)> provider;
typedef fmt::internal::format_part<char>
expected_parts_type[EXPECTED_PARTS_COUNT];
static_assert(std::is_same<typename provider::parts_container,
expected_parts_type>::value,
"CompileTimePreparedPartsTypeProvider test failed");
}
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
@@ -253,40 +65,6 @@ TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
}
#endif
class custom_parts_container {
public:
typedef fmt::internal::format_part<char> format_part_type;
private:
typedef std::deque<format_part_type> parts;
public:
void add(format_part_type part) { parts_.push_back(std::move(part)); }
void substitute_last(format_part_type part) {
parts_.back() = std::move(part);
}
format_part_type last() { return parts_.back(); }
auto begin() -> decltype(std::declval<parts>().begin()) {
return parts_.begin();
}
auto begin() const -> decltype(std::declval<const parts>().begin()) {
return parts_.begin();
}
auto end() -> decltype(std::declval<parts>().begin()) { return parts_.end(); }
auto end() const -> decltype(std::declval<const parts>().begin()) {
return parts_.end();
}
private:
parts parts_;
};
TEST(CompileTest, PassStringLiteralFormat) {
const auto prepared = fmt::compile<int>("test {}");
EXPECT_EQ("test 42", fmt::format(prepared, 42));
@@ -338,3 +116,28 @@ TEST(CompileTest, FormattedSize) {
auto f = fmt::compile<int>("{:10}");
EXPECT_EQ(fmt::formatted_size(f, 42), 10);
}
TEST(CompileTest, MultipleTypes) {
auto f = fmt::compile<int, int>("{} {}");
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
}
struct formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<formattable> : formatter<const char*> {
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
return formatter<const char*>::format("foo", ctx);
}
};
FMT_END_NAMESPACE
TEST(CompileTest, FormatUserDefinedType) {
auto f = fmt::compile<formattable>("{}");
EXPECT_EQ(fmt::format(f, formattable()), "foo");
}
TEST(CompileTest, EmptyFormatString) {
auto f = fmt::compile<>("");
EXPECT_EQ(fmt::format(f), "");
}

View File

@@ -0,0 +1,73 @@
# We can find some usecases which follow the guide of CMake which uses
# `enable_language(CUDA)` instead of `find_package(CUDA)` and let the CMake
# built-in functions use NVCC.
# See: https://cmake.org/cmake/help/latest/module/FindCUDA.html#replacement
#
# However, this requires CMake version 3.10 or higher and we can't be sure most
# of the CUDA projects are using those.
#
# This test relies on `find_package(CUDA)` in the parent CMake config.
# These can be updated when NVCC becomes ready for C++ 17 features
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
set(CMAKE_CUDA_STANDARD 14)
set(CMAKE_CUDA_STANDARD_REQUIRED 14)
# In this test, we assume that the user is going to compile CUDA source code
# with some libraries (fmt in this case).
#
# In addition to that, this test invokes both the C++ host compiler and NVCC
# by providing another (non-CUDA) C++ source code.
if (${CMAKE_VERSION} VERSION_LESS 3.15)
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html
list(APPEND CUDA_NVCC_FLAGS "-std=c++14")
if (MSVC)
# This is the solution of pytorch:
# https://github.com/pytorch/pytorch/pull/7118
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/std:c++14")
list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/Zc:__cplusplus")
# for the reason of this -Xcompiler options, see below.
endif ()
cuda_add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
if (MSVC)
# This part is for (non-CUDA) C++ code. MSVC can define incorrect
# `__cplusplus` macro. Fix for the issue is to use additional compiler flag.
#
# See Also:
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
# https://github.com/Microsoft/vscode-cpptools/issues/2595
target_compile_options(fmt-in-cuda-test PRIVATE /Zc:__cplusplus /permissive-)
endif ()
else()
# now using a "new" way of handling CUDA
add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc)
set_target_properties(fmt-in-cuda-test PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14)
if (MSVC)
# with MSVC, 'cxx_std_14' will only propagate to the host code (MSVC), but will
# not set __cplusplus correctly anyway, while nvcc will ignore it.
# If specified for nvcc on the command line as '-std=c++14' nvcc will emit this
# message instead:
# nvcc warning : The -std=c++14 flag is not supported with the configured host
# compiler. Flag will be ignored.
set_property(SOURCE cuda-cpp14.cu APPEND PROPERTY
COMPILE_OPTIONS -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus)
set_property(SOURCE cpp14.cc APPEND PROPERTY
COMPILE_OPTIONS /std:c++14 /Zc:__cplusplus)
endif()
endif()
get_target_property(IN_USE_CUDA_STANDARD fmt-in-cuda-test CUDA_STANDARD)
message(STATUS "cuda_standard: ${IN_USE_CUDA_STANDARD}")
get_target_property(IN_USE_CUDA_STANDARD_REQUIRED
fmt-in-cuda-test CUDA_STANDARD_REQUIRED)
message(STATUS "cuda_standard_required: ${IN_USE_CUDA_STANDARD_REQUIRED}")
# We don't use PUBLIC or other keyword for reasons explained in the
# CUDA_LINK_LIBRARIES_KEYWORD section in
# https://cmake.org/cmake/help/latest/module/FindCUDA.html
target_link_libraries(fmt-in-cuda-test fmt::fmt)

11
test/cuda-test/cpp14.cc Normal file
View File

@@ -0,0 +1,11 @@
#include <fmt/core.h>
// The purpose of this part is to ensure NVCC's host compiler also supports
// the standard version. See 'cuda-cpp14.cu'.
//
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for host compiler");
auto make_message_cpp() -> std::string {
return fmt::format("host compiler \t: __cplusplus == {}", __cplusplus);
}

View File

@@ -0,0 +1,28 @@
// Direct NVCC command line example:
//
// nvcc ./cuda-cpp14.cu -x cu -I"../include" -l"fmtd" -L"../build/Debug" \
// -std=c++14 -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus
// Ensure that we are using the latest C++ standard for NVCC
// The version is C++14
//
// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-cplusplus-language-support
// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros
static_assert(__cplusplus >= 201402L, "expect C++ 2014 for nvcc");
#include <fmt/core.h>
#include <cuda.h>
#include <iostream>
extern auto make_message_cpp() -> std::string;
extern auto make_message_cuda() -> std::string;
int main() {
std::cout << make_message_cuda() << std::endl;
std::cout << make_message_cpp() << std::endl;
}
auto make_message_cuda() -> std::string {
return fmt::format("nvcc compiler \t: __cplusplus == {}", __cplusplus);
}

View File

@@ -18,9 +18,9 @@
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class custom_arg_formatter
: public fmt::arg_formatter<fmt::internal::buffer_range<char>> {
: public fmt::arg_formatter<fmt::buffer_range<char>> {
public:
using range = fmt::internal::buffer_range<char>;
using range = fmt::buffer_range<char>;
typedef fmt::arg_formatter<range> base;
custom_arg_formatter(fmt::format_context& ctx,

View File

@@ -690,9 +690,9 @@ struct formatter {
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
specs_.width, specs_.width_ref, ctx, nullptr);
specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, nullptr);
specs_.precision, specs_.precision_ref, ctx);
using range_type = fmt::internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
@@ -718,7 +718,7 @@ template<class... Args>
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::internal::buffer<char>& buf = mbuf;
using range = fmt::internal::buffer_range<char>;
using range = fmt::buffer_range<char>;
detail::format_handler<detail::arg_formatter<range>, char, format_context>
h(range(std::back_inserter(buf)), fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);

View File

@@ -22,7 +22,42 @@
#undef max
using fmt::internal::bigint;
using fmt::internal::fp;
using fmt::internal::max_value;
static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
TEST(BigIntTest, Construct) {
EXPECT_EQ("", fmt::format("{}", bigint()));
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
}
TEST(BigIntTest, ShiftLeft) {
bigint n(0x42);
n <<= 0;
EXPECT_EQ("42", fmt::format("{}", n));
n <<= 1;
EXPECT_EQ("84", fmt::format("{}", n));
n <<= 25;
EXPECT_EQ("108000000", fmt::format("{}", n));
}
TEST(BigIntTest, Multiply) {
bigint n(0x42);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
auto max = max_value<uint32_t>();
bigint bigmax(max);
bigmax *= max;
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
}
template <bool is_iec559> void test_construct_from_double() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
@@ -39,10 +74,10 @@ TEST(FPTest, ConstructFromDouble) {
}
TEST(FPTest, Normalize) {
auto v = fp(0xbeef, 42);
v.normalize();
EXPECT_EQ(0xbeef000000000000, v.f);
EXPECT_EQ(-6, v.e);
const auto v = fp(0xbeef, 42);
auto normalized = normalize(v);
EXPECT_EQ(0xbeef000000000000, normalized.f);
EXPECT_EQ(-6, normalized.e);
}
TEST(FPTest, ComputeBoundariesSubnormal) {
@@ -100,7 +135,7 @@ TEST(FPTest, GetRoundDirection) {
EXPECT_EQ(fmt::internal::up, get_round_direction(100, 60, 10));
for (int i = 41; i < 60; ++i)
EXPECT_EQ(fmt::internal::unknown, get_round_direction(100, i, 10));
uint64_t max = std::numeric_limits<uint64_t>::max();
uint64_t max = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
@@ -132,7 +167,7 @@ TEST(FPTest, FixedHandler) {
// Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
// Check that 2 * error doesn't overflow.
uint64_t max = std::numeric_limits<uint64_t>::max();
uint64_t max = max_value<uint64_t>();
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false),
digits::error);
}
@@ -149,10 +184,21 @@ template <typename T> struct value_extractor {
template <typename U> FMT_NORETURN T operator()(U) {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
}
#ifdef __apple_build_version__
// Apple Clang does not define typeid for __int128_t and __uint128_t.
FMT_NORETURN T operator()(__int128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__int128_t"));
}
FMT_NORETURN T operator()(__uint128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__uint128_t"));
}
#endif
};
TEST(FormatTest, ArgConverter) {
long long value = std::numeric_limits<long long>::max();
long long value = max_value<long long>();
auto arg = fmt::internal::make_arg<fmt::format_context>(value);
fmt::visit_format_arg(
fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
@@ -243,7 +289,7 @@ TEST(FormatTest, CountCodePoints) {
// Tests fmt::internal::count_digits for integer type Int.
template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i));
for (Int i = 1, n = 1, end = std::numeric_limits<Int>::max() / 10; n <= end;
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end;
++i) {
n *= 10;
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));

View File

@@ -35,6 +35,7 @@ using std::size_t;
using fmt::basic_memory_buffer;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using fmt::format;
using fmt::format_error;
using fmt::memory_buffer;
@@ -99,7 +100,7 @@ void std_format(long double value, std::wstring& result) {
template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::internal::buffer_range<Char>;
using range = fmt::buffer_range<Char>;
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
@@ -162,7 +163,7 @@ TEST(UtilTest, Increment) {
}
TEST(UtilTest, ParseNonnegativeInt) {
if (std::numeric_limits<int>::max() !=
if (max_value<int>() !=
static_cast<int>(static_cast<unsigned>(1) << 31)) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
@@ -477,7 +478,7 @@ TEST(UtilTest, FormatSystemError) {
message = fmt::memory_buffer();
// Check if std::allocator throws on allocating max size_t / 2 chars.
size_t max_size = std::numeric_limits<size_t>::max() / 2;
size_t max_size = max_value<size_t>() / 2;
bool throws_on_alloc = false;
try {
std::allocator<char> alloc;
@@ -527,7 +528,7 @@ TEST(UtilTest, FormatWindowsError) {
actual_message.resize(0);
fmt::internal::format_windows_error(
actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, std::numeric_limits<size_t>::max()));
fmt::string_view(0, max_value<size_t>()));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message));
}
@@ -593,31 +594,31 @@ TEST(WriterTest, WriteInt) {
CHECK_WRITE(static_cast<short>(12));
CHECK_WRITE(34u);
CHECK_WRITE(std::numeric_limits<int>::min());
CHECK_WRITE(std::numeric_limits<int>::max());
CHECK_WRITE(std::numeric_limits<unsigned>::max());
CHECK_WRITE(max_value<int>());
CHECK_WRITE(max_value<unsigned>());
}
TEST(WriterTest, WriteLong) {
CHECK_WRITE(56l);
CHECK_WRITE(78ul);
CHECK_WRITE(std::numeric_limits<long>::min());
CHECK_WRITE(std::numeric_limits<long>::max());
CHECK_WRITE(std::numeric_limits<unsigned long>::max());
CHECK_WRITE(max_value<long>());
CHECK_WRITE(max_value<unsigned long>());
}
TEST(WriterTest, WriteLongLong) {
CHECK_WRITE(56ll);
CHECK_WRITE(78ull);
CHECK_WRITE(std::numeric_limits<long long>::min());
CHECK_WRITE(std::numeric_limits<long long>::max());
CHECK_WRITE(std::numeric_limits<unsigned long long>::max());
CHECK_WRITE(max_value<long long>());
CHECK_WRITE(max_value<unsigned long long>());
}
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
auto min = std::numeric_limits<double>::min();
auto max = std::numeric_limits<double>::max();
auto max = max_value<double>();
if (fmt::internal::use_grisu<double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
@@ -637,7 +638,7 @@ TEST(WriterTest, WriteLongDouble) {
else
fmt::print("warning: long double formatting with std::swprintf is broken");
auto min = std::numeric_limits<long double>::min();
auto max = std::numeric_limits<long double>::max();
auto max = max_value<long double>();
if (fmt::internal::use_grisu<long double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
@@ -697,12 +698,6 @@ TEST(FormatToTest, WideString) {
EXPECT_STREQ(buf.data(), L"42");
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42.0", buffer);
}
TEST(FormatToTest, FormatToMemoryBuffer) {
fmt::basic_memory_buffer<char, 100> buffer;
fmt::format_to(buffer, "{}", "foo");
@@ -856,6 +851,7 @@ TEST(FormatterTest, RightAlign) {
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
}
#if FMT_NUMERIC_ALIGN
TEST(FormatterTest, NumericAlign) {
EXPECT_EQ(" 42", format("{0:=4}", 42));
EXPECT_EQ("+ 42", format("{0:=+4}", 42));
@@ -882,6 +878,13 @@ TEST(FormatterTest, NumericAlign) {
EXPECT_EQ(" 1.0", fmt::format("{:= }", 1.0));
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42.0", buffer);
}
#endif
TEST(FormatterTest, CenterAlign) {
EXPECT_EQ(" 42 ", format("{0:^5}", 42));
EXPECT_EQ(" 42 ", format("{0:^5o}", 042));
@@ -1001,6 +1004,7 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("0x42", format("{0:#x}", 0x42));
EXPECT_EQ("0X42", format("{0:#X}", 0x42));
EXPECT_EQ("-0x42", format("{0:#x}", -0x42));
EXPECT_EQ("0", format("{0:#o}", 0));
EXPECT_EQ("042", format("{0:#o}", 042));
EXPECT_EQ("-042", format("{0:#o}", -042));
EXPECT_EQ("42", format("{0:#}", 42u));
@@ -1330,9 +1334,17 @@ TEST(FormatterTest, FormatBin) {
EXPECT_EQ("10010001101000101011001111000", format("{0:b}", 0x12345678));
EXPECT_EQ("10010000101010111100110111101111", format("{0:b}", 0x90ABCDEF));
EXPECT_EQ("11111111111111111111111111111111",
format("{0:b}", std::numeric_limits<uint32_t>::max()));
format("{0:b}", max_value<uint32_t>()));
}
#if FMT_USE_INT128
constexpr auto int128_max = static_cast<__int128_t>(
(static_cast<__uint128_t>(1) << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1);
constexpr auto int128_min = -int128_max - 1;
constexpr auto uint128_max = ~static_cast<__uint128_t>(0);
#endif
TEST(FormatterTest, FormatDec) {
EXPECT_EQ("0", format("{0}", 0));
EXPECT_EQ("42", format("{0}", 42));
@@ -1341,6 +1353,23 @@ TEST(FormatterTest, FormatDec) {
EXPECT_EQ("-42", format("{0}", -42));
EXPECT_EQ("12345", format("{0}", 12345));
EXPECT_EQ("67890", format("{0}", 67890));
#if FMT_USE_INT128
EXPECT_EQ("0", format("{0}", static_cast<__int128_t>(0)));
EXPECT_EQ("0", format("{0}", static_cast<__uint128_t>(0)));
EXPECT_EQ("9223372036854775808",
format("{0}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-9223372036854775809",
format("{0}", static_cast<__int128_t>(INT64_MIN) - 1));
EXPECT_EQ("18446744073709551616",
format("{0}", static_cast<__int128_t>(UINT64_MAX) + 1));
EXPECT_EQ("170141183460469231731687303715884105727",
format("{0}", int128_max));
EXPECT_EQ("-170141183460469231731687303715884105728",
format("{0}", int128_min));
EXPECT_EQ("340282366920938463463374607431768211455",
format("{0}", uint128_max));
#endif
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%d", INT_MIN);
EXPECT_EQ(buffer, format("{0}", INT_MIN));
@@ -1365,6 +1394,19 @@ TEST(FormatterTest, FormatHex) {
EXPECT_EQ("90abcdef", format("{0:x}", 0x90abcdef));
EXPECT_EQ("12345678", format("{0:X}", 0x12345678));
EXPECT_EQ("90ABCDEF", format("{0:X}", 0x90ABCDEF));
#if FMT_USE_INT128
EXPECT_EQ("0", format("{0:x}", static_cast<__int128_t>(0)));
EXPECT_EQ("0", format("{0:x}", static_cast<__uint128_t>(0)));
EXPECT_EQ("8000000000000000",
format("{0:x}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-8000000000000001",
format("{0:x}", static_cast<__int128_t>(INT64_MIN) - 1));
EXPECT_EQ("10000000000000000",
format("{0:x}", static_cast<__int128_t>(UINT64_MAX) + 1));
EXPECT_EQ("7fffffffffffffffffffffffffffffff", format("{0:x}", int128_max));
EXPECT_EQ("-80000000000000000000000000000000", format("{0:x}", int128_min));
EXPECT_EQ("ffffffffffffffffffffffffffffffff", format("{0:x}", uint128_max));
#endif
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "-%x", 0 - static_cast<unsigned>(INT_MIN));
@@ -1387,6 +1429,23 @@ TEST(FormatterTest, FormatOct) {
EXPECT_EQ("42", format("{0:o}", 042u));
EXPECT_EQ("-42", format("{0:o}", -042));
EXPECT_EQ("12345670", format("{0:o}", 012345670));
#if FMT_USE_INT128
EXPECT_EQ("0", format("{0:o}", static_cast<__int128_t>(0)));
EXPECT_EQ("0", format("{0:o}", static_cast<__uint128_t>(0)));
EXPECT_EQ("1000000000000000000000",
format("{0:o}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-1000000000000000000001",
format("{0:o}", static_cast<__int128_t>(INT64_MIN) - 1));
EXPECT_EQ("2000000000000000000000",
format("{0:o}", static_cast<__int128_t>(UINT64_MAX) + 1));
EXPECT_EQ("1777777777777777777777777777777777777777777",
format("{0:o}", int128_max));
EXPECT_EQ("-2000000000000000000000000000000000000000000",
format("{0:o}", int128_min));
EXPECT_EQ("3777777777777777777777777777777777777777777",
format("{0:o}", uint128_max));
#endif
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "-%o", 0 - static_cast<unsigned>(INT_MIN));
EXPECT_EQ(buffer, format("{0:o}", INT_MIN));
@@ -1407,7 +1466,7 @@ TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("1,234", format("{:n}", 1234));
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
EXPECT_EQ("4,294,967,295",
format("{:n}", std::numeric_limits<uint32_t>::max()));
format("{:n}", max_value<uint32_t>()));
}
struct ConvertibleToLongLong {
@@ -1745,9 +1804,9 @@ TEST(FormatIntTest, FormatInt) {
EXPECT_EQ("42", fmt::format_int(42ull).str());
EXPECT_EQ("-42", fmt::format_int(-42ll).str());
std::ostringstream os;
os << std::numeric_limits<int64_t>::max();
os << max_value<int64_t>();
EXPECT_EQ(os.str(),
fmt::format_int(std::numeric_limits<int64_t>::max()).str());
fmt::format_int(max_value<int64_t>()).str());
}
TEST(FormatTest, Print) {
@@ -1894,6 +1953,11 @@ TEST(FormatTest, UdlTemplate) {
EXPECT_EQ("foo", "foo"_format());
EXPECT_EQ(" 42", "{0:10}"_format(42));
}
TEST(FormatTest, UdlPassUserDefinedObjectAsLvalue) {
Date date(2015, 10, 21);
EXPECT_EQ("2015-10-21", "{}"_format(date));
}
#endif // FMT_USE_USER_DEFINED_LITERALS
enum TestEnum { A };
@@ -1906,17 +1970,23 @@ TEST(FormatTest, FormatterNotSpecialized) {
}
#if FMT_HAS_FEATURE(cxx_strong_enums)
enum TestFixedEnum : short { B };
enum big_enum : unsigned long long { big_enum_value = 5000000000ULL };
TEST(FormatTest, FixedEnum) { EXPECT_EQ("0", fmt::format("{}", B)); }
TEST(FormatTest, StrongEnum) {
EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value));
}
#endif
using buffer_range = fmt::internal::buffer_range<char>;
using buffer_range = fmt::buffer_range<char>;
class mock_arg_formatter
: public fmt::internal::arg_formatter_base<buffer_range> {
private:
#if FMT_USE_INT128
MOCK_METHOD1(call, void(__int128_t value));
#else
MOCK_METHOD1(call, void(long long value));
#endif
public:
typedef fmt::internal::arg_formatter_base<buffer_range> base;
@@ -1929,14 +1999,14 @@ class mock_arg_formatter
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type
typename std::enable_if<fmt::internal::is_integral<T>::value, iterator>::type
operator()(T value) {
call(value);
return base::operator()(value);
}
template <typename T>
typename std::enable_if<!std::is_integral<T>::value, iterator>::type
typename std::enable_if<!fmt::internal::is_integral<T>::value, iterator>::type
operator()(T value) {
return base::operator()(value);
}
@@ -1988,8 +2058,10 @@ TEST(FormatTest, DynamicFormatter) {
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:{0}}", num), format_error,
"cannot switch from automatic to manual argument indexing");
#if FMT_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{:=}", str), format_error,
"format specifier requires numeric argument");
#endif
EXPECT_THROW_MSG(format("{:+}", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{:-}", str), format_error,
@@ -2372,13 +2444,15 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
# if FMT_MSC_VER >= 1916
// This causes an internal compiler error in MSVC2017.
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
EXPECT_ERROR("{:10000000000}", "number is too big", int);
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
#if FMT_NUMERIC_ALIGN
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
const char*);
#endif
EXPECT_ERROR("{:+}", "format specifier requires numeric argument",
const char*);
EXPECT_ERROR("{:-}", "format specifier requires numeric argument",

View File

@@ -64,7 +64,7 @@ TEST(OStreamTest, Enum) {
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
}
using range = fmt::internal::buffer_range<char>;
using range = fmt::buffer_range<char>;
struct test_arg_formatter : fmt::arg_formatter<range> {
fmt::format_parse_context parse_ctx;
@@ -95,8 +95,10 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
#if FMT_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
"format specifier requires numeric argument");
#endif
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
@@ -143,8 +145,8 @@ TEST(OStreamTest, WriteToOStream) {
}
TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
std::size_t max_size = fmt::internal::max_value<std::size_t>();
std::streamsize max_streamsize = fmt::internal::max_value<std::streamsize>();
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return;
struct test_buffer : fmt::internal::buffer<char> {
@@ -240,3 +242,16 @@ TEST(FormatTest, UDL) {
EXPECT_EQ("{}"_format("test"), "test");
}
#endif
template <typename T>
struct convertible {
T value;
explicit convertible(const T& val) : value(val) {}
operator T() const { return value; }
};
TEST(OStreamTest, DisableBuiltinOStreamOperators) {
EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42)));
EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}

View File

@@ -368,12 +368,6 @@ TEST(FileTest, Fdopen) {
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
}
TEST(FileTest, FdopenError) {
file f;
EXPECT_SYSTEM_ERROR_NOASSERT(f.fdopen("r"), EBADF,
"cannot associate stream with file descriptor");
}
#ifdef FMT_LOCALE
TEST(LocaleTest, Strtod) {
fmt::Locale locale;

View File

@@ -16,6 +16,7 @@
using fmt::format;
using fmt::format_error;
using fmt::internal::max_value;
const unsigned BIG_NUM = INT_MAX + 1u;
@@ -295,13 +296,12 @@ void TestLength(const char* length_spec, U value) {
long long signed_value = 0;
unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument.
using std::numeric_limits;
unsigned long long max = numeric_limits<U>::max();
unsigned long long max = max_value<U>();
using fmt::internal::const_check;
if (const_check(max <= static_cast<unsigned>(numeric_limits<int>::max()))) {
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned>(value);
} else if (const_check(max <= numeric_limits<unsigned>::max())) {
} else if (const_check(max <= max_value<unsigned>())) {
signed_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned>(value);
}
@@ -332,25 +332,25 @@ void TestLength(const char* length_spec, U value) {
}
template <typename T> void TestLength(const char* length_spec) {
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
T min = std::numeric_limits<T>::min(), max = max_value<T>();
TestLength<T>(length_spec, 42);
TestLength<T>(length_spec, -42);
TestLength<T>(length_spec, min);
TestLength<T>(length_spec, max);
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
unsigned long long long_long_max = std::numeric_limits<long long>::max();
unsigned long long long_long_max = max_value<long long>();
if (static_cast<unsigned long long>(max) < long_long_max)
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
TestLength<T>(length_spec, std::numeric_limits<short>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned short>::max());
TestLength<T>(length_spec, max_value<unsigned short>());
TestLength<T>(length_spec, std::numeric_limits<int>::min());
TestLength<T>(length_spec, std::numeric_limits<int>::max());
TestLength<T>(length_spec, max_value<int>());
TestLength<T>(length_spec, std::numeric_limits<unsigned>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned>::max());
TestLength<T>(length_spec, max_value<unsigned>());
TestLength<T>(length_spec, std::numeric_limits<long long>::min());
TestLength<T>(length_spec, std::numeric_limits<long long>::max());
TestLength<T>(length_spec, max_value<long long>());
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::max());
TestLength<T>(length_spec, max_value<unsigned long long>());
}
TEST(PrintfTest, Length) {
@@ -366,7 +366,7 @@ TEST(PrintfTest, Length) {
TestLength<intmax_t>("j");
TestLength<std::size_t>("z");
TestLength<std::ptrdiff_t>("t");
long double max = std::numeric_limits<long double>::max();
long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
}
@@ -389,7 +389,7 @@ TEST(PrintfTest, Int) {
TEST(PrintfTest, long_long) {
// fmt::printf allows passing long long arguments to %d without length
// specifiers.
long long max = std::numeric_limits<long long>::max();
long long max = max_value<long long>();
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
}
@@ -425,7 +425,7 @@ TEST(PrintfTest, Inf) {
TEST(PrintfTest, Char) {
EXPECT_PRINTF("x", "%c", 'x');
int max = std::numeric_limits<int>::max();
int max = max_value<int>();
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
// EXPECT_PRINTF("x", "%lc", L'x');
EXPECT_PRINTF(L"x", L"%c", L'x');
@@ -561,8 +561,7 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
#endif
}
typedef fmt::printf_arg_formatter<fmt::internal::buffer_range<char>>
formatter_t;
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t;
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
// A custom printf argument formatter that doesn't print `-` for floating-point

View File

@@ -9,13 +9,13 @@
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
/// Check if 'if constexpr' is supported.
#include "fmt/ranges.h"
#include "gtest.h"
// Check if 'if constexpr' is supported.
#if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
# include "fmt/ranges.h"
# include "gtest.h"
# include <array>
# include <map>
# include <string>
@@ -49,6 +49,25 @@ TEST(RangesTest, FormatTuple) {
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", tu1));
}
TEST(RangesTest, JoinTuple) {
// Value tuple args
std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
EXPECT_EQ("(a, 1, 2.0)", fmt::format("({})", fmt::join(t1, ", ")));
// Testing lvalue tuple args
int x = 4;
std::tuple<char, int&> t2{'b', x};
EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + ")));
// Empty tuple
std::tuple<> t3;
EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|")));
// Single element tuple
std::tuple<float> t4{4.0f};
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
}
struct my_struct {
int32_t i;
std::string str; // can throw
@@ -100,3 +119,16 @@ TEST(RangesTest, PathLike) {
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
// 201402L && _MSC_VER >= 1910)
#ifdef FMT_USE_STRING_VIEW
struct string_like {
const char* begin();
const char* end();
explicit operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; }
};
TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like()));
}
#endif // FMT_USE_STRING_VIEW

View File

@@ -30,18 +30,14 @@ TEST(StdFormatTest, Alignment) {
// Error: '=' with charT and no integer presentation type
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
string s6 = format("{:6d}", c); // s6 == " 120"
string s7 = format("{:=+06d}", c); // s7 == "+00120"
string s8 = format("{:0=#6x}", 0xa); // s8 == "0x000a"
string s9 = format("{:6}", true); // s9 == "true "
string s7 = format("{:6}", true); // s9 == "true "
EXPECT_EQ(s0, " 42");
EXPECT_EQ(s1, "x ");
EXPECT_EQ(s2, "x*****");
EXPECT_EQ(s3, "*****x");
EXPECT_EQ(s4, "**x***");
EXPECT_EQ(s6, " 120");
EXPECT_EQ(s7, "+00120");
EXPECT_EQ(s8, "0x000a");
EXPECT_EQ(s9, "true ");
EXPECT_EQ(s7, "true ");
}
TEST(StdFormatTest, Float) {