Upgrade {fmt} to 10.1.1 to fix warnings

This commit is contained in:
Marek Roszko 2023-09-09 18:39:24 -04:00
parent b5d5eb842a
commit bd52ed2054
18 changed files with 1156 additions and 1211 deletions

View File

@ -297,7 +297,11 @@ if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
target_compile_features(fmt PUBLIC cxx_std_11) if (cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
target_compile_features(fmt PUBLIC cxx_std_11)
else ()
message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler")
endif ()
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -350,7 +354,7 @@ if (FMT_INSTALL)
"Installation directory for libraries, a relative path that " "Installation directory for libraries, a relative path that "
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.") "will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE STRING
"Installation directory for pkgconfig (.pc) files, a relative " "Installation directory for pkgconfig (.pc) files, a relative "
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an " "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
"absolute path.") "absolute path.")
@ -380,7 +384,6 @@ if (FMT_INSTALL)
LIBRARY DESTINATION ${FMT_LIB_DIR} LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
FRAMEWORK DESTINATION "."
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced

View File

@ -1,3 +1,233 @@
10.1.1 - 2023-08-28
-------------------
* Added formatters for ``std::atomic`` and ``atomic_flag``
(`#3574 <https://github.com/fmtlib/fmt/pull/3574>`_,
`#3594 <https://github.com/fmtlib/fmt/pull/3594>`_).
Thanks `@wangzw (Zhanwei Wang) <https://github.com/wangzw>`_ and
`@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
* Fixed an error about partial specialization of ``formatter<string>``
after instantiation when compiled with gcc and C++20
(`#3584 <https://github.com/fmtlib/fmt/issues/3584>`_).
* Fixed compilation as a C++20 module with gcc and clang
(`#3587 <https://github.com/fmtlib/fmt/issues/3587>`_,
`#3597 <https://github.com/fmtlib/fmt/pull/3597>`_,
`#3605 <https://github.com/fmtlib/fmt/pull/3605>`_). Thanks
`@MathewBensonCode (Mathew Benson) <https://github.com/MathewBensonCode>`_.
* Made ``fmt::to_string`` work with types that have ``format_as`` overloads
(`#3575 <https://github.com/fmtlib/fmt/pull/3575>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Made ``formatted_size`` work with integral format specifiers at compile time
(`#3591 <https://github.com/fmtlib/fmt/pull/3591>`_).
Thanks `@elbeno (Ben Deane) <https://github.com/elbeno>`_.
* Fixed a warning about the ``no_unique_address`` attribute on clang-cl
(`#3599 <https://github.com/fmtlib/fmt/pull/3599>`_).
Thanks `@lukester1975 <https://github.com/lukester1975>`_.
* Improved compatibility with the legacy GBK encoding
(`#3598 <https://github.com/fmtlib/fmt/issues/3598>`_,
`#3599 <https://github.com/fmtlib/fmt/pull/3599>`_).
Thanks `@YuHuanTin <https://github.com/YuHuanTin>`_.
* Added OpenSSF Scorecard analysis
(`#3530 <https://github.com/fmtlib/fmt/issues/3530>`_,
`#3571 <https://github.com/fmtlib/fmt/pull/3571>`_).
Thanks `@joycebrum (Joyce) <https://github.com/joycebrum>`_.
* Updated CI dependencies
(`#3591 <https://github.com/fmtlib/fmt/pull/3591>`_,
`#3592 <https://github.com/fmtlib/fmt/pull/3592>`_,
`#3593 <https://github.com/fmtlib/fmt/pull/3593>`_,
`#3602 <https://github.com/fmtlib/fmt/pull/3602>`_).
10.1.0 - 2023-08-12
-------------------
* Optimized format string compilation resulting in up to 40% speed up in
compiled ``format_to`` and ~4x speed up in compiled ``format_to_n`` on a
concatenation benchmark (`#3133 <https://github.com/fmtlib/fmt/issues/3133>`_,
`#3484 <https://github.com/fmtlib/fmt/issues/3484>`_).
{fmt} 10.0::
---------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------
BM_format_to 78.9 ns 78.9 ns 8881746
BM_format_to_n 568 ns 568 ns 1232089
{fmt} 10.1::
---------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------
BM_format_to 54.9 ns 54.9 ns 12727944
BM_format_to_n 133 ns 133 ns 5257795
* Optimized storage of an empty allocator in ``basic_memory_buffer``
(`#3485 <https://github.com/fmtlib/fmt/pull/3485>`_).
Thanks `@Minty-Meeo <https://github.com/Minty-Meeo>`_.
* Added formatters for proxy references to elements of ``std::vector<bool>`` and
``std::bitset<N>`` (`#3567 <https://github.com/fmtlib/fmt/issues/3567>`_,
`#3570 <https://github.com/fmtlib/fmt/pull/3570>`_).
For example (`godbolt <https://godbolt.org/z/zYb79Pvn8>`__):
.. code:: c++
#include <vector>
#include <fmt/std.h>
int main() {
auto v = std::vector<bool>{true};
fmt::print("{}", v[0]);
}
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
`@felix642 (Félix-Antoine Constantin) <https://github.com/felix642>`_.
* Fixed an ambiguous formatter specialization for containers that look like
container adaptors such as ``boost::flat_set``
(`#3556 <https://github.com/fmtlib/fmt/issues/3556>`_,
`#3561 <https://github.com/fmtlib/fmt/pull/3561>`_).
Thanks `@5chmidti <https://github.com/5chmidti>`_.
* Fixed compilation when formatting durations not convertible from
``std::chrono::seconds`` (`#3430 <https://github.com/fmtlib/fmt/pull/3430>`_).
Thanks `@patlkli (Patrick Geltinger) <https://github.com/patlkli>`_.
* Made the ``formatter`` specialization for ``char*`` const-correct
(`#3432 <https://github.com/fmtlib/fmt/pull/3432>`_).
Thanks `@timsong-cpp <https://github.com/timsong-cpp>`_.
* Made ``{}`` and ``{:}`` handled consistently during compile-time checks
(`#3526 <https://github.com/fmtlib/fmt/issues/3526>`_).
* Disallowed passing temporaries to ``make_format_args`` to improve API safety
by preventing dangling references.
* Improved the compile-time error for unformattable types
(`#3478 <https://github.com/fmtlib/fmt/pull/3478>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Improved the floating-point formatter
(`#3448 <https://github.com/fmtlib/fmt/pull/3448>`_,
`#3450 <https://github.com/fmtlib/fmt/pull/3450>`_).
Thanks `@florimond-collette (Florimond Collette)
<https://github.com/florimond-collette>`_.
* Fixed handling of precision for ``long double`` larger than 64 bits.
(`#3539 <https://github.com/fmtlib/fmt/issues/3539>`_,
`#3564 <https://github.com/fmtlib/fmt/issues/3564>`_).
* Made floating-point and chrono tests less platform-dependent
(`#3337 <https://github.com/fmtlib/fmt/issues/3337>`_,
`#3433 <https://github.com/fmtlib/fmt/issues/3433>`_,
`#3434 <https://github.com/fmtlib/fmt/pull/3434>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Removed the remnants of the Grisu floating-point formatter that has been
replaced by Dragonbox in earlier versions.
* Added ``throw_format_error`` to the public API
(`#3551 <https://github.com/fmtlib/fmt/pull/3551>`_).
Thanks `@mjerabek (Martin Jeřábek) <https://github.com/mjerabek>`_.
* Made ``FMT_THROW`` assert even if assertions are disabled when compiling with
exceptions disabled (`#3418 <https://github.com/fmtlib/fmt/issues/3418>`_,
`#3439 <https://github.com/fmtlib/fmt/pull/3439>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Made ``format_as`` and ``std::filesystem::path`` formatter work with exotic
code unit types.
(`#3457 <https://github.com/fmtlib/fmt/pull/3457>`_,
`#3476 <https://github.com/fmtlib/fmt/pull/3476>`_).
Thanks `@gix (Nico Rieck) <https://github.com/gix>`_,
`@hmbj (Hans-Martin B. Jensen) <https://github.com/hmbj>`_.
* Added support for the ``?`` format specifier to ``std::filesystem::path`` and
made the default unescaped for consistency with strings.
* Deprecated the wide stream overload of ``printf``.
* Removed unused ``basic_printf_parse_context``.
* Improved RTTI detection used when formatting exceptions
(`#3468 <https://github.com/fmtlib/fmt/pull/3468>`_).
Thanks `@danakj (Dana Jansens) <https://github.com/danakj>`_.
* Improved compatibility with VxWorks7
(`#3467 <https://github.com/fmtlib/fmt/pull/3467>`_).
Thanks `@wenshan1 (Bin Lan) <https://github.com/wenshan1>`_.
* Improved documentation
(`#3174 <https://github.com/fmtlib/fmt/issues/3174>`_,
`#3423 <https://github.com/fmtlib/fmt/issues/3423>`_,
`#3454 <https://github.com/fmtlib/fmt/pull/3454>`_,
`#3458 <https://github.com/fmtlib/fmt/issues/3458>`_,
`#3461 <https://github.com/fmtlib/fmt/pull/3461>`_,
`#3487 <https://github.com/fmtlib/fmt/issues/3487>`_,
`#3515 <https://github.com/fmtlib/fmt/pull/3515>`_).
Thanks `@zencatalyst (Kasra Hashemi) <https://github.com/zencatalyst>`_,
`@rlalik <https://github.com/rlalik>`_,
`@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_.
* Improved build and CI configurations
(`#3449 <https://github.com/fmtlib/fmt/issues/3449>`_,
`#3451 <https://github.com/fmtlib/fmt/pull/3451>`_,
`#3452 <https://github.com/fmtlib/fmt/pull/3452>`_,
`#3453 <https://github.com/fmtlib/fmt/pull/3453>`_,
`#3459 <https://github.com/fmtlib/fmt/pull/3459>`_,
`#3481 <https://github.com/fmtlib/fmt/issues/3481>`_,
`#3486 <https://github.com/fmtlib/fmt/pull/3486>`_,
`#3489 <https://github.com/fmtlib/fmt/issues/3489>`_,
`#3496 <https://github.com/fmtlib/fmt/pull/3496>`_,
`#3517 <https://github.com/fmtlib/fmt/issues/3517>`_,
`#3523 <https://github.com/fmtlib/fmt/pull/3523>`_,
`#3563 <https://github.com/fmtlib/fmt/pull/3563>`_).
Thanks `@joycebrum (Joyce) <https://github.com/joycebrum>`_,
`@glebm (Gleb Mazovetskiy) <https://github.com/glebm>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@petrmanek (Petr Mánek) <https://github.com/petrmanek>`_,
`@setoye (Alta) <https://github.com/setoye>`_,
`@abouvier (Alexandre Bouvier) <https://github.com/abouvier>`_.
* Fixed various warnings and compilation issues
(`#3408 <https://github.com/fmtlib/fmt/issues/3408>`_,
`#3424 <https://github.com/fmtlib/fmt/issues/3424>`_,
`#3444 <https://github.com/fmtlib/fmt/issues/3444>`_,
`#3446 <https://github.com/fmtlib/fmt/pull/3446>`_,
`#3475 <https://github.com/fmtlib/fmt/pull/3475>`_,
`#3482 <https://github.com/fmtlib/fmt/pull/3482>`_,
`#3492 <https://github.com/fmtlib/fmt/issues/3492>`_,
`#3493 <https://github.com/fmtlib/fmt/pull/3493>`_,
`#3508 <https://github.com/fmtlib/fmt/pull/3508>`_,
`#3509 <https://github.com/fmtlib/fmt/issues/3509>`_,
`#3533 <https://github.com/fmtlib/fmt/issues/3533>`_,
`#3542 <https://github.com/fmtlib/fmt/pull/3542>`_,
`#3543 <https://github.com/fmtlib/fmt/issues/3543>`_,
`#3540 <https://github.com/fmtlib/fmt/issues/3540>`_,
`#3544 <https://github.com/fmtlib/fmt/pull/3544>`_,
`#3548 <https://github.com/fmtlib/fmt/issues/3548>`_,
`#3549 <https://github.com/fmtlib/fmt/pull/3549>`_,
`#3550 <https://github.com/fmtlib/fmt/pull/3550>`_,
`#3552 <https://github.com/fmtlib/fmt/pull/3552>`_).
Thanks `@adesitter (Arnaud Desitter) <https://github.com/adesitter>`_,
`@hmbj (Hans-Martin B. Jensen) <https://github.com/hmbj>`_,
`@Minty-Meeo <https://github.com/Minty-Meeo>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@TobiSchluter (Tobias Schlüter) <https://github.com/TobiSchluter>`_,
`@kieranclancy (Kieran Clancy) <https://github.com/kieranclancy>`_,
`@alexeedm (Dmitry Alexeev) <https://github.com/alexeedm>`_,
`@jurihock (Jürgen Hock) <https://github.com/jurihock>`_,
`@Ozomahtli <https://github.com/Ozomahtli>`_,
`@razaqq <https://github.com/razaqq>`_.
10.0.0 - 2023-05-09 10.0.0 - 2023-05-09
------------------- -------------------
@ -29,6 +259,8 @@
`#3272 <https://github.com/fmtlib/fmt/pull/3272>`_). `#3272 <https://github.com/fmtlib/fmt/pull/3272>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_. Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Made handling of ``#`` consistent with ``std::format``.
* Improved C++20 module support * Improved C++20 module support
(`#3134 <https://github.com/fmtlib/fmt/pull/3134>`_, (`#3134 <https://github.com/fmtlib/fmt/pull/3134>`_,
`#3254 <https://github.com/fmtlib/fmt/pull/3254>`_, `#3254 <https://github.com/fmtlib/fmt/pull/3254>`_,
@ -293,6 +525,9 @@
(`#3267 <https://github.com/fmtlib/fmt/pull/3267>`_). (`#3267 <https://github.com/fmtlib/fmt/pull/3267>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_. Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Renamed the ``FMT_EXPORT`` macro for shared library usage to
``FMT_LIB_EXPORT``.
* Improved documentation * Improved documentation
(`#3108 <https://github.com/fmtlib/fmt/issues/3108>`_, (`#3108 <https://github.com/fmtlib/fmt/issues/3108>`_,
`#3169 <https://github.com/fmtlib/fmt/issues/3169>`_, `#3169 <https://github.com/fmtlib/fmt/issues/3169>`_,

View File

@ -22,6 +22,9 @@
:alt: Ask questions at StackOverflow with the tag fmt :alt: Ask questions at StackOverflow with the tag fmt
:target: https://stackoverflow.com/questions/tagged/fmt :target: https://stackoverflow.com/questions/tagged/fmt
.. image:: https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge
:target: https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt
**{fmt}** is an open-source formatting library providing a fast and safe **{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams. alternative to C stdio and C++ iostreams.
@ -49,6 +52,7 @@ Features
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and * Fast IEEE 754 floating-point formatter with correct rounding, shortness and
round-trip guarantees using the `Dragonbox <https://github.com/jk-jeon/dragonbox>`_ round-trip guarantees using the `Dragonbox <https://github.com/jk-jeon/dragonbox>`_
algorithm algorithm
* Portable Unicode support
* Safe `printf implementation * Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX <https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
extension for positional arguments extension for positional arguments
@ -65,7 +69,7 @@ Features
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed <https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed
<https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20 <https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20
Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_ Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_
* Safety: the library is fully type safe, errors in format strings can be * Safety: the library is fully type-safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow reported at compile time, automatic memory management prevents buffer overflow
errors errors
* Ease of use: small self-contained code base, no external dependencies, * Ease of use: small self-contained code base, no external dependencies,
@ -75,7 +79,7 @@ Features
consistent output across platforms and support for older compilers consistent output across platforms and support for older compilers
* Clean warning-free codebase even on high warning levels such as * Clean warning-free codebase even on high warning levels such as
``-Wall -Wextra -pedantic`` ``-Wall -Wextra -pedantic``
* Locale-independence by default * Locale independence by default
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
See the `documentation <https://fmt.dev>`_ for more details. See the `documentation <https://fmt.dev>`_ for more details.
@ -225,7 +229,7 @@ The script `bloat-test.py
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_ from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects. tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting five times in each to simulate a medium-sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables. macOS Sierra, best of three) is shown in the following tables.
@ -246,7 +250,7 @@ As you can see, {fmt} has 60% less overhead in terms of resulting binary code
size compared to iostreams and comes pretty close to ``printf``. Boost Format size compared to iostreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads. and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>`` ``printf+string`` is the same as ``printf`` but with an extra ``<string>``
include to measure the overhead of the latter. include to measure the overhead of the latter.
**Non-optimized build** **Non-optimized build**
@ -262,14 +266,14 @@ Boost Format 54.1 365 303
Folly Format 79.9 445 430 Folly Format 79.9 445 430
============= =============== ==================== ================== ============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to ``libc``, ``lib(std)c++``, and ``libfmt`` are all linked as shared libraries to
compare formatting function overhead only. Boost Format is a compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options. header-only library so it doesn't provide any linkage options.
Running the tests Running the tests
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build Please refer to `Building the library`__ for instructions on how to build
the library and run the unit tests. the library and run the unit tests.
__ https://fmt.dev/latest/usage.html#building-the-library __ https://fmt.dev/latest/usage.html#building-the-library
@ -294,9 +298,12 @@ or the bloat test::
Migrating code Migrating code
-------------- --------------
`clang-tidy-fmt <https://github.com/mikecrowe/clang-tidy-fmt>`_ provides clang `clang-tidy <https://clang.llvm.org/extra/clang-tidy/>`_ v17 (not yet
tidy checks for converting occurrences of ``printf`` and ``fprintf`` to released) provides the `modernize-use-std-print
``fmt::print``. <https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html>`_
check that is capable of converting occurrences of ``printf`` and
``fprintf`` to ``fmt::print`` if configured to do so. (By default it
converts to ``std::print``.)
Projects using this library Projects using this library
--------------------------- ---------------------------
@ -304,8 +311,6 @@ Projects using this library
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform * `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
real-time strategy game real-time strategy game
* `2GIS <https://2gis.ru/>`_: free business listings with a city map
* `AMPL/MP <https://github.com/ampl/mp>`_: * `AMPL/MP <https://github.com/ampl/mp>`_:
an open-source library for mathematical programming an open-source library for mathematical programming
@ -396,7 +401,7 @@ Projects using this library
proxy proxy
* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement * `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement
for mission critical systems written in C++ for mission-critical systems written in C++
* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client * `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client
library library
@ -480,7 +485,7 @@ error handling is awkward.
Boost Format Boost Format
~~~~~~~~~~~~ ~~~~~~~~~~~~
This is a very powerful library which supports both ``printf``-like format This is a very powerful library that supports both ``printf``-like format
strings and positional arguments. Its main drawback is performance. According to strings and positional arguments. Its main drawback is performance. According to
various benchmarks, it is much slower than other methods considered here. Boost various benchmarks, it is much slower than other methods considered here. Boost
Format also has excessive build times and severe code bloat issues (see Format also has excessive build times and severe code bloat issues (see
@ -489,7 +494,7 @@ Format also has excessive build times and severe code bloat issues (see
FastFormat FastFormat
~~~~~~~~~~ ~~~~~~~~~~
This is an interesting library which is fast, safe and has positional arguments. This is an interesting library that is fast, safe, and has positional arguments.
However, it has significant limitations, citing its author: However, it has significant limitations, citing its author:
Three features that have no hope of being accommodated within the Three features that have no hope of being accommodated within the
@ -505,7 +510,7 @@ restrictive for using it in some projects.
Boost Spirit.Karma Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
This is not really a formatting library but I decided to include it here for This is not a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing verbatim text completeness. As iostreams, it suffers from the problem of mixing verbatim text
with arguments. The library is pretty fast, but slower on integer formatting with arguments. The library is pretty fast, but slower on integer formatting
than ``fmt::format_to`` with format string compilation on Karma's own benchmark, than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
@ -524,7 +529,7 @@ Documentation License
The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_ The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_. documentation <https://docs.python.org/3/library/string.html#module-string>`_.
For this reason the documentation is distributed under the Python Software For this reason, the documentation is distributed under the Python Software
Foundation license available in `doc/python-license.txt Foundation license available in `doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_. <https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of {fmt}. It only applies if you distribute the documentation of {fmt}.

View File

@ -1,4 +1,4 @@
// Formatting library for C++ - dynamic format arguments // Formatting library for C++ - dynamic argument lists
// //
// Copyright (c) 2012 - present, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.

View File

@ -377,8 +377,8 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
unit_t unit; unit_t unit;
write_codecvt(unit, in, loc); write_codecvt(unit, in, loc);
// In UTF-8 is used one to four one-byte code units. // In UTF-8 is used one to four one-byte code units.
unicode_to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>> auto u =
u; to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
FMT_THROW(format_error("failed to format time")); FMT_THROW(format_error("failed to format time"));
return copy_str<char>(u.c_str(), u.c_str() + u.size(), out); return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
@ -519,7 +519,7 @@ inline std::tm gmtime(std::time_t time) {
} }
#endif #endif
}; };
dispatcher gt(time); auto gt = dispatcher(time);
// Too big time values may be unsupported. // Too big time values may be unsupported.
if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
return gt.tm_; return gt.tm_;
@ -530,50 +530,7 @@ inline std::tm gmtime(
return gmtime(std::chrono::system_clock::to_time_t(time_point)); return gmtime(std::chrono::system_clock::to_time_t(time_point));
} }
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// DEPRECATED!
template <typename Char>
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
format_specs<Char>& specs) -> const Char* {
FMT_ASSERT(begin != end, "");
auto align = align::none;
auto p = begin + code_point_length(begin);
if (end - p <= 0) p = begin;
for (;;) {
switch (to_ascii(*p)) {
case '<':
align = align::left;
break;
case '>':
align = align::right;
break;
case '^':
align = align::center;
break;
}
if (align != align::none) {
if (p != begin) {
auto c = *begin;
if (c == '}') return begin;
if (c == '{') {
throw_format_error("invalid fill character '{'");
return begin;
}
specs.fill = {begin, to_unsigned(p - begin)};
begin = p + 1;
} else {
++begin;
}
break;
} else if (p == begin) {
break;
}
p = begin;
}
specs.align = align;
return begin;
}
// Writes two-digit numbers a, b and c separated by sep to buf. // Writes two-digit numbers a, b and c separated by sep to buf.
// The method by Pavel Novikov based on // The method by Pavel Novikov based on
@ -1997,7 +1954,7 @@ struct chrono_formatter {
} }
}; };
FMT_END_DETAIL_NAMESPACE } // namespace detail
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
using weekday = std::chrono::weekday; using weekday = std::chrono::weekday;
@ -2047,80 +2004,67 @@ template <typename Char> struct formatter<weekday, Char> {
template <typename Rep, typename Period, typename Char> template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> { struct formatter<std::chrono::duration<Rep, Period>, Char> {
private: private:
format_specs<Char> specs; format_specs<Char> specs_;
int precision = -1; detail::arg_ref<Char> width_ref_;
using arg_ref_type = detail::arg_ref<Char>; detail::arg_ref<Char> precision_ref_;
arg_ref_type width_ref; bool localized_ = false;
arg_ref_type precision_ref; basic_string_view<Char> format_str_;
bool localized = false;
basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
begin = detail::parse_align(begin, end, specs);
if (begin == end) return {begin, begin};
begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
if (begin == end) return {begin, begin};
auto checker = detail::chrono_format_checker();
if (*begin == '.') {
checker.has_precision_integral = !std::is_floating_point<Rep>::value;
begin =
detail::parse_precision(begin, end, precision, precision_ref, ctx);
}
if (begin != end && *begin == 'L') {
++begin;
localized = true;
}
end = detail::parse_chrono_format(begin, end, checker);
return {begin, end};
}
public: public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto range = do_parse(ctx); auto it = ctx.begin(), end = ctx.end();
format_str = basic_string_view<Char>( if (it == end || *it == '}') return it;
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end; it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it == end) return it;
auto checker = detail::chrono_format_checker();
if (*it == '.') {
checker.has_precision_integral = !std::is_floating_point<Rep>::value;
it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
ctx);
}
if (it != end && *it == 'L') {
localized_ = true;
++it;
}
end = detail::parse_chrono_format(it, end, checker);
format_str_ = {it, detail::to_unsigned(end - it)};
return end;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const duration& d, FormatContext& ctx) const auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto specs_copy = specs; auto specs = specs_;
auto precision_copy = precision; auto precision = specs.precision;
auto begin = format_str.begin(), end = format_str.end(); specs.precision = -1;
auto begin = format_str_.begin(), end = format_str_.end();
// As a possible future optimization, we could avoid extra copying if width // As a possible future optimization, we could avoid extra copying if width
// is not specified. // is not specified.
basic_memory_buffer<Char> buf; auto buf = basic_memory_buffer<Char>();
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width, detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
width_ref, ctx); ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision_copy, detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref, ctx); precision_ref_, ctx);
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = detail::format_duration_value<Char>(out, d.count(), precision_copy); out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out); detail::format_duration_unit<Char, Period>(out);
} else { } else {
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f( using chrono_formatter =
ctx, out, d); detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
f.precision = precision_copy; auto f = chrono_formatter(ctx, out, d);
f.localized = localized; f.precision = precision;
f.localized = localized_;
detail::parse_chrono_format(begin, end, f); detail::parse_chrono_format(begin, end, f);
} }
return detail::write( return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy); ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
} }
}; };
@ -2128,21 +2072,23 @@ template <typename Char, typename Duration>
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>, struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char> : formatter<std::tm, Char> { Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() { FMT_CONSTEXPR formatter() {
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{}; this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
} }
template <typename FormatContext> template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val, auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
FormatContext& ctx) const -> decltype(ctx.out()) { FormatContext& ctx) const -> decltype(ctx.out()) {
using period = typename Duration::period; using period = typename Duration::period;
if (period::num != 1 || period::den != 1 || if (detail::const_check(
std::is_floating_point<typename Duration::rep>::value) { period::num != 1 || period::den != 1 ||
std::is_floating_point<typename Duration::rep>::value)) {
const auto epoch = val.time_since_epoch(); const auto epoch = val.time_since_epoch();
auto subsecs = std::chrono::duration_cast<Duration>( auto subsecs = std::chrono::duration_cast<Duration>(
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch)); epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) { if (subsecs.count() < 0) {
auto second = std::chrono::seconds(1); auto second =
std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
if (epoch.count() < ((Duration::min)() + second).count()) if (epoch.count() < ((Duration::min)() + second).count())
FMT_THROW(format_error("duration is too small")); FMT_THROW(format_error("duration is too small"));
subsecs += second; subsecs += second;
@ -2164,7 +2110,7 @@ template <typename Char, typename Duration>
struct formatter<std::chrono::local_time<Duration>, Char> struct formatter<std::chrono::local_time<Duration>, Char>
: formatter<std::tm, Char> { : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() { FMT_CONSTEXPR formatter() {
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{}; this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
} }
template <typename FormatContext> template <typename FormatContext>
@ -2207,51 +2153,46 @@ struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
template <typename Char> struct formatter<std::tm, Char> { template <typename Char> struct formatter<std::tm, Char> {
private: private:
format_specs<Char> specs; format_specs<Char> specs_;
detail::arg_ref<Char> width_ref; detail::arg_ref<Char> width_ref_;
protected: protected:
basic_string_view<Char> format_str; basic_string_view<Char> format_str_;
FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return begin;
begin = detail::parse_align(begin, end, specs);
if (begin == end) return end;
begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
if (begin == end) return end;
end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
// Replace default format_str only if the new spec is not empty.
if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)};
return end;
}
template <typename FormatContext, typename Duration> template <typename FormatContext, typename Duration>
auto do_format(const std::tm& tm, FormatContext& ctx, auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) { const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs_copy = specs; auto specs = specs_;
basic_memory_buffer<Char> buf; auto buf = basic_memory_buffer<Char>();
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width, detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
width_ref, ctx); ctx);
const auto loc_ref = ctx.locale(); auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref); detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = auto w =
detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs); detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
detail::parse_chrono_format(format_str.begin(), format_str.end(), w); detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w);
return detail::write( return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy); ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
} }
public: public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
return this->do_parse(ctx); auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it == end) return it;
end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
// Replace the default format_str only if the new spec is not empty.
if (end != it) format_str_ = {it, detail::to_unsigned(end - it)};
return end;
} }
template <typename FormatContext> template <typename FormatContext>

View File

@ -203,7 +203,7 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
@ -225,8 +225,7 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace detail
FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */ /** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
@ -323,7 +322,7 @@ FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@ -457,7 +456,7 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
if (has_style) detail::reset_color<Char>(buf); if (has_style) detail::reset_color<Char>(buf);
} }
FMT_END_DETAIL_NAMESPACE } // namespace detail
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
format_args args) { format_args args) {

View File

@ -19,84 +19,6 @@ FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
return it + (end - begin); return it + (end - begin);
} }
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code. // A compile-time string which is compiled into fast formatting code.
class compiled_string {}; class compiled_string {};
@ -196,7 +118,8 @@ template <typename Char> struct code_unit {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value); *out++ = value;
return out;
} }
}; };
@ -220,7 +143,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
return write<Char>(out, get_arg_checked<T, N>(args...)); const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) {
auto s = basic_string_view<Char>(arg);
return copy_str<Char>(s.begin(), s.end(), out);
}
return write<Char>(out, arg);
} }
}; };
@ -448,20 +376,18 @@ constexpr auto compile_format_string(S format_str) {
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index = constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) { if constexpr (arg_index >= 0) {
constexpr auto next_id = constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail< return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str); arg_index, next_id>(format_str);
} else { } else if constexpr (c == '}') {
if constexpr (c == '}') { return parse_tail<Args, arg_id_end_pos + 1, ID>(
return parse_tail<Args, arg_id_end_pos + 1, ID>( runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, format_str);
format_str); } else if constexpr (c == ':') {
} else if constexpr (c == ':') { return unknown_format(); // no type info for specs parsing
return unknown_format(); // no type info for specs parsing
}
} }
} }
} }
@ -564,9 +490,10 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), using traits = detail::fixed_buffer_traits;
format_str, std::forward<Args>(args)...); auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
return {it.base(), it.count()}; format_to(std::back_inserter(buf), format_str, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,

View File

@ -13,11 +13,12 @@
#include <cstring> // std::strlen #include <cstring> // std::strlen
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <memory> // std::addressof
#include <string> #include <string>
#include <type_traits> #include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch. // The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 100000 #define FMT_VERSION 100101
#if defined(__clang__) && !defined(__ibmxl__) #if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@ -92,7 +93,7 @@
#ifndef FMT_USE_CONSTEXPR #ifndef FMT_USE_CONSTEXPR
# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ # if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
(FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \
!FMT_ICC_VERSION && !defined(__NVCC__) !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L)
# define FMT_USE_CONSTEXPR 1 # define FMT_USE_CONSTEXPR 1
# else # else
# define FMT_USE_CONSTEXPR 0 # define FMT_USE_CONSTEXPR 0
@ -162,9 +163,6 @@
# endif # endif
#endif #endif
// An inline std::forward replacement.
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
#ifdef _MSC_VER #ifdef _MSC_VER
# define FMT_UNCHECKED_ITERATOR(It) \ # define FMT_UNCHECKED_ITERATOR(It) \
using _Unchecked_type = It // Mark iterator as checked. using _Unchecked_type = It // Mark iterator as checked.
@ -181,8 +179,8 @@
} }
#endif #endif
#ifndef FMT_MODULE_EXPORT #ifndef FMT_EXPORT
# define FMT_MODULE_EXPORT # define FMT_EXPORT
# define FMT_BEGIN_EXPORT # define FMT_BEGIN_EXPORT
# define FMT_END_EXPORT # define FMT_END_EXPORT
#endif #endif
@ -244,12 +242,6 @@
# endif # endif
#endif #endif
#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
# define FMT_INLINE_VARIABLE inline
#else
# define FMT_INLINE_VARIABLE
#endif
// Enable minimal optimizations for more compact code in debug mode. // Enable minimal optimizations for more compact code in debug mode.
FMT_GCC_PRAGMA("GCC push_options") FMT_GCC_PRAGMA("GCC push_options")
#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ #if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
@ -276,6 +268,11 @@ template <typename T> using type_identity_t = typename type_identity<T>::type;
template <typename T> template <typename T>
using underlying_t = typename std::underlying_type<T>::type; using underlying_t = typename std::underlying_type<T>::type;
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
struct monostate { struct monostate {
constexpr monostate() {} constexpr monostate() {}
}; };
@ -289,8 +286,11 @@ struct monostate {
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 # define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
#endif #endif
// This is defined in core.h instead of format.h to avoid injecting in std.
// It is a template to avoid undesirable implicit conversions to std::byte.
#ifdef __cpp_lib_byte #ifdef __cpp_lib_byte
inline auto format_as(std::byte b) -> unsigned char { template <typename T, FMT_ENABLE_IF(std::is_same<T, std::byte>::value)>
inline auto format_as(T b) -> unsigned char {
return static_cast<unsigned char>(b); return static_cast<unsigned char>(b);
} }
#endif #endif
@ -394,7 +394,7 @@ FMT_CONSTEXPR inline auto is_utf8() -> bool {
compiled with a different ``-std`` option than the client code (which is not compiled with a different ``-std`` option than the client code (which is not
recommended). recommended).
*/ */
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> class basic_string_view { template <typename Char> class basic_string_view {
private: private:
const Char* data_; const Char* data_;
@ -497,11 +497,11 @@ template <typename Char> class basic_string_view {
} }
}; };
FMT_MODULE_EXPORT FMT_EXPORT
using string_view = basic_string_view<char>; using string_view = basic_string_view<char>;
/** Specifies if ``T`` is a character type. Can be specialized by users. */ /** Specifies if ``T`` is a character type. Can be specialized by users. */
FMT_MODULE_EXPORT FMT_EXPORT
template <typename T> struct is_char : std::false_type {}; template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {}; template <> struct is_char<char> : std::true_type {};
@ -639,6 +639,9 @@ struct error_handler {
}; };
} // namespace detail } // namespace detail
/** Throws ``format_error`` with a given message. */
using detail::throw_format_error;
/** String's character type. */ /** String's character type. */
template <typename S> using char_t = typename detail::char_t_impl<S>::type; template <typename S> using char_t = typename detail::char_t_impl<S>::type;
@ -649,7 +652,7 @@ template <typename S> using char_t = typename detail::char_t_impl<S>::type;
You can use the ``format_parse_context`` type alias for ``char`` instead. You can use the ``format_parse_context`` type alias for ``char`` instead.
\endrst \endrst
*/ */
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> class basic_format_parse_context { template <typename Char> class basic_format_parse_context {
private: private:
basic_string_view<Char> format_str_; basic_string_view<Char> format_str_;
@ -715,7 +718,7 @@ template <typename Char> class basic_format_parse_context {
FMT_CONSTEXPR void check_dynamic_spec(int arg_id); FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
}; };
FMT_MODULE_EXPORT FMT_EXPORT
using format_parse_context = basic_format_parse_context<char>; using format_parse_context = basic_format_parse_context<char>;
namespace detail { namespace detail {
@ -756,72 +759,6 @@ class compile_parse_context : public basic_format_parse_context<Char> {
#endif #endif
} }
}; };
} // namespace detail
template <typename Char>
FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
// Argument id is only checked at compile-time during parsing because
// formatting has its own validation.
if (detail::is_constant_evaluated() &&
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
using context = detail::compile_parse_context<Char>;
if (id >= static_cast<context*>(this)->num_args())
detail::throw_format_error("argument not found");
}
}
template <typename Char>
FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
int arg_id) {
if (detail::is_constant_evaluated() &&
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
using context = detail::compile_parse_context<Char>;
static_cast<context*>(this)->check_dynamic_spec(arg_id);
}
}
FMT_MODULE_EXPORT template <typename Context> class basic_format_arg;
FMT_MODULE_EXPORT template <typename Context> class basic_format_args;
FMT_MODULE_EXPORT template <typename Context> class dynamic_format_arg_store;
// A formatter for objects of type T.
FMT_MODULE_EXPORT
template <typename T, typename Char = char, typename Enable = void>
struct formatter {
// A deleted default constructor indicates a disabled formatter.
formatter() = delete;
};
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
class appender;
namespace detail {
template <typename Context, typename T>
constexpr auto has_const_formatter_impl(T*)
-> decltype(typename Context::template formatter_type<T>().format(
std::declval<const T&>(), std::declval<Context&>()),
true) {
return true;
}
template <typename Context>
constexpr auto has_const_formatter_impl(...) -> bool {
return false;
}
template <typename T, typename Context>
constexpr auto has_const_formatter() -> bool {
return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
}
// Extracts a reference to the container from back_insert_iterator. // Extracts a reference to the container from back_insert_iterator.
template <typename Container> template <typename Container>
@ -903,10 +840,8 @@ template <typename T> class buffer {
/** Returns the capacity of this buffer. */ /** Returns the capacity of this buffer. */
constexpr auto capacity() const noexcept -> size_t { return capacity_; } constexpr auto capacity() const noexcept -> size_t { return capacity_; }
/** Returns a pointer to the buffer data. */ /** Returns a pointer to the buffer data (not null-terminated). */
FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
/** Returns a pointer to the buffer data. */
FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
/** Clears this buffer. */ /** Clears this buffer. */
@ -1099,6 +1034,79 @@ template <typename T = char> class counting_buffer final : public buffer<T> {
auto count() -> size_t { return count_ + this->size(); } auto count() -> size_t { return count_ + this->size(); }
}; };
} // namespace detail
template <typename Char>
FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
// Argument id is only checked at compile-time during parsing because
// formatting has its own validation.
if (detail::is_constant_evaluated() &&
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
using context = detail::compile_parse_context<Char>;
if (id >= static_cast<context*>(this)->num_args())
detail::throw_format_error("argument not found");
}
}
template <typename Char>
FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
int arg_id) {
if (detail::is_constant_evaluated() &&
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
using context = detail::compile_parse_context<Char>;
static_cast<context*>(this)->check_dynamic_spec(arg_id);
}
}
FMT_EXPORT template <typename Context> class basic_format_arg;
FMT_EXPORT template <typename Context> class basic_format_args;
FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
// A formatter for objects of type T.
FMT_EXPORT
template <typename T, typename Char = char, typename Enable = void>
struct formatter {
// A deleted default constructor indicates a disabled formatter.
formatter() = delete;
};
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
// An output iterator that appends to a buffer.
// It is used to reduce symbol sizes for the common case.
class appender : public std::back_insert_iterator<detail::buffer<char>> {
using base = std::back_insert_iterator<detail::buffer<char>>;
public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
appender(base it) noexcept : base(it) {}
FMT_UNCHECKED_ITERATOR(appender);
auto operator++() noexcept -> appender& { return *this; }
auto operator++(int) noexcept -> appender { return *this; }
};
namespace detail {
template <typename Context, typename T>
constexpr auto has_const_formatter_impl(T*)
-> decltype(typename Context::template formatter_type<T>().format(
std::declval<const T&>(), std::declval<Context&>()),
true) {
return true;
}
template <typename Context>
constexpr auto has_const_formatter_impl(...) -> bool {
return false;
}
template <typename T, typename Context>
constexpr auto has_const_formatter() -> bool {
return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
}
template <typename T> template <typename T>
using buffer_appender = conditional_t<std::is_same<T, char>::value, appender, using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
@ -1274,9 +1282,9 @@ template <typename Context> class value {
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size) FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
: named_args{args, size} {} : named_args{args, size} {}
template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) { template <typename T> FMT_CONSTEXPR20 FMT_INLINE value(T& val) {
using value_type = remove_cvref_t<T>; using value_type = remove_const_t<T>;
custom.value = const_cast<value_type*>(&val); custom.value = const_cast<value_type*>(std::addressof(val));
// Get the formatter type through the context to allow different contexts // Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and // have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`. // `printf_formatter<T>` for `printf`.
@ -1301,9 +1309,6 @@ template <typename Context> class value {
} }
}; };
template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
// To minimize the number of types we need to deal with, long is translated // To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size. // either to int or to long long depending on its size.
enum { long_short = sizeof(long) == sizeof(int) }; enum { long_short = sizeof(long) == sizeof(int) };
@ -1415,9 +1420,8 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_pointer<T>::value || std::is_member_pointer<T>::value || std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
std::is_function<typename std::remove_pointer<T>::type>::value || std::is_function<typename std::remove_pointer<T>::type>::value ||
(std::is_convertible<const T&, const void*>::value && (std::is_array<T>::value &&
!std::is_convertible<const T&, const char_type*>::value && !std::is_convertible<T, const char_type*>::value))>
!has_formatter<T, Context>::value))>
FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
return {}; return {};
} }
@ -1435,30 +1439,28 @@ template <typename Context> struct arg_mapper {
return map(format_as(val)); return map(format_as(val));
} }
template <typename T, typename U = remove_cvref_t<T>> template <typename T, typename U = remove_const_t<T>>
struct formattable struct formattable : bool_constant<has_const_formatter<U, Context>() ||
: bool_constant<has_const_formatter<U, Context>() || (has_formatter<U, Context>::value &&
(has_formatter<U, Context>::value && !std::is_const<T>::value)> {};
!std::is_const<remove_reference_t<T>>::value)> {};
template <typename T, FMT_ENABLE_IF(formattable<T>::value)> template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& {
return val; return val;
} }
template <typename T, FMT_ENABLE_IF(!formattable<T>::value)> template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable { FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable {
return {}; return {};
} }
template <typename T, typename U = remove_cvref_t<T>, template <typename T, typename U = remove_const_t<T>,
FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value || FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
std::is_union<U>::value) && std::is_union<U>::value) &&
!is_string<U>::value && !is_char<U>::value && !is_string<U>::value && !is_char<U>::value &&
!is_named_arg<U>::value && !is_named_arg<U>::value &&
!std::is_arithmetic<format_as_t<U>>::value)> !std::is_arithmetic<format_as_t<U>>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(T&& val) FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) {
-> decltype(this->do_map(std::forward<T>(val))) { return do_map(val);
return do_map(std::forward<T>(val));
} }
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)> template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
@ -1481,22 +1483,121 @@ enum { packed_arg_bits = 4 };
enum { max_packed_args = 62 / packed_arg_bits }; enum { max_packed_args = 62 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
} // namespace detail
// An output iterator that appends to a buffer. template <typename Char, typename InputIt>
// It is used to reduce symbol sizes for the common case. auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
class appender : public std::back_insert_iterator<detail::buffer<char>> { get_container(out).append(begin, end);
using base = std::back_insert_iterator<detail::buffer<char>>; return out;
}
template <typename Char, typename InputIt>
auto copy_str(InputIt begin, InputIt end,
std::back_insert_iterator<std::string> out)
-> std::back_insert_iterator<std::string> {
get_container(out).append(begin, end);
return out;
}
template <typename Char, typename R, typename OutputIt>
FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
return detail::copy_str<Char>(rng.begin(), rng.end(), out);
}
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl { using type = void; };
template <typename... T> using void_t = typename void_t_impl<T...>::type;
#else
template <typename...> using void_t = void;
#endif
template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};
template <typename It, typename T>
struct is_output_iterator<
It, T,
void_t<typename std::iterator_traits<It>::iterator_category,
decltype(*std::declval<It>() = std::declval<T>())>>
: std::true_type {};
template <typename It> struct is_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
: std::true_type {};
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
private:
const void* locale_; // A type-erased pointer to std::locale.
public: public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
appender(base it) noexcept : base(it) {} template <typename Locale> explicit locale_ref(const Locale& loc);
FMT_UNCHECKED_ITERATOR(appender);
auto operator++() noexcept -> appender& { return *this; } explicit operator bool() const noexcept { return locale_ != nullptr; }
auto operator++(int) noexcept -> appender { return *this; }
template <typename Locale> auto get() const -> Locale;
}; };
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
}
#if defined(__cpp_if_constexpr)
// This type is intentionally undefined, only used for errors
template <typename T, typename Char> struct type_is_unformattable_for;
#endif
template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)>
FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> {
using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>;
constexpr bool formattable_char =
!std::is_same<arg_type, unformattable_char>::value;
static_assert(formattable_char, "Mixing character types is disallowed.");
// Formatting of arbitrary pointers is disallowed. If you want to format a
// pointer cast it to `void*` or `const void*`. In particular, this forbids
// formatting of `[const] volatile char*` printed as bool by iostreams.
constexpr bool formattable_pointer =
!std::is_same<arg_type, unformattable_pointer>::value;
static_assert(formattable_pointer,
"Formatting of non-void pointers is disallowed.");
constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
#if defined(__cpp_if_constexpr)
if constexpr (!formattable) {
type_is_unformattable_for<T, typename Context::char_type> _;
}
#endif
static_assert(
formattable,
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return {arg_mapper<Context>().map(val)};
}
template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
auto arg = basic_format_arg<Context>();
arg.type_ = mapped_type_constant<T, Context>::value;
arg.value_ = make_arg<true, Context>(val);
return arg;
}
template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)>
FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
return make_arg<Context>(val);
}
} // namespace detail
FMT_BEGIN_EXPORT
// A formatting argument. It is a trivially copyable/constructible type to // A formatting argument. It is a trivially copyable/constructible type to
// allow storage in basic_memory_buffer. // allow storage in basic_memory_buffer.
template <typename Context> class basic_format_arg { template <typename Context> class basic_format_arg {
@ -1505,7 +1606,7 @@ template <typename Context> class basic_format_arg {
detail::type type_; detail::type type_;
template <typename ContextType, typename T> template <typename ContextType, typename T>
friend FMT_CONSTEXPR auto detail::make_arg(T&& value) friend FMT_CONSTEXPR auto detail::make_arg(T& value)
-> basic_format_arg<ContextType>; -> basic_format_arg<ContextType>;
template <typename Visitor, typename Ctx> template <typename Visitor, typename Ctx>
@ -1559,7 +1660,7 @@ template <typename Context> class basic_format_arg {
``vis(value)`` will be called with the value of type ``double``. ``vis(value)`` will be called with the value of type ``double``.
\endrst \endrst
*/ */
FMT_MODULE_EXPORT // DEPRECATED!
template <typename Visitor, typename Context> template <typename Visitor, typename Context>
FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) { Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
@ -1601,124 +1702,6 @@ FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
return vis(monostate()); return vis(monostate());
} }
namespace detail {
template <typename Char, typename InputIt>
auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
get_container(out).append(begin, end);
return out;
}
template <typename Char, typename R, typename OutputIt>
FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
return detail::copy_str<Char>(rng.begin(), rng.end(), out);
}
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl { using type = void; };
template <typename... T> using void_t = typename void_t_impl<T...>::type;
#else
template <typename...> using void_t = void;
#endif
template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};
template <typename It, typename T>
struct is_output_iterator<
It, T,
void_t<typename std::iterator_traits<It>::iterator_category,
decltype(*std::declval<It>() = std::declval<T>())>>
: std::true_type {};
template <typename It> struct is_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
: std::true_type {};
template <typename It>
struct is_contiguous_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
: is_contiguous<Container> {};
template <>
struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const noexcept { return locale_ != nullptr; }
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
}
template <typename Context, typename T>
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
auto&& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
using arg_type = remove_cvref_t<decltype(arg)>;
constexpr bool formattable_char =
!std::is_same<arg_type, unformattable_char>::value;
static_assert(formattable_char, "Mixing character types is disallowed.");
// Formatting of arbitrary pointers is disallowed. If you want to format a
// pointer cast it to `void*` or `const void*`. In particular, this forbids
// formatting of `[const] volatile char*` printed as bool by iostreams.
constexpr bool formattable_pointer =
!std::is_same<arg_type, unformattable_pointer>::value;
static_assert(formattable_pointer,
"Formatting of non-void pointers is disallowed.");
constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
static_assert(
formattable,
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return {arg};
}
template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
auto arg = basic_format_arg<Context>();
arg.type_ = mapped_type_constant<T, Context>::value;
arg.value_ = make_value<Context>(value);
return arg;
}
// The DEPRECATED type template parameter is there to avoid an ODR violation
// when using a fallback formatter in one translation unit and an implicit
// conversion in another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
return make_value<Context>(val);
}
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(!IS_PACKED)>
FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
return make_arg<Context>(value);
}
} // namespace detail
FMT_BEGIN_EXPORT
// Formatting context. // Formatting context.
template <typename OutputIt, typename Char> class basic_format_context { template <typename OutputIt, typename Char> class basic_format_context {
private: private:
@ -1778,7 +1761,7 @@ using format_context = buffer_context<char>;
template <typename T, typename Char = char> template <typename T, typename Char = char>
using is_formattable = bool_constant<!std::is_base_of< using is_formattable = bool_constant<!std::is_base_of<
detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>() detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
.map(std::declval<T>()))>::value>; .map(std::declval<T&>()))>::value>;
/** /**
\rst \rst
@ -1796,7 +1779,7 @@ class format_arg_store
{ {
private: private:
static const size_t num_args = sizeof...(Args); static const size_t num_args = sizeof...(Args);
static const size_t num_named_args = detail::count_named_args<Args...>(); static constexpr size_t num_named_args = detail::count_named_args<Args...>();
static const bool is_packed = num_args <= detail::max_packed_args; static const bool is_packed = num_args <= detail::max_packed_args;
using value_type = conditional_t<is_packed, detail::value<Context>, using value_type = conditional_t<is_packed, detail::value<Context>,
@ -1817,16 +1800,14 @@ class format_arg_store
public: public:
template <typename... T> template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args)
: :
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this), basic_format_args<Context>(*this),
#endif #endif
data_{detail::make_arg< data_{detail::make_arg<is_packed, Context>(args)...} {
is_packed, Context, if (detail::const_check(num_named_args != 0))
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>( detail::init_named_args(data_.named_args(), 0, 0, args...);
FMT_FORWARD(args))...} {
detail::init_named_args(data_.named_args(), 0, 0, args...);
} }
}; };
@ -1834,14 +1815,15 @@ class format_arg_store
\rst \rst
Constructs a `~fmt::format_arg_store` object that contains references to Constructs a `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context` arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::context`. can be omitted in which case it defaults to `~fmt::format_context`.
See `~fmt::arg` for lifetime considerations. See `~fmt::arg` for lifetime considerations.
\endrst \endrst
*/ */
// Arguments are taken by lvalue references to avoid some lifetime issues.
template <typename Context = format_context, typename... T> template <typename Context = format_context, typename... T>
constexpr auto make_format_args(T&&... args) constexpr auto make_format_args(T&... args)
-> format_arg_store<Context, remove_cvref_t<T>...> { -> format_arg_store<Context, remove_cvref_t<T>...> {
return {FMT_FORWARD(args)...}; return {args...};
} }
/** /**
@ -1869,7 +1851,7 @@ FMT_END_EXPORT
``vformat``:: ``vformat``::
void vlog(string_view format_str, format_args args); // OK void vlog(string_view format_str, format_args args); // OK
format_args args = make_format_args(42); // Error: dangling reference format_args args = make_format_args(); // Error: dangling reference
\endrst \endrst
*/ */
template <typename Context> class basic_format_args { template <typename Context> class basic_format_args {
@ -1986,7 +1968,7 @@ template <typename Context> class basic_format_args {
/** An alias to ``basic_format_args<format_context>``. */ /** An alias to ``basic_format_args<format_context>``. */
// A separate type would result in shorter symbols but break ABI compatibility // A separate type would result in shorter symbols but break ABI compatibility
// between clang and gcc on ARM (#1919). // between clang and gcc on ARM (#1919).
FMT_MODULE_EXPORT using format_args = basic_format_args<format_context>; FMT_EXPORT using format_args = basic_format_args<format_context>;
// We cannot use enum classes as bit fields because of a gcc bug, so we put them // We cannot use enum classes as bit fields because of a gcc bug, so we put them
// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). // in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
@ -2558,7 +2540,17 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
mapped_type_constant<T, context>::value != type::custom_type, mapped_type_constant<T, context>::value != type::custom_type,
decltype(arg_mapper<context>().map(std::declval<const T&>())), decltype(arg_mapper<context>().map(std::declval<const T&>())),
typename strip_named_arg<T>::type>; typename strip_named_arg<T>::type>;
#if defined(__cpp_if_constexpr)
if constexpr (std::is_default_constructible_v<
formatter<mapped_type, char_type>>) {
return formatter<mapped_type, char_type>().parse(ctx);
} else {
type_is_unformattable_for<T, char_type> _;
return ctx.begin();
}
#else
return formatter<mapped_type, char_type>().parse(ctx); return formatter<mapped_type, char_type>().parse(ctx);
#endif
} }
// Checks char specs and returns true iff the presentation type is char-like. // Checks char specs and returns true iff the presentation type is char-like.
@ -2574,8 +2566,6 @@ FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
return true; return true;
} }
constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1;
#if FMT_USE_NONTYPE_TEMPLATE_ARGS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <int N, typename T, typename... Args, typename Char> template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
@ -2585,7 +2575,7 @@ constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (sizeof...(Args) > 0) if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<N + 1, Args...>(name); return get_arg_index_by_name<N + 1, Args...>(name);
(void)name; // Workaround an MSVC bug about "unused" parameter. (void)name; // Workaround an MSVC bug about "unused" parameter.
return invalid_arg_index; return -1;
} }
#endif #endif
@ -2596,7 +2586,7 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
return get_arg_index_by_name<0, Args...>(name); return get_arg_index_by_name<0, Args...>(name);
#endif #endif
(void)name; (void)name;
return invalid_arg_index; return -1;
} }
template <typename Char, typename... Args> class format_string_checker { template <typename Char, typename... Args> class format_string_checker {
@ -2610,15 +2600,15 @@ template <typename Char, typename... Args> class format_string_checker {
// needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
using parse_func = const Char* (*)(parse_context_type&); using parse_func = const Char* (*)(parse_context_type&);
type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
parse_context_type context_; parse_context_type context_;
parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1]; parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
public: public:
explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt) explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
: context_(fmt, num_args, types_), : types_{mapped_type_constant<Args, buffer_context<Char>>::value...},
parse_funcs_{&parse_format_specs<Args, parse_context_type>...}, context_(fmt, num_args, types_),
types_{mapped_type_constant<Args, buffer_context<Char>>::value...} {} parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
@ -2629,7 +2619,7 @@ template <typename Char, typename... Args> class format_string_checker {
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int { FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
#if FMT_USE_NONTYPE_TEMPLATE_ARGS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
auto index = get_arg_index_by_name<Args...>(id); auto index = get_arg_index_by_name<Args...>(id);
if (index == invalid_arg_index) on_error("named argument is not found"); if (index < 0) on_error("named argument is not found");
return index; return index;
#else #else
(void)id; (void)id;
@ -2638,7 +2628,9 @@ template <typename Char, typename... Args> class format_string_checker {
#endif #endif
} }
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
on_format_specs(id, begin, begin); // Call parse() on empty specs.
}
FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
-> const Char* { -> const Char* {
@ -2721,27 +2713,6 @@ struct formatter<T, Char,
-> decltype(ctx.out()); -> decltype(ctx.out());
}; };
#define FMT_FORMAT_AS(Type, Base) \
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
auto format(const Type& val, FormatContext& ctx) const \
-> decltype(ctx.out()) { \
return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
} \
}
FMT_FORMAT_AS(signed char, int);
FMT_FORMAT_AS(unsigned char, unsigned);
FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS(unsigned short, unsigned);
FMT_FORMAT_AS(long, long long);
FMT_FORMAT_AS(unsigned long, unsigned long long);
FMT_FORMAT_AS(Char*, const Char*);
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(std::nullptr_t, const void*);
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
template <typename Char = char> struct runtime_format_string { template <typename Char = char> struct runtime_format_string {
basic_string_view<Char> str; basic_string_view<Char> str;
}; };

View File

@ -1128,16 +1128,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
} }
// Remove trailing zeros from n and return the number of zeros removed (float) // Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
FMT_ASSERT(n != 0, ""); FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
// See https://github.com/fmtlib/fmt/issues/3163 for more details. constexpr uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_5 = 0xcccccccd; constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
// Casts are needed to workaround a bug in MSVC 19.22 and older.
const uint32_t mod_inv_25 =
static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
int s = 0;
while (true) { while (true) {
auto q = rotr(n * mod_inv_25, 2); auto q = rotr(n * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break; if (q > max_value<uint32_t>() / 100) break;
@ -1162,32 +1158,17 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Is n is divisible by 10^8? // Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
// If yes, work with the quotient. // If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64)); auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
// ... and use the 32 bit variant of the function
const uint32_t mod_inv_5 = 0xcccccccd; int s = remove_trailing_zeros(n32, 8);
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
int s = 8;
while (true) {
auto q = rotr(n32 * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break;
n32 = q;
s += 2;
}
auto q = rotr(n32 * mod_inv_5, 1);
if (q <= max_value<uint32_t>() / 10) {
n32 = q;
s |= 1;
}
n = n32; n = n32;
return s; return s;
} }
// If n is not divisible by 10^8, work with n itself. // If n is not divisible by 10^8, work with n itself.
const uint64_t mod_inv_5 = 0xcccccccccccccccd; constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5; constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5
int s = 0; int s = 0;
while (true) { while (true) {
@ -1458,7 +1439,7 @@ FMT_FUNC bool write_console(std::FILE* f, string_view text) {
auto u16 = utf8_to_utf16(text); auto u16 = utf8_to_utf16(text);
auto written = dword(); auto written = dword();
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(), return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<uint32_t>(u16.size()), &written, nullptr); static_cast<uint32_t>(u16.size()), &written, nullptr) != 0;
} }
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.

View File

@ -48,9 +48,10 @@
#include "core.h" #include "core.h"
#ifndef FMT_BEGIN_DETAIL_NAMESPACE #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { # define FMT_INLINE_VARIABLE inline
# define FMT_END_DETAIL_NAMESPACE } #else
# define FMT_INLINE_VARIABLE
#endif #endif
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) #if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
@ -78,16 +79,24 @@
# endif # endif
#endif #endif
#if FMT_GCC_VERSION #ifndef FMT_NO_UNIQUE_ADDRESS
# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) # if FMT_CPLUSPLUS >= 202002L
#else # if FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
# define FMT_GCC_VISIBILITY_HIDDEN # define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485)
# elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION
# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
# endif
# endif
#endif
#ifndef FMT_NO_UNIQUE_ADDRESS
# define FMT_NO_UNIQUE_ADDRESS
#endif #endif
#ifdef __NVCC__ #if FMT_GCC_VERSION || defined(__clang__)
# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) # define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else #else
# define FMT_CUDA_VERSION 0 # define FMT_VISIBILITY(value)
#endif #endif
#ifdef __has_builtin #ifdef __has_builtin
@ -120,10 +129,8 @@ FMT_END_NAMESPACE
# define FMT_THROW(x) throw x # define FMT_THROW(x) throw x
# endif # endif
# else # else
# define FMT_THROW(x) \ # define FMT_THROW(x) \
do { \ ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what())
FMT_ASSERT(false, (x).what()); \
} while (false)
# endif # endif
#endif #endif
@ -362,8 +369,6 @@ class uint128_fallback {
private: private:
uint64_t lo_, hi_; uint64_t lo_, hi_;
friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
public: public:
constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
@ -536,6 +541,8 @@ FMT_INLINE void assume(bool condition) {
(void)condition; (void)condition;
#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
__builtin_assume(condition); __builtin_assume(condition);
#elif FMT_GCC_VERSION
if (!condition) __builtin_unreachable();
#endif #endif
} }
@ -554,20 +561,6 @@ inline auto get_data(Container& c) -> typename Container::value_type* {
return c.data(); return c.data();
} }
#if defined(_SECURE_SCL) && _SECURE_SCL
// Make a checked iterator to avoid MSVC warnings.
template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
template <typename T>
constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> {
return {p, size};
}
#else
template <typename T> using checked_ptr = T*;
template <typename T> constexpr auto make_checked(T* p, size_t) -> T* {
return p;
}
#endif
// Attempts to reserve space for n extra characters in the output range. // Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to it. // Returns a pointer to the reserved range or a reference to it.
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)> template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
@ -575,12 +568,12 @@ template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
__attribute__((no_sanitize("undefined"))) __attribute__((no_sanitize("undefined")))
#endif #endif
inline auto inline auto
reserve(std::back_insert_iterator<Container> it, size_t n) reserve(std::back_insert_iterator<Container> it, size_t n) ->
-> checked_ptr<typename Container::value_type> { typename Container::value_type* {
Container& c = get_container(it); Container& c = get_container(it);
size_t size = c.size(); size_t size = c.size();
c.resize(size + n); c.resize(size + n);
return make_checked(get_data(c) + size, n); return get_data(c) + size;
} }
template <typename T> template <typename T>
@ -612,8 +605,8 @@ template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
} }
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)> template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
inline auto base_iterator(std::back_insert_iterator<Container>& it, inline auto base_iterator(std::back_insert_iterator<Container> it,
checked_ptr<typename Container::value_type>) typename Container::value_type*)
-> std::back_insert_iterator<Container> { -> std::back_insert_iterator<Container> {
return it; return it;
} }
@ -881,7 +874,7 @@ void buffer<T>::append(const U* begin, const U* end) {
try_reserve(size_ + count); try_reserve(size_ + count);
auto free_cap = capacity_ - size_; auto free_cap = capacity_ - size_;
if (free_cap < count) count = free_cap; if (free_cap < count) count = free_cap;
std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); std::uninitialized_copy_n(begin, count, ptr_ + size_);
size_ += count; size_ += count;
begin += count; begin += count;
} }
@ -926,8 +919,8 @@ class basic_memory_buffer final : public detail::buffer<T> {
private: private:
T store_[SIZE]; T store_[SIZE];
// Don't inherit from Allocator avoid generating type_info for it. // Don't inherit from Allocator to avoid generating type_info for it.
Allocator alloc_; FMT_NO_UNIQUE_ADDRESS Allocator alloc_;
// Deallocate memory allocated by the buffer. // Deallocate memory allocated by the buffer.
FMT_CONSTEXPR20 void deallocate() { FMT_CONSTEXPR20 void deallocate() {
@ -948,9 +941,10 @@ class basic_memory_buffer final : public detail::buffer<T> {
T* old_data = this->data(); T* old_data = this->data();
T* new_data = T* new_data =
std::allocator_traits<Allocator>::allocate(alloc_, new_capacity); std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
// Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
detail::assume(this->size() <= new_capacity);
// The following code doesn't throw, so the raw pointer above doesn't leak. // The following code doesn't throw, so the raw pointer above doesn't leak.
std::uninitialized_copy(old_data, old_data + this->size(), std::uninitialized_copy_n(old_data, this->size(), new_data);
detail::make_checked(new_data, new_capacity));
this->set(new_data, new_capacity); this->set(new_data, new_capacity);
// deallocate must not throw according to the standard, but even if it does, // deallocate must not throw according to the standard, but even if it does,
// the buffer already uses the new storage and will deallocate it in // the buffer already uses the new storage and will deallocate it in
@ -978,8 +972,7 @@ class basic_memory_buffer final : public detail::buffer<T> {
size_t size = other.size(), capacity = other.capacity(); size_t size = other.size(), capacity = other.capacity();
if (data == other.store_) { if (data == other.store_) {
this->set(store_, capacity); this->set(store_, capacity);
detail::copy_str<T>(other.store_, other.store_ + size, detail::copy_str<T>(other.store_, other.store_ + size, store_);
detail::make_checked(store_, capacity));
} else { } else {
this->set(data, capacity); this->set(data, capacity);
// Set pointer to the inline array so that delete is not called // Set pointer to the inline array so that delete is not called
@ -1044,6 +1037,7 @@ namespace detail {
FMT_API bool write_console(std::FILE* f, string_view text); FMT_API bool write_console(std::FILE* f, string_view text);
FMT_API void print(std::FILE*, string_view); FMT_API void print(std::FILE*, string_view);
} // namespace detail } // namespace detail
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
// Suppress a misleading warning in older versions of clang. // Suppress a misleading warning in older versions of clang.
@ -1052,7 +1046,7 @@ FMT_BEGIN_EXPORT
#endif #endif
/** An error reported from a formatting function. */ /** An error reported from a formatting function. */
class FMT_API format_error : public std::runtime_error { class FMT_VISIBILITY("default") format_error : public std::runtime_error {
public: public:
using std::runtime_error::runtime_error; using std::runtime_error::runtime_error;
}; };
@ -1128,7 +1122,7 @@ template <typename Locale> class format_facet : public Locale::facet {
} }
}; };
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// Returns true if value is negative, false otherwise. // Returns true if value is negative, false otherwise.
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
@ -1257,7 +1251,7 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int {
FMT_INLINE auto do_count_digits(uint32_t n) -> int { FMT_INLINE auto do_count_digits(uint32_t n) -> int {
// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. // An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
// This increments the upper 32 bits (log10(T) - 1) when >= T is added. // This increments the upper 32 bits (log10(T) - 1) when >= T is added.
# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) # define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T)
static constexpr uint64_t table[] = { static constexpr uint64_t table[] = {
FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8
FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64
@ -1393,8 +1387,8 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
} }
template <unsigned BASE_BITS, typename Char, typename It, typename UInt> template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits,
-> It { bool upper = false) -> It {
if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) { if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
format_uint<BASE_BITS>(ptr, value, num_digits, upper); format_uint<BASE_BITS>(ptr, value, num_digits, upper);
return out; return out;
@ -1418,19 +1412,20 @@ class utf8_to_utf16 {
auto str() const -> std::wstring { return {&buffer_[0], size()}; } auto str() const -> std::wstring { return {&buffer_[0], size()}; }
}; };
enum class to_utf8_error_policy { abort, replace };
// A converter from UTF-16/UTF-32 (host endian) to UTF-8. // A converter from UTF-16/UTF-32 (host endian) to UTF-8.
template <typename WChar, typename Buffer = memory_buffer> template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
class unicode_to_utf8 {
private: private:
Buffer buffer_; Buffer buffer_;
public: public:
unicode_to_utf8() {} to_utf8() {}
explicit unicode_to_utf8(basic_string_view<WChar> s) { explicit to_utf8(basic_string_view<WChar> s,
to_utf8_error_policy policy = to_utf8_error_policy::abort) {
static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
"Expect utf16 or utf32"); "Expect utf16 or utf32");
if (!convert(s, policy))
if (!convert(s))
FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
: "invalid utf32")); : "invalid utf32"));
} }
@ -1442,23 +1437,28 @@ class unicode_to_utf8 {
// Performs conversion returning a bool instead of throwing exception on // Performs conversion returning a bool instead of throwing exception on
// conversion error. This method may still throw in case of memory allocation // conversion error. This method may still throw in case of memory allocation
// error. // error.
bool convert(basic_string_view<WChar> s) { bool convert(basic_string_view<WChar> s,
if (!convert(buffer_, s)) return false; to_utf8_error_policy policy = to_utf8_error_policy::abort) {
if (!convert(buffer_, s, policy)) return false;
buffer_.push_back(0); buffer_.push_back(0);
return true; return true;
} }
static bool convert(Buffer& buf, basic_string_view<WChar> s) { static bool convert(
Buffer& buf, basic_string_view<WChar> s,
to_utf8_error_policy policy = to_utf8_error_policy::abort) {
for (auto p = s.begin(); p != s.end(); ++p) { for (auto p = s.begin(); p != s.end(); ++p) {
uint32_t c = static_cast<uint32_t>(*p); uint32_t c = static_cast<uint32_t>(*p);
if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
// surrogate pair // Handle a surrogate pair.
++p; ++p;
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
return false; if (policy == to_utf8_error_policy::abort) return false;
buf.append(string_view("\xEF\xBF\xBD"));
--p;
} else {
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
} }
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00; } else if (c < 0x80) {
}
if (c < 0x80) {
buf.push_back(static_cast<char>(c)); buf.push_back(static_cast<char>(c));
} else if (c < 0x800) { } else if (c < 0x800) {
buf.push_back(static_cast<char>(0xc0 | (c >> 6))); buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
@ -1486,9 +1486,9 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y); auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)}; return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
#elif defined(_MSC_VER) && defined(_M_X64) #elif defined(_MSC_VER) && defined(_M_X64)
auto result = uint128_fallback(); auto hi = uint64_t();
result.lo_ = _umul128(x, y, &result.hi_); auto lo = _umul128(x, y, &hi);
return result; return {hi, lo};
#else #else
const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>()); const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
@ -1737,119 +1737,31 @@ FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
} }
template <typename T = void> struct basic_data { template <typename T = void> struct basic_data {
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
// These are generated by support/compute-powers.py.
static constexpr uint64_t pow10_significands[87] = {
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
};
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wnarrowing"
#endif
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
// to significands above.
static constexpr int16_t pow10_exponents[87] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
# pragma GCC diagnostic pop
#endif
static constexpr uint64_t power_of_10_64[20] = {
1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
// For checking rounding thresholds. // For checking rounding thresholds.
// The kth entry is chosen to be the smallest integer such that the // The kth entry is chosen to be the smallest integer such that the
// upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
static constexpr uint32_t fractional_part_rounding_thresholds[8] = { static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
2576980378, // ceil(2^31 + 2^32/10^1) 2576980378U, // ceil(2^31 + 2^32/10^1)
2190433321, // ceil(2^31 + 2^32/10^2) 2190433321U, // ceil(2^31 + 2^32/10^2)
2151778616, // ceil(2^31 + 2^32/10^3) 2151778616U, // ceil(2^31 + 2^32/10^3)
2147913145, // ceil(2^31 + 2^32/10^4) 2147913145U, // ceil(2^31 + 2^32/10^4)
2147526598, // ceil(2^31 + 2^32/10^5) 2147526598U, // ceil(2^31 + 2^32/10^5)
2147487943, // ceil(2^31 + 2^32/10^6) 2147487943U, // ceil(2^31 + 2^32/10^6)
2147484078, // ceil(2^31 + 2^32/10^7) 2147484078U, // ceil(2^31 + 2^32/10^7)
2147483691 // ceil(2^31 + 2^32/10^8) 2147483691U // ceil(2^31 + 2^32/10^8)
}; };
}; };
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
struct data : basic_data<> {};
#if FMT_CPLUSPLUS < 201703L #if FMT_CPLUSPLUS < 201703L
template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
template <typename T> template <typename T>
constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[]; constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
#endif #endif
// This is a struct rather than an alias to avoid shadowing warnings in gcc. template <typename T, bool doublish = num_bits<T>() == num_bits<double>()>
struct data : basic_data<> {};
// 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_CONSTEXPR inline fp get_cached_power(int min_exponent,
int& pow10_exponent) {
const int shift = 32;
// log10(2) = 0x0.4d104d427de7fbcc...
const int64_t significand = 0x4d104d427de7fbcc;
int index = static_cast<int>(
((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
((int64_t(1) << shift) - 1)) // ceil
>> 32 // arithmetic shift
);
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
// Difference between 2 consecutive decimal exponents in cached powers of 10.
const int dec_exp_step = 8;
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
pow10_exponent = first_dec_exp + index * dec_exp_step;
// Using *(x + index) instead of x[index] avoids an issue with some compilers
// using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
return {*(data::pow10_significands + index),
*(data::pow10_exponents + index)};
}
template <typename T>
using convert_float_result = using convert_float_result =
conditional_t<std::is_same<T, float>::value || conditional_t<std::is_same<T, float>::value || doublish, double, T>;
std::numeric_limits<T>::digits ==
std::numeric_limits<double>::digits,
double, T>;
template <typename T> template <typename T>
constexpr auto convert_float(T value) -> convert_float_result<T> { constexpr auto convert_float(T value) -> convert_float_result<T> {
@ -1970,7 +1882,7 @@ inline auto find_escape(const char* begin, const char* end)
[] { \ [] { \
/* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
/* Use a macro-like name to avoid shadowing warnings. */ \ /* Use a macro-like name to avoid shadowing warnings. */ \
struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \
using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \ using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
operator fmt::basic_string_view<char_type>() const { \ operator fmt::basic_string_view<char_type>() const { \
@ -2475,6 +2387,49 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
return base_iterator(out, it); return base_iterator(out, it);
} }
// DEPRECATED!
template <typename Char>
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
format_specs<Char>& specs) -> const Char* {
FMT_ASSERT(begin != end, "");
auto align = align::none;
auto p = begin + code_point_length(begin);
if (end - p <= 0) p = begin;
for (;;) {
switch (to_ascii(*p)) {
case '<':
align = align::left;
break;
case '>':
align = align::right;
break;
case '^':
align = align::center;
break;
}
if (align != align::none) {
if (p != begin) {
auto c = *begin;
if (c == '}') return begin;
if (c == '{') {
throw_format_error("invalid fill character '{'");
return begin;
}
specs.fill = {begin, to_unsigned(p - begin)};
begin = p + 1;
} else {
++begin;
}
break;
} else if (p == begin) {
break;
}
p = begin;
}
specs.align = align;
return begin;
}
// A floating-point presentation format. // A floating-point presentation format.
enum class float_format : unsigned char { enum class float_format : unsigned char {
general, // General: exponent notation or fixed point based on magnitude. general, // General: exponent notation or fixed point based on magnitude.
@ -2833,78 +2788,6 @@ FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
return std::signbit(static_cast<double>(value)); return std::signbit(static_cast<double>(value));
} }
enum class round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
// some number v and the error, returns whether v should be rounded up, down, or
// whether the rounding direction can't be determined due to error.
// error should be less than divisor / 2.
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
uint64_t remainder,
uint64_t error) {
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
// Round down if (remainder + error) * 2 <= divisor.
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
return round_direction::down;
// Round up if (remainder - error) * 2 >= divisor.
if (remainder >= error &&
remainder - error >= divisor - (remainder - error)) {
return round_direction::up;
}
return round_direction::unknown;
}
namespace digits {
enum result {
more, // Generate more digits.
done, // Done generating digits.
error // Digit generation cancelled due to an error.
};
}
struct gen_digits_handler {
char* buf;
int size;
int precision;
int exp10;
bool fixed;
FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
uint64_t remainder, uint64_t error,
bool integral) {
FMT_ASSERT(remainder < divisor, "");
buf[size++] = digit;
if (!integral && error >= remainder) return digits::error;
if (size < precision) return digits::more;
if (!integral) {
// Check if error * 2 < divisor with overflow prevention.
// The check is not needed for the integral part because error = 1
// and divisor > (1 << 32) there.
if (error >= divisor || error >= divisor - error) return digits::error;
} else {
FMT_ASSERT(error == 1 && divisor > 2, "");
}
auto dir = get_round_direction(divisor, remainder, error);
if (dir != round_direction::up)
return dir == round_direction::down ? digits::done : digits::error;
++buf[size - 1];
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
buf[i] = '0';
++buf[i - 1];
}
if (buf[0] > '9') {
buf[0] = '1';
if (fixed)
buf[size++] = '0';
else
++exp10;
}
return digits::done;
}
};
inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
// Adjust fixed precision by exponent because it is relative to decimal // Adjust fixed precision by exponent because it is relative to decimal
// point. // point.
@ -2913,101 +2796,6 @@ inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
precision += exp10; precision += exp10;
} }
// Generates output using the Grisu digit-gen algorithm.
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
int& exp,
gen_digits_handler& handler)
-> digits::result {
const fp one(1ULL << -value.e, value.e);
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
// zero because it contains a product of two 64-bit numbers with MSB set (due
// to normalization) - 1, shifted right by at most 60 bits.
auto integral = static_cast<uint32_t>(value.f >> -one.e);
FMT_ASSERT(integral != 0, "");
FMT_ASSERT(integral == value.f >> -one.e, "");
// The fractional part of scaled value (p2 in Grisu) c = value % one.
uint64_t fractional = value.f & (one.f - 1);
exp = count_digits(integral); // kappa in Grisu.
// Non-fixed formats require at least one digit and no precision adjustment.
if (handler.fixed) {
adjust_precision(handler.precision, exp + handler.exp10);
// Check if precision is satisfied just by leading zeros, e.g.
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
if (handler.precision <= 0) {
if (handler.precision < 0) return digits::done;
// Divide by 10 to prevent overflow.
uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
auto dir = get_round_direction(divisor, value.f / 10, error * 10);
if (dir == round_direction::unknown) return digits::error;
handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
return digits::done;
}
}
// Generate digits for the integral part. This can produce up to 10 digits.
do {
uint32_t digit = 0;
auto divmod_integral = [&](uint32_t divisor) {
digit = integral / divisor;
integral %= divisor;
};
// This optimization by Milo Yip reduces the number of integer divisions by
// one per iteration.
switch (exp) {
case 10:
divmod_integral(1000000000);
break;
case 9:
divmod_integral(100000000);
break;
case 8:
divmod_integral(10000000);
break;
case 7:
divmod_integral(1000000);
break;
case 6:
divmod_integral(100000);
break;
case 5:
divmod_integral(10000);
break;
case 4:
divmod_integral(1000);
break;
case 3:
divmod_integral(100);
break;
case 2:
divmod_integral(10);
break;
case 1:
digit = integral;
integral = 0;
break;
default:
FMT_ASSERT(false, "invalid number of digits");
}
--exp;
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
auto result = handler.on_digit(static_cast<char>('0' + digit),
data::power_of_10_64[exp] << -one.e,
remainder, error, true);
if (result != digits::more) return result;
} while (exp > 0);
// Generate digits for the fractional part.
for (;;) {
fractional *= 10;
error *= 10;
char digit = static_cast<char>('0' + (fractional >> -one.e));
fractional &= one.f - 1;
--exp;
auto result = handler.on_digit(digit, one.f, fractional, error, false);
if (result != digits::more) return result;
}
}
class bigint { class bigint {
private: private:
// A bigint is stored as an array of bigits (big digits), with bigit at index // A bigint is stored as an array of bigits (big digits), with bigit at index
@ -3108,7 +2896,7 @@ class bigint {
auto size = other.bigits_.size(); auto size = other.bigits_.size();
bigits_.resize(size); bigits_.resize(size);
auto data = other.bigits_.data(); auto data = other.bigits_.data();
std::copy(data, data + size, make_checked(bigits_.data(), size)); copy_str<bigit>(data, data + size, bigits_.data());
exp_ = other.exp_; exp_ = other.exp_;
} }
@ -3322,6 +3110,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
} }
int even = static_cast<int>((value.f & 1) == 0); int even = static_cast<int>((value.f & 1) == 0);
if (!upper) upper = &lower; if (!upper) upper = &lower;
bool shortest = num_digits < 0;
if ((flags & dragon::fixup) != 0) { if ((flags & dragon::fixup) != 0) {
if (add_compare(numerator, *upper, denominator) + even <= 0) { if (add_compare(numerator, *upper, denominator) + even <= 0) {
--exp10; --exp10;
@ -3334,7 +3123,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
} }
// Invariant: value == (numerator / denominator) * pow(10, exp10). // Invariant: value == (numerator / denominator) * pow(10, exp10).
if (num_digits < 0) { if (shortest) {
// Generate the shortest representation. // Generate the shortest representation.
num_digits = 0; num_digits = 0;
char* data = buf.data(); char* data = buf.data();
@ -3364,7 +3153,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
} }
// Generate the given number of digits. // Generate the given number of digits.
exp10 -= num_digits - 1; exp10 -= num_digits - 1;
if (num_digits == 0) { if (num_digits <= 0) {
denominator *= 10; denominator *= 10;
auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
buf.push_back(digit); buf.push_back(digit);
@ -3389,7 +3178,8 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
} }
if (buf[0] == overflow) { if (buf[0] == overflow) {
buf[0] = '1'; buf[0] = '1';
++exp10; if ((flags & dragon::fixed) != 0) buf.push_back('0');
else ++exp10;
} }
return; return;
} }
@ -3508,7 +3298,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
int exp = 0; int exp = 0;
bool use_dragon = true; bool use_dragon = true;
unsigned dragon_flags = 0; unsigned dragon_flags = 0;
if (!is_fast_float<Float>()) { if (!is_fast_float<Float>() || is_constant_evaluated()) {
const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10)
using info = dragonbox::float_info<decltype(converted_value)>; using info = dragonbox::float_info<decltype(converted_value)>;
const auto f = basic_fp<typename info::carrier_uint>(converted_value); const auto f = basic_fp<typename info::carrier_uint>(converted_value);
@ -3516,10 +3306,11 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
// 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
// This is based on log10(value) == log2(value) / log2(10) and approximation // This is based on log10(value) == log2(value) / log2(10) and approximation
// of log2(value) by e + num_fraction_bits idea from double-conversion. // of log2(value) by e + num_fraction_bits idea from double-conversion.
exp = static_cast<int>( auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10;
std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); exp = static_cast<int>(e);
if (e > exp) ++exp; // Compute ceil.
dragon_flags = dragon::fixup; dragon_flags = dragon::fixup;
} else if (!is_constant_evaluated() && precision < 0) { } else if (precision < 0) {
// Use Dragonbox for the shortest format. // Use Dragonbox for the shortest format.
if (specs.binary32) { if (specs.binary32) {
auto dec = dragonbox::to_decimal(static_cast<float>(value)); auto dec = dragonbox::to_decimal(static_cast<float>(value));
@ -3529,25 +3320,6 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
auto dec = dragonbox::to_decimal(static_cast<double>(value)); auto dec = dragonbox::to_decimal(static_cast<double>(value));
write<char>(buffer_appender<char>(buf), dec.significand); write<char>(buffer_appender<char>(buf), dec.significand);
return dec.exponent; return dec.exponent;
} else if (is_constant_evaluated()) {
// Use Grisu + Dragon4 for the given precision:
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu.
fp normalized = normalize(fp(converted_value));
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
normalized = normalized * cached_pow;
gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
!is_constant_evaluated()) {
exp += handler.exp10;
buf.try_resize(to_unsigned(handler.size));
use_dragon = false;
} else {
exp += handler.size - cached_exp10 - 1;
precision = handler.precision;
}
} else { } else {
// Extract significand bits and exponent bits. // Extract significand bits and exponent bits.
using info = dragonbox::float_info<double>; using info = dragonbox::float_info<double>;
@ -3566,7 +3338,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
significand <<= 1; significand <<= 1;
} else { } else {
// Normalize subnormal inputs. // Normalize subnormal inputs.
FMT_ASSERT(significand != 0, "zeros should not appear hear"); FMT_ASSERT(significand != 0, "zeros should not appear here");
int shift = countl_zero(significand); int shift = countl_zero(significand);
FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(), FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(),
""); "");
@ -3603,9 +3375,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
} }
// Compute the actual number of decimal digits to print. // Compute the actual number of decimal digits to print.
if (fixed) { if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment);
adjust_precision(precision, exp + digits_in_the_first_segment);
}
// Use Dragon4 only when there might be not enough digits in the first // Use Dragon4 only when there might be not enough digits in the first
// segment. // segment.
@ -4091,8 +3861,7 @@ FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
} }
template <typename Context, typename ID> template <typename Context, typename ID>
FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
typename Context::format_arg {
auto arg = ctx.arg(id); auto arg = ctx.arg(id);
if (!arg) ctx.on_error("argument not found"); if (!arg) ctx.on_error("argument not found");
return arg; return arg;
@ -4117,15 +3886,6 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value,
} }
#if FMT_USE_USER_DEFINED_LITERALS #if FMT_USE_USER_DEFINED_LITERALS
template <typename Char> struct udl_formatter {
basic_string_view<Char> str;
template <typename... T>
auto operator()(T&&... args) const -> std::basic_string<Char> {
return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
}
};
# if FMT_USE_NONTYPE_TEMPLATE_ARGS # if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename T, typename Char, size_t N, template <typename T, typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str> fmt::detail_exported::fixed_string<Char, N> Str>
@ -4179,28 +3939,28 @@ FMT_API void format_error_code(buffer<char>& out, int error_code,
FMT_API void report_error(format_func func, int error_code, FMT_API void report_error(format_func func, int error_code,
const char* message) noexcept; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE } // namespace detail
FMT_API auto vsystem_error(int error_code, string_view format_str, FMT_API auto vsystem_error(int error_code, string_view format_str,
format_args args) -> std::system_error; format_args args) -> std::system_error;
/** /**
\rst \rst
Constructs :class:`std::system_error` with a message formatted with Constructs :class:`std::system_error` with a message formatted with
``fmt::format(fmt, args...)``. ``fmt::format(fmt, args...)``.
*error_code* is a system error code as given by ``errno``. *error_code* is a system error code as given by ``errno``.
**Example**:: **Example**::
// This throws std::system_error with the description // This throws std::system_error with the description
// cannot open file 'madeup': No such file or directory // cannot open file 'madeup': No such file or directory
// or similar (system message may vary). // or similar (system message may vary).
const char* filename = "madeup"; const char* filename = "madeup";
std::FILE* file = std::fopen(filename, "r"); std::FILE* file = std::fopen(filename, "r");
if (!file) if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename); throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst \endrst
*/ */
template <typename... T> template <typename... T>
auto system_error(int error_code, format_string<T...> fmt, T&&... args) auto system_error(int error_code, format_string<T...> fmt, T&&... args)
-> std::system_error { -> std::system_error {
@ -4292,8 +4052,8 @@ class format_int {
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>> struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
: private formatter<detail::format_as_t<T>> { : private formatter<detail::format_as_t<T>, Char> {
using base = formatter<detail::format_as_t<T>>; using base = formatter<detail::format_as_t<T>, Char>;
using base::parse; using base::parse;
template <typename FormatContext> template <typename FormatContext>
@ -4302,22 +4062,24 @@ struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
} }
}; };
template <typename Char> #define FMT_FORMAT_AS(Type, Base) \
struct formatter<void*, Char> : formatter<const void*, Char> { template <typename Char> \
template <typename FormatContext> struct formatter<Type, Char> : formatter<Base, Char> {}
auto format(void* val, FormatContext& ctx) const -> decltype(ctx.out()) {
return formatter<const void*, Char>::format(val, ctx); FMT_FORMAT_AS(signed char, int);
} FMT_FORMAT_AS(unsigned char, unsigned);
}; FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS(unsigned short, unsigned);
FMT_FORMAT_AS(long, detail::long_type);
FMT_FORMAT_AS(unsigned long, detail::ulong_type);
FMT_FORMAT_AS(Char*, const Char*);
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(std::nullptr_t, const void*);
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(void*, const void*);
template <typename Char, size_t N> template <typename Char, size_t N>
struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> { struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
template <typename FormatContext>
FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<basic_string_view<Char>, Char>::format(val, ctx);
}
};
/** /**
\rst \rst
@ -4393,7 +4155,9 @@ template <> struct formatter<bytes> {
}; };
// group_digits_view is not derived from view because it copies the argument. // group_digits_view is not derived from view because it copies the argument.
template <typename T> struct group_digits_view { T value; }; template <typename T> struct group_digits_view {
T value;
};
/** /**
\rst \rst
@ -4523,7 +4287,8 @@ auto join(Range&& range, string_view sep)
std::string answer = fmt::to_string(42); std::string answer = fmt::to_string(42);
\endrst \endrst
*/ */
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
!detail::has_format_as<T>::value)>
inline auto to_string(const T& value) -> std::string { inline auto to_string(const T& value) -> std::string {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::write<char>(appender(buffer), value); detail::write<char>(appender(buffer), value);
@ -4548,7 +4313,15 @@ FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
return std::basic_string<Char>(buf.data(), size); return std::basic_string<Char>(buf.data(), size);
} }
FMT_BEGIN_DETAIL_NAMESPACE template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
detail::has_format_as<T>::value)>
inline auto to_string(const T& value) -> std::string {
return to_string(format_as(value));
}
FMT_END_EXPORT
namespace detail {
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt, void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
@ -4619,6 +4392,8 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc)); detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
} }
FMT_BEGIN_EXPORT
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
extern template FMT_API void vformat_to(buffer<char>&, string_view, extern template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, typename vformat_args<>::type,
@ -4631,7 +4406,7 @@ extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
#endif // FMT_HEADER_ONLY #endif // FMT_HEADER_ONLY
FMT_END_DETAIL_NAMESPACE } // namespace detail
#if FMT_USE_USER_DEFINED_LITERALS #if FMT_USE_USER_DEFINED_LITERALS
inline namespace literals { inline namespace literals {

View File

@ -123,10 +123,10 @@ using wcstring_view = basic_cstring_view<wchar_t>;
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE }
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args); format_args args);
@ -328,7 +328,7 @@ class FMT_API file {
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
struct buffer_size { struct buffer_size {
buffer_size() = default; buffer_size() = default;
@ -387,7 +387,7 @@ class file_buffer final : public buffer<char> {
} }
}; };
FMT_END_DETAIL_NAMESPACE } // namespace detail
// Added {} below to work around default constructor error known to // Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1. // occur in Xcode versions 7.2.1 and 8.2.1.

View File

@ -155,7 +155,7 @@ inline void vprint_directly(std::ostream& os, string_view format_str,
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT template <typename Char> FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os, void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str, basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
@ -174,7 +174,7 @@ void vprint(std::basic_ostream<Char>& os,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_MODULE_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) { void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...); const auto& vargs = fmt::make_format_args(args...);
if (detail::is_utf8()) if (detail::is_utf8())
@ -183,7 +183,7 @@ void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
detail::vprint_directly(os, fmt, vargs); detail::vprint_directly(os, fmt, vargs);
} }
FMT_MODULE_EXPORT FMT_EXPORT
template <typename... Args> template <typename... Args>
void print(std::wostream& os, void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
@ -191,12 +191,12 @@ void print(std::wostream& os,
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
} }
FMT_MODULE_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) { void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...)); fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
} }
FMT_MODULE_EXPORT FMT_EXPORT
template <typename... Args> template <typename... Args>
void println(std::wostream& os, void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, basic_format_string<wchar_t, type_identity_t<Args>...> fmt,

View File

@ -18,20 +18,14 @@ FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; }; template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char> template <typename Char> class basic_printf_context {
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context {
private: private:
OutputIt out_; detail::buffer_appender<Char> out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
public: public:
using char_type = Char; using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>; using parse_context_type = basic_format_parse_context<Char>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>; template <typename T> using formatter_type = printf_formatter<T>;
/** /**
@ -40,68 +34,68 @@ template <typename OutputIt, typename Char> class basic_printf_context {
stored in the context object so make sure they have appropriate lifetimes. stored in the context object so make sure they have appropriate lifetimes.
\endrst \endrst
*/ */
basic_printf_context(OutputIt out, basic_printf_context(detail::buffer_appender<Char> out,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {} : out_(out), args_(args) {}
OutputIt out() { return out_; } auto out() -> detail::buffer_appender<Char> { return out_; }
void advance_to(OutputIt it) { out_ = it; } void advance_to(detail::buffer_appender<Char>) {}
detail::locale_ref locale() { return {}; } auto locale() -> detail::locale_ref { return {}; }
format_arg arg(int id) const { return args_.get(id); } auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
FMT_CONSTEXPR void on_error(const char* message) { FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message); detail::error_handler().on_error(message);
} }
}; };
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> struct int_checker { template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) { template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>(); unsigned max = max_value<int>();
return value <= max; return value <= max;
} }
static bool fits_in_int(bool) { return true; } static auto fits_in_int(bool) -> bool { return true; }
}; };
template <> struct int_checker<true> { template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) { template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() && return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>(); value <= max_value<int>();
} }
static bool fits_in_int(int) { return true; } static auto fits_in_int(int) -> bool { return true; }
}; };
class printf_precision_handler { struct printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) { auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big"); throw_format_error("number is too big");
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) { auto operator()(T) -> int {
throw_format_error("precision is not integer"); throw_format_error("precision is not integer");
return 0; return 0;
} }
}; };
// An argument visitor that returns true iff arg is a zero integer. // An argument visitor that returns true iff arg is a zero integer.
class is_zero_int { struct is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) { auto operator()(T value) -> bool {
return value == 0; return value == 0;
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) { auto operator()(T) -> bool {
return false; return false;
} }
}; };
@ -132,22 +126,23 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_ = detail::make_arg<Context>( auto n = static_cast<int>(static_cast<target_type>(value));
static_cast<int>(static_cast<target_type>(value))); arg_ = detail::make_arg<Context>(n);
} else { } else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>( auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
static_cast<unsigned>(static_cast<unsigned_type>(value))); arg_ = detail::make_arg<Context>(n);
} }
} else { } else {
if (is_signed) { if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types: // glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254" // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB. // but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value)); auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else { } else {
arg_ = detail::make_arg<Context>( auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
static_cast<typename make_unsigned_or_bool<U>::type>(value)); arg_ = detail::make_arg<Context>(n);
} }
} }
} }
@ -175,8 +170,8 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
arg_ = detail::make_arg<Context>( auto c = static_cast<typename Context::char_type>(value);
static_cast<typename Context::char_type>(value)); arg_ = detail::make_arg<Context>(c);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -186,8 +181,8 @@ template <typename Context> class char_converter {
// An argument visitor that return a pointer to a C string if argument is a // An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise. // string or null otherwise.
template <typename Char> struct get_cstring { template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; } template <typename T> auto operator()(T) -> const Char* { return nullptr; }
const Char* operator()(const Char* s) { return s; } auto operator()(const Char* s) -> const Char* { return s; }
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
@ -200,7 +195,7 @@ template <typename Char> class printf_width_handler {
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {} explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) { if (detail::is_negative(value)) {
specs_.align = align::left; specs_.align = align::left;
@ -212,7 +207,7 @@ template <typename Char> class printf_width_handler {
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) { auto operator()(T) -> unsigned {
throw_format_error("width is not integer"); throw_format_error("width is not integer");
return 0; return 0;
} }
@ -227,80 +222,85 @@ auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
} }
// The ``printf`` argument formatter. // The ``printf`` argument formatter.
template <typename OutputIt, typename Char> template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
using base = arg_formatter<Char>; using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<Char>;
context_type& context_; context_type& context_;
OutputIt write_null_pointer(bool is_string = false) { void write_null_pointer(bool is_string = false) {
auto s = this->specs; auto s = this->specs;
s.type = presentation_type::none; s.type = presentation_type::none;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
} }
public: public:
printf_arg_formatter(OutputIt iter, format_specs<Char>& s, context_type& ctx) printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {} : base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); } void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) { void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use // MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead. // std::is_same instead.
if (std::is_same<T, Char>::value) { if (!std::is_same<T, Char>::value) {
format_specs<Char> fmt_specs = this->specs; base::operator()(value);
if (fmt_specs.type != presentation_type::none && return;
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
} }
return base::operator()(value); format_specs<Char> fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
write<Char>(this->out, static_cast<Char>(value), fmt_specs);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) { void operator()(T value) {
return base::operator()(value); base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
OutputIt operator()(const char* value) { void operator()(const char* value) {
if (value) return base::operator()(value); if (value)
return write_null_pointer(this->specs.type != presentation_type::pointer); base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) { void operator()(const wchar_t* value) {
if (value) return base::operator()(value); if (value)
return write_null_pointer(this->specs.type != presentation_type::pointer); base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
} }
OutputIt operator()(basic_string_view<Char> value) { void operator()(basic_string_view<Char> value) { base::operator()(value); }
return base::operator()(value);
}
/** Formats a pointer. */ /** Formats a pointer. */
OutputIt operator()(const void* value) { void operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer(); if (value)
base::operator()(value);
else
write_null_pointer();
} }
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) { void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = auto parse_ctx = basic_format_parse_context<Char>({});
basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_); handle.format(parse_ctx, context_);
return this->out;
} }
}; };
@ -318,9 +318,7 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
specs.fill[0] = '0'; specs.fill[0] = '0';
break; break;
case ' ': case ' ':
if (specs.sign != sign::plus) { if (specs.sign != sign::plus) specs.sign = sign::space;
specs.sign = sign::space;
}
break; break;
case '#': case '#':
specs.alt = true; specs.alt = true;
@ -332,8 +330,8 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
GetArg get_arg) { GetArg get_arg) -> int {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
@ -414,8 +412,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
using iterator = buffer_appender<Char>; using iterator = buffer_appender<Char>;
auto out = iterator(buf); auto out = iterator(buf);
auto context = basic_printf_context<iterator, Char>(out, args); auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format); auto parse_ctx = basic_format_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next // Returns the argument with specified index or, if arg_index is -1, the next
// argument. // argument.
@ -437,12 +435,11 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
Char c = *it++; Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs<Char>(); auto specs = format_specs<Char>();
specs.align = align::right; specs.align = align::right;
@ -469,16 +466,17 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index); auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral()) {
specs.fill[0] = // Ignore '0' for non-numeric types or if '-' present.
' '; // Ignore '0' flag for non-numeric types or if '-' present. specs.fill[0] = ' ';
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) { if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(get_cstring<Char>(), arg); auto str = visit_format_arg(get_cstring<Char>(), arg);
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
arg = make_arg<basic_printf_context<iterator, Char>>( auto sv = basic_string_view<Char>(
basic_string_view<Char>( str, to_unsigned(nul != str_end ? nul - str : specs.precision));
str, to_unsigned(nul != str_end ? nul - str : specs.precision))); arg = make_arg<basic_printf_context<Char>>(sv);
} }
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0') {
@ -540,8 +538,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
type = 'd'; type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg( visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
char_converter<basic_printf_context<iterator, Char>>(arg), arg);
break; break;
} }
} }
@ -552,19 +549,14 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg( visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
printf_arg_formatter<iterator, Char>(out, specs, context), arg);
} }
write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE } // namespace detail
template <typename Char> using printf_context = basic_printf_context<char>;
using basic_printf_context_t = using wprintf_context = basic_printf_context<wchar_t>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>; using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>; using wprintf_args = basic_format_args<wprintf_context>;
@ -581,25 +573,20 @@ inline auto make_printf_args(const T&... args)
return {args...}; return {args...};
} }
/** // DEPRECATED!
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T> template <typename... T>
inline auto make_wprintf_args(const T&... args) inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> { -> format_arg_store<wprintf_context, T...> {
return {args...}; return {args...};
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vsprintf( inline auto vsprintf(
const S& fmt, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, detail::to_string_view(fmt), args); detail::vprintf(buf, fmt, args);
return to_string(buf); return to_string(buf);
} }
@ -615,18 +602,17 @@ inline auto vsprintf(
template <typename S, typename... T, template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>;
return vsprintf(detail::to_string_view(fmt), return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<context>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vfprintf( inline auto vfprintf(
std::FILE* f, const S& fmt, std::FILE* f, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int { -> int {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, detail::to_string_view(fmt), args); detail::vprintf(buf, fmt, args);
size_t size = buf.size(); size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1 ? -1
@ -644,17 +630,16 @@ inline auto vfprintf(
*/ */
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>;
return vfprintf(f, detail::to_string_view(fmt), return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<context>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vprintf( FMT_DEPRECATED inline auto vprintf(
const S& fmt, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int { -> int {
return vfprintf(stdout, detail::to_string_view(fmt), args); return vfprintf(stdout, fmt, args);
} }
/** /**
@ -666,11 +651,14 @@ inline auto vprintf(
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> template <typename... T>
inline auto printf(const S& fmt, const T&... args) -> int { inline auto printf(string_view fmt, const T&... args) -> int {
return vprintf( return vfprintf(stdout, fmt, make_printf_args(args...));
detail::to_string_view(fmt), }
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_wprintf_args(args...));
} }
FMT_END_EXPORT FMT_END_EXPORT

View File

@ -668,8 +668,11 @@ template <typename Container> struct all {
} // namespace detail } // namespace detail
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct formatter<
enable_if_t<detail::is_container_adaptor_like<T>::value>> T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> { : formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>; using all = detail::all<typename T::container_type>;
template <typename FormatContext> template <typename FormatContext>

View File

@ -8,6 +8,8 @@
#ifndef FMT_STD_H_ #ifndef FMT_STD_H_
#define FMT_STD_H_ #define FMT_STD_H_
#include <atomic>
#include <bitset>
#include <cstdlib> #include <cstdlib>
#include <exception> #include <exception>
#include <memory> #include <memory>
@ -15,7 +17,9 @@
#include <type_traits> #include <type_traits>
#include <typeinfo> #include <typeinfo>
#include <utility> #include <utility>
#include <vector>
#include "format.h"
#include "ostream.h" #include "ostream.h"
#if FMT_HAS_INCLUDE(<version>) #if FMT_HAS_INCLUDE(<version>)
@ -44,27 +48,48 @@
# endif # endif
#endif #endif
// Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
#ifdef __cpp_lib_filesystem #ifdef __cpp_lib_filesystem
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename Char> auto get_path_string(const std::filesystem::path& p) {
return p.string<Char>();
}
template <typename Char> template <typename Char>
void write_escaped_path(basic_memory_buffer<Char>& quoted, void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p) { const std::filesystem::path& p) {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
} }
# ifdef _WIN32 # ifdef _WIN32
template <>
inline auto get_path_string<char>(const std::filesystem::path& p) {
return to_utf8<wchar_t>(p.native(), to_utf8_error_policy::replace);
}
template <> template <>
inline void write_escaped_path<char>(memory_buffer& quoted, inline void write_escaped_path<char>(memory_buffer& quoted,
const std::filesystem::path& p) { const std::filesystem::path& p) {
auto buf = basic_memory_buffer<wchar_t>(); auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native()); write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
// Convert UTF-16 to UTF-8. bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()})) FMT_ASSERT(valid, "invalid utf16");
FMT_THROW(std::runtime_error("invalid utf16"));
} }
# endif # endif // _WIN32
template <> template <>
inline void write_escaped_path<std::filesystem::path::value_type>( inline void write_escaped_path<std::filesystem::path::value_type>(
basic_memory_buffer<std::filesystem::path::value_type>& quoted, basic_memory_buffer<std::filesystem::path::value_type>& quoted,
@ -75,36 +100,59 @@ inline void write_escaped_path<std::filesystem::path::value_type>(
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> template <typename Char> struct formatter<std::filesystem::path, Char> {
struct formatter<std::filesystem::path, Char> private:
: formatter<basic_string_view<Char>> { format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto out = formatter<basic_string_view<Char>>::parse(ctx); auto it = ctx.begin(), end = ctx.end();
this->set_debug_format(false); if (it == end) return it;
return out;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
return it;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const -> auto format(const std::filesystem::path& p, FormatContext& ctx) const {
typename FormatContext::iterator { auto specs = specs_;
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>(); auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p); detail::write_escaped_path(quoted, p);
return formatter<basic_string_view<Char>>::format( return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef __cpp_lib_optional #ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::optional<T>, Char, struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> { std::enable_if_t<is_formattable<T, Char>::value>> {
@ -148,22 +196,6 @@ FMT_END_NAMESPACE
#ifdef __cpp_lib_variant #ifdef __cpp_lib_variant
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "monostate");
return out;
}
};
namespace detail { namespace detail {
template <typename T> template <typename T>
@ -197,6 +229,7 @@ auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
} }
} // namespace detail } // namespace detail
template <typename T> struct is_variant_like { template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value; static constexpr const bool value = detail::is_variant_like_<T>::value;
}; };
@ -206,7 +239,21 @@ template <typename T, typename C> struct is_variant_formattable {
detail::is_variant_formattable_<T, C>::value; detail::is_variant_formattable_<T, C>::value;
}; };
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char> template <typename Variant, typename Char>
struct formatter< struct formatter<
Variant, Char, Variant, Char,
@ -223,13 +270,14 @@ struct formatter<
auto out = ctx.out(); auto out = ctx.out();
out = detail::write<Char>(out, "variant("); out = detail::write<Char>(out, "variant(");
try { FMT_TRY {
std::visit( std::visit(
[&](const auto& v) { [&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v); out = detail::write_variant_alternative<Char>(out, v);
}, },
value); value);
} catch (const std::bad_variant_access&) { }
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception"); detail::write<Char>(out, "valueless by exception");
} }
*out++ = ')'; *out++ = ')';
@ -240,7 +288,7 @@ FMT_END_NAMESPACE
#endif // __cpp_lib_variant #endif // __cpp_lib_variant
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> { template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
@ -258,7 +306,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
} }
}; };
FMT_MODULE_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
@ -274,7 +322,7 @@ struct formatter<
if (it == end || *it == '}') return it; if (it == end || *it == '}') return it;
if (*it == 't') { if (*it == 't') {
++it; ++it;
with_typename_ = true; with_typename_ = FMT_USE_TYPEID != 0;
} }
return it; return it;
} }
@ -287,8 +335,9 @@ struct formatter<
if (!with_typename_) if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec); return detail::write_bytes(out, string_view(ex.what()), spec);
#if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex); const std::type_info& ti = typeid(ex);
#ifdef FMT_HAS_ABI_CXA_DEMANGLE # ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0; int status = 0;
std::size_t size = 0; std::size_t size = 0;
std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr( std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
@ -327,23 +376,90 @@ struct formatter<
demangled_name_view = string_view(ti.name()); demangled_name_view = string_view(ti.name());
} }
out = detail::write_bytes(out, demangled_name_view, spec); out = detail::write_bytes(out, demangled_name_view, spec);
#elif FMT_MSC_VERSION # elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name()); string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class ")) if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6); demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct ")) else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7); demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec); out = detail::write_bytes(out, demangled_name_view, spec);
#else # else
out = detail::write_bytes(out, string_view(ti.name()), spec); out = detail::write_bytes(out, string_view(ti.name()), spec);
# endif
*out++ = ':';
*out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec);
#endif #endif
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, Char(' '));
out = detail::write_bytes(out, string_view(ex.what()), spec);
return out;
} }
}; };
FMT_END_NAMESPACE
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char>
: formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@ -62,9 +62,9 @@ template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {}; template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {}; template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args> template <typename... T>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args( constexpr format_arg_store<wformat_context, T...> make_wformat_args(
const Args&... args) { const T&... args) {
return {args...}; return {args...};
} }
@ -99,9 +99,9 @@ template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str, auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buf, format_str, args);
return to_string(buffer); return to_string(buf);
} }
template <typename... T> template <typename... T>
@ -111,10 +111,10 @@ auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
// Pass char_t as a default template parameter instead of using // Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size. // std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... T, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value && FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)> !std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str), return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
@ -129,11 +129,10 @@ inline auto vformat(
return detail::vformat(loc, detail::to_string_view(format_str), args); return detail::vformat(loc, detail::to_string_view(format_str), args);
} }
template <typename Locale, typename S, typename... Args, template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args) inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), return detail::vformat(loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
@ -150,11 +149,11 @@ auto vformat_to(OutputIt out, const S& format_str,
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt), return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
@ -174,12 +173,12 @@ inline auto vformat_to(
} }
template < template <
typename OutputIt, typename Locale, typename S, typename... Args, typename OutputIt, typename Locale, typename S, typename... T,
typename Char = char_t<S>, typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value&& bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> T&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(format_str), return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
@ -192,36 +191,36 @@ inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str, OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, using traits = detail::fixed_buffer_traits;
n); auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
const Args&... args) -> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, detail::to_string_view(fmt), return vformat_to_n(out, n, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... T, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
detail::counting_buffer<Char> buf; auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
return buf.count(); return buf.count();
} }
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer; auto buf = wmemory_buffer();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buf, fmt, args);
buffer.push_back(L'\0'); buf.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1) if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }

View File

@ -72,15 +72,10 @@ module;
export module fmt; export module fmt;
#define FMT_MODULE_EXPORT export #define FMT_EXPORT export
#define FMT_BEGIN_EXPORT export { #define FMT_BEGIN_EXPORT export {
#define FMT_END_EXPORT } #define FMT_END_EXPORT }
#define FMT_BEGIN_DETAIL_NAMESPACE \
} \
namespace detail {
#define FMT_END_DETAIL_NAMESPACE \
} \
export {
// If you define FMT_ATTACH_TO_GLOBAL_MODULE // If you define FMT_ATTACH_TO_GLOBAL_MODULE
// - all declarations are detached from module 'fmt' // - all declarations are detached from module 'fmt'
// - the module behaves like a traditional static library, too // - the module behaves like a traditional static library, too
@ -102,14 +97,13 @@ extern "C++" {
#include "fmt/std.h" #include "fmt/std.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE #ifdef FMT_ATTACH_TO_GLOBAL_MODULE
} }
#endif #endif
// gcc doesn't yet implement private module fragments // gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION #if !FMT_GCC_VERSION
module :private; module : private;
#endif #endif
#include "format.cc" #include "format.cc"

View File

@ -18,6 +18,10 @@
# include <sys/stat.h> # include <sys/stat.h>
# include <sys/types.h> # include <sys/types.h>
# ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize
# endif
# ifndef _WIN32 # ifndef _WIN32
# include <unistd.h> # include <unistd.h>
# else # else
@ -110,9 +114,9 @@ class utf8_system_category final : public std::error_category {
public: public:
const char* name() const noexcept override { return "system"; } const char* name() const noexcept override { return "system"; }
std::string message(int error_code) const override { std::string message(int error_code) const override {
system_message msg(error_code); auto&& msg = system_message(error_code);
if (msg) { if (msg) {
unicode_to_utf8<wchar_t> utf8_message; auto utf8_message = to_utf8<wchar_t>();
if (utf8_message.convert(msg)) { if (utf8_message.convert(msg)) {
return utf8_message.str(); return utf8_message.str();
} }
@ -137,12 +141,12 @@ std::system_error vwindows_error(int err_code, string_view format_str,
void detail::format_windows_error(detail::buffer<char>& out, int error_code, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
const char* message) noexcept { const char* message) noexcept {
FMT_TRY { FMT_TRY {
system_message msg(error_code); auto&& msg = system_message(error_code);
if (msg) { if (msg) {
unicode_to_utf8<wchar_t> utf8_message; auto utf8_message = to_utf8<wchar_t>();
if (utf8_message.convert(msg)) { if (utf8_message.convert(msg)) {
fmt::format_to(buffer_appender<char>(out), FMT_STRING("{}: {}"), fmt::format_to(appender(out), FMT_STRING("{}: {}"), message,
message, string_view(utf8_message)); string_view(utf8_message));
return; return;
} }
} }
@ -337,9 +341,8 @@ file file::open_windows_file(wcstring_view path, int oflag) {
int fd = -1; int fd = -1;
auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode); auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode);
if (fd == -1) { if (fd == -1) {
FMT_THROW( FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"),
system_error(err, FMT_STRING("cannot open file {}"), detail::to_utf8<wchar_t>(path.c_str()).c_str()));
detail::unicode_to_utf8<wchar_t>(path.c_str()).c_str()));
} }
return file(fd); return file(fd);
} }
@ -352,7 +355,12 @@ long getpagesize() {
GetSystemInfo(&si); GetSystemInfo(&si);
return si.dwPageSize; return si.dwPageSize;
# else # else
# ifdef _WRS_KERNEL
long size = FMT_POSIX_CALL(getpagesize());
# else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
# endif
if (size < 0) if (size < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size"))); FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size")));
return size; return size;