Dep/fmt: Upgrade to 7.1.3 (#26816)
Printing enum values as integers was broken before 7.1.0. Upgrade the library so we include commits https://github.com/fmtlib/fmt/commit/20d4f2e83677be035a15693127e404c4a5d68ac9 https://github.com/fmtlib/fmt/commit/86287b8d5625ef390da6ef10fc77a872b8ad41ac which fix the issue.
This commit is contained in:
1 parent
1b91ed5424
commit
d271e59818
17 files changed
+3547
-1149
No files matched your search
+1
-1
@@ -22,7 +22,7 @@ efws (Entropia File System Watcher - crossplatform file system watcher)
|
|||||||
|
|
||||||
fmt (a small, safe and fast formatting library)
|
fmt (a small, safe and fast formatting library)
|
||||||
https://github.com/fmtlib/fmt
|
https://github.com/fmtlib/fmt
|
||||||
Version: 7.0.3 https://github.com/fmtlib/fmt/releases/tag/7.0.3
|
Version: 7.1.3 https://github.com/fmtlib/fmt/releases/tag/7.1.3
|
||||||
|
|
||||||
G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License)
|
G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License)
|
||||||
http://g3d.sourceforge.net/
|
http://g3d.sourceforge.net/
|
||||||
|
|||||||
@@ -14,4 +14,7 @@ exceptions:
|
|||||||
* snake_case should be used instead of UpperCamelCase for function and type
|
* snake_case should be used instead of UpperCamelCase for function and type
|
||||||
names
|
names
|
||||||
|
|
||||||
|
All documentation must adhere to the [Google Developer Documentation Style
|
||||||
|
Guide](https://developers.google.com/style).
|
||||||
|
|
||||||
Thanks for contributing!
|
Thanks for contributing!
|
||||||
+434
-9
@@ -1,3 +1,428 @@
|
|||||||
|
7.1.3 - 2020-11-24
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Fixed handling of buffer boundaries in ``format_to_n``
|
||||||
|
(`#1996 <https://github.com/fmtlib/fmt/issues/1996>`_,
|
||||||
|
`#2029 <https://github.com/fmtlib/fmt/issues/2029>`_).
|
||||||
|
|
||||||
|
* Fixed linkage errors when linking with a shared library
|
||||||
|
(`#2011 <https://github.com/fmtlib/fmt/issues/2011>`_).
|
||||||
|
|
||||||
|
* Reintroduced ostream support to range formatters
|
||||||
|
(`#2014 <https://github.com/fmtlib/fmt/issues/2014>`_).
|
||||||
|
|
||||||
|
* Worked around an issue with mixing std versions in gcc
|
||||||
|
(`#2017 <https://github.com/fmtlib/fmt/issues/2017>`_).
|
||||||
|
|
||||||
|
7.1.2 - 2020-11-04
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Fixed floating point formatting with large precision
|
||||||
|
(`#1976 <https://github.com/fmtlib/fmt/issues/1976>`_).
|
||||||
|
|
||||||
|
7.1.1 - 2020-11-01
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Fixed ABI compatibility with 7.0.x
|
||||||
|
(`#1961 <https://github.com/fmtlib/fmt/issues/1961>`_).
|
||||||
|
|
||||||
|
* Added the ``FMT_ARM_ABI_COMPATIBILITY`` macro to work around ABI
|
||||||
|
incompatibility between GCC and Clang on ARM
|
||||||
|
(`#1919 <https://github.com/fmtlib/fmt/issues/1919>`_).
|
||||||
|
|
||||||
|
* Worked around a SFINAE bug in GCC 8
|
||||||
|
(`#1957 <https://github.com/fmtlib/fmt/issues/1957>`_).
|
||||||
|
|
||||||
|
* Fixed linkage errors when building with GCC's LTO
|
||||||
|
(`#1955 <https://github.com/fmtlib/fmt/issues/1955>`_).
|
||||||
|
|
||||||
|
* Fixed a compilation error when building without ``__builtin_clz`` or equivalent
|
||||||
|
(`#1968 <https://github.com/fmtlib/fmt/pull/1968>`_).
|
||||||
|
Thanks `@tohammer (Tobias Hammer) <https://github.com/tohammer>`_.
|
||||||
|
|
||||||
|
* Fixed a sign conversion warning
|
||||||
|
(`#1964 <https://github.com/fmtlib/fmt/pull/1964>`_).
|
||||||
|
Thanks `@OptoCloud <https://github.com/OptoCloud>`_.
|
||||||
|
|
||||||
|
7.1.0 - 2020-10-25
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Switched from `Grisu3
|
||||||
|
<https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf>`_
|
||||||
|
to `Dragonbox <https://github.com/jk-jeon/dragonbox>`_ for the default
|
||||||
|
floating-point formatting which gives the shortest decimal representation
|
||||||
|
with round-trip guarantee and correct rounding
|
||||||
|
(`#1882 <https://github.com/fmtlib/fmt/pull/1882>`_,
|
||||||
|
`#1887 <https://github.com/fmtlib/fmt/pull/1887>`_,
|
||||||
|
`#1894 <https://github.com/fmtlib/fmt/pull/1894>`_). This makes {fmt} up to
|
||||||
|
20-30x faster than common implementations of ``std::ostringstream`` and
|
||||||
|
``sprintf`` on `dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_
|
||||||
|
and faster than double-conversion and Ryū:
|
||||||
|
|
||||||
|
.. image:: https://user-images.githubusercontent.com/576385/
|
||||||
|
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
|
||||||
|
|
||||||
|
It is possible to get even better performance at the cost of larger binary
|
||||||
|
size by compiling with the ``FMT_USE_FULL_CACHE_DRAGONBOX`` macro set to 1.
|
||||||
|
|
||||||
|
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
|
||||||
|
|
||||||
|
* Added an experimental unsynchronized file output API which, together with
|
||||||
|
`format string compilation <https://fmt.dev/latest/api.html#compile-api>`_,
|
||||||
|
can give `5-9 times speed up compared to fprintf
|
||||||
|
<https://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_
|
||||||
|
on common platforms (`godbolt <https://godbolt.org/z/nsTcG8>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/os.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto f = fmt::output_file("guide");
|
||||||
|
f.print("The answer is {}.", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
* Added a formatter for ``std::chrono::time_point<system_clock>``
|
||||||
|
(`#1819 <https://github.com/fmtlib/fmt/issues/1819>`_,
|
||||||
|
`#1837 <https://github.com/fmtlib/fmt/pull/1837>`_). For example
|
||||||
|
(`godbolt <https://godbolt.org/z/c4M6fh>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
fmt::print("The time is {:%H:%M:%S}.\n", now);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thanks `@adamburgess (Adam Burgess) <https://github.com/adamburgess>`_.
|
||||||
|
|
||||||
|
* Added support for ranges with non-const ``begin``/``end`` to ``fmt::join``
|
||||||
|
(`#1784 <https://github.com/fmtlib/fmt/issues/1784>`_,
|
||||||
|
`#1786 <https://github.com/fmtlib/fmt/pull/1786>`_). For example
|
||||||
|
(`godbolt <https://godbolt.org/z/jP63Tv>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
#include <range/v3/view/filter.hpp>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using std::literals::string_literals::operator""s;
|
||||||
|
auto strs = std::array{"a"s, "bb"s, "ccc"s};
|
||||||
|
auto range = strs | ranges::views::filter(
|
||||||
|
[] (const std::string &x) { return x.size() != 2; }
|
||||||
|
);
|
||||||
|
fmt::print("{}\n", fmt::join(range, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
prints "accc".
|
||||||
|
|
||||||
|
Thanks `@tonyelewis (Tony E Lewis) <https://github.com/tonyelewis>`_.
|
||||||
|
|
||||||
|
* Added a ``memory_buffer::append`` overload that takes a range
|
||||||
|
(`#1806 <https://github.com/fmtlib/fmt/pull/1806>`_).
|
||||||
|
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||||
|
|
||||||
|
* Improved handling of single code units in ``FMT_COMPILE``. For example:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/compile.h>
|
||||||
|
|
||||||
|
char* f(char* buf) {
|
||||||
|
return fmt::format_to(buf, FMT_COMPILE("x{}"), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiles to just (`godbolt <https://godbolt.org/z/5vncz3>`__):
|
||||||
|
|
||||||
|
.. code:: asm
|
||||||
|
|
||||||
|
_Z1fPc:
|
||||||
|
movb $120, (%rdi)
|
||||||
|
xorl %edx, %edx
|
||||||
|
cmpl $42, _ZN3fmt2v76detail10basic_dataIvE23zero_or_powers_of_10_32E+8(%rip)
|
||||||
|
movl $3, %eax
|
||||||
|
seta %dl
|
||||||
|
subl %edx, %eax
|
||||||
|
movzwl _ZN3fmt2v76detail10basic_dataIvE6digitsE+84(%rip), %edx
|
||||||
|
cltq
|
||||||
|
addq %rdi, %rax
|
||||||
|
movw %dx, -2(%rax)
|
||||||
|
ret
|
||||||
|
|
||||||
|
Here a single ``mov`` instruction writes ``'x'`` (``$120``) to the output
|
||||||
|
buffer.
|
||||||
|
|
||||||
|
* Added dynamic width support to format string compilation
|
||||||
|
(`#1809 <https://github.com/fmtlib/fmt/issues/1809>`_).
|
||||||
|
|
||||||
|
* Improved error reporting for unformattable types: now you'll get the type name
|
||||||
|
directly in the error message instead of the note:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
struct how_about_no {};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{}", how_about_no());
|
||||||
|
}
|
||||||
|
|
||||||
|
Error (`godbolt <https://godbolt.org/z/GoxM4e>`__):
|
||||||
|
|
||||||
|
``fmt/core.h:1438:3: error: static_assert failed due to requirement
|
||||||
|
'fmt::v7::formattable<how_about_no>()' "Cannot format an argument.
|
||||||
|
To make type T formattable provide a formatter<T> specialization:
|
||||||
|
https://fmt.dev/latest/api.html#udt"
|
||||||
|
...``
|
||||||
|
|
||||||
|
* Added the `make_args_checked <https://fmt.dev/7.1.0/api.html#argument-lists>`_
|
||||||
|
function template that allows you to write formatting functions with
|
||||||
|
compile-time format string checks and avoid binary code bloat
|
||||||
|
(`godbolt <https://godbolt.org/z/PEf9qr>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
void vlog(const char* file, int line, fmt::string_view format,
|
||||||
|
fmt::format_args args) {
|
||||||
|
fmt::print("{}: {}: ", file, line);
|
||||||
|
fmt::vprint(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
void log(const char* file, int line, const S& format, Args&&... args) {
|
||||||
|
vlog(file, line, format,
|
||||||
|
fmt::make_args_checked<Args...>(format, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MY_LOG(format, ...) \
|
||||||
|
log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
|
||||||
|
|
||||||
|
MY_LOG("invalid squishiness: {}", 42);
|
||||||
|
|
||||||
|
* Replaced ``snprintf`` fallback with a faster internal IEEE 754 ``float`` and
|
||||||
|
``double`` formatter for arbitrary precision. For example
|
||||||
|
(`godbolt <https://godbolt.org/z/dPhWvj>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{:.500}\n", 4.9406564584124654E-324);
|
||||||
|
}
|
||||||
|
|
||||||
|
prints
|
||||||
|
|
||||||
|
``4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208324319360923829e-324``.
|
||||||
|
|
||||||
|
* Made ``format_to_n`` and ``formatted_size`` part of the `core API
|
||||||
|
<https://fmt.dev/latest/api.html#core-api>`__
|
||||||
|
(`godbolt <https://godbolt.org/z/sPjY1K>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
char buffer[10];
|
||||||
|
auto result = fmt::format_to_n(buffer, sizeof(buffer), "{}", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
* Added ``fmt::format_to_n`` overload with format string compilation
|
||||||
|
(`#1764 <https://github.com/fmtlib/fmt/issues/1764>`_,
|
||||||
|
`#1767 <https://github.com/fmtlib/fmt/pull/1767>`_,
|
||||||
|
`#1869 <https://github.com/fmtlib/fmt/pull/1869>`_). For example
|
||||||
|
(`godbolt <https://godbolt.org/z/93h86q>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/compile.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
char buffer[8];
|
||||||
|
fmt::format_to_n(buffer, sizeof(buffer), FMT_COMPILE("{}"), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thanks `@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_,
|
||||||
|
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
|
||||||
|
|
||||||
|
* Added ``fmt::format_to`` overload that take ``text_style``
|
||||||
|
(`#1593 <https://github.com/fmtlib/fmt/issues/1593>`_,
|
||||||
|
`#1842 <https://github.com/fmtlib/fmt/issues/1842>`_,
|
||||||
|
`#1843 <https://github.com/fmtlib/fmt/pull/1843>`_). For example
|
||||||
|
(`godbolt <https://godbolt.org/z/91153r>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/color.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string out;
|
||||||
|
fmt::format_to(std::back_inserter(out),
|
||||||
|
fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"The answer is {}.", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thanks `@Naios (Denis Blank) <https://github.com/Naios>`_.
|
||||||
|
|
||||||
|
* Made the ``#`` specifier emit trailing zeros in addition to the decimal point
|
||||||
|
(`#1797 <https://github.com/fmtlib/fmt/issues/1797>`_). For example
|
||||||
|
(`godbolt <https://godbolt.org/z/bhdcW9>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{:#.2g}", 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
prints ``0.50``.
|
||||||
|
|
||||||
|
* Changed the default floating point format to not include ``.0`` for
|
||||||
|
consistency with ``std::format`` and ``std::to_chars``
|
||||||
|
(`#1893 <https://github.com/fmtlib/fmt/issues/1893>`_,
|
||||||
|
`#1943 <https://github.com/fmtlib/fmt/issues/1943>`_). It is possible to get
|
||||||
|
the decimal point and trailing zero with the ``#`` specifier.
|
||||||
|
|
||||||
|
* Fixed an issue with floating-point formatting that could result in addition of
|
||||||
|
a non-significant trailing zero in rare cases e.g. ``1.00e-34`` instead of
|
||||||
|
``1.0e-34`` (`#1873 <https://github.com/fmtlib/fmt/issues/1873>`_,
|
||||||
|
`#1917 <https://github.com/fmtlib/fmt/issues/1917>`_).
|
||||||
|
|
||||||
|
* Made ``fmt::to_string`` fallback on ``ostream`` insertion operator if
|
||||||
|
the ``formatter`` specialization is not provided
|
||||||
|
(`#1815 <https://github.com/fmtlib/fmt/issues/1815>`_,
|
||||||
|
`#1829 <https://github.com/fmtlib/fmt/pull/1829>`_).
|
||||||
|
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
|
||||||
|
|
||||||
|
* Added support for the append mode to the experimental file API and
|
||||||
|
improved ``fcntl.h`` detection.
|
||||||
|
(`#1847 <https://github.com/fmtlib/fmt/pull/1847>`_,
|
||||||
|
`#1848 <https://github.com/fmtlib/fmt/pull/1848>`_).
|
||||||
|
Thanks `@t-wiser <https://github.com/t-wiser>`_.
|
||||||
|
|
||||||
|
* Fixed handling of types that have both an implicit conversion operator and
|
||||||
|
an overloaded ``ostream`` insertion operator
|
||||||
|
(`#1766 <https://github.com/fmtlib/fmt/issues/1766>`_).
|
||||||
|
|
||||||
|
* Fixed a slicing issue in an internal iterator type
|
||||||
|
(`#1822 <https://github.com/fmtlib/fmt/pull/1822>`_).
|
||||||
|
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||||
|
|
||||||
|
* Fixed an issue in locale-specific integer formatting
|
||||||
|
(`#1927 <https://github.com/fmtlib/fmt/issues/1927>`_).
|
||||||
|
|
||||||
|
* Fixed handling of exotic code unit types
|
||||||
|
(`#1870 <https://github.com/fmtlib/fmt/issues/1870>`_,
|
||||||
|
`#1932 <https://github.com/fmtlib/fmt/issues/1932>`_).
|
||||||
|
|
||||||
|
* Improved ``FMT_ALWAYS_INLINE``
|
||||||
|
(`#1878 <https://github.com/fmtlib/fmt/pull/1878>`_).
|
||||||
|
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
|
||||||
|
|
||||||
|
* Removed dependency on ``windows.h``
|
||||||
|
(`#1900 <https://github.com/fmtlib/fmt/pull/1900>`_).
|
||||||
|
Thanks `@bernd5 (Bernd Baumanns) <https://github.com/bernd5>`_.
|
||||||
|
|
||||||
|
* Optimized counting of decimal digits on MSVC
|
||||||
|
(`#1890 <https://github.com/fmtlib/fmt/pull/1890>`_).
|
||||||
|
Thanks `@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
|
* Improved documentation
|
||||||
|
(`#1772 <https://github.com/fmtlib/fmt/issues/1772>`_,
|
||||||
|
`#1775 <https://github.com/fmtlib/fmt/pull/1775>`_,
|
||||||
|
`#1792 <https://github.com/fmtlib/fmt/pull/1792>`_,
|
||||||
|
`#1838 <https://github.com/fmtlib/fmt/pull/1838>`_,
|
||||||
|
`#1888 <https://github.com/fmtlib/fmt/pull/1888>`_,
|
||||||
|
`#1918 <https://github.com/fmtlib/fmt/pull/1918>`_,
|
||||||
|
`#1939 <https://github.com/fmtlib/fmt/pull/1939>`_).
|
||||||
|
Thanks `@leolchat (Léonard Gérard) <https://github.com/leolchat>`_,
|
||||||
|
`@pepsiman (Malcolm Parsons) <https://github.com/pepsiman>`_,
|
||||||
|
`@Klaim (Joël Lamotte) <https://github.com/Klaim>`_,
|
||||||
|
`@ravijanjam (Ravi J) <https://github.com/ravijanjam>`_,
|
||||||
|
`@francesco-st <https://github.com/francesco-st>`_,
|
||||||
|
`@udnaan (Adnan) <https://github.com/udnaan>`_.
|
||||||
|
|
||||||
|
* Added the ``FMT_REDUCE_INT_INSTANTIATIONS`` CMake option that reduces the
|
||||||
|
binary code size at the cost of some integer formatting performance. This can
|
||||||
|
be useful for extremely memory-constrained embedded systems
|
||||||
|
(`#1778 <https://github.com/fmtlib/fmt/issues/1778>`_,
|
||||||
|
`#1781 <https://github.com/fmtlib/fmt/pull/1781>`_).
|
||||||
|
Thanks `@kammce (Khalil Estell) <https://github.com/kammce>`_.
|
||||||
|
|
||||||
|
* Added the ``FMT_USE_INLINE_NAMESPACES`` macro to control usage of inline
|
||||||
|
namespaces (`#1945 <https://github.com/fmtlib/fmt/pull/1945>`_).
|
||||||
|
Thanks `@darklukee <https://github.com/darklukee>`_.
|
||||||
|
|
||||||
|
* Improved build configuration
|
||||||
|
(`#1760 <https://github.com/fmtlib/fmt/pull/1760>`_,
|
||||||
|
`#1770 <https://github.com/fmtlib/fmt/pull/1770>`_,
|
||||||
|
`#1779 <https://github.com/fmtlib/fmt/issues/1779>`_,
|
||||||
|
`#1783 <https://github.com/fmtlib/fmt/pull/1783>`_,
|
||||||
|
`#1823 <https://github.com/fmtlib/fmt/pull/1823>`_).
|
||||||
|
Thanks `@dvetutnev (Dmitriy Vetutnev) <https://github.com/dvetutnev>`_,
|
||||||
|
`@xvitaly (Vitaly Zaitsev) <https://github.com/xvitaly>`_,
|
||||||
|
`@tambry (Raul Tambre) <https://github.com/tambry>`_,
|
||||||
|
`@medithe <https://github.com/medithe>`_,
|
||||||
|
`@martinwuehrer (Martin Wührer) <https://github.com/martinwuehrer>`_.
|
||||||
|
|
||||||
|
* Fixed various warnings and compilation issues
|
||||||
|
(`#1790 <https://github.com/fmtlib/fmt/pull/1790>`_,
|
||||||
|
`#1802 <https://github.com/fmtlib/fmt/pull/1802>`_,
|
||||||
|
`#1808 <https://github.com/fmtlib/fmt/pull/1808>`_,
|
||||||
|
`#1810 <https://github.com/fmtlib/fmt/issues/1810>`_,
|
||||||
|
`#1811 <https://github.com/fmtlib/fmt/issues/1811>`_,
|
||||||
|
`#1812 <https://github.com/fmtlib/fmt/pull/1812>`_,
|
||||||
|
`#1814 <https://github.com/fmtlib/fmt/pull/1814>`_,
|
||||||
|
`#1816 <https://github.com/fmtlib/fmt/pull/1816>`_,
|
||||||
|
`#1817 <https://github.com/fmtlib/fmt/pull/1817>`_,
|
||||||
|
`#1818 <https://github.com/fmtlib/fmt/pull/1818>`_,
|
||||||
|
`#1825 <https://github.com/fmtlib/fmt/issues/1825>`_,
|
||||||
|
`#1836 <https://github.com/fmtlib/fmt/pull/1836>`_,
|
||||||
|
`#1855 <https://github.com/fmtlib/fmt/pull/1855>`_,
|
||||||
|
`#1856 <https://github.com/fmtlib/fmt/pull/1856>`_,
|
||||||
|
`#1860 <https://github.com/fmtlib/fmt/pull/1860>`_,
|
||||||
|
`#1877 <https://github.com/fmtlib/fmt/pull/1877>`_,
|
||||||
|
`#1879 <https://github.com/fmtlib/fmt/pull/1879>`_,
|
||||||
|
`#1880 <https://github.com/fmtlib/fmt/pull/1880>`_,
|
||||||
|
`#1896 <https://github.com/fmtlib/fmt/issues/1896>`_,
|
||||||
|
`#1897 <https://github.com/fmtlib/fmt/pull/1897>`_,
|
||||||
|
`#1898 <https://github.com/fmtlib/fmt/pull/1898>`_,
|
||||||
|
`#1904 <https://github.com/fmtlib/fmt/issues/1904>`_,
|
||||||
|
`#1908 <https://github.com/fmtlib/fmt/pull/1908>`_,
|
||||||
|
`#1911 <https://github.com/fmtlib/fmt/issues/1911>`_,
|
||||||
|
`#1912 <https://github.com/fmtlib/fmt/issues/1912>`_,
|
||||||
|
`#1928 <https://github.com/fmtlib/fmt/issues/1928>`_,
|
||||||
|
`#1929 <https://github.com/fmtlib/fmt/pull/1929>`_,
|
||||||
|
`#1935 <https://github.com/fmtlib/fmt/issues/1935>`_
|
||||||
|
`#1937 <https://github.com/fmtlib/fmt/pull/1937>`_,
|
||||||
|
`#1942 <https://github.com/fmtlib/fmt/pull/1942>`_,
|
||||||
|
`#1949 <https://github.com/fmtlib/fmt/issues/1949>`_).
|
||||||
|
Thanks `@TheQwertiest <https://github.com/TheQwertiest>`_,
|
||||||
|
`@medithe <https://github.com/medithe>`_,
|
||||||
|
`@martinwuehrer (Martin Wührer) <https://github.com/martinwuehrer>`_,
|
||||||
|
`@n16h7hunt3r <https://github.com/n16h7hunt3r>`_,
|
||||||
|
`@Othereum (Seokjin Lee) <https://github.com/Othereum>`_,
|
||||||
|
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
|
||||||
|
`@AlexanderLanin (Alexander Lanin) <https://github.com/AlexanderLanin>`_,
|
||||||
|
`@gcerretani (Giovanni Cerretani) <https://github.com/gcerretani>`_,
|
||||||
|
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
|
||||||
|
`@noizefloor (Jan Schwers) <https://github.com/noizefloor>`_,
|
||||||
|
`@akohlmey (Axel Kohlmeyer) <https://github.com/akohlmey>`_,
|
||||||
|
`@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_,
|
||||||
|
`@rimathia <https://github.com/rimathia>`_,
|
||||||
|
`@rglarix (Riccardo Ghetta (larix)) <https://github.com/rglarix>`_,
|
||||||
|
`@moiwi <https://github.com/moiwi>`_,
|
||||||
|
`@heckad (Kazantcev Andrey) <https://github.com/heckad>`_,
|
||||||
|
`@MarcDirven <https://github.com/MarcDirven>`_.
|
||||||
|
`@BartSiwek (Bart Siwek) <https://github.com/BartSiwek>`_,
|
||||||
|
`@darklukee <https://github.com/darklukee>`_.
|
||||||
|
|
||||||
7.0.3 - 2020-08-06
|
7.0.3 - 2020-08-06
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@@ -49,7 +474,7 @@
|
|||||||
<http://www.zverovich.net/2020/05/21/reducing-library-size.html>`_.
|
<http://www.zverovich.net/2020/05/21/reducing-library-size.html>`_.
|
||||||
|
|
||||||
* Added a simpler and more efficient `format string compilation API
|
* Added a simpler and more efficient `format string compilation API
|
||||||
<https://fmt.dev/dev/api.html#compile-api>`_:
|
<https://fmt.dev/7.0.0/api.html#compile-api>`_:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@@ -188,7 +613,7 @@
|
|||||||
|
|
||||||
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||||
|
|
||||||
* Added support for named args, ``clear`` and ``reserve`` to
|
* Added support for named arguments, ``clear`` and ``reserve`` to
|
||||||
``dynamic_format_arg_store``
|
``dynamic_format_arg_store``
|
||||||
(`#1655 <https://github.com/fmtlib/fmt/issues/1655>`_,
|
(`#1655 <https://github.com/fmtlib/fmt/issues/1655>`_,
|
||||||
`#1663 <https://github.com/fmtlib/fmt/pull/1663>`_,
|
`#1663 <https://github.com/fmtlib/fmt/pull/1663>`_,
|
||||||
@@ -1705,7 +2130,7 @@
|
|||||||
|
|
||||||
* Implemented ``constexpr`` parsing of format strings and `compile-time format
|
* Implemented ``constexpr`` parsing of format strings and `compile-time format
|
||||||
string checks
|
string checks
|
||||||
<https://fmt.dev/dev/api.html#compile-time-format-string-checks>`_. For
|
<https://fmt.dev/latest/api.html#compile-time-format-string-checks>`_. For
|
||||||
example
|
example
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
@@ -1766,7 +2191,7 @@
|
|||||||
throw format_error("invalid specifier");
|
throw format_error("invalid specifier");
|
||||||
|
|
||||||
* Added `iterator support
|
* Added `iterator support
|
||||||
<https://fmt.dev/dev/api.html#output-iterator-support>`_:
|
<https://fmt.dev/latest/api.html#output-iterator-support>`_:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@@ -1777,7 +2202,7 @@
|
|||||||
fmt::format_to(std::back_inserter(out), "{}", 42);
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||||
|
|
||||||
* Added the `format_to_n
|
* Added the `format_to_n
|
||||||
<https://fmt.dev/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
|
<https://fmt.dev/latest/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
|
||||||
function that restricts the output to the specified number of characters
|
function that restricts the output to the specified number of characters
|
||||||
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
|
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
|
||||||
|
|
||||||
@@ -1788,7 +2213,7 @@
|
|||||||
// out == "1234" (without terminating '\0')
|
// out == "1234" (without terminating '\0')
|
||||||
|
|
||||||
* Added the `formatted_size
|
* Added the `formatted_size
|
||||||
<https://fmt.dev/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
|
<https://fmt.dev/latest/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
|
||||||
function for computing the output size:
|
function for computing the output size:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
@@ -1798,7 +2223,7 @@
|
|||||||
auto size = fmt::formatted_size("{}", 12345); // size == 5
|
auto size = fmt::formatted_size("{}", 12345); // size == 5
|
||||||
|
|
||||||
* Improved compile times by reducing dependencies on standard headers and
|
* Improved compile times by reducing dependencies on standard headers and
|
||||||
providing a lightweight `core API <https://fmt.dev/dev/api.html#core-api>`_:
|
providing a lightweight `core API <https://fmt.dev/latest/api.html#core-api>`_:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@@ -1810,7 +2235,7 @@
|
|||||||
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_.
|
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_.
|
||||||
|
|
||||||
* Added the `make_format_args
|
* Added the `make_format_args
|
||||||
<https://fmt.dev/dev/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_
|
<https://fmt.dev/latest/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_
|
||||||
function for capturing formatting arguments:
|
function for capturing formatting arguments:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
@@ -1892,7 +2317,7 @@
|
|||||||
fmt::format("{} {two}", 1, fmt::arg("two", 2));
|
fmt::format("{} {two}", 1, fmt::arg("two", 2));
|
||||||
|
|
||||||
* Removed the write API in favor of the `format API
|
* Removed the write API in favor of the `format API
|
||||||
<https://fmt.dev/dev/api.html#format-api>`_ with compile-time handling of
|
<https://fmt.dev/latest/api.html#format-api>`_ with compile-time handling of
|
||||||
format strings.
|
format strings.
|
||||||
|
|
||||||
* Disallowed formatting of multibyte strings into a wide character target
|
* Disallowed formatting of multibyte strings into a wide character target
|
||||||
|
|||||||
+141
-129
@@ -7,48 +7,57 @@
|
|||||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
||||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||||
|
|
||||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
|
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||||
:alt: fmt is continuously fuzzed att oss-fuzz
|
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||||
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
|
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
|
||||||
Summary&q=proj%3Dlibfmt&can=1
|
Summary&q=proj%3Dfmt&can=1
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
||||||
: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
|
||||||
|
|
||||||
**{fmt}** is an open-source formatting library for C++.
|
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||||
It can be used as a safe and fast alternative to (s)printf and iostreams.
|
alternative to C stdio and C++ iostreams.
|
||||||
|
|
||||||
`Documentation <https://fmt.dev/latest/>`__
|
If you like this project, please consider donating to BYSOL,
|
||||||
|
an initiative to help victims of political repressions in Belarus:
|
||||||
|
https://www.facebook.com/donate/759400044849707/108388587646909/.
|
||||||
|
|
||||||
|
`Documentation <https://fmt.dev>`__
|
||||||
|
|
||||||
Q&A: ask questions on `StackOverflow with the tag fmt
|
Q&A: ask questions on `StackOverflow with the tag fmt
|
||||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||||
|
|
||||||
|
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* Simple `format API <https://fmt.dev/dev/api.html>`_ with positional arguments
|
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
|
||||||
for localization
|
for localization
|
||||||
* Implementation of `C++20 std::format
|
* Implementation of `C++20 std::format
|
||||||
<https://en.cppreference.com/w/cpp/utility/format>`__
|
<https://en.cppreference.com/w/cpp/utility/format>`__
|
||||||
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
|
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
|
||||||
of Python's
|
|
||||||
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
||||||
|
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
|
||||||
|
round-trip guarantees
|
||||||
* Safe `printf implementation
|
* Safe `printf implementation
|
||||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
|
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
|
||||||
the POSIX extension for positional arguments
|
extension for positional arguments
|
||||||
* Extensibility: support for user-defined types
|
* Extensibility: `support for user-defined types
|
||||||
|
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_
|
||||||
* High performance: faster than common standard library implementations of
|
* High performance: faster than common standard library implementations of
|
||||||
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_,
|
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
|
||||||
iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ and
|
and `Converting a hundred million integers to strings per second
|
||||||
`Converting a hundred million integers to strings per second
|
|
||||||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||||
* Small code size both in terms of source code (the minimum configuration
|
* Small code size both in terms of source code with the minimum configuration
|
||||||
consists of just three header files, ``core.h``, ``format.h`` and
|
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
|
||||||
``format-inl.h``) and compiled code. See `Compile time and code bloat`_
|
and compiled code; see `Compile time and code bloat`_
|
||||||
* Reliability: the library has an extensive set of `unit tests
|
* Reliability: the library has an extensive set of `tests
|
||||||
<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
|
||||||
|
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
|
||||||
@@ -57,18 +66,17 @@ Features
|
|||||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
||||||
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
|
* 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
|
||||||
* Support for wide strings
|
|
||||||
* 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/latest/>`_ for more details.
|
See the `documentation <https://fmt.dev>`_ for more details.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Print ``Hello, world!`` to ``stdout``:
|
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@@ -78,100 +86,95 @@ Print ``Hello, world!`` to ``stdout``:
|
|||||||
fmt::print("Hello, world!\n");
|
fmt::print("Hello, world!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Format a string:
|
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
std::string s = fmt::format("The answer is {}.", 42);
|
std::string s = fmt::format("The answer is {}.", 42);
|
||||||
// s == "The answer is 42."
|
// s == "The answer is 42."
|
||||||
|
|
||||||
Format a string using positional arguments:
|
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
||||||
// s == "I'd rather be happy than right."
|
// s == "I'd rather be happy than right."
|
||||||
|
|
||||||
Print a chrono duration:
|
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
using namespace std::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
fmt::print("Elapsed time: {}", 42ms);
|
fmt::print("Default format: {} {}\n", 42s, 100ms);
|
||||||
|
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
|
||||||
}
|
}
|
||||||
|
|
||||||
prints "Elapsed time: 42ms".
|
Output::
|
||||||
|
|
||||||
Check a format string at compile time:
|
Default format: 42s 100ms
|
||||||
|
strftime-like format: 03:15:30
|
||||||
|
|
||||||
|
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_)
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
// test.cc
|
#include <vector>
|
||||||
#include <fmt/format.h>
|
#include <fmt/ranges.h>
|
||||||
std::string s = format(FMT_STRING("{:d}"), "hello");
|
|
||||||
|
|
||||||
gives a compile-time error because ``d`` is an invalid format specifier for a
|
int main() {
|
||||||
string.
|
std::vector<int> v = {1, 2, 3};
|
||||||
|
fmt::print("{}\n", v);
|
||||||
Use {fmt} as a safe portable replacement for ``itoa``
|
|
||||||
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
|
|
||||||
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
|
|
||||||
// access the string with to_string(buf) or buf.data()
|
|
||||||
|
|
||||||
Format objects of user-defined types via a simple `extension API
|
|
||||||
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
struct date {
|
|
||||||
int year, month, day;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<date> {
|
|
||||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const date& d, FormatContext& ctx) {
|
|
||||||
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
|
|
||||||
// s == "The date is 2012-12-9"
|
|
||||||
|
|
||||||
Create your own functions similar to `format
|
|
||||||
<https://fmt.dev/latest/api.html#format>`_ and
|
|
||||||
`print <https://fmt.dev/latest/api.html#print>`_
|
|
||||||
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
|
|
||||||
|
|
||||||
.. code:: c++
|
|
||||||
|
|
||||||
// Prints formatted error message.
|
|
||||||
void vreport_error(const char* format, fmt::format_args args) {
|
|
||||||
fmt::print("Error: ");
|
|
||||||
fmt::vprint(format, args);
|
|
||||||
}
|
|
||||||
template <typename... Args>
|
|
||||||
void report_error(const char* format, const Args & ... args) {
|
|
||||||
vreport_error(format, fmt::make_format_args(args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
report_error("file not found: {}", path);
|
Output::
|
||||||
|
|
||||||
Note that ``vreport_error`` is not parameterized on argument types which can
|
{1, 2, 3}
|
||||||
improve compile times and reduce code size compared to a fully parameterized
|
|
||||||
version.
|
**Check a format string at compile time**
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
std::string s = fmt::format(FMT_STRING("{:d}"), "don't panic");
|
||||||
|
|
||||||
|
This gives a compile-time error because ``d`` is an invalid format specifier for
|
||||||
|
a string.
|
||||||
|
|
||||||
|
**Write a file from a single thread**
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/os.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto out = fmt::output_file("guide.txt");
|
||||||
|
out.print("Don't {}", "Panic");
|
||||||
|
}
|
||||||
|
|
||||||
|
This can be `5 to 9 times faster than fprintf
|
||||||
|
<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_.
|
||||||
|
|
||||||
|
**Print with colors and text styles**
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/color.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
|
||||||
|
"Hello, {}!\n", "world");
|
||||||
|
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
|
||||||
|
fmt::emphasis::underline, "Hello, {}!\n", "мир");
|
||||||
|
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
|
||||||
|
"Hello, {}!\n", "世界");
|
||||||
|
}
|
||||||
|
|
||||||
|
Output on a modern terminal:
|
||||||
|
|
||||||
|
.. image:: https://user-images.githubusercontent.com/
|
||||||
|
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
|
||||||
|
|
||||||
Benchmarks
|
Benchmarks
|
||||||
----------
|
----------
|
||||||
@@ -198,12 +201,14 @@ or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
|||||||
further details refer to the `source
|
further details refer to the `source
|
||||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
||||||
|
|
||||||
{fmt} is up to 10x faster than ``std::ostringstream`` and ``sprintf`` on
|
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||||
and faster than `double-conversion <https://github.com/google/double-conversion>`_:
|
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
|
||||||
|
`ryu <https://github.com/ulfjack/ryu>`_:
|
||||||
|
|
||||||
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
|
.. image:: https://user-images.githubusercontent.com/576385/
|
||||||
:target: https://fmt.dev/unknown_mac64_clang10.0.html
|
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
|
||||||
|
:target: https://fmt.dev/unknown_mac64_clang12.0.html
|
||||||
|
|
||||||
Compile time and code bloat
|
Compile time and code bloat
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -282,30 +287,33 @@ or the bloat test::
|
|||||||
Projects using this library
|
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
|
||||||
|
|
||||||
* `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
|
||||||
|
|
||||||
* `Aseprite <https://github.com/aseprite/aseprite>`_:
|
* `Aseprite <https://github.com/aseprite/aseprite>`_:
|
||||||
Animated sprite editor & pixel art tool
|
animated sprite editor & pixel art tool
|
||||||
|
|
||||||
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
|
* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft
|
||||||
operations suite
|
operations suite
|
||||||
|
|
||||||
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
|
* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform
|
||||||
|
|
||||||
|
* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space
|
||||||
|
|
||||||
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
|
* `Ceph <https://ceph.com/>`_: a scalable distributed storage system
|
||||||
|
|
||||||
* `ccache <https://ccache.dev/>`_: A compiler cache
|
* `ccache <https://ccache.dev/>`_: a compiler cache
|
||||||
|
|
||||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database management system
|
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
||||||
|
management system
|
||||||
|
|
||||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||||
vehicle
|
vehicle
|
||||||
|
|
||||||
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
|
||||||
for nonlinear dynamical systems (MIT)
|
for nonlinear dynamical systems (MIT)
|
||||||
|
|
||||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||||
@@ -318,66 +326,70 @@ Projects using this library
|
|||||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
Player vs Player Gaming Network with tweaks
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
* `KBEngine <https://kbengine.org/>`_: An open-source MMOG server engine
|
* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server
|
||||||
|
engine
|
||||||
|
|
||||||
* `Keypirinha <https://keypirinha.com/>`_: A semantic launcher for Windows
|
* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows
|
||||||
|
|
||||||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
|
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software
|
||||||
|
|
||||||
* `Knuth <https://kth.cash/>`_: High-performance Bitcoin full-node
|
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
|
||||||
|
|
||||||
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
||||||
Research programming language for concurrent ownership
|
research programming language for concurrent ownership
|
||||||
|
|
||||||
* `MongoDB <https://mongodb.com/>`_: Distributed document database
|
* `MongoDB <https://mongodb.com/>`_: distributed document database
|
||||||
|
|
||||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to
|
||||||
generate randomized datasets
|
generate randomized datasets
|
||||||
|
|
||||||
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
|
* `OpenSpace <https://openspaceproject.com/>`_: an open-source
|
||||||
astrovisualization framework
|
astrovisualization framework
|
||||||
|
|
||||||
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
||||||
An MMO server, compatible with most Ultima Online clients
|
an MMO server, compatible with most Ultima Online clients
|
||||||
|
|
||||||
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
|
* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine
|
||||||
learning library
|
learning library
|
||||||
|
|
||||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
|
||||||
associative database
|
associative database
|
||||||
|
|
||||||
|
* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library
|
||||||
|
|
||||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify
|
||||||
|
navigation, and executing complex multi-line terminal command sequences
|
||||||
|
|
||||||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster
|
||||||
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
|
||||||
|
|
||||||
* `Salesforce Analytics Cloud
|
* `Salesforce Analytics Cloud
|
||||||
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||||
Business intelligence software
|
business intelligence software
|
||||||
|
|
||||||
* `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
|
* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store
|
||||||
that can handle 1 million transactions per second on a single server
|
that can handle 1 million transactions per second on a single server
|
||||||
|
|
||||||
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
|
* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++
|
||||||
framework for high-performance server applications on modern hardware
|
framework for high-performance server applications on modern hardware
|
||||||
|
|
||||||
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
|
* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library
|
||||||
|
|
||||||
* `Stellar <https://www.stellar.org/>`_: Financial platform
|
* `Stellar <https://www.stellar.org/>`_: financial platform
|
||||||
|
|
||||||
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
|
* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator
|
||||||
|
|
||||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
|
||||||
MMORPG framework
|
MMORPG framework
|
||||||
|
|
||||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
|
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
|
||||||
Terminal
|
terminal
|
||||||
|
|
||||||
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
||||||
|
|
||||||
@@ -435,7 +447,7 @@ Boost Format
|
|||||||
|
|
||||||
This is a very powerful library which supports both ``printf``-like format
|
This is a very powerful library which 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
|
||||||
`Benchmarks`_).
|
`Benchmarks`_).
|
||||||
|
|
||||||
|
|||||||
@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
static_assert(F::is_integer, "From must be integral");
|
static_assert(F::is_integer, "From must be integral");
|
||||||
static_assert(T::is_integer, "To must be integral");
|
static_assert(T::is_integer, "To must be integral");
|
||||||
|
|
||||||
if (F::is_signed && !T::is_signed) {
|
if (detail::const_check(F::is_signed && !T::is_signed)) {
|
||||||
// From may be negative, not allowed!
|
// From may be negative, not allowed!
|
||||||
if (fmt::detail::is_negative(from)) {
|
if (fmt::detail::is_negative(from)) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// From is positive. Can it always fit in To?
|
// From is positive. Can it always fit in To?
|
||||||
if (F::digits <= T::digits) {
|
if (F::digits > T::digits &&
|
||||||
// yes, From always fits in To.
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
} else {
|
ec = 1;
|
||||||
// from may not fit in To, we have to do a dynamic check
|
return {};
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!F::is_signed && T::is_signed) {
|
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
|
||||||
// can from be held in To?
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
if (F::digits < T::digits) {
|
ec = 1;
|
||||||
// yes, From always fits in To.
|
return {};
|
||||||
} else {
|
|
||||||
// from may not fit in To, we have to do a dynamic check
|
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
// outside range.
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return static_cast<To>(from); // Lossless conversion.
|
||||||
// reaching here means all is ok for lossless conversion.
|
}
|
||||||
return static_cast<To>(from);
|
|
||||||
|
|
||||||
} // function
|
|
||||||
|
|
||||||
template <typename To, typename From,
|
template <typename To, typename From,
|
||||||
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||||
@@ -190,11 +174,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
// safe conversion to IntermediateRep
|
// safe conversion to IntermediateRep
|
||||||
IntermediateRep count =
|
IntermediateRep count =
|
||||||
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||||
if (ec) {
|
if (ec) return {};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
// multiply with Factor::num without overflow or underflow
|
// multiply with Factor::num without overflow or underflow
|
||||||
if (Factor::num != 1) {
|
if (detail::const_check(Factor::num != 1)) {
|
||||||
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
||||||
if (count > max1) {
|
if (count > max1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
@@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
count *= Factor::num;
|
count *= Factor::num;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this can't go wrong, right? den>0 is checked earlier.
|
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
|
||||||
if (Factor::den != 1) {
|
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
|
||||||
count /= Factor::den;
|
return ec ? To() : To(tocount);
|
||||||
}
|
|
||||||
// convert to the to type, safely
|
|
||||||
using ToRep = typename To::rep;
|
|
||||||
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
|
||||||
if (ec) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return To{tocount};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -351,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
return lt.tm_;
|
return lt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::tm localtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return localtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
// Thread-safe replacement for std::gmtime
|
||||||
inline std::tm gmtime(std::time_t time) {
|
inline std::tm gmtime(std::time_t time) {
|
||||||
struct dispatcher {
|
struct dispatcher {
|
||||||
@@ -387,6 +366,11 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::tm gmtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
inline size_t strftime(char* str, size_t count, const char* format,
|
inline size_t strftime(char* str, size_t count, const char* format,
|
||||||
const std::tm* time) {
|
const std::tm* time) {
|
||||||
@@ -399,6 +383,17 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
|||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
|
||||||
|
: formatter<std::tm, Char> {
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(std::chrono::time_point<std::chrono::system_clock> val,
|
||||||
|
FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
std::tm time = localtime(val);
|
||||||
|
return formatter<std::tm, Char>::format(time, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
|||||||
+47
-10
@@ -463,16 +463,16 @@ template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
const char* begin = data::reset_color;
|
const char* begin = data::reset_color;
|
||||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||||
buffer.append(begin, end);
|
buffer.append(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
bool has_style = false;
|
bool has_style = false;
|
||||||
if (ts.has_emphasis()) {
|
if (ts.has_emphasis()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
@@ -496,7 +496,7 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
|||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
buf.push_back(Char(0));
|
buf.push_back(Char(0));
|
||||||
@@ -504,20 +504,22 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
\rst
|
||||||
Formats a string and prints it to the specified file stream using ANSI
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
escape sequences to specify text formatting.
|
escape sequences to specify text formatting.
|
||||||
Example:
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
detail::check_format_string<Args...>(format_str);
|
vprint(f, ts, format_str,
|
||||||
using context = buffer_context<char_t<S>>;
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -558,7 +560,42 @@ template <typename S, typename... Args, typename Char = char_t<S>>
|
|||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return vformat(ts, to_string_view(format_str),
|
return vformat(ts, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string with the given text_style and writes the output to ``out``.
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename Char,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
OutputIt vformat_to(
|
||||||
|
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
|
detail::vformat_to(buf, ts, format_str, args);
|
||||||
|
return detail::get_iterator(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments with the given text_style, writes the result to the output
|
||||||
|
iterator ``out`` and returns the iterator past the end of the output range.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out),
|
||||||
|
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||||
|
detail::is_string<S>::value>
|
||||||
|
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||||
|
Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
return vformat_to(out, ts, to_string_view(format_str),
|
||||||
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@@ -368,7 +368,8 @@ template <typename... Args> struct type_list {};
|
|||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
template <int N, typename T, typename... Args>
|
template <int N, typename T, typename... Args>
|
||||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||||
|
[[maybe_unused]] const Args&... rest) {
|
||||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
@@ -406,6 +407,19 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
|||||||
return {{&s[pos], size}};
|
return {{&s[pos], size}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct code_unit {
|
||||||
|
Char value;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&...) const {
|
||||||
|
return write<Char>(out, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||||
|
|
||||||
// A replacement field that refers to argument N.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename T, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
@@ -430,7 +444,9 @@ template <typename Char, typename T, int N> struct spec_field {
|
|||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
const T& arg = get<N>(args...);
|
const T& arg = get<N>(args...);
|
||||||
basic_format_context<OutputIt, Char> ctx(out, {});
|
const auto& vargs =
|
||||||
|
make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
return fmt.format(arg, ctx);
|
return fmt.format(arg, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -489,16 +505,17 @@ constexpr auto parse_tail(T head, S format_str) {
|
|||||||
template <typename T, typename Char> struct parse_specs_result {
|
template <typename T, typename Char> struct parse_specs_result {
|
||||||
formatter<T, Char> fmt;
|
formatter<T, Char> fmt;
|
||||||
size_t end;
|
size_t end;
|
||||||
|
int next_arg_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
size_t pos) {
|
size_t pos, int arg_id) {
|
||||||
str.remove_prefix(pos);
|
str.remove_prefix(pos);
|
||||||
auto ctx = basic_format_parse_context<Char>(str);
|
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
|
||||||
auto f = formatter<T, Char>();
|
auto f = formatter<T, Char>();
|
||||||
auto end = f.parse(ctx);
|
auto end = f.parse(ctx);
|
||||||
return {f, pos + (end - str.data()) + 1};
|
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
@@ -518,8 +535,8 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
format_str);
|
format_str);
|
||||||
} else if constexpr (str[POS + 1] == ':') {
|
} else if constexpr (str[POS + 1] == ':') {
|
||||||
using type = get_type<ID, Args>;
|
using type = get_type<ID, Args>;
|
||||||
constexpr auto result = parse_specs<type>(str, POS + 2);
|
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
||||||
return parse_tail<Args, result.end, ID + 1>(
|
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||||
} else {
|
} else {
|
||||||
return unknown_format();
|
return unknown_format();
|
||||||
@@ -530,8 +547,13 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
if constexpr (end - POS > 1) {
|
||||||
format_str);
|
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,8 +609,7 @@ template <typename CompiledFormat, typename... Args,
|
|||||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
detail::buffer<Char>& base = buffer;
|
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
||||||
cf.format(std::back_inserter(base), args...);
|
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,8 +629,7 @@ template <typename CompiledFormat, typename... Args,
|
|||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
using context = buffer_context<Char>;
|
using context = buffer_context<Char>;
|
||||||
detail::buffer<Char>& base = buffer;
|
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
|
||||||
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
|
|
||||||
make_format_args<context>(args...));
|
make_format_args<context>(args...));
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
@@ -618,9 +638,13 @@ template <typename S, typename... Args,
|
|||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = S();
|
#ifdef __cpp_if_constexpr
|
||||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
return fmt::to_string(detail::first(args...));
|
constexpr basic_string_view<typename S::char_type> str = S();
|
||||||
|
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
||||||
|
return fmt::to_string(detail::first(args...));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
@@ -643,18 +667,30 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
|||||||
return format_to(out, compiled, args...);
|
return format_to(out, compiled, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
||||||
typename OutputIt, typename CompiledFormat, typename... Args,
|
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
const Args&... args) ->
|
||||||
detail::basic_compiled_format, CompiledFormat>::value)>
|
typename std::enable_if<
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
detail::is_output_iterator<OutputIt,
|
||||||
const CompiledFormat& cf,
|
typename CompiledFormat::char_type>::value &&
|
||||||
const Args&... args) {
|
std::is_base_of<detail::basic_compiled_format,
|
||||||
|
CompiledFormat>::value,
|
||||||
|
format_to_n_result<OutputIt>>::type {
|
||||||
auto it =
|
auto it =
|
||||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
||||||
|
const Args&... args) {
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
|
||||||
|
args...);
|
||||||
|
return {it.base(), it.count()};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename CompiledFormat, typename... Args>
|
||||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||||
|
|||||||
+344
-104
@@ -18,7 +18,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// 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 70003
|
#define FMT_VERSION 70103
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
# define FMT_MSC_VER 0
|
# define FMT_MSC_VER 0
|
||||||
# define FMT_SUPPRESS_MSC_WARNING(n)
|
# define FMT_SUPPRESS_MSC_WARNING(n)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __has_feature
|
#ifdef __has_feature
|
||||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||||
#else
|
#else
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
||||||
!(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600)
|
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
|
||||||
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
||||||
#else
|
#else
|
||||||
# define FMT_HAS_INCLUDE(x) 0
|
# define FMT_HAS_INCLUDE(x) 0
|
||||||
@@ -99,7 +100,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_OVERRIDE
|
#ifndef FMT_OVERRIDE
|
||||||
# if FMT_HAS_FEATURE(cxx_override) || \
|
# if FMT_HAS_FEATURE(cxx_override_control) || \
|
||||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
||||||
# define FMT_OVERRIDE override
|
# define FMT_OVERRIDE override
|
||||||
# else
|
# else
|
||||||
@@ -152,7 +153,7 @@
|
|||||||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||||
# define FMT_DEPRECATED [[deprecated]]
|
# define FMT_DEPRECATED [[deprecated]]
|
||||||
# else
|
# else
|
||||||
# if defined(__GNUC__) || defined(__clang__)
|
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
|
||||||
# define FMT_DEPRECATED __attribute__((deprecated))
|
# define FMT_DEPRECATED __attribute__((deprecated))
|
||||||
# elif FMT_MSC_VER
|
# elif FMT_MSC_VER
|
||||||
# define FMT_DEPRECATED __declspec(deprecated)
|
# define FMT_DEPRECATED __declspec(deprecated)
|
||||||
@@ -177,9 +178,17 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_BEGIN_NAMESPACE
|
#ifndef FMT_USE_INLINE_NAMESPACES
|
||||||
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
|
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
|
||||||
FMT_MSC_VER >= 1900
|
(FMT_MSC_VER >= 1900 && !_MANAGED)
|
||||||
|
# define FMT_USE_INLINE_NAMESPACES 1
|
||||||
|
# else
|
||||||
|
# define FMT_USE_INLINE_NAMESPACES 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_BEGIN_NAMESPACE
|
||||||
|
# if FMT_USE_INLINE_NAMESPACES
|
||||||
# define FMT_INLINE_NAMESPACE inline namespace
|
# define FMT_INLINE_NAMESPACE inline namespace
|
||||||
# define FMT_END_NAMESPACE \
|
# define FMT_END_NAMESPACE \
|
||||||
} \
|
} \
|
||||||
@@ -269,8 +278,7 @@ struct monostate {};
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// A helper function to suppress bogus "conditional expression is constant"
|
// A helper function to suppress "conditional expression is constant" warnings.
|
||||||
// warnings.
|
|
||||||
template <typename T> constexpr T const_check(T value) { return value; }
|
template <typename T> constexpr T const_check(T value) { return value; }
|
||||||
|
|
||||||
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||||
@@ -299,7 +307,8 @@ template <typename T> struct std_string_view {};
|
|||||||
|
|
||||||
#ifdef FMT_USE_INT128
|
#ifdef FMT_USE_INT128
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER)
|
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
|
||||||
|
!(FMT_CLANG_VERSION && FMT_MSC_VER)
|
||||||
# define FMT_USE_INT128 1
|
# define FMT_USE_INT128 1
|
||||||
using int128_t = __int128_t;
|
using int128_t = __int128_t;
|
||||||
using uint128_t = __uint128_t;
|
using uint128_t = __uint128_t;
|
||||||
@@ -506,6 +515,18 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
|||||||
using type = typename result::value_type;
|
using type = typename result::value_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reports a compile-time error if S is not a valid format string.
|
||||||
|
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
||||||
|
FMT_INLINE void check_format_string(const S&) {
|
||||||
|
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||||
|
static_assert(is_compile_string<S>::value,
|
||||||
|
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
||||||
|
"FMT_STRING.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
void check_format_string(S);
|
||||||
|
|
||||||
struct error_handler {
|
struct error_handler {
|
||||||
constexpr error_handler() = default;
|
constexpr error_handler() = default;
|
||||||
constexpr error_handler(const error_handler&) = default;
|
constexpr error_handler(const error_handler&) = default;
|
||||||
@@ -545,8 +566,9 @@ class basic_format_parse_context : private ErrorHandler {
|
|||||||
using iterator = typename basic_string_view<Char>::iterator;
|
using iterator = typename basic_string_view<Char>::iterator;
|
||||||
|
|
||||||
explicit constexpr basic_format_parse_context(
|
explicit constexpr basic_format_parse_context(
|
||||||
basic_string_view<Char> format_str, ErrorHandler eh = {})
|
basic_string_view<Char> format_str, ErrorHandler eh = {},
|
||||||
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
|
int next_arg_id = 0)
|
||||||
|
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns an iterator to the beginning of the format string range being
|
Returns an iterator to the beginning of the format string range being
|
||||||
@@ -616,8 +638,24 @@ template <typename T, typename Context>
|
|||||||
using has_formatter =
|
using has_formatter =
|
||||||
std::is_constructible<typename Context::template formatter_type<T>>;
|
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 {};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// Extracts a reference to the container from back_insert_iterator.
|
||||||
|
template <typename Container>
|
||||||
|
inline Container& get_container(std::back_insert_iterator<Container> it) {
|
||||||
|
using bi_iterator = std::back_insert_iterator<Container>;
|
||||||
|
struct accessor : bi_iterator {
|
||||||
|
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
||||||
|
using bi_iterator::container;
|
||||||
|
};
|
||||||
|
return *accessor(it).container;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
A contiguous memory buffer with an optional growing ability. It is an internal
|
A contiguous memory buffer with an optional growing ability. It is an internal
|
||||||
@@ -640,6 +678,8 @@ template <typename T> class buffer {
|
|||||||
size_(sz),
|
size_(sz),
|
||||||
capacity_(cap) {}
|
capacity_(cap) {}
|
||||||
|
|
||||||
|
~buffer() = default;
|
||||||
|
|
||||||
/** Sets the buffer data and capacity. */
|
/** Sets the buffer data and capacity. */
|
||||||
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
||||||
ptr_ = buf_data;
|
ptr_ = buf_data;
|
||||||
@@ -655,7 +695,6 @@ template <typename T> class buffer {
|
|||||||
|
|
||||||
buffer(const buffer&) = delete;
|
buffer(const buffer&) = delete;
|
||||||
void operator=(const buffer&) = delete;
|
void operator=(const buffer&) = delete;
|
||||||
virtual ~buffer() = default;
|
|
||||||
|
|
||||||
T* begin() FMT_NOEXCEPT { return ptr_; }
|
T* begin() FMT_NOEXCEPT { return ptr_; }
|
||||||
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
||||||
@@ -675,24 +714,26 @@ template <typename T> class buffer {
|
|||||||
/** Returns a pointer to the buffer data. */
|
/** Returns a pointer to the buffer data. */
|
||||||
const T* data() const FMT_NOEXCEPT { return ptr_; }
|
const T* data() const FMT_NOEXCEPT { return ptr_; }
|
||||||
|
|
||||||
/**
|
|
||||||
Resizes the buffer. If T is a POD type new elements may not be initialized.
|
|
||||||
*/
|
|
||||||
void resize(size_t new_size) {
|
|
||||||
reserve(new_size);
|
|
||||||
size_ = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clears this buffer. */
|
/** Clears this buffer. */
|
||||||
void clear() { size_ = 0; }
|
void clear() { size_ = 0; }
|
||||||
|
|
||||||
/** Reserves space to store at least *capacity* elements. */
|
// Tries resizing the buffer to contain *count* elements. If T is a POD type
|
||||||
void reserve(size_t new_capacity) {
|
// the new elements may not be initialized.
|
||||||
|
void try_resize(size_t count) {
|
||||||
|
try_reserve(count);
|
||||||
|
size_ = count <= capacity_ ? count : capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries increasing the buffer capacity to *new_capacity*. It can increase the
|
||||||
|
// capacity by a smaller amount than requested but guarantees there is space
|
||||||
|
// for at least one additional element either by increasing the capacity or by
|
||||||
|
// flushing the buffer if it is full.
|
||||||
|
void try_reserve(size_t new_capacity) {
|
||||||
if (new_capacity > capacity_) grow(new_capacity);
|
if (new_capacity > capacity_) grow(new_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_back(const T& value) {
|
void push_back(const T& value) {
|
||||||
reserve(size_ + 1);
|
try_reserve(size_ + 1);
|
||||||
ptr_[size_++] = value;
|
ptr_[size_++] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,32 +746,150 @@ template <typename T> class buffer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A container-backed buffer.
|
struct buffer_traits {
|
||||||
|
explicit buffer_traits(size_t) {}
|
||||||
|
size_t count() const { return 0; }
|
||||||
|
size_t limit(size_t size) { return size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class fixed_buffer_traits {
|
||||||
|
private:
|
||||||
|
size_t count_ = 0;
|
||||||
|
size_t limit_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
size_t limit(size_t size) {
|
||||||
|
size_t n = limit_ > count_ ? limit_ - count_ : 0;
|
||||||
|
count_ += size;
|
||||||
|
return size < n ? size : n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffer that writes to an output iterator when flushed.
|
||||||
|
template <typename OutputIt, typename T, typename Traits = buffer_traits>
|
||||||
|
class iterator_buffer final : public Traits, public buffer<T> {
|
||||||
|
private:
|
||||||
|
OutputIt out_;
|
||||||
|
enum { buffer_size = 256 };
|
||||||
|
T data_[buffer_size];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void grow(size_t) final FMT_OVERRIDE {
|
||||||
|
if (this->size() == buffer_size) flush();
|
||||||
|
}
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
|
||||||
|
: Traits(n),
|
||||||
|
buffer<T>(data_, 0, buffer_size),
|
||||||
|
out_(out) {}
|
||||||
|
~iterator_buffer() { flush(); }
|
||||||
|
|
||||||
|
OutputIt out() {
|
||||||
|
flush();
|
||||||
|
return out_;
|
||||||
|
}
|
||||||
|
size_t count() const { return Traits::count() + this->size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
|
||||||
|
protected:
|
||||||
|
void grow(size_t) final FMT_OVERRIDE {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
|
||||||
|
|
||||||
|
T* out() { return &*this->end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffer that writes to a container with the contiguous storage.
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
class container_buffer : public buffer<typename Container::value_type> {
|
class iterator_buffer<std::back_insert_iterator<Container>,
|
||||||
|
enable_if_t<is_contiguous<Container>::value,
|
||||||
|
typename Container::value_type>>
|
||||||
|
final : public buffer<typename Container::value_type> {
|
||||||
private:
|
private:
|
||||||
Container& container_;
|
Container& container_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void grow(size_t capacity) FMT_OVERRIDE {
|
void grow(size_t capacity) final FMT_OVERRIDE {
|
||||||
container_.resize(capacity);
|
container_.resize(capacity);
|
||||||
this->set(&container_[0], capacity);
|
this->set(&container_[0], capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit container_buffer(Container& c)
|
explicit iterator_buffer(Container& c)
|
||||||
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
||||||
|
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
|
||||||
|
: iterator_buffer(get_container(out)) {}
|
||||||
|
std::back_insert_iterator<Container> out() {
|
||||||
|
return std::back_inserter(container_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extracts a reference to the container from back_insert_iterator.
|
// A buffer that counts the number of code units written discarding the output.
|
||||||
template <typename Container>
|
template <typename T = char> class counting_buffer final : public buffer<T> {
|
||||||
inline Container& get_container(std::back_insert_iterator<Container> it) {
|
private:
|
||||||
using bi_iterator = std::back_insert_iterator<Container>;
|
enum { buffer_size = 256 };
|
||||||
struct accessor : bi_iterator {
|
T data_[buffer_size];
|
||||||
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
size_t count_ = 0;
|
||||||
using bi_iterator::container;
|
|
||||||
};
|
protected:
|
||||||
return *accessor(it).container;
|
void grow(size_t) final FMT_OVERRIDE {
|
||||||
|
if (this->size() != buffer_size) return;
|
||||||
|
count_ += this->size();
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
|
||||||
|
|
||||||
|
size_t count() { return count_ + this->size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output iterator that appends to the buffer.
|
||||||
|
// It is used to reduce symbol sizes for the common case.
|
||||||
|
template <typename T>
|
||||||
|
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
|
||||||
|
using base = std::back_insert_iterator<buffer<T>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit buffer_appender(buffer<T>& buf) : base(buf) {}
|
||||||
|
buffer_appender(base it) : base(it) {}
|
||||||
|
|
||||||
|
buffer_appender& operator++() {
|
||||||
|
base::operator++();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_appender operator++(int) {
|
||||||
|
buffer_appender tmp = *this;
|
||||||
|
++*this;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps an output iterator into a buffer.
|
||||||
|
template <typename T, typename OutputIt>
|
||||||
|
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
|
||||||
|
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
|
||||||
|
|
||||||
|
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
|
||||||
|
return get_container(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
|
||||||
|
return buf.out();
|
||||||
|
}
|
||||||
|
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
|
||||||
|
return buffer_appender<T>(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Char = char, typename Enable = void>
|
template <typename T, typename Char = char, typename Enable = void>
|
||||||
@@ -759,7 +918,8 @@ template <typename Char> struct named_arg_info {
|
|||||||
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
||||||
struct arg_data {
|
struct arg_data {
|
||||||
// args_[0].named_args points to named_args_ to avoid bloating format_args.
|
// args_[0].named_args points to named_args_ to avoid bloating format_args.
|
||||||
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
|
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
|
||||||
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
|
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
|
||||||
|
|
||||||
template <typename... U>
|
template <typename... U>
|
||||||
@@ -771,7 +931,8 @@ struct arg_data {
|
|||||||
|
|
||||||
template <typename T, typename Char, size_t NUM_ARGS>
|
template <typename T, typename Char, size_t NUM_ARGS>
|
||||||
struct arg_data<T, Char, NUM_ARGS, 0> {
|
struct arg_data<T, Char, NUM_ARGS, 0> {
|
||||||
T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
|
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
|
||||||
|
|
||||||
template <typename... U>
|
template <typename... U>
|
||||||
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
|
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
|
||||||
@@ -959,6 +1120,8 @@ enum { long_short = sizeof(long) == sizeof(int) };
|
|||||||
using long_type = conditional_t<long_short, int, long long>;
|
using long_type = conditional_t<long_short, int, long long>;
|
||||||
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
||||||
|
|
||||||
|
struct unformattable {};
|
||||||
|
|
||||||
// Maps formatting arguments to core types.
|
// Maps formatting arguments to core types.
|
||||||
template <typename Context> struct arg_mapper {
|
template <typename Context> struct arg_mapper {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
@@ -1067,15 +1230,7 @@ template <typename Context> struct arg_mapper {
|
|||||||
return map(val.value);
|
return map(val.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int map(...) {
|
unformattable map(...) { return {}; }
|
||||||
constexpr bool formattable = sizeof(Context) == 0;
|
|
||||||
static_assert(
|
|
||||||
formattable,
|
|
||||||
"Cannot format argument. To make type T formattable provide a "
|
|
||||||
"formatter<T> specialization: "
|
|
||||||
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A type constant after applying arg_mapper<Context>.
|
// A type constant after applying arg_mapper<Context>.
|
||||||
@@ -1199,15 +1354,25 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
|
|||||||
return vis(monostate());
|
return vis(monostate());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether T is a container with contiguous storage.
|
template <typename T> struct formattable : std::false_type {};
|
||||||
template <typename T> struct is_contiguous : std::false_type {};
|
|
||||||
template <typename Char>
|
|
||||||
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
|
||||||
template <typename Char>
|
|
||||||
struct is_contiguous<detail::buffer<Char>> : std::true_type {};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
||||||
|
template <typename... Ts> struct void_t_impl { using type = void; };
|
||||||
|
template <typename... Ts>
|
||||||
|
using void_t = typename detail::void_t_impl<Ts...>::type;
|
||||||
|
|
||||||
|
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 OutputIt>
|
template <typename OutputIt>
|
||||||
struct is_back_insert_iterator : std::false_type {};
|
struct is_back_insert_iterator : std::false_type {};
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
@@ -1219,6 +1384,9 @@ struct is_contiguous_back_insert_iterator : std::false_type {};
|
|||||||
template <typename Container>
|
template <typename Container>
|
||||||
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||||
: is_contiguous<Container> {};
|
: is_contiguous<Container> {};
|
||||||
|
template <typename Char>
|
||||||
|
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
||||||
class locale_ref {
|
class locale_ref {
|
||||||
@@ -1250,13 +1418,24 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
|
|||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> int check(unformattable) {
|
||||||
|
static_assert(
|
||||||
|
formattable<T>(),
|
||||||
|
"Cannot format an argument. To make type T formattable provide a "
|
||||||
|
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
template <typename T, typename U> inline const U& check(const U& val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
// The type template parameter is there to avoid an ODR violation when using
|
// The type template parameter is there to avoid an ODR violation when using
|
||||||
// a fallback formatter in one translation unit and an implicit conversion in
|
// a fallback formatter in one translation unit and an implicit conversion in
|
||||||
// another (not recommended).
|
// another (not recommended).
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
FMT_ENABLE_IF(IS_PACKED)>
|
FMT_ENABLE_IF(IS_PACKED)>
|
||||||
inline value<Context> make_arg(const T& val) {
|
inline value<Context> make_arg(const T& val) {
|
||||||
return arg_mapper<Context>().map(val);
|
return check<T>(arg_mapper<Context>().map(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
@@ -1356,13 +1535,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using buffer_context =
|
using buffer_context =
|
||||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_format_context<detail::buffer_appender<Char>, Char>;
|
||||||
using format_context = buffer_context<char>;
|
using format_context = buffer_context<char>;
|
||||||
using wformat_context = buffer_context<wchar_t>;
|
using wformat_context = buffer_context<wchar_t>;
|
||||||
|
|
||||||
// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
|
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
|
||||||
#define FMT_BUFFER_CONTEXT(Char) \
|
#define FMT_BUFFER_CONTEXT(Char) \
|
||||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
|
basic_format_context<detail::buffer_appender<Char>, Char>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
@@ -1414,7 +1593,7 @@ class format_arg_store
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Constructs an `~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::context`.
|
||||||
See `~fmt::arg` for lifetime considerations.
|
See `~fmt::arg` for lifetime considerations.
|
||||||
@@ -1426,6 +1605,27 @@ inline format_arg_store<Context, Args...> make_format_args(
|
|||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a `~fmt::format_arg_store` object that contains references
|
||||||
|
to arguments and can be implicitly converted to `~fmt::format_args`.
|
||||||
|
If ``format_str`` is a compile-time string then `make_args_checked` checks
|
||||||
|
its validity at compile time.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args, typename S, typename Char = char_t<S>>
|
||||||
|
inline auto make_args_checked(const S& format_str,
|
||||||
|
const remove_reference_t<Args>&... args)
|
||||||
|
-> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
|
||||||
|
static_assert(
|
||||||
|
detail::count<(
|
||||||
|
std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
|
||||||
|
std::is_reference<Args>::value)...>() == 0,
|
||||||
|
"passing views as lvalues is disallowed");
|
||||||
|
detail::check_format_string<Args...>(format_str);
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns a named argument to be used in a formatting function. It should only
|
Returns a named argument to be used in a formatting function. It should only
|
||||||
@@ -1729,7 +1929,14 @@ template <typename Context> class basic_format_args {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An alias to ``basic_format_args<context>``. */
|
#ifdef FMT_ARM_ABI_COMPATIBILITY
|
||||||
|
/** An alias to ``basic_format_args<format_context>``. */
|
||||||
|
// Separate types would result in shorter symbols but break ABI compatibility
|
||||||
|
// between clang and gcc on ARM (#1919).
|
||||||
|
using format_args = basic_format_args<format_context>;
|
||||||
|
using wformat_args = basic_format_args<wformat_context>;
|
||||||
|
#else
|
||||||
|
// DEPRECATED! These are kept for ABI compatibility.
|
||||||
// It is a separate type rather than an alias to make symbols readable.
|
// It is a separate type rather than an alias to make symbols readable.
|
||||||
struct format_args : basic_format_args<format_context> {
|
struct format_args : basic_format_args<format_context> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
@@ -1738,32 +1945,10 @@ struct format_args : basic_format_args<format_context> {
|
|||||||
struct wformat_args : basic_format_args<wformat_context> {
|
struct wformat_args : basic_format_args<wformat_context> {
|
||||||
using basic_format_args::basic_format_args;
|
using basic_format_args::basic_format_args;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// Reports a compile-time error if S is not a valid format string.
|
|
||||||
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
|
||||||
FMT_INLINE void check_format_string(const S&) {
|
|
||||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
|
||||||
static_assert(is_compile_string<S>::value,
|
|
||||||
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
|
||||||
"FMT_STRING.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
void check_format_string(S);
|
|
||||||
|
|
||||||
template <typename... Args, typename S, typename Char = char_t<S>>
|
|
||||||
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
|
|
||||||
make_args_checked(const S& format_str,
|
|
||||||
const remove_reference_t<Args>&... args) {
|
|
||||||
static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
|
|
||||||
std::is_reference<Args>::value)...>() == 0,
|
|
||||||
"passing views as lvalues is disallowed");
|
|
||||||
check_format_string<Args...>(format_str);
|
|
||||||
return {args...};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
@@ -1772,9 +1957,10 @@ std::basic_string<Char> vformat(
|
|||||||
FMT_API std::string vformat(string_view format_str, format_args args);
|
FMT_API std::string vformat(string_view format_str, format_args args);
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
|
void vformat_to(
|
||||||
buffer<Char>& buf, basic_string_view<Char> format_str,
|
buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
|
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
|
||||||
|
detail::locale_ref loc = {});
|
||||||
|
|
||||||
template <typename Char, typename Args,
|
template <typename Char, typename Args,
|
||||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
@@ -1789,26 +1975,80 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
|||||||
/** Formats a string and writes the output to ``out``. */
|
/** Formats a string and writes the output to ``out``. */
|
||||||
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
||||||
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
||||||
template <
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||||
typename OutputIt, typename S, typename Char = char_t<S>,
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value>
|
||||||
FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
auto vformat_to(OutputIt out, const S& format_str,
|
||||||
OutputIt vformat_to(
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
OutputIt out, const S& format_str,
|
-> typename std::enable_if<enable, OutputIt>::type {
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
auto& c = detail::get_container(out);
|
|
||||||
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
|
|
||||||
detail::vformat_to(buf, to_string_view(format_str), args);
|
detail::vformat_to(buf, to_string_view(format_str), args);
|
||||||
return out;
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Container, typename S, typename... Args,
|
/**
|
||||||
FMT_ENABLE_IF(
|
\rst
|
||||||
is_contiguous<Container>::value&& detail::is_string<S>::value)>
|
Formats arguments, writes the result to the output iterator ``out`` and returns
|
||||||
inline std::back_insert_iterator<Container> format_to(
|
the iterator past the end of the output range.
|
||||||
std::back_insert_iterator<Container> out, const S& format_str,
|
|
||||||
Args&&... args) {
|
**Example**::
|
||||||
return vformat_to(out, to_string_view(format_str),
|
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
|
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to(out, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> struct format_to_n_result {
|
||||||
|
/** Iterator past the end of the output range. */
|
||||||
|
OutputIt out;
|
||||||
|
/** Total (not truncated) output size. */
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
inline format_to_n_result<OutputIt> vformat_to_n(
|
||||||
|
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
|
||||||
|
n);
|
||||||
|
detail::vformat_to(buf, format_str, args);
|
||||||
|
return {buf.out(), buf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments, writes up to ``n`` characters of the result to the output
|
||||||
|
iterator ``out`` and returns the total output size and the iterator past the
|
||||||
|
end of the output range.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
|
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
|
||||||
|
const Args&... args) ->
|
||||||
|
typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to_n(out, n, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the number of characters in the output of
|
||||||
|
``format(format_str, args...)``.
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline size_t formatted_size(string_view format_str, Args&&... args) {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
detail::counting_buffer<> buf;
|
||||||
|
detail::vformat_to(buf, format_str, vargs);
|
||||||
|
return buf.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
@@ -1832,7 +2072,7 @@ FMT_INLINE std::basic_string<Char> vformat(
|
|||||||
// 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... Args, typename Char = char_t<S>>
|
||||||
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::vformat(to_string_view(format_str), vargs);
|
return detail::vformat(to_string_view(format_str), vargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1852,7 +2092,7 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
|
|||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::is_unicode<Char>()
|
return detail::is_unicode<Char>()
|
||||||
? vprint(f, to_string_view(format_str), vargs)
|
? vprint(f, to_string_view(format_str), vargs)
|
||||||
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
|
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
|
||||||
@@ -1871,7 +2111,7 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
|||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline void print(const S& format_str, Args&&... args) {
|
inline void print(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::is_unicode<Char>()
|
return detail::is_unicode<Char>()
|
||||||
? vprint(to_string_view(format_str), vargs)
|
? vprint(to_string_view(format_str), vargs)
|
||||||
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
||||||
|
|||||||
+1623
-275
File diff suppressed because it is too large.
Load diff
+695
-464
File diff suppressed because it is too large.
Load diff
@@ -15,22 +15,12 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Char>
|
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
|
||||||
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
|
|
||||||
args, detail::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
const std::locale& loc, 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) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
detail::vformat_to(loc, buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
|
||||||
return fmt::to_string(buffer);
|
return fmt::to_string(buffer);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -45,32 +35,28 @@ inline std::basic_string<Char> vformat(
|
|||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
inline std::basic_string<Char> format(const std::locale& loc,
|
||||||
const S& format_str, Args&&... args) {
|
const S& format_str, Args&&... args) {
|
||||||
return detail::vformat(
|
return detail::vformat(loc, to_string_view(format_str),
|
||||||
loc, to_string_view(format_str),
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = enable_if_t<
|
typename Char = char_t<S>,
|
||||||
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
inline OutputIt vformat_to(
|
inline OutputIt vformat_to(
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
OutputIt out, const std::locale& loc, const S& format_str,
|
||||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
using af = detail::arg_formatter<OutputIt, Char>;
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
return vformat_to<af>(out, to_string_view(format_str), args,
|
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||||
detail::locale_ref(loc));
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
detail::is_string<S>::value)>
|
inline auto format_to(OutputIt out, const std::locale& loc,
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
const S& format_str, Args&&... args) ->
|
||||||
const S& format_str, Args&&... args) {
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
detail::check_format_string<Args...>(format_str);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
using context = format_context_t<OutputIt, char_t<S>>;
|
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
return vformat_to(out, loc, to_string_view(format_str),
|
|
||||||
basic_format_args<context>(as));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
+68
-38
@@ -29,7 +29,8 @@
|
|||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
#endif
|
||||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# include <fcntl.h> // for O_RDONLY
|
# include <fcntl.h> // for O_RDONLY
|
||||||
# define FMT_USE_FCNTL 1
|
# define FMT_USE_FCNTL 1
|
||||||
@@ -278,7 +279,8 @@ class file {
|
|||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||||
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
|
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||||
|
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
@@ -343,36 +345,69 @@ class file {
|
|||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
class direct_buffered_file;
|
namespace detail {
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
struct buffer_size {
|
||||||
void print(direct_buffered_file& f, const S& format_str,
|
size_t value = 0;
|
||||||
const Args&... args);
|
buffer_size operator=(size_t val) const {
|
||||||
|
auto bs = buffer_size();
|
||||||
|
bs.value = val;
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// A buffered file with a direct buffer access and no synchronization.
|
struct ostream_params {
|
||||||
class direct_buffered_file {
|
int oflag = file::WRONLY | file::CREATE;
|
||||||
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||||
|
|
||||||
|
ostream_params() {}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, int oflag) : ostream_params(params...) {
|
||||||
|
this->oflag = oflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, detail::buffer_size bs)
|
||||||
|
: ostream_params(params...) {
|
||||||
|
this->buffer_size = bs.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
static constexpr detail::buffer_size buffer_size;
|
||||||
|
|
||||||
|
// A fast output stream which is not thread-safe.
|
||||||
|
class ostream final : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
enum { buffer_size = 4096 };
|
|
||||||
char buffer_[buffer_size];
|
|
||||||
int pos_;
|
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
if (pos_ == 0) return;
|
if (size() == 0) return;
|
||||||
file_.write(buffer_, pos_);
|
file_.write(data(), size());
|
||||||
pos_ = 0;
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int free_capacity() const { return buffer_size - pos_; }
|
FMT_API void grow(size_t) override final;
|
||||||
|
|
||||||
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
|
: file_(path, params.oflag) {
|
||||||
|
set(new char[params.buffer_size], params.buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
direct_buffered_file(cstring_view path, int oflag)
|
ostream(ostream&& other)
|
||||||
: file_(path, oflag), pos_(0) {}
|
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||||
|
file_(std::move(other.file_)) {
|
||||||
~direct_buffered_file() {
|
other.set(nullptr, 0);
|
||||||
flush();
|
|
||||||
}
|
}
|
||||||
|
~ostream() {
|
||||||
|
flush();
|
||||||
|
delete[] data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
flush();
|
flush();
|
||||||
@@ -380,25 +415,20 @@ class direct_buffered_file {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
template <typename S, typename... Args>
|
||||||
friend void print(direct_buffered_file& f, const S& format_str,
|
void print(const S& format_str, const Args&... args) {
|
||||||
const Args&... args) {
|
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
||||||
// We could avoid double buffering.
|
|
||||||
auto buf = fmt::memory_buffer();
|
|
||||||
fmt::format_to(std::back_inserter(buf), format_str, args...);
|
|
||||||
auto remaining_pos = 0;
|
|
||||||
auto remaining_size = buf.size();
|
|
||||||
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
|
|
||||||
auto size = f.free_capacity();
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
|
|
||||||
f.pos_ += size;
|
|
||||||
f.flush();
|
|
||||||
remaining_pos += size;
|
|
||||||
remaining_size -= size;
|
|
||||||
}
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
|
|
||||||
f.pos_ += static_cast<int>(remaining_size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Opens a file for writing. Supported parameters passed in `params`:
|
||||||
|
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
|
||||||
|
* ``buffer_size=<integer>``: Output buffer size
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
inline ostream output_file(cstring_view path, T... params) {
|
||||||
|
return {path, detail::ostream_params(params...)};
|
||||||
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
|||||||
@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct converter {
|
||||||
|
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||||
private:
|
private:
|
||||||
// Hide all operator<< from std::basic_ostream<Char>.
|
void_t<> operator<<(converter);
|
||||||
void_t<> operator<<(null<>);
|
|
||||||
void_t<> operator<<(const Char*);
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
|
||||||
!std::is_enum<T>::value)>
|
|
||||||
void_t<> operator<<(T);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hide insertion operators for built-in types.
|
||||||
|
template <typename Char, typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
||||||
|
template <typename Char, typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||||
// std::ostream).
|
// std::ostream).
|
||||||
template <typename T, typename Char> class is_streamable {
|
template <typename T, typename Char> class is_streamable {
|
||||||
@@ -103,7 +113,7 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
#endif
|
#endif
|
||||||
output << value;
|
output << value;
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
buf.resize(buf.size());
|
buf.try_resize(buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
@@ -160,7 +170,7 @@ template <typename S, typename... Args,
|
|||||||
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>>>
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint(os, to_string_view(format_str),
|
vprint(os, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
|
|||||||
template <typename Char, typename Context>
|
template <typename Char, typename Context>
|
||||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
basic_format_args<Context> args) {
|
basic_format_args<Context> args) {
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
Context(buffer_appender<Char>(buf), format, args).format();
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using basic_printf_context_t =
|
using basic_printf_context_t =
|
||||||
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||||
|
|
||||||
using printf_context = basic_printf_context_t<char>;
|
using printf_context = basic_printf_context_t<char>;
|
||||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
|||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||||
typename std::decay<Arg>::type>::value)>
|
typename std::decay<Arg>::type>::value)>
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
@@ -182,7 +185,6 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
|||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
return add_space ? L" '{}'" : L"'{}'";
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like {
|
template <typename T> struct is_tuple_like {
|
||||||
@@ -246,9 +248,18 @@ template <typename T, typename Char> struct is_range {
|
|||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename RangeT, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<RangeT, Char,
|
struct formatter<
|
||||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
T, Char,
|
||||||
|
enable_if_t<fmt::is_range<T, Char>::value
|
||||||
|
// Workaround a bug in MSVC 2017 and earlier.
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||||
|
&&
|
||||||
|
(has_formatter<detail::value_type<T>, format_context>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::value_type<T>,
|
||||||
|
format_context>::value)
|
||||||
|
#endif
|
||||||
|
>> {
|
||||||
formatting_range<Char> formatting;
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@@ -257,8 +268,7 @@ struct formatter<RangeT, Char,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(const RangeT& values,
|
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||||
FormatContext& ctx) {
|
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
auto it = values.begin();
|
auto it = values.begin();
|
||||||
|
|||||||
+32
-2
@@ -23,6 +23,36 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
|
|||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
: snprintf_ptr(buf, size, format, precision, value);
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// DEPRECATED! This function exists for ABI compatibility.
|
||||||
|
template <typename Char>
|
||||||
|
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
|
||||||
|
Char>::iterator
|
||||||
|
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||||
|
type_identity_t<Char>>>
|
||||||
|
args) {
|
||||||
|
using iterator = std::back_insert_iterator<buffer<char>>;
|
||||||
|
using context = basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||||
|
type_identity_t<Char>>;
|
||||||
|
auto out = iterator(buf);
|
||||||
|
format_handler<iterator, Char, context> h(out, format_str, args, {});
|
||||||
|
parse_format_string<false>(format_str, h);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template basic_format_context<std::back_insert_iterator<buffer<char>>,
|
||||||
|
char>::iterator
|
||||||
|
vformat_to(buffer<char>&, string_view,
|
||||||
|
basic_format_args<basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<char>>>,
|
||||||
|
type_identity_t<char>>>);
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||||
@@ -44,9 +74,9 @@ template FMT_API char detail::decimal_point_impl(locale_ref);
|
|||||||
|
|
||||||
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
template FMT_API void detail::vformat_to(
|
||||||
detail::buffer<char>&, string_view,
|
detail::buffer<char>&, string_view,
|
||||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
|
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||||
|
|
||||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||||
detail::buffer<char>&);
|
detail::buffer<char>&);
|
||||||
|
|||||||
+11
-6
@@ -62,7 +62,7 @@ using RWResult = int;
|
|||||||
inline unsigned convert_rwcount(std::size_t count) {
|
inline unsigned convert_rwcount(std::size_t count) {
|
||||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||||
}
|
}
|
||||||
#else
|
#elif FMT_USE_FCNTL
|
||||||
// Return type of read and write functions.
|
// Return type of read and write functions.
|
||||||
using RWResult = ssize_t;
|
using RWResult = ssize_t;
|
||||||
|
|
||||||
@@ -124,7 +124,8 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
|||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
utf16_to_utf8 utf8_message;
|
utf16_to_utf8 utf8_message;
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||||
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
|
format_to(buffer_appender<char>(out), "{}: {}", message,
|
||||||
|
utf8_message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -288,12 +289,12 @@ void file::pipe(file& read_end, file& write_end) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffered_file file::fdopen(const char* mode) {
|
buffered_file file::fdopen(const char* mode) {
|
||||||
// Don't retry as fdopen doesn't return EINTR.
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
#if defined(__MINGW32__) && defined(_POSIX_)
|
# if defined(__MINGW32__) && defined(_POSIX_)
|
||||||
FILE* f = ::fdopen(fd_, mode);
|
FILE* f = ::fdopen(fd_, mode);
|
||||||
#else
|
# else
|
||||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
#endif
|
# endif
|
||||||
if (!f)
|
if (!f)
|
||||||
FMT_THROW(
|
FMT_THROW(
|
||||||
system_error(errno, "cannot associate stream with file descriptor"));
|
system_error(errno, "cannot associate stream with file descriptor"));
|
||||||
@@ -313,5 +314,9 @@ long getpagesize() {
|
|||||||
return size;
|
return size;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_API void ostream::grow(size_t) {
|
||||||
|
if (this->size() == this->capacity()) flush();
|
||||||
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
Reference in new issue
Block a user